diff --git a/js/dict.js b/js/dict.js index 12ccac4..f687f14 100644 --- a/js/dict.js +++ b/js/dict.js @@ -113,14 +113,52 @@ export class Dict { async values() { const values = await this._client.hVals(this._key); - return values.map((x) => JSON.parse(x)); // Parse the values + return values.map((x) => { + const value = JSON.parse(x); + if (typeof value === "string") { + if (value.startsWith("znsocket.List:")) { + const refKey = value.split(/:(.+)/)[1]; + return new ZnSocketList({ client: this._client,socket: this._socket , key: refKey}); + } else if (value.startsWith("znsocket.Dict:")) { + const refKey = value.split(/:(.+)/)[1]; + return new Dict({ client: this._client, socket: this._socket , key: refKey}); + } + } + return value; + }); } - async entries() { // Renamed from items to entries + async entries() { const entries = await this._client.hGetAll(this._key); - return Object.entries(entries).map( - ([key, value]) => [key, JSON.parse(value)] - ); + return Object.entries(entries).map(([key, value]) => { + const parsedValue = JSON.parse(value); + + if (typeof parsedValue === "string") { + if (parsedValue.startsWith("znsocket.List:")) { + const refKey = parsedValue.split(/:(.+)/)[1]; + return [key, new ZnSocketList({ client: this._client, socket: this._socket, key: refKey })]; + } else if (parsedValue.startsWith("znsocket.Dict:")) { + const refKey = parsedValue.split(/:(.+)/)[1]; + return [key, new Dict({ client: this._client, socket: this._socket, key: refKey })]; + } + } + + return [key, parsedValue]; + }); + } + + async toObject() { + const entries = await this.entries(); + // go through all and if one of them is a Dict or List, call toObject on it + const obj = {}; + for (const [key, value] of entries) { + if (value instanceof Dict || value instanceof ZnSocketList) { + obj[key] = await value.toObject(); + } else { + obj[key] = value; + } + } + return obj; } onRefresh(callback) { diff --git a/js/index.d.ts b/js/index.d.ts index 939719b..c9110b9 100644 --- a/js/index.d.ts +++ b/js/index.d.ts @@ -88,7 +88,8 @@ export class Dict { clear(): Promise; keys(): Promise; values(): Promise; - entries(): Promise<[string, any][]>; // Renamed from items to entries + entries(): Promise<[string, any][]>; + toObject(): Promise>; onRefresh(callback: (data: { keys?: string[]; indices?: number[] }) => void): void; offRefresh(): void; diff --git a/js_tests/native.dict.test.js b/js_tests/native.dict.test.js index a8b43fa..9a0c0da 100644 --- a/js_tests/native.dict.test.js +++ b/js_tests/native.dict.test.js @@ -235,6 +235,13 @@ test("native_dict_in_dict", async () => { expect(dictValue._key).toBe(dct1._key); const helloValue2 = await dictValue.get("Hello"); expect(helloValue2).toBe("World"); + + const dct2Values = await dct2.values(); + expect(await dct2Values[0]["Hello"]).toEqual("World"); + + const dct2Entries = await dct2.entries(); + expect(await dct2Entries[0][1]["Hello"]).toEqual("World"); + expect(await dct2Entries[0][0]).toEqual("dict"); }); test("native_dict_with_list", async () => { @@ -251,3 +258,17 @@ test("native_dict_with_list", async () => { expect(await listInstance.get(0)).toBe("A"); expect(await listInstance.get(1)).toBe("B"); }); + + +test("native_dict_to_object", async () => { + let dct1 = new Dict({ client: client, key: "dict:test:1" }); + let dct2 = new Dict({ client: client, key: "dict:test:2" }); + + await dct1.set("Hello", "World"); + await dct2.set("dict", dct1); + + // convert to object + const obj = await dct2.toObject(); + expect(obj).toEqual({ dict: { Hello: "World" } }); + +}); diff --git a/package.json b/package.json index 443fb95..8735dfc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "znsocket", - "version": "0.2.5", + "version": "0.2.6", "description": "JS interface for the python znsocket package", "main": "js/index.js", "types": "js/index.d.ts", diff --git a/pyproject.toml b/pyproject.toml index b54956a..6abce23 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "znsocket" -version = "0.2.5" +version = "0.2.6" description = "Python implementation of a Redis-compatible API using websockets." authors = ["Fabian Zills "] license = "Apache-2.0" diff --git a/znsocket/objects/__init__.py b/znsocket/objects/__init__.py index 7dd00bd..b49b611 100644 --- a/znsocket/objects/__init__.py +++ b/znsocket/objects/__init__.py @@ -343,7 +343,7 @@ def __init__( callbacks: dict[str, Callable] optional function callbacks for methods which modify the database. - repr_type: str + repr_type: "keys"|"minimal"|"full" Control the `repr` appearance of the object. Reduce for better performance. @@ -374,7 +374,7 @@ def __getitem__(self, key: str) -> t.Any: value = List(r=self.redis, key=key) elif value.startswith("znsocket.Dict:"): key = value.split(":", 1)[1] - value = Dict(r=self.redis, key=key) + value = Dict(r=self.redis, key=key, repr_type=self.repr_type) return value def __setitem__(self, key: str, value: t.Any) -> None: @@ -415,13 +415,30 @@ def keys(self) -> list[str]: def values(self) -> list[t.Any]: response = [] for v in self.redis.hvals(self.key): - response.append(_decode(self, v)) + value = _decode(self, v) + if isinstance(value, str): + if value.startswith("znsocket.List:"): + key = value.split(":", 1)[1] + value = List(r=self.redis, key=key) + elif value.startswith("znsocket.Dict:"): + key = value.split(":", 1)[1] + value = Dict(r=self.redis, key=key, repr_type=self.repr_type) + response.append(value) return response def items(self) -> list[t.Tuple[str, t.Any]]: response = [] for k, v in self.redis.hgetall(self.key).items(): - response.append((k, _decode(self, v))) + value = _decode(self, v) + if isinstance(value, str): + if value.startswith("znsocket.List:"): + key = value.split(":", 1)[1] + value = List(r=self.redis, key=key) + elif value.startswith("znsocket.Dict:"): + key = value.split(":", 1)[1] + value = Dict(r=self.redis, key=key, repr_type=self.repr_type) + + response.append((k, value)) return response def __contains__(self, key: str) -> bool: