feat: add remote sync state
Signed-off-by: Sebastian <krupinski01@gmail.com>
This commit is contained in:
@@ -46,6 +46,11 @@ class CompositionManager {
|
|||||||
'sender' => $sender,
|
'sender' => $sender,
|
||||||
'message' => $message,
|
'message' => $message,
|
||||||
'attachments' => [],
|
'attachments' => [],
|
||||||
|
'remote' => [
|
||||||
|
'status' => 'dirty',
|
||||||
|
'entity' => null,
|
||||||
|
'error' => null,
|
||||||
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
// Construct the snapshot mutation that will run while the composition is locked.
|
// Construct the snapshot mutation that will run while the composition is locked.
|
||||||
@@ -111,7 +116,9 @@ class CompositionManager {
|
|||||||
$saveComposition,
|
$saveComposition,
|
||||||
) ?? $snapshot;
|
) ?? $snapshot;
|
||||||
|
|
||||||
return $snapshot;
|
$response = $snapshot;
|
||||||
|
unset($response['remote']);
|
||||||
|
return $response;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function patch(string $tenantId, string $userId, string $identifier, array $data): array {
|
public function patch(string $tenantId, string $userId, string $identifier, array $data): array {
|
||||||
@@ -140,6 +147,7 @@ class CompositionManager {
|
|||||||
$composed['revision'] = $data['revision'];
|
$composed['revision'] = $data['revision'];
|
||||||
$composed['sender'] = $data['sender'] ?? [];
|
$composed['sender'] = $data['sender'] ?? [];
|
||||||
$composed['message'] = $data['message'] ?? [];
|
$composed['message'] = $data['message'] ?? [];
|
||||||
|
$composed = $this->markRemoteDirty($composed);
|
||||||
$result = [
|
$result = [
|
||||||
'identifier' => $identifier,
|
'identifier' => $identifier,
|
||||||
'disposition' => 'patched',
|
'disposition' => 'patched',
|
||||||
@@ -175,7 +183,7 @@ class CompositionManager {
|
|||||||
|
|
||||||
$composed['sender'] = $sender;
|
$composed['sender'] = $sender;
|
||||||
$composed['message'] = $message;
|
$composed['message'] = $message;
|
||||||
return $composed;
|
return $this->markRemoteDirty($composed);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Execute the mutation and atomically persist the snapshot it returns.
|
// Execute the mutation and atomically persist the snapshot it returns.
|
||||||
@@ -367,6 +375,12 @@ class CompositionManager {
|
|||||||
$failed = $this->attachmentAddFromDocuments($tenantId, $userId, $composition, $documents, $composed);
|
$failed = $this->attachmentAddFromDocuments($tenantId, $userId, $composition, $documents, $composed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$deviceAttachmentsChanged = $uploads !== [];
|
||||||
|
$documentAttachmentsChanged = count($failed) < count($documents);
|
||||||
|
if ($deviceAttachmentsChanged || $documentAttachmentsChanged) {
|
||||||
|
$composed = $this->markRemoteDirty($composed);
|
||||||
|
}
|
||||||
|
|
||||||
return $composed;
|
return $composed;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -551,6 +565,7 @@ class CompositionManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
unset($composed['attachments'][$identifier]);
|
unset($composed['attachments'][$identifier]);
|
||||||
|
$composed = $this->markRemoteDirty($composed);
|
||||||
$result['disposition'] = 'removed';
|
$result['disposition'] = 'removed';
|
||||||
return $composed;
|
return $composed;
|
||||||
};
|
};
|
||||||
@@ -566,6 +581,19 @@ class CompositionManager {
|
|||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function markRemoteDirty(array $composed): array {
|
||||||
|
$remote = isset($composed['remote']) && is_array($composed['remote'])
|
||||||
|
? $composed['remote']
|
||||||
|
: [];
|
||||||
|
|
||||||
|
$remote['status'] = 'dirty';
|
||||||
|
$remote['entity'] ??= null;
|
||||||
|
$remote['error'] = null;
|
||||||
|
$composed['remote'] = $remote;
|
||||||
|
|
||||||
|
return $composed;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array<int,array|string> $entries
|
* @param array<int,array|string> $entries
|
||||||
* @return array<int,Address>
|
* @return array<int,Address>
|
||||||
|
|||||||
@@ -52,6 +52,61 @@ final class CompositionManagerTest extends TestCase {
|
|||||||
$this->deleteDir($this->rootDir);
|
$this->deleteDir($this->rootDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testStageInitializesDirtyRemoteState(): void {
|
||||||
|
$documentsManager = $this->createStub(\KTXM\DocumentsManager\Manager::class);
|
||||||
|
$manager = $this->makeManager($documentsManager);
|
||||||
|
|
||||||
|
$response = $manager->stage(
|
||||||
|
self::TENANT_ID,
|
||||||
|
self::USER_ID,
|
||||||
|
'draft-new',
|
||||||
|
'new',
|
||||||
|
['address' => 'sender@example.com'],
|
||||||
|
['subject' => 'Draft'],
|
||||||
|
);
|
||||||
|
|
||||||
|
$snapshot = $this->compositionStore->compositionFetch(self::TENANT_ID, self::USER_ID, 'draft-new');
|
||||||
|
$this->assertArrayNotHasKey('remote', $response);
|
||||||
|
$this->assertSame([
|
||||||
|
'status' => 'dirty',
|
||||||
|
'entity' => null,
|
||||||
|
'error' => null,
|
||||||
|
], $snapshot['remote']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPatchMarksExistingRemoteStateDirty(): void {
|
||||||
|
$compositionId = $this->stageEmptyComposition();
|
||||||
|
$setSynced = static function (?array $snapshot): array {
|
||||||
|
$snapshot['revision'] = 1;
|
||||||
|
$snapshot['remote'] = [
|
||||||
|
'status' => 'synced',
|
||||||
|
'entity' => 'provider:service:drafts:123',
|
||||||
|
'error' => 'old error',
|
||||||
|
];
|
||||||
|
return $snapshot;
|
||||||
|
};
|
||||||
|
$this->compositionStore->compositionSave(
|
||||||
|
self::TENANT_ID,
|
||||||
|
self::USER_ID,
|
||||||
|
$compositionId,
|
||||||
|
$setSynced,
|
||||||
|
);
|
||||||
|
|
||||||
|
$documentsManager = $this->createStub(\KTXM\DocumentsManager\Manager::class);
|
||||||
|
$manager = $this->makeManager($documentsManager);
|
||||||
|
$result = $manager->patch(self::TENANT_ID, self::USER_ID, $compositionId, [
|
||||||
|
'revision' => 2,
|
||||||
|
'sender' => ['address' => 'sender@example.com'],
|
||||||
|
'message' => ['subject' => 'Changed'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$snapshot = $this->compositionStore->compositionFetch(self::TENANT_ID, self::USER_ID, $compositionId);
|
||||||
|
$this->assertSame('patched', $result['disposition']);
|
||||||
|
$this->assertSame('dirty', $snapshot['remote']['status']);
|
||||||
|
$this->assertSame('provider:service:drafts:123', $snapshot['remote']['entity']);
|
||||||
|
$this->assertNull($snapshot['remote']['error']);
|
||||||
|
}
|
||||||
|
|
||||||
public function testAttachmentAddFromDocumentsHappyPath(): void {
|
public function testAttachmentAddFromDocumentsHappyPath(): void {
|
||||||
$compositionId = $this->stageEmptyComposition();
|
$compositionId = $this->stageEmptyComposition();
|
||||||
$documentsManager = $this->createStub(\KTXM\DocumentsManager\Manager::class);
|
$documentsManager = $this->createStub(\KTXM\DocumentsManager\Manager::class);
|
||||||
@@ -78,6 +133,11 @@ final class CompositionManagerTest extends TestCase {
|
|||||||
$this->assertSame('hello.txt', $attachment['name']);
|
$this->assertSame('hello.txt', $attachment['name']);
|
||||||
$this->assertSame('text/plain', $attachment['type']);
|
$this->assertSame('text/plain', $attachment['type']);
|
||||||
$this->assertSame(strlen('hello world'), $attachment['size']);
|
$this->assertSame(strlen('hello world'), $attachment['size']);
|
||||||
|
|
||||||
|
$snapshot = $this->compositionStore->compositionFetch(self::TENANT_ID, self::USER_ID, $compositionId);
|
||||||
|
$this->assertSame('dirty', $snapshot['remote']['status']);
|
||||||
|
$this->assertNull($snapshot['remote']['entity']);
|
||||||
|
$this->assertNull($snapshot['remote']['error']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testAttachmentAddFromDocumentsPartialFailure(): void {
|
public function testAttachmentAddFromDocumentsPartialFailure(): void {
|
||||||
|
|||||||
Reference in New Issue
Block a user