refactor: server side code to mirror common design
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
This commit is contained in:
@@ -11,10 +11,17 @@ namespace KTXM\PeopleManager\Controllers;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use KTXC\Http\Response\JsonResponse;
|
||||
use KTXC\Http\Response\Response;
|
||||
use KTXC\Http\Response\StreamedNdJsonResponse;
|
||||
use KTXC\SessionIdentity;
|
||||
use KTXC\SessionTenant;
|
||||
use KTXF\Controller\ControllerAbstract;
|
||||
use KTXF\Resource\Selector\SourceSelector;
|
||||
use KTXF\Json\JsonSerializable;
|
||||
use KTXF\Resource\Identifier\CollectionIdentifier;
|
||||
use KTXF\Resource\Identifier\EntityIdentifier;
|
||||
use KTXF\Resource\Identifier\ResourceIdentifier;
|
||||
use KTXF\Resource\Identifier\ResourceIdentifiers;
|
||||
use KTXF\Resource\Identifier\ServiceIdentifier;
|
||||
use KTXF\Routing\Attributes\AuthenticatedRoute;
|
||||
use KTXM\PeopleManager\Manager;
|
||||
use Psr\Log\LoggerInterface;
|
||||
@@ -22,20 +29,21 @@ use Throwable;
|
||||
|
||||
class DefaultController extends ControllerAbstract {
|
||||
|
||||
private const ERR_MISSING_PROVIDER = 'Missing parameter: provider';
|
||||
private const ERR_MISSING_PROVIDER = 'Missing parameter: provider';
|
||||
private const ERR_MISSING_IDENTIFIER = 'Missing parameter: identifier';
|
||||
private const ERR_MISSING_SERVICE = 'Missing parameter: service';
|
||||
private const ERR_MISSING_COLLECTION = 'Missing parameter: collection';
|
||||
private const ERR_MISSING_DATA = 'Missing parameter: data';
|
||||
private const ERR_MISSING_SOURCES = 'Missing parameter: sources';
|
||||
private const ERR_MISSING_IDENTIFIERS = 'Missing parameter: identifiers';
|
||||
private const ERR_MISSING_TARGET = 'Missing parameter: target';
|
||||
private const ERR_MISSING_TARGETS = 'Missing parameter: targets';
|
||||
private const ERR_INVALID_OPERATION = 'Invalid operation: ';
|
||||
private const ERR_INVALID_PROVIDER = 'Invalid parameter: provider must be a string';
|
||||
private const ERR_INVALID_SERVICE = 'Invalid parameter: service must be a string';
|
||||
private const ERR_INVALID_COLLECTION = 'Invalid parameter: collection must be a string';
|
||||
private const ERR_INVALID_IDENTIFIER = 'Invalid parameter: identifier must be a string';
|
||||
private const ERR_INVALID_COLLECTION = 'Invalid parameter: collection must be a string or integer';
|
||||
private const ERR_INVALID_SOURCES = 'Invalid parameter: sources must be an array';
|
||||
private const ERR_INVALID_IDENTIFIERS = 'Invalid parameter: identifiers must be an array';
|
||||
private const ERR_INVALID_TARGET = 'Invalid parameter: target must be an array';
|
||||
private const ERR_INVALID_TARGETS = 'Invalid parameter: targets must be an array';
|
||||
private const ERR_INVALID_DATA = 'Invalid parameter: data must be an array';
|
||||
|
||||
public function __construct(
|
||||
@@ -65,7 +73,7 @@ class DefaultController extends ControllerAbstract {
|
||||
string|null $operation = null,
|
||||
array|null $data = null,
|
||||
string|null $user = null
|
||||
): JsonResponse {
|
||||
): Response {
|
||||
|
||||
// authorize request
|
||||
$tenantId = $this->tenantIdentity->identifier();
|
||||
@@ -74,7 +82,12 @@ class DefaultController extends ControllerAbstract {
|
||||
try {
|
||||
|
||||
if ($operation !== null) {
|
||||
$result = $this->processOperation($tenantId, $userId, $operation, $data ?? [], []);
|
||||
$result = $this->processOperation($tenantId, $userId, $operation, $data ?? [], $version, $transaction);
|
||||
|
||||
if ($result instanceof Response) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
return new JsonResponse([
|
||||
'version' => $version,
|
||||
'transaction' => $transaction,
|
||||
@@ -101,10 +114,11 @@ class DefaultController extends ControllerAbstract {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process a single operation
|
||||
*/
|
||||
private function processOperation(string $tenantId, string $userId, string $operation, array $data): mixed {
|
||||
private function processOperation(string $tenantId, string $userId, string $operation, array $data, int $version = 1, string $transaction = ''): mixed {
|
||||
return match ($operation) {
|
||||
// Provider operations
|
||||
'provider.list' => $this->providerList($tenantId, $userId, $data),
|
||||
@@ -118,7 +132,7 @@ class DefaultController extends ControllerAbstract {
|
||||
'service.create' => $this->serviceCreate($tenantId, $userId, $data),
|
||||
'service.update' => $this->serviceUpdate($tenantId, $userId, $data),
|
||||
'service.delete' => $this->serviceDelete($tenantId, $userId, $data),
|
||||
'service.test' => $this->serviceTest($tenantId, $userId, $data),
|
||||
'service.test' => throw new InvalidArgumentException('Operation not implemented: ' . $operation),
|
||||
|
||||
// Collection operations
|
||||
'collection.list' => $this->collectionList($tenantId, $userId, $data),
|
||||
@@ -129,15 +143,16 @@ class DefaultController extends ControllerAbstract {
|
||||
'collection.delete' => $this->collectionDelete($tenantId, $userId, $data),
|
||||
|
||||
// Entity operations
|
||||
'entity.list' => $this->entityList($tenantId, $userId, $data),
|
||||
'entity.listBulk' => $this->entityListBulk($tenantId, $userId, $data),
|
||||
'entity.listStream' => $this->entityListStream($tenantId, $userId, $data, $version, $transaction),
|
||||
'entity.fetch' => $this->entityFetch($tenantId, $userId, $data),
|
||||
'entity.extant' => $this->entityExtant($tenantId, $userId, $data),
|
||||
'entity.delta' => $this->entityDelta($tenantId, $userId, $data),
|
||||
'entity.create' => $this->entityCreate($tenantId, $userId, $data),
|
||||
'entity.update' => $this->entityUpdate($tenantId, $userId, $data),
|
||||
'entity.delete' => $this->entityDelete($tenantId, $userId, $data),
|
||||
'entity.delta' => $this->entityDelta($tenantId, $userId, $data),
|
||||
'entity.move' => throw new InvalidArgumentException('Operation not implemented: ' . $operation),
|
||||
'entity.copy' => throw new InvalidArgumentException('Operation not implemented: ' . $operation),
|
||||
'entity.move' => $this->entityMove($tenantId, $userId, $data),
|
||||
'entity.copy' => $this->entityCopy($tenantId, $userId, $data),
|
||||
|
||||
default => throw new InvalidArgumentException(self::ERR_INVALID_OPERATION . $operation)
|
||||
};
|
||||
@@ -147,55 +162,66 @@ class DefaultController extends ControllerAbstract {
|
||||
|
||||
private function providerList(string $tenantId, string $userId, array $data): mixed {
|
||||
|
||||
$sources = null;
|
||||
if (isset($data['sources']) && is_array($data['sources'])) {
|
||||
$sources = new SourceSelector();
|
||||
$sources->jsonDeserialize($data['sources']);
|
||||
}
|
||||
if (isset($data['targets'])) {
|
||||
if (!is_array($data['targets'])) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_TARGETS);
|
||||
}
|
||||
|
||||
return $this->manager->providerList($tenantId, $userId, $sources);
|
||||
foreach ($data['targets'] as $target) {
|
||||
if (!is_string($target)) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_TARGETS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->manager->providerList($tenantId, $userId, $data['targets'] ?? []);
|
||||
|
||||
}
|
||||
|
||||
private function providerFetch(string $tenantId, string $userId, array $data): mixed {
|
||||
|
||||
if (!isset($data['identifier'])) {
|
||||
throw new InvalidArgumentException(self::ERR_MISSING_IDENTIFIER);
|
||||
if (!isset($data['target'])) {
|
||||
throw new InvalidArgumentException(self::ERR_MISSING_TARGET);
|
||||
}
|
||||
if (!is_string($data['identifier'])) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_IDENTIFIER);
|
||||
if (!is_string($data['target'])) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_TARGET);
|
||||
}
|
||||
|
||||
return $this->manager->providerFetch($tenantId, $userId, $data['identifier']);
|
||||
return $this->manager->providerFetch($tenantId, $userId, $data['target']);
|
||||
|
||||
}
|
||||
|
||||
private function providerExtant(string $tenantId, string $userId, array $data): mixed {
|
||||
|
||||
if (!isset($data['sources'])) {
|
||||
throw new InvalidArgumentException(self::ERR_MISSING_SOURCES);
|
||||
}
|
||||
if (!is_array($data['sources'])) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_SOURCES);
|
||||
}
|
||||
$sources = new SourceSelector();
|
||||
$sources->jsonDeserialize($data['sources']);
|
||||
if (!isset($data['targets'])) {
|
||||
throw new InvalidArgumentException(self::ERR_MISSING_TARGETS);
|
||||
}
|
||||
|
||||
return $this->manager->providerExtant($tenantId, $userId, $sources);
|
||||
foreach ($data['targets'] as $target) {
|
||||
if (!is_string($target)) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_TARGETS);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->manager->providerExtant($tenantId, $userId, $data['targets']);
|
||||
|
||||
}
|
||||
|
||||
|
||||
// ==================== Service Operations =====================
|
||||
|
||||
private function serviceList(string $tenantId, string $userId, array $data): mixed {
|
||||
|
||||
$sources = null;
|
||||
if (isset($data['sources']) && is_array($data['sources'])) {
|
||||
$sources = new SourceSelector();
|
||||
$sources->jsonDeserialize($data['sources']);
|
||||
$targets = null;
|
||||
if (isset($data['targets']) && is_array($data['targets'])) {
|
||||
$targets = ResourceIdentifiers::fromArray($data['targets']);
|
||||
foreach ($targets as $target) {
|
||||
if (!$target instanceof CollectionIdentifier && !$target instanceof ServiceIdentifier) {
|
||||
throw new InvalidArgumentException('Invalid parameter: targets must contain provider:service, provider:service:collection, or provider:service:collection:entity identifiers');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->manager->serviceList($tenantId, $userId, $sources);
|
||||
return $this->manager->serviceList($tenantId, $userId, $targets);
|
||||
|
||||
}
|
||||
|
||||
@@ -219,16 +245,20 @@ class DefaultController extends ControllerAbstract {
|
||||
|
||||
private function serviceExtant(string $tenantId, string $userId, array $data): mixed {
|
||||
|
||||
if (!isset($data['sources'])) {
|
||||
throw new InvalidArgumentException(self::ERR_MISSING_SOURCES);
|
||||
if (!isset($data['targets'])) {
|
||||
throw new InvalidArgumentException(self::ERR_MISSING_TARGETS);
|
||||
}
|
||||
if (!is_array($data['sources'])) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_SOURCES);
|
||||
if (!is_array($data['targets'])) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_TARGETS);
|
||||
}
|
||||
$targets = ResourceIdentifiers::fromArray($data['targets']);
|
||||
foreach ($targets as $target) {
|
||||
if (!$target instanceof ServiceIdentifier) {
|
||||
throw new InvalidArgumentException('Invalid parameter: targets must contain provider:service identifiers');
|
||||
}
|
||||
}
|
||||
$sources = new SourceSelector();
|
||||
$sources->jsonDeserialize($data['sources']);
|
||||
|
||||
return $this->manager->serviceExtant($tenantId, $userId, $sources);
|
||||
return $this->manager->serviceExtant($tenantId, $userId, $targets);
|
||||
}
|
||||
|
||||
private function serviceCreate(string $tenantId, string $userId, array $data): mixed {
|
||||
@@ -272,13 +302,17 @@ class DefaultController extends ControllerAbstract {
|
||||
if (!is_array($data['data'])) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_DATA);
|
||||
}
|
||||
if (isset($data['delta']) && !is_bool($data['delta'])) {
|
||||
throw new InvalidArgumentException('Invalid parameter: delta must be a boolean');
|
||||
}
|
||||
|
||||
return $this->manager->serviceUpdate(
|
||||
$tenantId,
|
||||
$userId,
|
||||
$data['provider'],
|
||||
$data['identifier'],
|
||||
$data['data']
|
||||
$data['data'],
|
||||
$data['delta'] ?? false,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -304,36 +338,17 @@ class DefaultController extends ControllerAbstract {
|
||||
);
|
||||
}
|
||||
|
||||
private function serviceTest(string $tenantId, string $userId, array $data): mixed {
|
||||
|
||||
if (!isset($data['provider'])) {
|
||||
throw new InvalidArgumentException(self::ERR_MISSING_PROVIDER);
|
||||
}
|
||||
if (!is_string($data['provider'])) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_PROVIDER);
|
||||
}
|
||||
|
||||
if (!isset($data['identifier']) && !isset($data['location']) && !isset($data['identity'])) {
|
||||
throw new InvalidArgumentException('Either a service identifier or location and identity must be provided for service test');
|
||||
}
|
||||
|
||||
return $this->manager->serviceTest(
|
||||
$tenantId,
|
||||
$userId,
|
||||
$data['provider'],
|
||||
$data['identifier'] ?? null,
|
||||
$data['location'] ?? null,
|
||||
$data['identity'] ?? null,
|
||||
);
|
||||
}
|
||||
|
||||
// ==================== Collection Operations ====================
|
||||
|
||||
private function collectionList(string $tenantId, string $userId, array $data): mixed {
|
||||
$sources = null;
|
||||
if (isset($data['sources']) && is_array($data['sources'])) {
|
||||
$sources = new SourceSelector();
|
||||
$sources->jsonDeserialize($data['sources']);
|
||||
$sources = ResourceIdentifiers::fromArray($data['sources']);
|
||||
foreach ($sources as $source) {
|
||||
if (!$source instanceof CollectionIdentifier && !$source instanceof ServiceIdentifier) {
|
||||
throw new InvalidArgumentException('Invalid parameter: sources must contain provider:service, provider:service:collection, or provider:service:collection:entity identifiers');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$filter = $data['filter'] ?? null;
|
||||
@@ -342,47 +357,45 @@ class DefaultController extends ControllerAbstract {
|
||||
return $this->manager->collectionList($tenantId, $userId, $sources, $filter, $sort);
|
||||
}
|
||||
|
||||
private function collectionExtant(string $tenantId, string $userId, array $data): mixed {
|
||||
if (!isset($data['sources'])) {
|
||||
throw new InvalidArgumentException(self::ERR_MISSING_SOURCES);
|
||||
}
|
||||
if (!is_array($data['sources'])) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_SOURCES);
|
||||
}
|
||||
|
||||
$sources = new SourceSelector();
|
||||
$sources->jsonDeserialize($data['sources']);
|
||||
|
||||
return $this->manager->collectionExtant($tenantId, $userId, $sources);
|
||||
}
|
||||
|
||||
private function collectionFetch(string $tenantId, string $userId, array $data): mixed {
|
||||
if (!isset($data['provider'])) {
|
||||
throw new InvalidArgumentException(self::ERR_MISSING_PROVIDER);
|
||||
if (!isset($data['targets'])) {
|
||||
throw new InvalidArgumentException(self::ERR_MISSING_TARGETS);
|
||||
}
|
||||
if (!is_string($data['provider'])) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_PROVIDER);
|
||||
}
|
||||
if (!isset($data['service'])) {
|
||||
throw new InvalidArgumentException(self::ERR_MISSING_SERVICE);
|
||||
}
|
||||
if (!is_string($data['service'])) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_SERVICE);
|
||||
}
|
||||
if (!isset($data['identifier'])) {
|
||||
throw new InvalidArgumentException(self::ERR_MISSING_IDENTIFIER);
|
||||
}
|
||||
if (!is_string($data['identifier']) && !is_int($data['identifier'])) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_COLLECTION);
|
||||
if (!is_array($data['targets'])) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_TARGETS);
|
||||
}
|
||||
|
||||
return $this->manager->collectionFetch(
|
||||
$targetIdentifiers = ResourceIdentifiers::fromArray($data['targets']);
|
||||
foreach ($targetIdentifiers as $targetIdentifier) {
|
||||
if (!$targetIdentifier instanceof CollectionIdentifier) {
|
||||
throw new InvalidArgumentException('Invalid parameter: target must be provider:service:collection');
|
||||
}
|
||||
}
|
||||
|
||||
$list = $this->manager->collectionFetch(
|
||||
$tenantId,
|
||||
$userId,
|
||||
$data['provider'],
|
||||
$data['service'],
|
||||
$data['identifier']
|
||||
$targetIdentifier
|
||||
);
|
||||
return $list;
|
||||
}
|
||||
|
||||
private function collectionExtant(string $tenantId, string $userId, array $data): mixed {
|
||||
if (!isset($data['targets'])) {
|
||||
throw new InvalidArgumentException(self::ERR_MISSING_TARGETS);
|
||||
}
|
||||
if (!is_array($data['targets'])) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_TARGETS);
|
||||
}
|
||||
|
||||
$sources = ResourceIdentifiers::fromArray($data['targets']);
|
||||
foreach ($sources as $source) {
|
||||
if (!$source instanceof CollectionIdentifier) {
|
||||
throw new InvalidArgumentException('Invalid parameter: targets must contain provider:service, provider:service:collection, or provider:service:collection:entity identifiers');
|
||||
}
|
||||
}
|
||||
|
||||
return $this->manager->collectionExtant($tenantId, $userId, $sources);
|
||||
}
|
||||
|
||||
private function collectionCreate(string $tenantId, string $userId, array $data): mixed {
|
||||
@@ -398,8 +411,8 @@ class DefaultController extends ControllerAbstract {
|
||||
if (!is_string($data['service'])) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_SERVICE);
|
||||
}
|
||||
if (isset($data['collection']) && !is_string($data['collection']) && !is_int($data['collection'])) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_COLLECTION);
|
||||
if (isset($data['target']) && !is_string($data['target']) && !is_int($data['target'])) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_TARGET);
|
||||
}
|
||||
if (!isset($data['properties'])) {
|
||||
throw new InvalidArgumentException(self::ERR_MISSING_DATA);
|
||||
@@ -408,34 +421,29 @@ class DefaultController extends ControllerAbstract {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_DATA);
|
||||
}
|
||||
|
||||
if (isset($data['target'])) {
|
||||
$targetIdentifier = ResourceIdentifier::fromString($data['target']);
|
||||
if (!$targetIdentifier instanceof CollectionIdentifier) {
|
||||
throw new InvalidArgumentException('Invalid parameter: target must be provider:service:collection');
|
||||
}
|
||||
}
|
||||
|
||||
return $this->manager->collectionCreate(
|
||||
$tenantId,
|
||||
$userId,
|
||||
$data['provider'],
|
||||
$data['service'],
|
||||
$data['collection'] ?? null,
|
||||
$targetIdentifier ?? null,
|
||||
$data['properties']
|
||||
);
|
||||
}
|
||||
|
||||
private function collectionUpdate(string $tenantId, string $userId, array $data): mixed {
|
||||
if (!isset($data['provider'])) {
|
||||
throw new InvalidArgumentException(self::ERR_MISSING_PROVIDER);
|
||||
if (!isset($data['target'])) {
|
||||
throw new InvalidArgumentException(self::ERR_MISSING_TARGET);
|
||||
}
|
||||
if (!is_string($data['provider'])) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_PROVIDER);
|
||||
}
|
||||
if (!isset($data['service'])) {
|
||||
throw new InvalidArgumentException(self::ERR_MISSING_SERVICE);
|
||||
}
|
||||
if (!is_string($data['service'])) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_SERVICE);
|
||||
}
|
||||
if (!isset($data['identifier'])) {
|
||||
throw new InvalidArgumentException(self::ERR_MISSING_IDENTIFIER);
|
||||
}
|
||||
if (!is_string($data['identifier']) && !is_int($data['identifier'])) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_COLLECTION);
|
||||
if (!is_string($data['target'])) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_TARGET);
|
||||
}
|
||||
if (!isset($data['properties'])) {
|
||||
throw new InvalidArgumentException(self::ERR_MISSING_DATA);
|
||||
@@ -444,49 +452,53 @@ class DefaultController extends ControllerAbstract {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_DATA);
|
||||
}
|
||||
|
||||
$targetIdentifier = ResourceIdentifier::fromString($data['target']);
|
||||
if (!$targetIdentifier instanceof CollectionIdentifier) {
|
||||
throw new InvalidArgumentException('Invalid parameter: target must be provider:service:collection');
|
||||
}
|
||||
|
||||
return $this->manager->collectionUpdate(
|
||||
$tenantId,
|
||||
$userId,
|
||||
$data['provider'],
|
||||
$data['service'],
|
||||
$data['identifier'],
|
||||
$targetIdentifier,
|
||||
$data['properties']
|
||||
);
|
||||
}
|
||||
|
||||
private function collectionDelete(string $tenantId, string $userId, array $data): mixed {
|
||||
if (!isset($data['provider'])) {
|
||||
throw new InvalidArgumentException(self::ERR_MISSING_PROVIDER);
|
||||
if (!isset($data['target'])) {
|
||||
throw new InvalidArgumentException(self::ERR_MISSING_TARGET);
|
||||
}
|
||||
if (!is_string($data['provider'])) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_PROVIDER);
|
||||
}
|
||||
if (!isset($data['service'])) {
|
||||
throw new InvalidArgumentException(self::ERR_MISSING_SERVICE);
|
||||
}
|
||||
if (!is_string($data['service'])) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_SERVICE);
|
||||
}
|
||||
if (!isset($data['identifier'])) {
|
||||
throw new InvalidArgumentException(self::ERR_MISSING_IDENTIFIER);
|
||||
}
|
||||
if (!is_string($data['identifier']) && !is_int($data['identifier'])) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_IDENTIFIER);
|
||||
if (!is_string($data['target'])) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_TARGET);
|
||||
}
|
||||
|
||||
return $this->manager->collectionDelete(
|
||||
$tenantId,
|
||||
$userId,
|
||||
$data['provider'],
|
||||
$data['service'],
|
||||
$data['identifier'],
|
||||
$data['options'] ?? []
|
||||
);
|
||||
$targetIdentifier = ResourceIdentifier::fromString($data['target']);
|
||||
if (!$targetIdentifier instanceof CollectionIdentifier) {
|
||||
throw new InvalidArgumentException('Invalid parameter: target must be provider:service:collection');
|
||||
}
|
||||
|
||||
$result = $this->manager->collectionDelete($tenantId, $userId, $targetIdentifier, $data['options'] ?? [] );
|
||||
|
||||
if (is_bool($result)) {
|
||||
return [
|
||||
'disposition' => 'deleted'
|
||||
];
|
||||
}
|
||||
|
||||
if ($result instanceof JsonSerializable) {
|
||||
return [
|
||||
'disposition' => 'moved',
|
||||
'mutation' => $result
|
||||
];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
// ==================== Entity Operations ====================
|
||||
|
||||
private function entityList(string $tenantId, string $userId, array $data): mixed {
|
||||
private function entityListBulk(string $tenantId, string $userId, array $data): mixed {
|
||||
if (!isset($data['sources'])) {
|
||||
throw new InvalidArgumentException(self::ERR_MISSING_SOURCES);
|
||||
}
|
||||
@@ -494,131 +506,199 @@ class DefaultController extends ControllerAbstract {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_SOURCES);
|
||||
}
|
||||
|
||||
$sources = new SourceSelector();
|
||||
$sources->jsonDeserialize($data['sources']);
|
||||
$sources = ResourceIdentifiers::fromArray($data['sources']);
|
||||
foreach ($sources as $source) {
|
||||
if (!$source instanceof EntityIdentifier) {
|
||||
throw new InvalidArgumentException('Invalid parameter: sources must contain provider:service:collection:entity identifiers');
|
||||
}
|
||||
}
|
||||
|
||||
$filter = $data['filter'] ?? null;
|
||||
$sort = $data['sort'] ?? null;
|
||||
$range = $data['range'] ?? null;
|
||||
|
||||
return $this->manager->entityList($tenantId, $userId, $sources, $filter, $sort, $range);
|
||||
return $this->manager->entityListBulk($tenantId, $userId, $sources, $filter, $sort, $range);
|
||||
|
||||
}
|
||||
|
||||
private function entityFetch(string $tenantId, string $userId, array $data): mixed {
|
||||
if (!isset($data['provider'])) {
|
||||
throw new InvalidArgumentException(self::ERR_MISSING_PROVIDER);
|
||||
private function entityListStream(string $tenantId, string $userId, array $data, int $version, string $transaction): StreamedNdJsonResponse {
|
||||
if (!isset($data['sources'])) {
|
||||
throw new InvalidArgumentException(self::ERR_MISSING_SOURCES);
|
||||
}
|
||||
if (!is_string($data['provider'])) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_PROVIDER);
|
||||
}
|
||||
if (!isset($data['service'])) {
|
||||
throw new InvalidArgumentException(self::ERR_MISSING_SERVICE);
|
||||
}
|
||||
if (!is_string($data['service'])) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_SERVICE);
|
||||
}
|
||||
if (!isset($data['collection'])) {
|
||||
throw new InvalidArgumentException(self::ERR_MISSING_COLLECTION);
|
||||
}
|
||||
if (!is_string($data['collection']) && !is_int($data['collection'])) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_COLLECTION);
|
||||
}
|
||||
if (!isset($data['identifiers'])) {
|
||||
throw new InvalidArgumentException(self::ERR_MISSING_IDENTIFIERS);
|
||||
}
|
||||
if (!is_array($data['identifiers'])) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_IDENTIFIERS);
|
||||
if (!is_array($data['sources'])) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_SOURCES);
|
||||
}
|
||||
|
||||
return $this->manager->entityFetch(
|
||||
$sources = ResourceIdentifiers::fromArray($data['sources']);
|
||||
foreach ($sources as $source) {
|
||||
if (!$source instanceof ServiceIdentifier && !$source instanceof CollectionIdentifier) {
|
||||
throw new InvalidArgumentException('Invalid parameter: sources must contain provider:service or provider:service:collection identifiers');
|
||||
}
|
||||
}
|
||||
|
||||
$filter = $data['filter'] ?? null;
|
||||
$sort = $data['sort'] ?? null;
|
||||
$range = $data['range'] ?? null;
|
||||
|
||||
$entityGenerator = $this->manager->entityListStream($tenantId, $userId, $sources, $filter, $sort, $range);
|
||||
$logger = $this->logger;
|
||||
|
||||
$responseGenerator = (function () use ($entityGenerator, $version, $transaction, $logger): \Generator {
|
||||
yield ['type' => 'control', 'status' => 'start', 'version' => $version, 'transaction' => $transaction];
|
||||
|
||||
$total = 0;
|
||||
try {
|
||||
foreach ($entityGenerator as $entity) {
|
||||
if (!$entity instanceof JsonSerializable) {
|
||||
continue;
|
||||
}
|
||||
yield [
|
||||
'type' => 'data',
|
||||
'data' => $entity->jsonSerialize()
|
||||
];
|
||||
$total++;
|
||||
}
|
||||
} catch (\Throwable $t) {
|
||||
$logger->error('Error streaming entities', ['exception' => $t]);
|
||||
yield ['type' => 'error', 'message' => $t->getMessage()];
|
||||
return;
|
||||
}
|
||||
|
||||
yield ['type' => 'control', 'status' => 'end', 'total' => $total];
|
||||
})();
|
||||
|
||||
return new StreamedNdJsonResponse($responseGenerator, 1, 200, ['Content-Type' => 'application/json']);
|
||||
}
|
||||
|
||||
private function entityFetch(string $tenantId, string $userId, array $data): mixed {
|
||||
if (!isset($data['targets'])) {
|
||||
throw new InvalidArgumentException(self::ERR_MISSING_TARGETS);
|
||||
}
|
||||
if (!is_array($data['targets'])) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_TARGETS);
|
||||
}
|
||||
|
||||
$targets = ResourceIdentifiers::fromArray($data['targets']);
|
||||
foreach ($targets as $target) {
|
||||
if (!$target instanceof EntityIdentifier) {
|
||||
throw new InvalidArgumentException('Invalid parameter: targets must contain provider:service:collection:entity identifiers');
|
||||
}
|
||||
}
|
||||
|
||||
return $this->manager->entityFetchBulk(
|
||||
$tenantId,
|
||||
$userId,
|
||||
$data['provider'],
|
||||
$data['service'],
|
||||
$data['collection'],
|
||||
$data['identifiers']
|
||||
...$targets->all()
|
||||
);
|
||||
}
|
||||
|
||||
private function entityExtant(string $tenantId, string $userId, array $data): mixed {
|
||||
if (!isset($data['sources'])) {
|
||||
throw new InvalidArgumentException(self::ERR_MISSING_SOURCES);
|
||||
if (!isset($data['targets'])) {
|
||||
throw new InvalidArgumentException(self::ERR_MISSING_TARGETS);
|
||||
}
|
||||
if (!is_array($data['sources'])) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_SOURCES);
|
||||
if (!is_array($data['targets'])) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_TARGETS);
|
||||
}
|
||||
|
||||
$sources = new SourceSelector();
|
||||
$sources->jsonDeserialize($data['sources']);
|
||||
$targets = ResourceIdentifiers::fromArray($data['targets']);
|
||||
foreach ($targets as $target) {
|
||||
if (!$target instanceof CollectionIdentifier && !$target instanceof EntityIdentifier) {
|
||||
throw new InvalidArgumentException('Invalid parameter: targets must contain provider:service:collection or provider:service:collection:entity identifiers');
|
||||
}
|
||||
}
|
||||
|
||||
return $this->manager->entityExtant($tenantId, $userId, $sources);
|
||||
return $this->manager->entityExtant($tenantId, $userId, $targets);
|
||||
}
|
||||
|
||||
private function entityCreate(string $tenantId, string $userId, array $data = []): mixed {
|
||||
|
||||
if (!isset($data['provider']) || !is_string($data['provider'])) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_PROVIDER);
|
||||
}
|
||||
if (!isset($data['service']) || !is_string($data['service'])) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_SERVICE);
|
||||
}
|
||||
if (!isset($data['collection'])) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_COLLECTION);
|
||||
}
|
||||
$properties = $data['properties'] ?? $data['data'] ?? null;
|
||||
if (!is_array($properties)) {
|
||||
throw new InvalidArgumentException('Invalid parameter: properties must be an array');
|
||||
}
|
||||
$options = $data['options'] ?? [];
|
||||
|
||||
return $this->manager->entityCreate($tenantId, $userId, $data['provider'], $data['service'], $data['collection'], $properties, $options);
|
||||
|
||||
}
|
||||
|
||||
private function entityUpdate(string $tenantId, string $userId, array $data = []): mixed {
|
||||
|
||||
if (!isset($data['provider']) || !is_string($data['provider'])) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_PROVIDER);
|
||||
}
|
||||
if (!isset($data['service']) || !is_string($data['service'])) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_SERVICE);
|
||||
}
|
||||
if (!isset($data['collection'])) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_COLLECTION);
|
||||
}
|
||||
if (!isset($data['identifier'])) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_IDENTIFIER);
|
||||
}
|
||||
$properties = $data['properties'] ?? $data['data'] ?? null;
|
||||
if (!is_array($properties)) {
|
||||
throw new InvalidArgumentException('Invalid parameter: properties must be an array');
|
||||
}
|
||||
|
||||
return $this->manager->entityUpdate($tenantId, $userId, $data['provider'], $data['service'], $data['collection'], $data['identifier'], $properties);
|
||||
|
||||
}
|
||||
|
||||
private function entityDelete(string $tenantId, string $userId, array $data = []): mixed {
|
||||
|
||||
if (!isset($data['provider']) || !is_string($data['provider'])) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_PROVIDER);
|
||||
}
|
||||
if (!isset($data['service']) || !is_string($data['service'])) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_SERVICE);
|
||||
}
|
||||
if (!isset($data['collection'])) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_COLLECTION);
|
||||
}
|
||||
if (!isset($data['identifier'])) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_IDENTIFIER);
|
||||
}
|
||||
|
||||
return $this->manager->entityDelete($tenantId, $userId, $data['provider'], $data['service'], $data['collection'], $data['identifier']);
|
||||
|
||||
}
|
||||
|
||||
private function entityDelta(string $tenantId, string $userId, array $data): mixed {
|
||||
if (!isset($data['targets'])) {
|
||||
throw new InvalidArgumentException(self::ERR_MISSING_TARGETS);
|
||||
}
|
||||
if (!is_array($data['targets'])) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_TARGETS);
|
||||
}
|
||||
|
||||
$targets = ResourceIdentifiers::fromArray($data['targets']);
|
||||
foreach ($targets as $target) {
|
||||
if (!$target instanceof CollectionIdentifier && !$target instanceof EntityIdentifier) {
|
||||
throw new InvalidArgumentException('Invalid parameter: targets must contain provider:service:collection or provider:service:collection:signature identifiers');
|
||||
}
|
||||
}
|
||||
|
||||
return $this->manager->entityDelta($tenantId, $userId, $targets);
|
||||
}
|
||||
|
||||
private function entityCreate(string $tenantId, string $userId, array $data = []): mixed {
|
||||
if (!isset($data['target'])) {
|
||||
throw new InvalidArgumentException(self::ERR_MISSING_TARGET);
|
||||
}
|
||||
if (!is_string($data['target'])) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_TARGET);
|
||||
}
|
||||
if (!isset($data['properties'])) {
|
||||
throw new InvalidArgumentException(self::ERR_MISSING_DATA);
|
||||
}
|
||||
if (!is_array($data['properties'])) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_DATA);
|
||||
}
|
||||
|
||||
$target = ResourceIdentifier::fromString($data['target']);
|
||||
if (!$target instanceof CollectionIdentifier) {
|
||||
throw new InvalidArgumentException('Invalid parameter: target must be provider:service:collection');
|
||||
}
|
||||
|
||||
$options = $data['options'] ?? [];
|
||||
|
||||
return $this->manager->entityCreate($tenantId, $userId, $target, $data['properties'], $options);
|
||||
}
|
||||
|
||||
private function entityUpdate(string $tenantId, string $userId, array $data = []): mixed {
|
||||
if (!isset($data['target'])) {
|
||||
throw new InvalidArgumentException(self::ERR_MISSING_TARGET);
|
||||
}
|
||||
if (!is_string($data['target'])) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_TARGET);
|
||||
}
|
||||
if (!isset($data['properties'])) {
|
||||
throw new InvalidArgumentException(self::ERR_MISSING_DATA);
|
||||
}
|
||||
if (!is_array($data['properties'])) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_DATA);
|
||||
}
|
||||
|
||||
$target = ResourceIdentifier::fromString($data['target']);
|
||||
if (!$target instanceof EntityIdentifier) {
|
||||
throw new InvalidArgumentException('Invalid parameter: target must be provider:service:collection:entity');
|
||||
}
|
||||
|
||||
return $this->manager->entityModify($tenantId, $userId, $target, $data['properties']);
|
||||
}
|
||||
|
||||
private function entityDelete(string $tenantId, string $userId, array $data): mixed {
|
||||
if (!isset($data['targets'])) {
|
||||
throw new InvalidArgumentException(self::ERR_MISSING_TARGETS);
|
||||
}
|
||||
if (!is_array($data['targets'])) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_TARGETS);
|
||||
}
|
||||
|
||||
$targets = ResourceIdentifiers::fromArray($data['targets']);
|
||||
foreach ($targets as $target) {
|
||||
if (!$target instanceof EntityIdentifier) {
|
||||
throw new InvalidArgumentException('Invalid parameter: targets must contain provider:service:collection:entity identifiers');
|
||||
}
|
||||
}
|
||||
|
||||
return $this->manager->entityDelete($tenantId, $userId, ...$targets->all());
|
||||
}
|
||||
|
||||
private function entityMove(string $tenantId, string $userId, array $data): mixed {
|
||||
if (!isset($data['target'])) {
|
||||
throw new InvalidArgumentException(self::ERR_MISSING_TARGET);
|
||||
}
|
||||
if (!is_string($data['target'])) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_TARGET);
|
||||
}
|
||||
if (!isset($data['sources'])) {
|
||||
throw new InvalidArgumentException(self::ERR_MISSING_SOURCES);
|
||||
}
|
||||
@@ -626,10 +706,48 @@ class DefaultController extends ControllerAbstract {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_SOURCES);
|
||||
}
|
||||
|
||||
$sources = new SourceSelector();
|
||||
$sources->jsonDeserialize($data['sources']);
|
||||
$target = ResourceIdentifier::fromString($data['target']);
|
||||
if (!$target instanceof CollectionIdentifier) {
|
||||
throw new InvalidArgumentException('Invalid parameter: target must be provider:service:collection');
|
||||
}
|
||||
|
||||
return $this->manager->entityDelta($tenantId, $userId, $sources);
|
||||
$sources = ResourceIdentifiers::fromArray($data['sources']);
|
||||
foreach ($sources as $source) {
|
||||
if (!$source instanceof EntityIdentifier) {
|
||||
throw new InvalidArgumentException('Invalid parameter: sources must contain provider:service:collection:entity identifiers');
|
||||
}
|
||||
}
|
||||
|
||||
return $this->manager->entityMove($tenantId, $userId, $target, ...$sources->all());
|
||||
}
|
||||
|
||||
private function entityCopy(string $tenantId, string $userId, array $data): mixed {
|
||||
if (!isset($data['target'])) {
|
||||
throw new InvalidArgumentException(self::ERR_MISSING_TARGET);
|
||||
}
|
||||
if (!is_string($data['target'])) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_TARGET);
|
||||
}
|
||||
if (!isset($data['sources'])) {
|
||||
throw new InvalidArgumentException(self::ERR_MISSING_SOURCES);
|
||||
}
|
||||
if (!is_array($data['sources'])) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_SOURCES);
|
||||
}
|
||||
|
||||
$target = ResourceIdentifier::fromString($data['target']);
|
||||
if (!$target instanceof CollectionIdentifier) {
|
||||
throw new InvalidArgumentException('Invalid parameter: target must be provider:service:collection');
|
||||
}
|
||||
|
||||
$sources = ResourceIdentifiers::fromArray($data['sources']);
|
||||
foreach ($sources as $source) {
|
||||
if (!$source instanceof EntityIdentifier) {
|
||||
throw new InvalidArgumentException('Invalid parameter: sources must contain provider:service:collection:entity identifiers');
|
||||
}
|
||||
}
|
||||
|
||||
return $this->manager->entityCopy($tenantId, $userId, $target, ...$sources->all());
|
||||
}
|
||||
|
||||
}
|
||||
+619
-338
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user