Skip to content

Commit 760ac54

Browse files
committed
[ECO-5375] Created ObjectMessage.kt, declared data classes as per spec
1 parent 29d7854 commit 760ac54

File tree

3 files changed

+325
-0
lines changed

3 files changed

+325
-0
lines changed

lib/src/main/java/io/ably/lib/objects/LiveObjectsAdapter.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ public void setChannelSerial(@NotNull String channelName, @NotNull String channe
3030

3131
@Override
3232
public void send(ProtocolMessage msg, CompletionListener listener) throws AblyException {
33+
// Always queue LiveObjects messages to ensure reliable state synchronization and proper acknowledgment
3334
ably.connection.connectionManager.send(msg, true, listener);
3435
}
3536
}

live-objects/src/main/kotlin/io/ably/lib/objects/Helpers.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,10 @@ internal suspend fun LiveObjectsAdapter.sendAsync(message: ProtocolMessage) {
2222
}
2323
deferred.await()
2424
}
25+
26+
internal enum class MessageFormat(private val value: String) {
27+
MSGPACK("msgpack"),
28+
JSON("json");
29+
30+
override fun toString(): String = value
31+
}
Lines changed: 317 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,317 @@
1+
package io.ably.lib.objects
2+
3+
import java.nio.ByteBuffer
4+
5+
/**
6+
* An enum class representing the different actions that can be performed on an object.
7+
* Spec: OOP2
8+
*/
9+
internal enum class ObjectOperationAction(val code: Int) {
10+
MAP_CREATE(0),
11+
MAP_SET(1),
12+
MAP_REMOVE(2),
13+
COUNTER_CREATE(3),
14+
COUNTER_INC(4),
15+
OBJECT_DELETE(5);
16+
}
17+
18+
/**
19+
* An enum class representing the conflict-resolution semantics used by a Map object.
20+
* Spec: MAP2
21+
*/
22+
internal enum class MapSemantics(val code: Int) {
23+
LWW(0);
24+
}
25+
26+
/**
27+
* An ObjectData represents a value in an object on a channel.
28+
* Spec: OD1
29+
*/
30+
internal data class ObjectData(
31+
/**
32+
* A reference to another object, used to support composable object structures.
33+
* Spec: OD2a
34+
*/
35+
val objectId: String? = null,
36+
37+
/**
38+
* Can be set by the client to indicate that value in `string` or `bytes` field have an encoding.
39+
* Spec: OD2b
40+
*/
41+
val encoding: String? = null,
42+
43+
/**
44+
* String, number, boolean or binary - a concrete value of the object
45+
* Spec: OD2c
46+
*/
47+
val value: Any? = null,
48+
)
49+
50+
/**
51+
* A MapOp describes an operation to be applied to a Map object.
52+
* Spec: MOP1
53+
*/
54+
internal data class MapOp(
55+
/**
56+
* The key of the map entry to which the operation should be applied.
57+
* Spec: MOP2a
58+
*/
59+
val key: String,
60+
61+
/**
62+
* The data that the map entry should contain if the operation is a MAP_SET operation.
63+
* Spec: MOP2b
64+
*/
65+
val data: ObjectData? = null
66+
)
67+
68+
/**
69+
* A CounterOp describes an operation to be applied to a Counter object.
70+
* Spec: COP1
71+
*/
72+
internal data class CounterOp(
73+
/**
74+
* The data value that should be added to the counter
75+
* Spec: COP2a
76+
*/
77+
val amount: Double
78+
)
79+
80+
/**
81+
* A MapEntry represents the value at a given key in a Map object.
82+
* Spec: ME1
83+
*/
84+
internal data class MapEntry(
85+
/**
86+
* Indicates whether the map entry has been removed.
87+
* Spec: ME2a
88+
*/
89+
val tombstone: Boolean? = null,
90+
91+
/**
92+
* The serial value of the last operation that was applied to the map entry.
93+
* It is optional in a MAP_CREATE operation and might be missing, in which case the client should use a nullish value for it
94+
* and treat it as the "earliest possible" serial for comparison purposes.
95+
* Spec: ME2b
96+
*/
97+
val timeserial: String? = null,
98+
99+
/**
100+
* The data that represents the value of the map entry.
101+
* Spec: ME2c
102+
*/
103+
val data: ObjectData? = null
104+
)
105+
106+
/**
107+
* An ObjectMap object represents a map of key-value pairs.
108+
* Spec: MAP1
109+
*/
110+
internal data class ObjectMap(
111+
/**
112+
* The conflict-resolution semantics used by the map object.
113+
* Spec: MAP3a
114+
*/
115+
val semantics: MapSemantics? = null,
116+
117+
/**
118+
* The map entries, indexed by key.
119+
* Spec: MAP3b
120+
*/
121+
val entries: Map<String, MapEntry>? = null
122+
)
123+
124+
/**
125+
* An ObjectCounter object represents an incrementable and decrementable value
126+
* Spec: CNT1
127+
*/
128+
internal data class ObjectCounter(
129+
/**
130+
* The value of the counter
131+
* Spec: CNT2a
132+
*/
133+
val count: Double? = null
134+
)
135+
136+
/**
137+
* An ObjectOperation describes an operation to be applied to an object on a channel.
138+
* Spec: OOP1
139+
*/
140+
internal data class ObjectOperation(
141+
/**
142+
* Defines the operation to be applied to the object.
143+
* Spec: OOP3a
144+
*/
145+
val action: ObjectOperationAction,
146+
147+
/**
148+
* The object ID of the object on a channel to which the operation should be applied.
149+
* Spec: OOP3b
150+
*/
151+
val objectId: String,
152+
153+
/**
154+
* The payload for the operation if it is an operation on a Map object type.
155+
* Spec: OOP3c
156+
*/
157+
val mapOp: MapOp? = null,
158+
159+
/**
160+
* The payload for the operation if it is an operation on a Counter object type.
161+
* Spec: OOP3d
162+
*/
163+
val counterOp: CounterOp? = null,
164+
165+
/**
166+
* The payload for the operation if the operation is MAP_CREATE.
167+
* Defines the initial value for the Map object.
168+
* Spec: OOP3e
169+
*/
170+
val map: ObjectMap? = null,
171+
172+
/**
173+
* The payload for the operation if the operation is COUNTER_CREATE.
174+
* Defines the initial value for the Counter object.
175+
* Spec: OOP3f
176+
*/
177+
val counter: ObjectCounter? = null,
178+
179+
/**
180+
* The nonce, must be present on create operations. This is the random part
181+
* that has been hashed with the type and initial value to create the object ID.
182+
* Spec: OOP3g
183+
*/
184+
val nonce: String? = null,
185+
186+
/**
187+
* The initial value bytes for the object. These bytes should be used along with the nonce
188+
* and timestamp to create the object ID. Frontdoor will use this to verify the object ID.
189+
* After verification the bytes will be decoded into the Map or Counter objects and
190+
* the initialValue, nonce, and initialValueEncoding will be removed.
191+
* Spec: OOP3h
192+
*/
193+
val initialValue: ByteBuffer? = null,
194+
195+
/** The initial value encoding defines how the initialValue should be interpreted.
196+
* Spec: OOP3i
197+
*/
198+
val initialValueEncoding: MessageFormat? = null
199+
)
200+
201+
/**
202+
* An ObjectState describes the instantaneous state of an object on a channel.
203+
* Spec: OST1
204+
*/
205+
internal data class ObjectState(
206+
/**
207+
* The identifier of the object.
208+
* Spec: OST2a
209+
*/
210+
val objectId: String,
211+
212+
/**
213+
* A map of serials keyed by a {@link ObjectMessage.siteCode},
214+
* representing the last operations applied to this object
215+
* Spec: OST2b
216+
*/
217+
val siteTimeserials: Map<String, String>,
218+
219+
/**
220+
* True if the object has been tombstoned.
221+
* Spec: OST2c
222+
*/
223+
val tombstone: Boolean,
224+
225+
/**
226+
* The operation that created the object.
227+
* Can be missing if create operation for the object is not known at this point.
228+
* Spec: OST2d
229+
*/
230+
val createOp: ObjectOperation? = null,
231+
232+
/**
233+
* The data that represents the result of applying all operations to a Map object
234+
* excluding the initial value from the create operation if it is a Map object type.
235+
* Spec: OST2e
236+
*/
237+
val map: ObjectMap? = null,
238+
239+
/**
240+
* The data that represents the result of applying all operations to a Counter object
241+
* excluding the initial value from the create operation if it is a Counter object type.
242+
* Spec: OST2f
243+
*/
244+
val counter: ObjectCounter? = null
245+
)
246+
247+
/**
248+
* An @ObjectMessage@ represents an individual object message to be sent or received via the Ably Realtime service.
249+
* Spec: OM1
250+
*/
251+
internal data class ObjectMessage(
252+
/**
253+
* unique ID for this object message. This attribute is always populated for object messages received over REST.
254+
* For object messages received over Realtime, if the object message does not contain an @id@,
255+
* it should be set to @protocolMsgId:index@, where @protocolMsgId@ is the id of the @ProtocolMessage@ encapsulating it,
256+
* and @index@ is the index of the object message inside the @state@ array of the @ProtocolMessage@
257+
* Spec: OM2a
258+
*/
259+
val id: String? = null,
260+
261+
/**
262+
* time in milliseconds since epoch. If an object message received from Ably does not contain a @timestamp@,
263+
* it should be set to the @timestamp@ of the encapsulating @ProtocolMessage@
264+
* Spec: OM2e
265+
*/
266+
val timestamp: Long? = null,
267+
268+
/**
269+
* Spec: OM2b
270+
*/
271+
val clientId: String? = null,
272+
273+
/**
274+
* If an object message received from Ably does not contain a @connectionId@,
275+
* it should be set to the @connectionId@ of the encapsulating @ProtocolMessage@
276+
* Spec: OM2c
277+
*/
278+
val connectionId: String? = null,
279+
280+
/**
281+
* JSON-encodable object, used to contain any arbitrary key value pairs which may also contain other primitive JSON types,
282+
* JSON-encodable objects or JSON-encodable arrays. The @extras@ field is provided to contain message metadata and/or
283+
* ancillary payloads in support of specific functionality. For 3.1 no specific functionality is specified for
284+
* @extras@ in object messages. Unless otherwise specified, the client library should not attempt to do any filtering
285+
* or validation of the @extras@ field itself, but should treat it opaquely, encoding it and passing it to realtime unaltered
286+
* Spec: OM2d
287+
*/
288+
val extras: Any? = null,
289+
290+
/**
291+
* Describes an operation to be applied to an object.
292+
* Mutually exclusive with the `object` field. This field is only set on object messages if the `action` field of the
293+
* `ProtocolMessage` encapsulating it is `OBJECT`.
294+
* Spec: OM2f
295+
*/
296+
val operation: ObjectOperation? = null,
297+
298+
/**
299+
* Describes the instantaneous state of an object.
300+
* Mutually exclusive with the `operation` field. This field is only set on object messages if the `action` field of
301+
* the `ProtocolMessage` encapsulating it is `OBJECT_SYNC`.
302+
* Spec: OM2g
303+
*/
304+
val `object`: ObjectState? = null,
305+
306+
/**
307+
* An opaque string that uniquely identifies this object message.
308+
* Spec: OM2h
309+
*/
310+
val serial: String? = null,
311+
312+
/**
313+
* An opaque string used as a key to update the map of serial values on an object.
314+
* Spec: OM2i
315+
*/
316+
val siteCode: String? = null
317+
)

0 commit comments

Comments
 (0)