Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add doc.subscribe('status', callback) function #828

Merged
merged 7 commits into from
Jul 1, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions src/document/document.ts
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,7 @@ type DocEventCallbackMap<P extends Indexable> = {
'my-presence': NextFn<InitializedEvent<P> | PresenceChangedEvent<P>>;
others: NextFn<WatchedEvent<P> | UnwatchedEvent<P> | PresenceChangedEvent<P>>;
connection: NextFn<ConnectionChangedEvent>;
status: NextFn<StatusChangedEvent>;
chacha912 marked this conversation as resolved.
Show resolved Hide resolved
sync: NextFn<SyncStatusChangedEvent>;
all: NextFn<TransactionEvent<P>>;
};
Expand Down Expand Up @@ -782,6 +783,16 @@ export class Document<T, P extends Indexable = Indexable> {
error?: ErrorFn,
complete?: CompleteFn,
): Unsubscribe;
/**
* `subscribe` registers a callback to subscribe to events on the document.
* The callback will be called when the document status changes.
*/
public subscribe(
type: 'status',
next: DocEventCallbackMap<P>['status'],
error?: ErrorFn,
complete?: CompleteFn,
): Unsubscribe;
chacha912 marked this conversation as resolved.
Show resolved Hide resolved
/**
* `subscribe` registers a callback to subscribe to events on the document.
* The callback will be called when the document is synced with the server.
Expand Down Expand Up @@ -922,6 +933,21 @@ export class Document<T, P extends Indexable = Indexable> {
arg4,
);
}
if (arg1 === 'status') {
const callback = arg2 as DocEventCallbackMap<P>['status'];
return this.eventStream.subscribe(
(event) => {
for (const docEvent of event) {
if (docEvent.type !== DocEventType.StatusChanged) {
continue;
}
callback(docEvent);
}
},
arg3,
arg4,
);
}
if (arg1 === 'sync') {
const callback = arg2 as DocEventCallbackMap<P>['sync'];
return this.eventStream.subscribe(
Expand Down
93 changes: 93 additions & 0 deletions test/integration/document_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import type { CRDTElement } from '@yorkie-js-sdk/src/document/crdt/element';
import {
DocumentStatus,
DocEventType,
StatusChangedEvent,
chacha912 marked this conversation as resolved.
Show resolved Hide resolved
} from '@yorkie-js-sdk/src/document/document';
import { OperationInfo } from '@yorkie-js-sdk/src/document/operation/operation';
import { YorkieError } from '@yorkie-js-sdk/src/util/error';
Expand Down Expand Up @@ -111,6 +112,98 @@ describe('Document', function () {
await client3.deactivate();
});

it('Can subscribe to events related to document status changes', async function ({
task,
}) {
const c1 = new yorkie.Client(testRPCAddr);
const c2 = new yorkie.Client(testRPCAddr);
await c1.activate();
await c2.activate();
const c1ID = c1.getID()!;
const c2ID = c2.getID()!;
chacha912 marked this conversation as resolved.
Show resolved Hide resolved

const docKey = toDocKey(`${task.name}-${new Date().getTime()}`);
const d1 = new yorkie.Document(docKey);
const d2 = new yorkie.Document(docKey);
const eventCollectorD1 = new EventCollector<StatusChangedEvent['value']>();
const eventCollectorD2 = new EventCollector<StatusChangedEvent['value']>();
const unsub1 = d1.subscribe('status', (event) => {
console.log('c1', event.value);
eventCollectorD1.add(event.value);
});
const unsub2 = d2.subscribe('status', (event) => {
console.log('c2', event.value);
eventCollectorD2.add(event.value);
});

// 1. When the client attaches a document, it receives an attached event.
await c1.attach(d1);
await c2.attach(d2);

await eventCollectorD1.waitAndVerifyNthEvent(1, {
status: DocumentStatus.Attached,
actorID: c1ID,
});
await eventCollectorD2.waitAndVerifyNthEvent(1, {
status: DocumentStatus.Attached,
actorID: c2ID,
});

// 2. When c1 detaches a document, it receives a detached event.
await c1.detach(d1);
await eventCollectorD1.waitAndVerifyNthEvent(2, {
status: DocumentStatus.Detached,
});

// 3. When c2 deactivates, it should also receive a detached event.
// TODO(chacha912): The following line should be uncommented.
// await c2.deactivate();
// await eventCollectorD2.waitAndVerifyNthEvent(2, {
// status: DocumentStatus.Detached,
// });
chacha912 marked this conversation as resolved.
Show resolved Hide resolved

// 4. When the document is re-attached, it receives an attached event.
await c1.attach(d1, { syncMode: SyncMode.Manual });
await eventCollectorD1.waitAndVerifyNthEvent(3, {
status: DocumentStatus.Attached,
actorID: c1ID,
});

// TODO(chacha912): The following line should be uncommented.
// await c2.activate();
// c2ID = c2.getID()!;
// await c2.attach(d2, { syncMode: SyncMode.Manual });
// await eventCollectorD2.waitAndVerifyNthEvent(2, {
// status: DocumentStatus.Attached,
// actorID: c2ID,
// });
chacha912 marked this conversation as resolved.
Show resolved Hide resolved

// 5. When c1 removes a document, it receives a removed event.
await c1.remove(d1);
// await c1.sync();
await eventCollectorD1.waitAndVerifyNthEvent(4, {
status: DocumentStatus.Removed,
});

// 6. When c4 syncs, it should also receive a removed event.
// TODO(chacha912): The following line should be uncommented.
// await c2.sync();
await eventCollectorD2.waitAndVerifyNthEvent(2, {
status: DocumentStatus.Removed,
});

// 7. If the document is in the removed state, a detached event should not occur when deactivating.
const eventCount1 = eventCollectorD1.getLength();
const eventCount2 = eventCollectorD2.getLength();
await c1.deactivate();
await c2.deactivate();
assert.equal(eventCollectorD1.getLength(), eventCount1);
assert.equal(eventCollectorD2.getLength(), eventCount2);

unsub1();
unsub2();
});
chacha912 marked this conversation as resolved.
Show resolved Hide resolved

it('Can watch documents', async function ({ task }) {
const c1 = new yorkie.Client(testRPCAddr);
const c2 = new yorkie.Client(testRPCAddr);
Expand Down
Loading