Skip to content

Commit 316e8ee

Browse files
committed
Update ObjectOperation fields for protocol v6
Based on realtime implementation in [1], and DR [2]. Resolves AIT-315 [1] ably/realtime#8025 [2] https://ably.atlassian.net/wiki/x/AQAPEgE
1 parent c18f9fb commit 316e8ee

File tree

7 files changed

+301
-215
lines changed

7 files changed

+301
-215
lines changed

src/plugins/liveobjects/livecounter.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { __livetype } from '../../../ably';
22
import { LiveCounter as PublicLiveCounter } from '../../../liveobjects';
33
import { LiveObject, LiveObjectData, LiveObjectUpdate, LiveObjectUpdateNoop } from './liveobject';
4-
import { ObjectData, ObjectMessage, ObjectOperation, ObjectOperationAction, ObjectsCounterOp } from './objectmessage';
4+
import { CounterInc, ObjectData, ObjectMessage, ObjectOperation, ObjectOperationAction } from './objectmessage';
55
import { RealtimeObject } from './realtimeobject';
66

77
export interface LiveCounterData extends LiveObjectData {
@@ -54,7 +54,7 @@ export class LiveCounter extends LiveObject<LiveCounterData, LiveCounterUpdate>
5454
operation: {
5555
action: ObjectOperationAction.COUNTER_INC,
5656
objectId,
57-
counterOp: { amount },
57+
counterInc: { number: amount },
5858
} as ObjectOperation<ObjectData>,
5959
},
6060
client.Utils,
@@ -135,12 +135,12 @@ export class LiveCounter extends LiveObject<LiveCounterData, LiveCounterUpdate>
135135
break;
136136

137137
case ObjectOperationAction.COUNTER_INC:
138-
if (this._client.Utils.isNil(op.counterOp)) {
138+
if (this._client.Utils.isNil(op.counterInc)) {
139139
this._throwNoPayloadError(op);
140140
// leave an explicit return here, so that TS knows that update object is always set after the switch statement.
141141
return;
142142
} else {
143-
update = this._applyCounterInc(op.counterOp, msg);
143+
update = this._applyCounterInc(op.counterInc, msg);
144144
}
145145
break;
146146

@@ -253,11 +253,11 @@ export class LiveCounter extends LiveObject<LiveCounterData, LiveCounterUpdate>
253253
// note that it is intentional to SUM the incoming count from the create op.
254254
// if we got here, it means that current counter instance is missing the initial value in its data reference,
255255
// which we're going to add now.
256-
this._dataRef.data += objectOperation.counter?.count ?? 0; // RTLC6d1
256+
this._dataRef.data += objectOperation.counterCreate?.count ?? 0; // RTLC6d1
257257
this._createOperationIsMerged = true; // RTLC6d2
258258

259259
return {
260-
update: { amount: objectOperation.counter?.count ?? 0 },
260+
update: { amount: objectOperation.counterCreate?.count ?? 0 },
261261
objectMessage: msg,
262262
_type: 'LiveCounterUpdate',
263263
};
@@ -291,10 +291,10 @@ export class LiveCounter extends LiveObject<LiveCounterData, LiveCounterUpdate>
291291
return this._mergeInitialDataFromCreateOperation(op, msg);
292292
}
293293

