feat: Atomically read, mutate, and replace a composition snapshot.
Signed-off-by: Sebastian <krupinski01@gmail.com>
This commit is contained in:
+148
-94
@@ -48,9 +48,12 @@ class CompositionManager {
|
||||
'attachments' => [],
|
||||
];
|
||||
|
||||
$this->compositionStore->compositionSave($tenantId, $userId, $identifier, $snapshot);
|
||||
// Construct the snapshot mutation that will run while the composition is locked.
|
||||
$saveComposition = function () use ($tenantId, $userId, $identifier, $action, $source, $snapshot): array {
|
||||
if ($action !== 'forward') {
|
||||
return $snapshot;
|
||||
}
|
||||
|
||||
if ($action === 'forward') {
|
||||
$sourceIndentifier = ResourceIdentifier::fromString($source);
|
||||
if ($sourceIndentifier === null) {
|
||||
throw new InvalidArgumentException('Invalid source identifier');
|
||||
@@ -96,46 +99,63 @@ class CompositionManager {
|
||||
$meta['size'] = $attachment->getSize() ?? $meta['size'];
|
||||
$snapshot['attachments'][$meta['identifier']] = $meta;
|
||||
}
|
||||
// update the snapshot with the staged attachments
|
||||
$this->compositionStore->compositionSave($tenantId, $userId, $identifier, $snapshot);
|
||||
}
|
||||
|
||||
return $snapshot;
|
||||
};
|
||||
|
||||
// Execute the mutation and atomically persist the snapshot it returns.
|
||||
$snapshot = $this->compositionStore->compositionSave(
|
||||
$tenantId,
|
||||
$userId,
|
||||
$identifier,
|
||||
$saveComposition,
|
||||
) ?? $snapshot;
|
||||
|
||||
return $snapshot;
|
||||
}
|
||||
|
||||
public function patch(string $tenantId, string $userId, string $identifier, array $data): array {
|
||||
$composed = $this->compositionStore->compositionFetch($tenantId, $userId, $identifier);
|
||||
$result = [];
|
||||
// Construct the snapshot mutation that will run while the composition is locked.
|
||||
$patchComposition = function (?array $composed) use ($data, $identifier, &$result): ?array {
|
||||
if ($composed === null) {
|
||||
$result['disposition'] = 'error';
|
||||
$result['error'] = [
|
||||
'type' => 'composition_not_found',
|
||||
'message' => 'Composition not found',
|
||||
];
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($composed === null) {
|
||||
$result['disposition'] = 'error';
|
||||
$result['error'] = [
|
||||
'type' => 'composition_not_found',
|
||||
'message' => 'Composition not found',
|
||||
if (!isset($data['revision']) || !is_int($data['revision']) || $data['revision'] < $composed['revision']) {
|
||||
$result['disposition'] = 'error';
|
||||
$result['error'] = [
|
||||
'type' => 'revision_mismatch',
|
||||
'message' => 'Revision mismatch',
|
||||
'currentRevision' => $composed['revision'],
|
||||
];
|
||||
return null;
|
||||
}
|
||||
|
||||
$composed['revision'] = $data['revision'];
|
||||
$composed['sender'] = $data['sender'] ?? [];
|
||||
$composed['message'] = $data['message'] ?? [];
|
||||
$result = [
|
||||
'identifier' => $identifier,
|
||||
'disposition' => 'patched',
|
||||
];
|
||||
return $result;
|
||||
}
|
||||
|
||||
if (!isset($data['revision']) || !is_int($data['revision']) || $data['revision'] < $composed['revision']) {
|
||||
$result['disposition'] = 'error';
|
||||
$result['error'] = [
|
||||
'type' => 'revision_mismatch',
|
||||
'message' => 'Revision mismatch',
|
||||
'currentRevision' => $composed['revision'],
|
||||
];
|
||||
return $result;
|
||||
}
|
||||
return $composed;
|
||||
};
|
||||
|
||||
// Apply the patch data to the composed message
|
||||
$composed['revision'] = $data['revision'];
|
||||
$composed['sender'] = $data['sender'] ?? [];
|
||||
$composed['message'] = $data['message'] ?? [];
|
||||
// Execute the mutation and atomically persist the snapshot it returns.
|
||||
$this->compositionStore->compositionSave(
|
||||
$tenantId,
|
||||
$userId,
|
||||
$identifier,
|
||||
$patchComposition,
|
||||
);
|
||||
|
||||
$this->compositionStore->compositionSave($tenantId, $userId, $identifier, $composed);
|
||||
|
||||
$result = [
|
||||
'identifier' => $identifier,
|
||||
'disposition' => 'patched',
|
||||
];
|
||||
return $result;
|
||||
}
|
||||
|
||||
@@ -147,22 +167,35 @@ class CompositionManager {
|
||||
}
|
||||
|
||||
public function send(string $tenantId, string $userId, string $identifier, array $sender, array $message, array $attachments): array {
|
||||
$composed = $this->compositionStore->compositionFetch($tenantId, $userId, $identifier);
|
||||
// Construct the snapshot mutation that will run while the composition is locked.
|
||||
$saveComposition = function (?array $composed) use ($sender, $message): ?array {
|
||||
if ($composed === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$composed['sender'] = $sender;
|
||||
$composed['message'] = $message;
|
||||
return $composed;
|
||||
};
|
||||
|
||||
// Execute the mutation and atomically persist the snapshot it returns.
|
||||
$composed = $this->compositionStore->compositionSave(
|
||||
$tenantId,
|
||||
$userId,
|
||||
$identifier,
|
||||
$saveComposition,
|
||||
);
|
||||
|
||||
if ($composed === null) {
|
||||
$result['disposition'] = 'error';
|
||||
$result['error'] = [
|
||||
'type' => 'composition_not_found',
|
||||
'message' => 'Composition not found',
|
||||
return [
|
||||
'disposition' => 'error',
|
||||
'error' => [
|
||||
'type' => 'composition_not_found',
|
||||
'message' => 'Composition not found',
|
||||
],
|
||||
];
|
||||
return $result;
|
||||
}
|
||||
|
||||
// store the message and sender information before sending
|
||||
$composed['sender'] = $sender;
|
||||
$composed['message'] = $message;
|
||||
$this->compositionStore->compositionSave($tenantId, $userId, $identifier, $composed);
|
||||
|
||||
// validate the sender address before attempting to send the message
|
||||
if ($sender['address'] === '' || !filter_var($sender['address'], FILTER_VALIDATE_EMAIL)) {
|
||||
$result['disposition'] = 'error';
|
||||
@@ -306,38 +339,49 @@ class CompositionManager {
|
||||
'composition' => $composition,
|
||||
'attachments' => [],
|
||||
];
|
||||
// fetch the composition to ensure it exists
|
||||
$composed = $this->compositionStore->compositionFetch($tenantId, $userId, $composition);
|
||||
$failed = [];
|
||||
// Construct the snapshot mutation that will run while the composition is locked.
|
||||
$addAttachments = function (?array $composed) use ($tenantId, $userId, $composition, $attachments, &$result, &$failed): ?array {
|
||||
if ($composed === null) {
|
||||
$result['disposition'] = 'error';
|
||||
$result['error'] = [
|
||||
'type' => 'composition_not_found',
|
||||
'message' => 'Composition not found',
|
||||
];
|
||||
return null;
|
||||
}
|
||||
|
||||
$uploads = [];
|
||||
$documents = [];
|
||||
foreach ($attachments as $attachment) {
|
||||
if (isset($attachment['origin']) && $attachment['origin'] === 'documents') {
|
||||
$documents[] = $attachment;
|
||||
} elseif (isset($attachment['origin']) && $attachment['origin'] === 'device') {
|
||||
$uploads[] = $attachment;
|
||||
}
|
||||
}
|
||||
if ($uploads !== []) {
|
||||
$this->attachmentAddFromDevice($tenantId, $userId, $composition, $uploads, $composed);
|
||||
}
|
||||
if ($documents !== []) {
|
||||
$failed = $this->attachmentAddFromDocuments($tenantId, $userId, $composition, $documents, $composed);
|
||||
}
|
||||
|
||||
return $composed;
|
||||
};
|
||||
|
||||
// Execute the mutation and atomically persist the snapshot it returns.
|
||||
$composed = $this->compositionStore->compositionSave(
|
||||
$tenantId,
|
||||
$userId,
|
||||
$composition,
|
||||
$addAttachments,
|
||||
);
|
||||
|
||||
if ($composed === null) {
|
||||
$result['disposition'] = 'error';
|
||||
$result['error'] = [
|
||||
'type' => 'composition_not_found',
|
||||
'message' => 'Composition not found',
|
||||
];
|
||||
return $result;
|
||||
}
|
||||
// separate attachments by type
|
||||
$uploads = [];
|
||||
$documents = [];
|
||||
foreach ($attachments as $attachment) {
|
||||
if (isset($attachment['origin']) && $attachment['origin'] === 'documents') {
|
||||
$documents[] = $attachment;
|
||||
} elseif (isset($attachment['origin']) && $attachment['origin'] === 'device') {
|
||||
$uploads[] = $attachment;
|
||||
}
|
||||
}
|
||||
// process uploaded attachments
|
||||
if ($uploads !== []) {
|
||||
$this->attachmentAddFromDevice($tenantId, $userId, $composition, $uploads, $composed);
|
||||
}
|
||||
// process attachments from the documents module
|
||||
if ($documents !== []) {
|
||||
$failed = $documents !== []
|
||||
? $this->attachmentAddFromDocuments($tenantId, $userId, $composition, $documents, $composed)
|
||||
: [];
|
||||
}
|
||||
// save the updated composition with new attachments
|
||||
$this->compositionStore->compositionSave($tenantId, $userId, $composition, $composed);
|
||||
|
||||
// construct the result
|
||||
$result['attachments'] = $composed['attachments'];
|
||||
$result['failed'] = $failed;
|
||||
@@ -486,29 +530,39 @@ class CompositionManager {
|
||||
'composition' => $composition,
|
||||
'identifier' => $identifier,
|
||||
];
|
||||
|
||||
$composed = $this->compositionStore->compositionFetch($tenantId, $userId, $composition);
|
||||
if ($composed === null) {
|
||||
$result['disposition'] = 'error';
|
||||
$result['error'] = [
|
||||
'type' => 'composition_not_found',
|
||||
'message' => 'Composition not found',
|
||||
];
|
||||
return $result;
|
||||
}
|
||||
if (!isset($composed['attachments'][$identifier])) {
|
||||
$result['disposition'] = 'error';
|
||||
$result['error'] = [
|
||||
'type' => 'attachment_not_found',
|
||||
'message' => 'Attachment not found',
|
||||
];
|
||||
return $result;
|
||||
}
|
||||
|
||||
unset($composed['attachments'][$identifier]);
|
||||
$this->compositionStore->compositionSave($tenantId, $userId, $composition, $composed);
|
||||
// Construct the snapshot mutation that will run while the composition is locked.
|
||||
$removeAttachment = function (?array $composed) use ($identifier, &$result): ?array {
|
||||
if ($composed === null) {
|
||||
$result['disposition'] = 'error';
|
||||
$result['error'] = [
|
||||
'type' => 'composition_not_found',
|
||||
'message' => 'Composition not found',
|
||||
];
|
||||
return null;
|
||||
}
|
||||
if (!isset($composed['attachments'][$identifier])) {
|
||||
$result['disposition'] = 'error';
|
||||
$result['error'] = [
|
||||
'type' => 'attachment_not_found',
|
||||
'message' => 'Attachment not found',
|
||||
];
|
||||
return null;
|
||||
}
|
||||
|
||||
unset($composed['attachments'][$identifier]);
|
||||
$result['disposition'] = 'removed';
|
||||
return $composed;
|
||||
};
|
||||
|
||||
// Execute the mutation and atomically persist the snapshot it returns.
|
||||
$this->compositionStore->compositionSave(
|
||||
$tenantId,
|
||||
$userId,
|
||||
$composition,
|
||||
$removeAttachment,
|
||||
);
|
||||
|
||||
$result['disposition'] = 'removed';
|
||||
return $result;
|
||||
}
|
||||
|
||||
@@ -555,4 +609,4 @@ class CompositionManager {
|
||||
}
|
||||
fclose($resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user