Skip to content

Commit

Permalink
Update LiveMap.get to have undefined as a possible return value
Browse files Browse the repository at this point in the history
This is a temporary solution for handling the access API for tombstoned
objects. See comment in Confluence for the relevant discussion [1].

The return type for the LiveCounter.value is not changed as part of this
commit, as the tombstoned LiveCounter has a value of 0 for its data.

[1] https://ably.atlassian.net/wiki/spaces/LOB/pages/3556671496/LODR-026+Correctness+of+OBJECT_DELETE?focusedCommentId=3593928705
  • Loading branch information
VeskeR committed Dec 11, 2024
1 parent a44b392 commit f370f62
Show file tree
Hide file tree
Showing 3 changed files with 13 additions and 10 deletions.
6 changes: 4 additions & 2 deletions ably.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2105,10 +2105,12 @@ export declare interface LiveMap<T extends LiveMapType> extends LiveObject<LiveM
/**
* Returns the value associated with a given key. Returns `undefined` if the key doesn't exist in a map or if the associated {@link LiveObject} has been deleted.
*
* Always returns undefined if this map object is deleted.
*
* @param key - The key to retrieve the value for.
* @returns A {@link LiveObject}, a primitive type (string, number, boolean, or binary data) or `undefined` if the key doesn't exist in a map or the associated {@link LiveObject} has been deleted.
* @returns A {@link LiveObject}, a primitive type (string, number, boolean, or binary data) or `undefined` if the key doesn't exist in a map or the associated {@link LiveObject} has been deleted. Always `undefined` if this map object is deleted.
*/
get<TKey extends keyof T & string>(key: TKey): T[TKey] extends StateValue ? T[TKey] : T[TKey] | undefined;
get<TKey extends keyof T & string>(key: TKey): T[TKey] | undefined;

/**
* Returns the number of key/value pairs in the map.
Expand Down
3 changes: 2 additions & 1 deletion src/plugins/liveobjects/livemap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,13 @@ export class LiveMap<T extends API.LiveMapType> extends LiveObject<LiveMapData,
*
* - If no entry is associated with the specified key, `undefined` is returned.
* - If map entry is tombstoned (deleted), `undefined` is returned.
* - If this map object is tombstoned, `undefined` is returned.
* - If the value associated with the provided key is an objectId string of another Live Object, a reference to that Live Object
* is returned, provided it exists in the local pool and is not tombstoned. Otherwise, `undefined` is returned.
* - If the value is not an objectId, then that value is returned.
*/
// force the key to be of type string as we only allow strings as key in a map
get<TKey extends keyof T & string>(key: TKey): T[TKey] extends StateValue ? T[TKey] : T[TKey] | undefined {
get<TKey extends keyof T & string>(key: TKey): T[TKey] | undefined {
const element = this._dataRef.data.get(key);

if (element === undefined) {
Expand Down
14 changes: 7 additions & 7 deletions test/package/browser/template/src/index-liveobjects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@ globalThis.testAblyPackage = async function () {
const size: number = root.size();

// check custom user provided typings via LiveObjectsTypes are working:
// any LiveMap.get() call can return undefined, as the LiveMap itself can be tombstoned (has empty state),
// or referenced object is tombstoned.
// keys on a root:
const aNumber: number = root.get('numberKey');
const aString: string = root.get('stringKey');
const aBoolean: boolean = root.get('booleanKey');
const couldBeUndefined: string | undefined = root.get('couldBeUndefined');
const aNumber: number | undefined = root.get('numberKey');
const aString: string | undefined = root.get('stringKey');
const aBoolean: boolean | undefined = root.get('booleanKey');
const userProvidedUndefined: string | undefined = root.get('couldBeUndefined');
// live objects on a root:
// LiveMap.get can still return undefined for LiveObject typed properties even if custom typings have them as non-optional.
// objects can be tombstoned and result in the undefined value
const counter: Ably.LiveCounter | undefined = root.get('counterKey');
const map: LiveObjectsTypes['root']['mapKey'] | undefined = root.get('mapKey');
// check string literal types works
Expand Down Expand Up @@ -63,5 +63,5 @@ globalThis.testAblyPackage = async function () {

// check can provide custom types for the getRoot method, ignoring global LiveObjectsTypes interface
const explicitRoot: Ably.LiveMap<ExplicitRootType> = await liveObjects.getRoot<ExplicitRootType>();
const someOtherKey: string = explicitRoot.get('someOtherKey');
const someOtherKey: string | undefined = explicitRoot.get('someOtherKey');
};

0 comments on commit f370f62

Please sign in to comment.