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 Component Locking #102

Merged
merged 13 commits into from
Aug 11, 2023
6 changes: 5 additions & 1 deletion __mocks__/ably/promises/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Types } from 'ably/promises';
import Ably, { Types } from 'ably/promises';

const MOCK_CLIENT_ID = 'MOCK_CLIENT_ID';

Expand Down Expand Up @@ -72,4 +72,8 @@ class MockRealtime {
}
}

// maintain the PresenceMessage class so tests can initialise it directly using
// PresenceMessage.fromValues.
MockRealtime.PresenceMessage = Ably.Rest.PresenceMessage;

export { MockRealtime as Realtime };
93 changes: 93 additions & 0 deletions examples/locks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// An example of members locating at and locking elements in a slides
// application.
import Ably from 'ably/promises';
import Spaces from '../dist/cjs/Spaces.js';
import { Lock, LockAttributes } from '../dist/cjs/Locks.js';

// SlideElement represents an element on a slide which a member can both be
// located at and attempt to lock (e.g. an editable text box).
class SlideElement {
slideId: string;
elementId: string;

constructor(slideId: string, elementId: string) {
this.slideId = slideId;
this.elementId = elementId;
}

// the identifier to use to lock this SlideElement.
lockId(): string {
return `/slides/${this.slideId}/element/${this.elementId}`;
}

// the attributes to use when locking this SlideElement.
lockAttributes(): LockAttributes {
const attributes = new LockAttributes();
attributes.set('slideId', this.slideId);
attributes.set('elementId', this.elementId);
return attributes;
}
}

// define a main async function since we can't use await at the top-level.
const main = async () => {
info('initialising Ably client');
const client = new Ably.Realtime.Promise({
key: process.env.ABLY_API_KEY,
clientId: 'Alice',
});

info('entering the "example" space');
const spaces = new Spaces(client);
const space = await spaces.get('example');
await space.enter();

const location = new SlideElement('123', '456');
info(`setting location to ${JSON.stringify(location)}`);
await space.locations.set(location);

info('checking if location is locked');
const lockId = location.lockId();
const isLocked = space.locks.get(lockId) !== undefined;
if (isLocked) {
info('location is already locked');
process.exit();
} else {
info('location is not locked');
}

// initialise a Promise which resolves when the lock changes status.
const lockEvent = new Promise<Lock>((resolve) => {
dpiatek marked this conversation as resolved.
Show resolved Hide resolved
const listener = (lock: Lock) => {
if (lock.request.id === lockId) {
info(`received lock update for "${lockId}", status=${lock.request.status}`);
resolve(lock);
info('unsubscribing from lock events');
space.locks.unsubscribe(listener);
}
};
info('subscribing to lock events');
space.locks.subscribe('update', listener);
});

info(`attempting to lock "${lockId}"`);
const req = await space.locks.acquire(lockId, { attributes: location.lockAttributes() });
info(`lock status is "${req.status}"`);

info('waiting for lock event');
const lock = await lockEvent;

info(`lock status is "${lock.request.status}"`);

info('releasing the lock');
await space.locks.release(lockId);

info('done');
client.close();
};

const info = (msg: string) => {
console.log(new Date(), 'INFO', msg);
}

main();
Loading