294-
private _applyCounterInc(op: ObjectsCounterOp, msg: ObjectMessage): LiveCounterUpdate {
295-
this._dataRef.data += op.amount;
294+
private _applyCounterInc(op: CounterInc, msg: ObjectMessage): LiveCounterUpdate {
295+
this._dataRef.data += op.number;
296296
return {
297-
update: { amount: op.amount },
297+
update: { amount: op.number },
298298
objectMessage: msg,
299299
_type: 'LiveCounterUpdate',
300300
};

src/plugins/liveobjects/livecountervaluetype.ts

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import { __livetype } from '../../../ably';
22
import { LiveCounter } from '../../../liveobjects';
33
import { ObjectId } from './objectid';
44
import {
5-
createInitialValueJSONString,
5+
CounterCreate,
6+
encodePartialObjectOperationForWire,
67
ObjectData,
78
ObjectMessage,
89
ObjectOperation,
@@ -58,8 +59,9 @@ export class LiveCounterValueType implements LiveCounter {
5859
throw new client.ErrorInfo('Counter value should be a valid number', 40003, 400);
5960
}
6061

61-
const initialValueOperation = LiveCounterValueType.createInitialValueOperation(count);
62-
const initialValueJSONString = createInitialValueJSONString(initialValueOperation, client);
62+
const counterCreate = LiveCounterValueType._getCounterCreate(count);
63+
const { counterCreate: encodedCounterCreate } = encodePartialObjectOperationForWire({ counterCreate }, client);
64+
const initialValueJSONString = JSON.stringify(encodedCounterCreate);
6365
const nonce = client.Utils.cheapRandStr();
6466
const msTimestamp = await client.getTimestamp(true);
6567

@@ -74,11 +76,13 @@ export class LiveCounterValueType implements LiveCounter {
7476
const msg = ObjectMessage.fromValues(
7577
{
7678
operation: {
77-
...initialValueOperation,
7879
action: ObjectOperationAction.COUNTER_CREATE,
7980
objectId,
80-
nonce,
81-
initialValue: initialValueJSONString,
81+
counterCreateWithObjectId: {
82+
nonce,
83+
// initialValue is the JSON string representation of the encoded counterCreate operation that contains the initial value
84+
initialValue: initialValueJSONString,
85+
},
8286
} as ObjectOperation<ObjectData>,
8387
},
8488
client.Utils,
@@ -88,11 +92,9 @@ export class LiveCounterValueType implements LiveCounter {
8892
return msg;
8993
}
9094

91-
private static createInitialValueOperation(count?: number): Pick<ObjectOperation<ObjectData>, 'counter'> {
95+
private static _getCounterCreate(count?: number): CounterCreate {
9296
return {
93-
counter: {
94-
count: count ?? 0,
95-
},
97+
count: count ?? 0,
9698
};
9799
}
98100
}

src/plugins/liveobjects/livemap.ts

Lines changed: 24 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,23 @@ import { __livetype } from '../../../ably';
44
import {
55
CompactedJsonValue,
66
CompactedValue,
7-
Primitive,
87
LiveMap as PublicLiveMap,
98
LiveObject as PublicLiveObject,
9+
Primitive,
1010
Value,
1111
} from '../../../liveobjects';
1212
import { LiveCounter } from './livecounter';
1313
import { LiveCounterValueType } from './livecountervaluetype';
1414
import { LiveMapValueType } from './livemapvaluetype';
1515
import { LiveObject, LiveObjectData, LiveObjectUpdate, LiveObjectUpdateNoop } from './liveobject';
1616
import {
17+
MapRemove,
18+
MapSet,
1719
ObjectData,
1820
ObjectMessage,
1921
ObjectOperation,
2022
ObjectOperationAction,
2123
ObjectsMapEntry,
22-
ObjectsMapOp,
2324
ObjectsMapSemantics,
2425
} from './objectmessage';
2526
import { RealtimeObject } from './realtimeobject';
@@ -130,9 +131,9 @@ export class LiveMap<T extends Record<string, Value> = Record<string, Value>>
130131
operation: {
131132
action: ObjectOperationAction.MAP_SET,
132133
objectId,
133-
mapOp: {
134+
mapSet: {
134135
key,
135-
data: objectData,
136+
value: objectData,
136137
},
137138
} as ObjectOperation<ObjectData>,
138139
},
@@ -158,7 +159,7 @@ export class LiveMap<T extends Record<string, Value> = Record<string, Value>>
158159
operation: {
159160
action: ObjectOperationAction.MAP_REMOVE,
160161
objectId,
161-
mapOp: { key },
162+
mapRemove: { key },
162163
} as ObjectOperation<ObjectData>,
163164
},
164165
client.Utils,
@@ -332,22 +333,22 @@ export class LiveMap<T extends Record<string, Value> = Record<string, Value>>
332333
break;
333334

334335
case ObjectOperationAction.MAP_SET:
335-
if (this._client.Utils.isNil(op.mapOp)) {
336+
if (this._client.Utils.isNil(op.mapSet)) {
336337
this._throwNoPayloadError(op);
337338
// leave an explicit return here, so that TS knows that update object is always set after the switch statement.
338339
return;
339340
} else {
340-
update = this._applyMapSet(op.mapOp, opSerial, msg);
341+
update = this._applyMapSet(op.mapSet, opSerial, msg);
341342
}
342343
break;
343344

344345
case ObjectOperationAction.MAP_REMOVE:
345-
if (this._client.Utils.isNil(op.mapOp)) {
346+
if (this._client.Utils.isNil(op.mapRemove)) {
346347
this._throwNoPayloadError(op);
347348
// leave an explicit return here, so that TS knows that update object is always set after the switch statement.
348349
return;
349350
} else {
350-
update = this._applyMapRemove(op.mapOp, opSerial, msg.serialTimestamp, msg);
351+
update = this._applyMapRemove(op.mapRemove, opSerial, msg.serialTimestamp, msg);
351352
}
352353
break;
353354

@@ -410,9 +411,9 @@ export class LiveMap<T extends Record<string, Value> = Record<string, Value>>
410411
);
411412
}
412413

413-
if (objectState.createOp.map?.semantics !== this._semantics) {
414+
if (objectState.createOp.mapCreate?.semantics !== this._semantics) {
414415
throw new this._client.ErrorInfo(
415-
`Invalid object state: object state createOp map semantics=${objectState.createOp.map?.semantics}; LiveMap semantics=${this._semantics}`,
416+
`Invalid object state: object state createOp map semantics=${objectState.createOp.mapCreate?.semantics}; LiveMap semantics=${this._semantics}`,
416417
92000,
417418
500,
418419
);
@@ -643,7 +644,7 @@ export class LiveMap<T extends Record<string, Value> = Record<string, Value>>
643644
objectOperation: ObjectOperation<ObjectData>,
644645
msg: ObjectMessage,
645646
): LiveMapUpdate<T> {
646-
if (this._client.Utils.isNil(objectOperation.map)) {
647+
if (this._client.Utils.isNil(objectOperation.mapCreate)) {
647648
// if a map object is missing for the MAP_CREATE op, the initial value is implicitly an empty map.
648649
// in this case there is nothing to merge into the current map, so we can just end processing the op.
649650
return { update: {}, objectMessage: msg, _type: 'LiveMapUpdate' };
@@ -657,7 +658,7 @@ export class LiveMap<T extends Record<string, Value> = Record<string, Value>>
657658
// RTLM6d1
658659
// in order to apply MAP_CREATE op for an existing map, we should merge their underlying entries keys.
659660
// we can do this by iterating over entries from MAP_CREATE op and apply changes on per-key basis as if we had MAP_SET, MAP_REMOVE operations.
660-
Object.entries(objectOperation.map.entries ?? {}).forEach(([key, entry]) => {
661+
Object.entries(objectOperation.mapCreate.entries ?? {}).forEach(([key, entry]) => {
661662
// for a MAP_CREATE operation we must use the serial value available on an entry, instead of a serial on a message
662663
const opSerial = entry.timeserial;
663664
let update: LiveMapUpdate<T> | LiveObjectUpdateNoop;
@@ -666,7 +667,7 @@ export class LiveMap<T extends Record<string, Value> = Record<string, Value>>
666667
update = this._applyMapRemove({ key }, opSerial, entry.serialTimestamp, msg);
667668
} else {
668669
// RTLM6d1a - entry in MAP_CREATE op is not removed, try to set it via MAP_SET op
669-
update = this._applyMapSet({ key, data: entry.data }, opSerial, msg);
670+
update = this._applyMapSet({ key, value: entry.data! }, opSerial, msg);
670671
}
671672

672673
// skip noop updates
@@ -708,9 +709,9 @@ export class LiveMap<T extends Record<string, Value> = Record<string, Value>>
708709
return { noop: true };
709710
}
710711

711-
if (this._semantics !== op.map?.semantics) {
712+
if (this._semantics !== op.mapCreate?.semantics) {
712713
throw new this._client.ErrorInfo(
713-
`Cannot apply MAP_CREATE op on LiveMap objectId=${this.getObjectId()}; map's semantics=${this._semantics}, but op expected ${op.map?.semantics}`,
714+
`Cannot apply MAP_CREATE op on LiveMap objectId=${this.getObjectId()}; map's semantics=${this._semantics}, but op expected ${op.mapCreate?.semantics}`,
714715
92000,
715716
500,
716717
);
@@ -721,7 +722,7 @@ export class LiveMap<T extends Record<string, Value> = Record<string, Value>>
721722

722723
/** @spec RTLM7 */
723724
private _applyMapSet(
724-
op: ObjectsMapOp<ObjectData>,
725+
op: MapSet<ObjectData>,
725726
opSerial: string | undefined,
726727
msg: ObjectMessage,
727728
): LiveMapUpdate<T> | LiveObjectUpdateNoop {
@@ -740,7 +741,7 @@ export class LiveMap<T extends Record<string, Value> = Record<string, Value>>
740741
return { noop: true };
741742
}
742743

743-
if (Utils.isNil(op.data) || (Utils.isNil(op.data.objectId) && Utils.isNil(op.data.value))) {
744+
if (Utils.isNil(op.value) || (Utils.isNil(op.value.objectId) && Utils.isNil(op.value.value))) {
744745
throw new ErrorInfo(
745746
`Invalid object data for MAP_SET op on objectId=${this.getObjectId()} on key="${op.key}"`,
746747
92000,
@@ -750,15 +751,15 @@ export class LiveMap<T extends Record<string, Value> = Record<string, Value>>
750751

751752
let liveData: LiveMapObjectData;
752753
// RTLM7c
753-
if (!Utils.isNil(op.data.objectId)) {
754-
liveData = { objectId: op.data.objectId } as ObjectIdObjectData;
754+
if (!Utils.isNil(op.value.objectId)) {
755+
liveData = { objectId: op.value.objectId } as ObjectIdObjectData;
755756
// this MAP_SET op is setting a key to point to another object via its object id,
756757
// but it is possible that we don't have the corresponding object in the pool yet (for example, we haven't seen the *_CREATE op for it).
757758
// we don't want to return undefined from this map's .get() method even if we don't have the object,
758759
// so instead we create a zero-value object for that object id if it not exists.
759-
this._realtimeObject.getPool().createZeroValueObjectIfNotExists(op.data.objectId); // RTLM7c1
760+
this._realtimeObject.getPool().createZeroValueObjectIfNotExists(op.value.objectId); // RTLM7c1
760761
} else {
761-
liveData = { value: op.data.value } as ValueObjectData;
762+
liveData = { value: op.value.value } as ValueObjectData;
762763
}
763764

764765
if (existingEntry) {
@@ -808,7 +809,7 @@ export class LiveMap<T extends Record<string, Value> = Record<string, Value>>
808809

809810
/** @spec RTLM8 */
810811
private _applyMapRemove(
811-
op: ObjectsMapOp<ObjectData>,
812+
op: MapRemove,
812813
opSerial: string | undefined,
813814
opTimestamp: number | undefined,
814815
msg: ObjectMessage,

src/plugins/liveobjects/livemapvaluetype.ts

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import { __livetype } from '../../../ably';
2-
import { Primitive, LiveMap as PublicLiveMap, Value } from '../../../liveobjects';
2+
import { LiveMap as PublicLiveMap, Primitive, Value } from '../../../liveobjects';
33
import { LiveCounterValueType } from './livecountervaluetype';
44
import { LiveMap, LiveMapObjectData, ObjectIdObjectData, ValueObjectData } from './livemap';
55
import { ObjectId } from './objectid';
66
import {
7-
createInitialValueJSONString,
7+
encodePartialObjectOperationForWire,
8+
MapCreate,
89
ObjectData,
910
ObjectMessage,
1011
ObjectOperation,
@@ -75,11 +76,9 @@ export class LiveMapValueType<T extends Record<string, Value> = Record<string, V
7576

7677
Object.entries(entries ?? {}).forEach(([key, value]) => LiveMap.validateKeyValue(realtimeObject, key, value));
7778

78-
const { initialValueOperation, nestedObjectsCreateMsgs } = await LiveMapValueType._createInitialValueOperation(
79-
realtimeObject,
80-
entries,
81-
);
82-
const initialValueJSONString = createInitialValueJSONString(initialValueOperation, client);
79+
const { mapCreate, nestedObjectsCreateMsgs } = await LiveMapValueType._getMapCreate(realtimeObject, entries);
80+
const { mapCreate: encodedMapCreate } = encodePartialObjectOperationForWire({ mapCreate }, client);
81+
const initialValueJSONString = JSON.stringify(encodedMapCreate);
8382
const nonce = client.Utils.cheapRandStr();
8483
const msTimestamp = await client.getTimestamp(true);
8584

@@ -94,11 +93,13 @@ export class LiveMapValueType<T extends Record<string, Value> = Record<string, V
9493
const mapCreateMsg = ObjectMessage.fromValues(
9594
{
9695
operation: {
97-
...initialValueOperation,
9896
action: ObjectOperationAction.MAP_CREATE,
9997
objectId,
100-
nonce,
101-
initialValue: initialValueJSONString,
98+
mapCreateWithObjectId: {
99+
nonce,
100+
// initialValue is the JSON string representation of the encoded mapCreate operation that contains the initial value
101+
initialValue: initialValueJSONString,
102+
},
102103
} as ObjectOperation<ObjectData>,
103104
},
104105
client.Utils,
@@ -111,11 +112,11 @@ export class LiveMapValueType<T extends Record<string, Value> = Record<string, V
111112
};
112113
}
113114

114-
private static async _createInitialValueOperation(
115+
private static async _getMapCreate(
115116
realtimeObject: RealtimeObject,
116117
entries?: Record<string, Value>,
117118
): Promise<{
118-
initialValueOperation: Pick<ObjectOperation<ObjectData>, 'map'>;
119+
mapCreate: MapCreate<ObjectData>;
119120
nestedObjectsCreateMsgs: ObjectMessage[];
120121
}> {
121122
const mapEntries: Record<string, ObjectsMapEntry<ObjectData>> = {};
@@ -146,15 +147,13 @@ export class LiveMapValueType<T extends Record<string, Value> = Record<string, V
146147
};
147148
}
148149

149-
const initialValueOperation = {
150-
map: {
151-
semantics: ObjectsMapSemantics.LWW,
152-
entries: mapEntries,
153-
},
150+
const mapCreate: MapCreate<ObjectData> = {
151+
semantics: ObjectsMapSemantics.LWW,
152+
entries: mapEntries,
154153
};
155154

156155
return {
157-
initialValueOperation,
156+
mapCreate,
158157
nestedObjectsCreateMsgs,
159158
};
160159
}

0 commit comments

Comments
 (0)