Skip to content

Commit ae875c8

Browse files
feat: add return types
1 parent 6880087 commit ae875c8

File tree

4 files changed

+277
-18
lines changed

4 files changed

+277
-18
lines changed
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
import { describe, it, expect } from "vitest";
2+
import { parseEntities } from "../getEntities";
3+
import { SchemaType } from "../types";
4+
import * as torii from "@dojoengine/torii-client";
5+
6+
// Mock SchemaType for testing
7+
interface TestSchema extends SchemaType {
8+
player: {
9+
id: string;
10+
name: string;
11+
score: number;
12+
};
13+
item: {
14+
id: string;
15+
name: string;
16+
rarity: string;
17+
};
18+
}
19+
20+
describe("parseEntities", () => {
21+
it("should parse entities correctly", () => {
22+
const mockEntities: torii.Entities = {
23+
player: {
24+
"0x1": {
25+
id: {
26+
type: "primitive",
27+
type_name: "felt252",
28+
value: "0x1",
29+
key: true,
30+
},
31+
name: {
32+
type: "primitive",
33+
type_name: "felt252",
34+
value: "Alice",
35+
key: false,
36+
},
37+
score: {
38+
type: "primitive",
39+
type_name: "u32",
40+
value: 100,
41+
key: false,
42+
},
43+
},
44+
"0x2": {
45+
id: {
46+
type: "primitive",
47+
type_name: "felt252",
48+
value: "0x2",
49+
key: true,
50+
},
51+
name: {
52+
type: "primitive",
53+
type_name: "felt252",
54+
value: "Bob",
55+
key: false,
56+
},
57+
score: {
58+
type: "primitive",
59+
type_name: "u32",
60+
value: 200,
61+
key: false,
62+
},
63+
},
64+
},
65+
item: {
66+
"0x3": {
67+
id: {
68+
type: "primitive",
69+
type_name: "felt252",
70+
value: "0x3",
71+
key: true,
72+
},
73+
name: {
74+
type: "primitive",
75+
type_name: "felt252",
76+
value: "Sword",
77+
key: false,
78+
},
79+
rarity: {
80+
type: "primitive",
81+
type_name: "felt252",
82+
value: "Rare",
83+
key: false,
84+
},
85+
},
86+
},
87+
};
88+
89+
const query: { [P in keyof TestSchema]?: Partial<TestSchema[P]> } = {
90+
player: {},
91+
item: {},
92+
};
93+
94+
const result = parseEntities<TestSchema, keyof TestSchema>(
95+
mockEntities,
96+
query
97+
);
98+
99+
expect(result).toEqual({
100+
player: [
101+
{ id: "0x1", name: "Alice", score: 100 },
102+
{ id: "0x2", name: "Bob", score: 200 },
103+
],
104+
item: [{ id: "0x3", name: "Sword", rarity: "Rare" }],
105+
});
106+
});
107+
108+
it("should handle empty entities", () => {
109+
const mockEntities: torii.Entities = {};
110+
const query: { [P in keyof TestSchema]?: Partial<TestSchema[P]> } = {
111+
player: {},
112+
item: {},
113+
};
114+
115+
const result = parseEntities<TestSchema, keyof TestSchema>(
116+
mockEntities,
117+
query
118+
);
119+
120+
expect(result).toEqual({
121+
player: [],
122+
item: [],
123+
});
124+
});
125+
126+
it("should handle partial queries", () => {
127+
const mockEntities: torii.Entities = {
128+
player: {
129+
"0x1": {
130+
id: {
131+
type: "primitive",
132+
type_name: "felt252",
133+
value: "0x1",
134+
key: true,
135+
},
136+
name: {
137+
type: "primitive",
138+
type_name: "felt252",
139+
value: "Alice",
140+
key: false,
141+
},
142+
score: {
143+
type: "primitive",
144+
type_name: "u32",
145+
value: 100,
146+
key: false,
147+
},
148+
},
149+
},
150+
};
151+
152+
const query: { [P in keyof TestSchema]?: Partial<TestSchema[P]> } = {
153+
player: {},
154+
};
155+
156+
const result = parseEntities<TestSchema, keyof TestSchema>(
157+
mockEntities,
158+
query
159+
);
160+
161+
expect(result).toEqual({
162+
player: [{ id: "0x1", name: "Alice", score: 100 }],
163+
});
164+
});
165+
166+
it("should handle entities with missing fields", () => {
167+
const mockEntities: torii.Entities = {
168+
player: {
169+
"0x1": {
170+
id: {
171+
type: "primitive",
172+
type_name: "felt252",
173+
value: "0x1",
174+
key: true,
175+
},
176+
name: {
177+
type: "primitive",
178+
type_name: "felt252",
179+
value: "Alice",
180+
key: false,
181+
},
182+
// score is missing
183+
},
184+
},
185+
};
186+
187+
const query: { [P in keyof TestSchema]?: Partial<TestSchema[P]> } = {
188+
player: {},
189+
};
190+
191+
const result = parseEntities<TestSchema, keyof TestSchema>(
192+
mockEntities,
193+
query
194+
);
195+
196+
expect(result).toEqual({
197+
player: [{ id: "0x1", name: "Alice" }],
198+
});
199+
});
200+
});

packages/sdk/src/getEntities.ts

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,49 @@ import { convertQueryToClause } from "./convertQuerytoClause";
22
import { SchemaType } from "./types";
33
import * as torii from "@dojoengine/torii-client";
44

