chore: code cleanup
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
This commit is contained in:
@@ -14,8 +14,6 @@ use KTXC\Http\Response\JsonResponse;
|
||||
use KTXC\SessionIdentity;
|
||||
use KTXC\SessionTenant;
|
||||
use KTXF\Controller\ControllerAbstract;
|
||||
use KTXF\Mail\Entity\Message;
|
||||
use KTXF\Mail\Queue\SendOptions;
|
||||
use KTXF\Resource\Selector\SourceSelector;
|
||||
use KTXF\Routing\Attributes\AuthenticatedRoute;
|
||||
use KTXM\MailManager\Manager;
|
||||
@@ -30,7 +28,6 @@ use Throwable;
|
||||
*/
|
||||
class DefaultController extends ControllerAbstract {
|
||||
|
||||
// Error message constants
|
||||
private const ERR_MISSING_PROVIDER = 'Missing parameter: provider';
|
||||
private const ERR_MISSING_IDENTIFIER = 'Missing parameter: identifier';
|
||||
private const ERR_MISSING_SERVICE = 'Missing parameter: service';
|
||||
@@ -38,7 +35,7 @@ class DefaultController extends ControllerAbstract {
|
||||
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_MESSAGE = 'Missing parameter: message';
|
||||
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_IDENTIFIER = 'Invalid parameter: identifier must be a string';
|
||||
@@ -46,7 +43,6 @@ class DefaultController extends ControllerAbstract {
|
||||
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_DATA = 'Invalid parameter: data must be an array';
|
||||
private const ERR_INVALID_MESSAGE = 'Invalid parameter: message must be an array';
|
||||
|
||||
public function __construct(
|
||||
private readonly SessionTenant $tenantIdentity,
|
||||
@@ -59,23 +55,21 @@ class DefaultController extends ControllerAbstract {
|
||||
* Main API endpoint for mail operations
|
||||
*
|
||||
* Single operation:
|
||||
* { "version": 1, "transaction": "tx-1", "operation": "message.send", "data": {...} }
|
||||
*
|
||||
* Batch operations:
|
||||
* { "version": 1, "transaction": "tx-1", "operations": [
|
||||
* {"id": "op1", "operation": "message.send", "data": {...}},
|
||||
* {"id": "op2", "operation": "message.destroy", "data": {"collection": "#op1.draftId"}}
|
||||
* ]}
|
||||
* {
|
||||
* "version": 1,
|
||||
* "transaction": "tx-1",
|
||||
* "operation": "entity.create",
|
||||
* "data": {...}
|
||||
* }
|
||||
*
|
||||
* @return JsonResponse
|
||||
*/
|
||||
*/
|
||||
#[AuthenticatedRoute('/v1', name: 'mail.manager.v1', methods: ['POST'])]
|
||||
public function index(
|
||||
int $version,
|
||||
string $transaction,
|
||||
string|null $operation = null,
|
||||
array|null $data = null,
|
||||
array|null $operations = null,
|
||||
int $version,
|
||||
string $transaction,
|
||||
string|null $operation = null,
|
||||
array|null $data = null,
|
||||
string|null $user = null
|
||||
): JsonResponse {
|
||||
|
||||
@@ -84,7 +78,7 @@ class DefaultController extends ControllerAbstract {
|
||||
$userId = $this->userIdentity->identifier();
|
||||
|
||||
try {
|
||||
// Single operation mode
|
||||
|
||||
if ($operation !== null) {
|
||||
$result = $this->processOperation($tenantId, $userId, $operation, $data ?? [], []);
|
||||
return new JsonResponse([
|
||||
@@ -96,21 +90,10 @@ class DefaultController extends ControllerAbstract {
|
||||
], JsonResponse::HTTP_OK);
|
||||
}
|
||||
|
||||
// Batch operations mode
|
||||
if ($operations !== null && is_array($operations)) {
|
||||
$results = $this->processBatch($tenantId, $userId, $operations);
|
||||
return new JsonResponse([
|
||||
'version' => $version,
|
||||
'transaction' => $transaction,
|
||||
'status' => 'success',
|
||||
'operations' => $results
|
||||
], JsonResponse::HTTP_OK);
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException('Either operation or operations must be provided');
|
||||
throw new InvalidArgumentException('Operation must be provided');
|
||||
|
||||
} catch (Throwable $t) {
|
||||
$this->logger->error('Error processing mail manager request', ['exception' => $t]);
|
||||
$this->logger->error('Error processing request', ['exception' => $t]);
|
||||
return new JsonResponse([
|
||||
'version' => $version,
|
||||
'transaction' => $transaction,
|
||||
@@ -124,105 +107,10 @@ class DefaultController extends ControllerAbstract {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process batch operations with result references
|
||||
*/
|
||||
private function processBatch(string $tenantId, string $userId, array $operations): array {
|
||||
$results = [];
|
||||
$resultMap = []; // Store results by operation ID for references
|
||||
|
||||
foreach ($operations as $index => $op) {
|
||||
$opId = $op['id'] ?? "op{$index}";
|
||||
$operation = $op['operation'] ?? null;
|
||||
$data = $op['data'] ?? [];
|
||||
|
||||
if ($operation === null) {
|
||||
$results[] = [
|
||||
'id' => $opId,
|
||||
'status' => 'error',
|
||||
'data' => ['message' => 'Missing operation name']
|
||||
];
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
// Resolve result references in data (e.g., "#op1.id")
|
||||
$data = $this->resolveReferences($data, $resultMap);
|
||||
|
||||
$result = $this->processOperation($tenantId, $userId, $operation, $data, $resultMap);
|
||||
|
||||
$results[] = [
|
||||
'id' => $opId,
|
||||
'operation' => $operation,
|
||||
'status' => 'success',
|
||||
'data' => $result
|
||||
];
|
||||
|
||||
// Store result for future references
|
||||
$resultMap[$opId] = $result;
|
||||
|
||||
} catch (Throwable $t) {
|
||||
$this->logger->warning('Batch operation failed', [
|
||||
'operation' => $operation,
|
||||
'opId' => $opId,
|
||||
'error' => $t->getMessage()
|
||||
]);
|
||||
|
||||
$results[] = [
|
||||
'id' => $opId,
|
||||
'operation' => $operation,
|
||||
'status' => 'error',
|
||||
'data' => [
|
||||
'code' => $t->getCode(),
|
||||
'message' => $t->getMessage()
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve result references in operation data
|
||||
*
|
||||
* Transforms "#op1.id" into the actual value from previous operation results
|
||||
*/
|
||||
private function resolveReferences(mixed $data, array $resultMap): mixed {
|
||||
if (is_string($data) && str_starts_with($data, '#')) {
|
||||
// Parse reference like "#op1.id" or "#op1.collection.id"
|
||||
$parts = explode('.', substr($data, 1));
|
||||
$opId = array_shift($parts);
|
||||
|
||||
if (!isset($resultMap[$opId])) {
|
||||
throw new InvalidArgumentException("Reference to undefined operation: #{$opId}");
|
||||
}
|
||||
|
||||
$value = $resultMap[$opId];
|
||||
foreach ($parts as $key) {
|
||||
if (is_array($value) && isset($value[$key])) {
|
||||
$value = $value[$key];
|
||||
} elseif (is_object($value) && isset($value->$key)) {
|
||||
$value = $value->$key;
|
||||
} else {
|
||||
throw new InvalidArgumentException("Invalid reference path: {$data}");
|
||||
}
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
if (is_array($data)) {
|
||||
return array_map(fn($item) => $this->resolveReferences($item, $resultMap), $data);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a single operation
|
||||
*/
|
||||
private function processOperation(string $tenantId, string $userId, string $operation, array $data, array $resultMap): mixed {
|
||||
private function processOperation(string $tenantId, string $userId, string $operation, array $data): mixed {
|
||||
return match ($operation) {
|
||||
// Provider operations
|
||||
'provider.list' => $this->providerList($tenantId, $userId, $data),
|
||||
@@ -261,7 +149,7 @@ class DefaultController extends ControllerAbstract {
|
||||
'entity.copy' => throw new InvalidArgumentException('Operation not implemented: ' . $operation),
|
||||
'entity.transmit' => $this->entityTransmit($tenantId, $userId, $data),
|
||||
|
||||
default => throw new InvalidArgumentException('Unknown operation: ' . $operation)
|
||||
default => throw new InvalidArgumentException(self::ERR_INVALID_OPERATION . $operation)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -279,6 +167,18 @@ class DefaultController extends ControllerAbstract {
|
||||
|
||||
}
|
||||
|
||||
private function providerFetch(string $tenantId, string $userId, array $data): mixed {
|
||||
|
||||
if (!isset($data['identifier'])) {
|
||||
throw new InvalidArgumentException(self::ERR_MISSING_IDENTIFIER);
|
||||
}
|
||||
if (!is_string($data['identifier'])) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_IDENTIFIER);
|
||||
}
|
||||
|
||||
return $this->mailManager->providerFetch($tenantId, $userId, $data['identifier']);
|
||||
}
|
||||
|
||||
private function providerExtant(string $tenantId, string $userId, array $data): mixed {
|
||||
|
||||
if (!isset($data['sources'])) {
|
||||
@@ -294,18 +194,6 @@ class DefaultController extends ControllerAbstract {
|
||||
|
||||
}
|
||||
|
||||
private function providerFetch(string $tenantId, string $userId, array $data): mixed {
|
||||
|
||||
if (!isset($data['identifier'])) {
|
||||
throw new InvalidArgumentException(self::ERR_MISSING_IDENTIFIER);
|
||||
}
|
||||
if (!is_string($data['identifier'])) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_IDENTIFIER);
|
||||
}
|
||||
|
||||
return $this->mailManager->providerFetch($tenantId, $userId, $data['identifier']);
|
||||
}
|
||||
|
||||
// ==================== Service Operations =====================
|
||||
|
||||
private function serviceList(string $tenantId, string $userId, array $data): mixed {
|
||||
@@ -320,20 +208,6 @@ 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 (!is_array($data['sources'])) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_SOURCES);
|
||||
}
|
||||
$sources = new SourceSelector();
|
||||
$sources->jsonDeserialize($data['sources']);
|
||||
|
||||
return $this->mailManager->serviceExtant($tenantId, $userId, $sources);
|
||||
}
|
||||
|
||||
private function serviceFetch(string $tenantId, string $userId, array $data): mixed {
|
||||
|
||||
if (!isset($data['provider'])) {
|
||||
@@ -352,41 +226,18 @@ class DefaultController extends ControllerAbstract {
|
||||
return $this->mailManager->serviceFetch($tenantId, $userId, $data['provider'], $data['identifier']);
|
||||
}
|
||||
|
||||
private function serviceDiscover(string $tenantId, string $userId, array $data): mixed {
|
||||
|
||||
if (!isset($data['identity']) || empty($data['identity']) || !is_string($data['identity'])) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_DATA);
|
||||
}
|
||||
|
||||
$provider = $data['provider'] ?? null;
|
||||
$identity = $data['identity'];
|
||||
$location = $data['location'] ?? null;
|
||||
$secret = $data['secret'] ?? null;
|
||||
private function serviceExtant(string $tenantId, string $userId, array $data): mixed {
|
||||
|
||||
return $this->mailManager->serviceDiscover($tenantId, $userId, $provider, $identity, $location, $secret);
|
||||
}
|
||||
|
||||
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->mailManager->serviceTest(
|
||||
$tenantId,
|
||||
$userId,
|
||||
$data['provider'],
|
||||
$data['identifier'] ?? null,
|
||||
$data['location'] ?? null,
|
||||
$data['identity'] ?? null,
|
||||
);
|
||||
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->mailManager->serviceExtant($tenantId, $userId, $sources);
|
||||
}
|
||||
|
||||
private function serviceCreate(string $tenantId, string $userId, array $data): mixed {
|
||||
@@ -462,6 +313,43 @@ 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->mailManager->serviceTest(
|
||||
$tenantId,
|
||||
$userId,
|
||||
$data['provider'],
|
||||
$data['identifier'] ?? null,
|
||||
$data['location'] ?? null,
|
||||
$data['identity'] ?? null,
|
||||
);
|
||||
}
|
||||
|
||||
private function serviceDiscover(string $tenantId, string $userId, array $data): mixed {
|
||||
|
||||
if (!isset($data['identity']) || empty($data['identity']) || !is_string($data['identity'])) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_DATA);
|
||||
}
|
||||
|
||||
$provider = $data['provider'] ?? null;
|
||||
$identity = $data['identity'];
|
||||
$location = $data['location'] ?? null;
|
||||
$secret = $data['secret'] ?? null;
|
||||
|
||||
return $this->mailManager->serviceDiscover($tenantId, $userId, $provider, $identity, $location, $secret);
|
||||
}
|
||||
|
||||
// ==================== Collection Operations ====================
|
||||
|
||||
private function collectionList(string $tenantId, string $userId, array $data): mixed {
|
||||
@@ -640,34 +528,6 @@ class DefaultController extends ControllerAbstract {
|
||||
|
||||
}
|
||||
|
||||
private function entityDelta(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->mailManager->entityDelta($tenantId, $userId, $sources);
|
||||
}
|
||||
|
||||
private function entityExtant(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->mailManager->entityExtant($tenantId, $userId, $sources);
|
||||
}
|
||||
|
||||
private function entityFetch(string $tenantId, string $userId, array $data): mixed {
|
||||
if (!isset($data['provider'])) {
|
||||
throw new InvalidArgumentException(self::ERR_MISSING_PROVIDER);
|
||||
@@ -704,6 +564,34 @@ class DefaultController extends ControllerAbstract {
|
||||
);
|
||||
}
|
||||
|
||||
private function entityExtant(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->mailManager->entityExtant($tenantId, $userId, $sources);
|
||||
}
|
||||
|
||||
private function entityDelta(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->mailManager->entityDelta($tenantId, $userId, $sources);
|
||||
}
|
||||
|
||||
private function entityTransmit(string $tenantId, string $userId, array $data): mixed {
|
||||
if (!isset($data['provider'])) {
|
||||
throw new InvalidArgumentException(self::ERR_MISSING_PROVIDER);
|
||||
|
||||
Reference in New Issue
Block a user