Skip to content

Commit

Permalink
Merge pull request #605 from streamich/global-schema
Browse files Browse the repository at this point in the history
JSON CRDT: use global session ID for default value
  • Loading branch information
streamich authored Apr 28, 2024
2 parents f3d2298 + cc7ba8d commit 5121fab
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {Model} from '../../../../json-crdt/model';
import {ChunkSlice} from '../ChunkSlice';

const setup = () => {
const model = Model.withLogicalClock().setSchema(s.str('Hello world'));
const model = Model.withLogicalClock().setSchema(s.str('Hello world'), false);
const node = model.root.node();
const chunk = node.first()!;
return {
Expand Down
2 changes: 1 addition & 1 deletion src/json-crdt-patch/clock/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export interface ITimestampStruct {
* Session ID (or actor ID, site ID, process ID, etc.), a random identifier
* randomly assigned to each editing session.
*/
readonly sid: number;
sid: number;

/**
* Logical time (or sequence number, tick, etc.), a monotonically increasing
Expand Down
18 changes: 16 additions & 2 deletions src/json-crdt/model/Model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -396,10 +396,24 @@ export class Model<N extends JsonNode = JsonNode<any>> implements Printable {
* the document is empty.
*
* @param schema The schema to set for this model.
* @param sid Session ID to use for setting the default value of the document.
* Defaults to `SESSION.GLOBAL` (2), which is the default session ID
* for all operations operations that are not attributed to a specific
* session.
* @returns Strictly typed model.
*/
public setSchema<S extends NodeBuilder>(schema: S): Model<SchemaToJsonNode<S>> {
if (this.clock.time < 2) this.api.root(schema);
public setSchema<S extends NodeBuilder>(schema: S, useGlobalSession: boolean = true): Model<SchemaToJsonNode<S>> {
const c = this.clock;
const isNewDocument = c.time === 1;
if (isNewDocument) {
const oldSid = c.sid;
if (useGlobalSession) c.sid = SESSION.GLOBAL;
this.api.root(schema);
if (useGlobalSession) {
c.sid = oldSid;
c.observe(new clock.Timestamp(SESSION.GLOBAL, c.time - 1), 1);
}
}
return <any>this;
}

Expand Down
29 changes: 0 additions & 29 deletions src/json-crdt/model/__tests__/Model.schema.spec.ts

This file was deleted.

67 changes: 67 additions & 0 deletions src/json-crdt/model/__tests__/Model.setSchema.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import {nodes, s} from '../../../json-crdt-patch';
import {SESSION} from '../../../json-crdt-patch/constants';
import {Model} from '../Model';

test('can set object schema', () => {
const model = Model.withLogicalClock().setSchema(
s.obj({
str: s.str('asdf'),
con: s.con(123),
}),
);
expect(model.s.str.toApi().view()).toBe('asdf');
expect(model.s.con.toApi().view()).toBe(123);
});

test('can set map schema', () => {
const model = Model.withLogicalClock().setSchema(
s.map<nodes.str | nodes.con<number>>({
str: s.str('asdf'),
con1: s.con(123),
}),
);
expect(model.s.str.toApi().view()).toBe('asdf');
expect(model.s.con1.toApi().view()).toBe(123);
expect(model.view().str).toBe('asdf');
expect(model.view().con1).toBe(123);
expect(model.view().anyKeyAllowed).toBe(undefined);
});

test('uses global session ID by default', () => {
const model = Model.withLogicalClock().setSchema(
s.obj({
id: s.str<string>('asdf'),
num: s.con(123),
}),
);
expect(model.api.r.get().node.id.sid).toBe(SESSION.GLOBAL);
expect(model.api.r.get().get('id').node.id.sid).toBe(SESSION.GLOBAL);
expect(model.api.r.get().get('num').node.id.sid).toBe(SESSION.GLOBAL);
});

test('allows to specify custom session ID', () => {
const schema = s.obj({
id: s.str<string>('asdf'),
num: s.con(123),
});
const model = Model.withLogicalClock().setSchema(schema, false);
expect(model.api.r.get().node.id.sid).toBe(model.clock.sid);
expect(model.api.r.get().get('id').node.id.sid).toBe(model.clock.sid);
expect(model.api.r.get().get('num').node.id.sid).toBe(model.clock.sid);
});

test('resets session ID to user specified', () => {
const model = Model.withLogicalClock().setSchema(
s.obj({
id: s.str<string>('asdf'),
num: s.con(123),
}),
);
expect(model.view().num).toBe(123);
expect(model.api.r.get().get('num').node.id.sid).toBe(SESSION.GLOBAL);
model.api.r.get().set({
num: 456,
});
expect(model.view().num).toBe(456);
expect(model.api.r.get().get('num').node.id.sid).not.toBe(SESSION.GLOBAL);
});

0 comments on commit 5121fab

Please sign in to comment.