5+
export function parseEntities<T extends SchemaType, K extends keyof T>(
6+
entities: torii.Entities,
7+
query: { [P in K]?: Partial<T[P]> }
8+
): { [P in K]: T[P][] } {
9+
const result = {} as { [P in K]: T[P][] };
10+
11+
for (const modelName in query) {
12+
if (entities[modelName]) {
13+
result[modelName as K] = Object.values(entities[modelName]).map(
14+
(entity) => {
15+
const parsedEntity = {} as T[K];
16+
for (const key in entity) {
17+
const value = entity[key];
18+
if (
19+
value &&
20+
typeof value === "object" &&
21+
"value" in value
22+
) {
23+
parsedEntity[key as keyof T[K]] =
24+
value.value as any;
25+
}
26+
}
27+
return parsedEntity;
28+
}
29+
);
30+
} else {
31+
result[modelName as K] = [];
32+
}
33+
}
34+
35+
return result;
36+
}
37+
538
export async function getEntities<T extends SchemaType, K extends keyof T>(
639
client: torii.ToriiClient,
740
query: { [P in K]?: Partial<T[P]> },
8-
callback: (response: { entities?: torii.Entities; error?: Error }) => void,
41+
callback: (response: {
42+
data?: { [P in K]: T[P][] };
43+
error?: Error;
44+
}) => void,
945
limit: number = 100, // Default limit
1046
offset: number = 0 // Default offset
11-
): Promise<torii.Entities> {
47+
): Promise<{ [P in K]: T[P][] }> {
1248
const clauses = convertQueryToClause(query);
1349
let cursor = offset;
1450
let continueFetching = true;
@@ -29,17 +65,19 @@ export async function getEntities<T extends SchemaType, K extends keyof T>(
2965
try {
3066
const entities = await client.getEntities(toriiQuery);
3167
Object.assign(allEntities, entities);
32-
callback({ entities });
68+
const parsedEntities = parseEntities<T, K>(entities, query);
69+
callback({ data: parsedEntities });
3370

3471
if (Object.keys(entities).length < limit) {
3572
continueFetching = false;
3673
} else {
3774
cursor += limit;
3875
}
3976
} catch (error) {
77+
callback({ error: error as Error });
4078
throw error;
4179
}
4280
}
4381

44-
return allEntities;
82+
return parseEntities<T, K>(allEntities, query);
4583
}

packages/sdk/src/index.ts

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,20 @@ export async function init<T extends SchemaType>(
1313
options: torii.ClientConfig
1414
): Promise<{
1515
client: torii.ToriiClient;
16-
subscribeQuery: (
17-
query: { [P in keyof T]?: Partial<T[P]> },
18-
callback: (response: { data?: torii.Entities; error?: Error }) => void
16+
subscribeQuery: <K extends keyof T>(
17+
query: { [P in K]?: Partial<T[P]> },
18+
callback: (response: {
19+
data?: { [P in K]: T[P][] };
20+
error?: Error;
21+
}) => void
1922
) => Promise<torii.Subscription>;
20-
getEntities: (
21-
query: {
22-
[P in keyof T]?: Partial<T[P]>;
23-
},
24-
callback: (response: { data?: torii.Entities; error?: Error }) => void
25-
) => Promise<torii.Entities>;
23+
getEntities: <K extends keyof T>(
24+
query: { [P in K]?: Partial<T[P]> },
25+
callback: (response: {
26+
data?: { [P in K]: T[P][] };
27+
error?: Error;
28+
}) => void
29+
) => Promise<{ [P in K]: T[P][] }>;
2630
}> {
2731
const client = await createClient(options);
2832

@@ -33,7 +37,6 @@ export async function init<T extends SchemaType>(
3337
getEntities: (query, callback) => getEntities(client, query, callback),
3438
};
3539
}
36-
3740
// EXAMPLE FOR NOW
3841

3942
interface Todo {
@@ -43,8 +46,16 @@ interface Todo {
4346
createdAt: number;
4447
}
4548

49+
interface Goals {
50+
id: string;
51+
text: string;
52+
done: boolean;
53+
createdAt: number;
54+
}
55+
4656
type Schema = {
4757
todos: Todo;
58+
goals: Goals;
4859
};
4960

5061
async function exampleUsage() {
@@ -56,7 +67,7 @@ async function exampleUsage() {
5667
});
5768

5869
// Query all todos
59-
db.subscribeQuery({ todos: {} }, (resp) => {
70+
db.subscribeQuery({ todos: {}, goals: {} }, (resp) => {
6071
if (resp.error) {
6172
console.error("Error querying todos:", resp.error.message);
6273
return;

packages/sdk/src/subscribeQuery.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,28 @@
11
import * as torii from "@dojoengine/torii-client";
22
import { convertQueryToClauses } from "./convertQueryToClauses";
33
import { SchemaType } from "./types";
4+
import { parseEntities } from "./getEntities";
45

56
export async function subscribeQuery<T extends SchemaType, K extends keyof T>(
67
client: torii.ToriiClient,
78
query: { [P in K]?: Partial<T[P]> },
8-
callback: (response: { entities?: torii.Entities; error?: Error }) => void
9+
callback: (response: { data?: { [P in K]: T[P][] }; error?: Error }) => void
910
): Promise<torii.Subscription> {
1011
const clauses = convertQueryToClauses(query);
1112

1213
return client.onEntityUpdated(
1314
clauses,
14-
(entities: string, data: torii.Entities) => {
15-
callback({ [entities]: data });
15+
(_entities: string, data: torii.Entities) => {
16+
try {
17+
callback({ data: parseEntities<T, K>(data, query) });
18+
} catch (error) {
19+
callback({
20+
error:
21+
error instanceof Error
22+
? error
23+
: new Error(String(error)),
24+
});
25+
}
1626
}
1727
);
1828
}

0 commit comments

Comments
 (0)