@htlkg/data 0.0.19 → 0.0.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/index.d.ts +257 -1
- package/dist/client/index.js +59 -1
- package/dist/client/index.js.map +1 -1
- package/dist/common-DSxswsZ3.d.ts +40 -0
- package/dist/hooks/index.d.ts +162 -10
- package/dist/hooks/index.js +191 -33
- package/dist/hooks/index.js.map +1 -1
- package/dist/index.d.ts +9 -5
- package/dist/index.js +789 -13
- package/dist/index.js.map +1 -1
- package/dist/mutations/index.d.ts +342 -4
- package/dist/mutations/index.js +486 -0
- package/dist/mutations/index.js.map +1 -1
- package/dist/{productInstances-BA3cNsYc.d.ts → productInstances-BpQv1oLS.d.ts} +2 -40
- package/dist/queries/index.d.ts +113 -2
- package/dist/queries/index.js +192 -1
- package/dist/queries/index.js.map +1 -1
- package/dist/reservations-C0FNm__0.d.ts +154 -0
- package/dist/reservations-CdDfkcZ_.d.ts +172 -0
- package/package.json +14 -13
- package/src/client/index.ts +18 -0
- package/src/client/reservations.ts +336 -0
- package/src/hooks/createDataHook.test.ts +534 -0
- package/src/hooks/createDataHook.ts +20 -13
- package/src/hooks/index.ts +2 -0
- package/src/hooks/useContacts.test.ts +159 -0
- package/src/hooks/useContacts.ts +176 -0
- package/src/hooks/useReservations.ts +145 -0
- package/src/mutations/contacts.test.ts +604 -0
- package/src/mutations/contacts.ts +554 -0
- package/src/mutations/index.ts +32 -0
- package/src/mutations/productInstances/productInstances.test.ts +3 -3
- package/src/mutations/reservations.test.ts +459 -0
- package/src/mutations/reservations.ts +452 -0
- package/src/queries/contacts.test.ts +505 -0
- package/src/queries/contacts.ts +237 -0
- package/src/queries/index.ts +21 -0
- package/src/queries/reservations.test.ts +374 -0
- package/src/queries/reservations.ts +247 -0
|
@@ -0,0 +1,534 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* createDataHook Tests
|
|
3
|
+
*
|
|
4
|
+
* Tests for the proxy-based data hook factory that creates
|
|
5
|
+
* Vue composables for fetching data from GraphQL models.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
9
|
+
import { ref, computed, nextTick } from "vue";
|
|
10
|
+
import {
|
|
11
|
+
createDataHook,
|
|
12
|
+
resetClientInstance,
|
|
13
|
+
type BaseHookOptions,
|
|
14
|
+
type CreateDataHookOptions,
|
|
15
|
+
} from "./createDataHook";
|
|
16
|
+
|
|
17
|
+
// Mock Vue's onMounted to execute immediately for testing
|
|
18
|
+
vi.mock("vue", async () => {
|
|
19
|
+
const actual = await vi.importActual("vue");
|
|
20
|
+
return {
|
|
21
|
+
...actual,
|
|
22
|
+
onMounted: vi.fn((callback: () => void) => callback()),
|
|
23
|
+
};
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// Mock the proxy module
|
|
27
|
+
vi.mock("../client/proxy", () => ({
|
|
28
|
+
query: vi.fn(),
|
|
29
|
+
hasErrors: vi.fn((response: any) => !!(response.errors && response.errors.length > 0)),
|
|
30
|
+
getErrorMessage: vi.fn((response: any) => response.errors?.[0]?.message || null),
|
|
31
|
+
}));
|
|
32
|
+
|
|
33
|
+
// Import the mocked functions for assertions
|
|
34
|
+
import { query } from "../client/proxy";
|
|
35
|
+
|
|
36
|
+
interface MockItem {
|
|
37
|
+
id: string;
|
|
38
|
+
name: string;
|
|
39
|
+
status: "active" | "inactive";
|
|
40
|
+
accountId?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
describe("createDataHook", () => {
|
|
44
|
+
const mockItems: MockItem[] = [
|
|
45
|
+
{ id: "1", name: "Item 1", status: "active", accountId: "acc-1" },
|
|
46
|
+
{ id: "2", name: "Item 2", status: "inactive", accountId: "acc-1" },
|
|
47
|
+
{ id: "3", name: "Item 3", status: "active", accountId: "acc-2" },
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
beforeEach(() => {
|
|
51
|
+
vi.clearAllMocks();
|
|
52
|
+
// Default successful response
|
|
53
|
+
(query as any).mockResolvedValue({
|
|
54
|
+
data: mockItems,
|
|
55
|
+
errors: null,
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
describe("basic functionality", () => {
|
|
60
|
+
it("should create a hook function", () => {
|
|
61
|
+
const useItems = createDataHook<MockItem>({
|
|
62
|
+
model: "Item",
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
expect(typeof useItems).toBe("function");
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("should return reactive state refs", () => {
|
|
69
|
+
const useItems = createDataHook<MockItem>({
|
|
70
|
+
model: "Item",
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const result = useItems();
|
|
74
|
+
|
|
75
|
+
expect(result.data).toBeDefined();
|
|
76
|
+
expect(result.loading).toBeDefined();
|
|
77
|
+
expect(result.error).toBeDefined();
|
|
78
|
+
expect(result.refetch).toBeDefined();
|
|
79
|
+
expect(typeof result.refetch).toBe("function");
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it("should auto-fetch data on mount by default", async () => {
|
|
83
|
+
const useItems = createDataHook<MockItem>({
|
|
84
|
+
model: "Item",
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
useItems();
|
|
88
|
+
|
|
89
|
+
expect(query).toHaveBeenCalledWith("Item", "list", {});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it("should not auto-fetch when autoFetch is false", () => {
|
|
93
|
+
const useItems = createDataHook<MockItem>({
|
|
94
|
+
model: "Item",
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
useItems({ autoFetch: false });
|
|
98
|
+
|
|
99
|
+
expect(query).not.toHaveBeenCalled();
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it("should populate data on successful fetch", async () => {
|
|
103
|
+
const useItems = createDataHook<MockItem>({
|
|
104
|
+
model: "Item",
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
const result = useItems();
|
|
108
|
+
|
|
109
|
+
// Wait for async operation
|
|
110
|
+
await nextTick();
|
|
111
|
+
|
|
112
|
+
expect(result.data.value).toEqual(mockItems);
|
|
113
|
+
expect(result.loading.value).toBe(false);
|
|
114
|
+
expect(result.error.value).toBeNull();
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it("should set error on fetch failure", async () => {
|
|
118
|
+
(query as any).mockResolvedValue({
|
|
119
|
+
data: null,
|
|
120
|
+
errors: [{ message: "Query failed" }],
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
const useItems = createDataHook<MockItem>({
|
|
124
|
+
model: "Item",
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
const result = useItems();
|
|
128
|
+
await nextTick();
|
|
129
|
+
|
|
130
|
+
expect(result.error.value).toBeInstanceOf(Error);
|
|
131
|
+
expect(result.error.value?.message).toBe("Query failed");
|
|
132
|
+
expect(result.data.value).toEqual([]);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it("should handle network errors", async () => {
|
|
136
|
+
(query as any).mockRejectedValue(new Error("Network error"));
|
|
137
|
+
|
|
138
|
+
const useItems = createDataHook<MockItem>({
|
|
139
|
+
model: "Item",
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
const result = useItems();
|
|
143
|
+
await nextTick();
|
|
144
|
+
|
|
145
|
+
expect(result.error.value).toBeInstanceOf(Error);
|
|
146
|
+
expect(result.error.value?.message).toBe("Network error");
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
describe("query options", () => {
|
|
151
|
+
it("should pass limit to query", async () => {
|
|
152
|
+
const useItems = createDataHook<MockItem>({
|
|
153
|
+
model: "Item",
|
|
154
|
+
defaultLimit: 50,
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
useItems({ limit: 100 });
|
|
158
|
+
|
|
159
|
+
expect(query).toHaveBeenCalledWith("Item", "list", { limit: 100 });
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it("should use default limit when not specified", async () => {
|
|
163
|
+
const useItems = createDataHook<MockItem>({
|
|
164
|
+
model: "Item",
|
|
165
|
+
defaultLimit: 50,
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
useItems();
|
|
169
|
+
|
|
170
|
+
expect(query).toHaveBeenCalledWith("Item", "list", { limit: 50 });
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it("should pass filter to query", async () => {
|
|
174
|
+
const useItems = createDataHook<MockItem>({
|
|
175
|
+
model: "Item",
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
const filter = { status: { eq: "active" } };
|
|
179
|
+
useItems({ filter });
|
|
180
|
+
|
|
181
|
+
expect(query).toHaveBeenCalledWith("Item", "list", { filter });
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it("should pass selectionSet to query", async () => {
|
|
185
|
+
const useItems = createDataHook<MockItem>({
|
|
186
|
+
model: "Item",
|
|
187
|
+
selectionSet: ["id", "name", "status"],
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
useItems();
|
|
191
|
+
|
|
192
|
+
expect(query).toHaveBeenCalledWith("Item", "list", {
|
|
193
|
+
selectionSet: ["id", "name", "status"],
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it("should combine all query options", async () => {
|
|
198
|
+
const useItems = createDataHook<MockItem>({
|
|
199
|
+
model: "Item",
|
|
200
|
+
defaultLimit: 50,
|
|
201
|
+
selectionSet: ["id", "name"],
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
const filter = { status: { eq: "active" } };
|
|
205
|
+
useItems({ filter, limit: 25 });
|
|
206
|
+
|
|
207
|
+
expect(query).toHaveBeenCalledWith("Item", "list", {
|
|
208
|
+
filter,
|
|
209
|
+
limit: 25,
|
|
210
|
+
selectionSet: ["id", "name"],
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
describe("custom filter builder", () => {
|
|
216
|
+
interface UseItemsOptions extends BaseHookOptions {
|
|
217
|
+
accountId?: string;
|
|
218
|
+
activeOnly?: boolean;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
it("should use buildFilter to construct filters", async () => {
|
|
222
|
+
const useItems = createDataHook<MockItem, UseItemsOptions>({
|
|
223
|
+
model: "Item",
|
|
224
|
+
buildFilter: (options) => {
|
|
225
|
+
const filter: any = {};
|
|
226
|
+
if (options.accountId) {
|
|
227
|
+
filter.accountId = { eq: options.accountId };
|
|
228
|
+
}
|
|
229
|
+
if (options.activeOnly) {
|
|
230
|
+
filter.status = { eq: "active" };
|
|
231
|
+
}
|
|
232
|
+
return Object.keys(filter).length > 0 ? filter : undefined;
|
|
233
|
+
},
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
useItems({ accountId: "acc-123", activeOnly: true });
|
|
237
|
+
|
|
238
|
+
expect(query).toHaveBeenCalledWith("Item", "list", {
|
|
239
|
+
filter: {
|
|
240
|
+
accountId: { eq: "acc-123" },
|
|
241
|
+
status: { eq: "active" },
|
|
242
|
+
},
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it("should return undefined filter when buildFilter returns empty", async () => {
|
|
247
|
+
const useItems = createDataHook<MockItem, UseItemsOptions>({
|
|
248
|
+
model: "Item",
|
|
249
|
+
buildFilter: () => undefined,
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
useItems({});
|
|
253
|
+
|
|
254
|
+
expect(query).toHaveBeenCalledWith("Item", "list", {});
|
|
255
|
+
});
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
describe("data transformation", () => {
|
|
259
|
+
it("should apply transform function to fetched items", async () => {
|
|
260
|
+
const useItems = createDataHook<MockItem>({
|
|
261
|
+
model: "Item",
|
|
262
|
+
transform: (item) => ({
|
|
263
|
+
...item,
|
|
264
|
+
name: item.name.toUpperCase(),
|
|
265
|
+
}),
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
const result = useItems();
|
|
269
|
+
await nextTick();
|
|
270
|
+
|
|
271
|
+
expect(result.data.value[0].name).toBe("ITEM 1");
|
|
272
|
+
expect(result.data.value[1].name).toBe("ITEM 2");
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
it("should handle transform errors gracefully", async () => {
|
|
276
|
+
const useItems = createDataHook<MockItem>({
|
|
277
|
+
model: "Item",
|
|
278
|
+
transform: () => {
|
|
279
|
+
throw new Error("Transform error");
|
|
280
|
+
},
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
const result = useItems();
|
|
284
|
+
await nextTick();
|
|
285
|
+
|
|
286
|
+
expect(result.error.value).toBeInstanceOf(Error);
|
|
287
|
+
expect(result.error.value?.message).toBe("Transform error");
|
|
288
|
+
});
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
describe("computed properties", () => {
|
|
292
|
+
it("should create computed properties from config", async () => {
|
|
293
|
+
const useItems = createDataHook<MockItem, BaseHookOptions, { activeItems: MockItem[] }>({
|
|
294
|
+
model: "Item",
|
|
295
|
+
computedProperties: {
|
|
296
|
+
activeItems: (items) => items.filter((item) => item.status === "active"),
|
|
297
|
+
},
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
const result = useItems();
|
|
301
|
+
await nextTick();
|
|
302
|
+
|
|
303
|
+
expect(result.computed.activeItems.value).toHaveLength(2);
|
|
304
|
+
expect(result.computed.activeItems.value.every((i) => i.status === "active")).toBe(true);
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
it("should expose computed properties at top level for backwards compatibility", async () => {
|
|
308
|
+
const useItems = createDataHook<MockItem, BaseHookOptions, { activeItems: MockItem[] }>({
|
|
309
|
+
model: "Item",
|
|
310
|
+
computedProperties: {
|
|
311
|
+
activeItems: (items) => items.filter((item) => item.status === "active"),
|
|
312
|
+
},
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
const result = useItems();
|
|
316
|
+
await nextTick();
|
|
317
|
+
|
|
318
|
+
// Should be accessible at top level
|
|
319
|
+
expect(result.activeItems).toBeDefined();
|
|
320
|
+
expect(result.activeItems.value).toHaveLength(2);
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
it("should support multiple computed properties", async () => {
|
|
324
|
+
const useItems = createDataHook<
|
|
325
|
+
MockItem,
|
|
326
|
+
BaseHookOptions,
|
|
327
|
+
{ activeItems: MockItem[]; inactiveItems: MockItem[]; totalCount: number }
|
|
328
|
+
>({
|
|
329
|
+
model: "Item",
|
|
330
|
+
computedProperties: {
|
|
331
|
+
activeItems: (items) => items.filter((item) => item.status === "active"),
|
|
332
|
+
inactiveItems: (items) => items.filter((item) => item.status === "inactive"),
|
|
333
|
+
totalCount: (items) => items.length,
|
|
334
|
+
},
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
const result = useItems();
|
|
338
|
+
await nextTick();
|
|
339
|
+
|
|
340
|
+
expect(result.computed.activeItems.value).toHaveLength(2);
|
|
341
|
+
expect(result.computed.inactiveItems.value).toHaveLength(1);
|
|
342
|
+
expect(result.computed.totalCount.value).toBe(3);
|
|
343
|
+
});
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
describe("custom data property name", () => {
|
|
347
|
+
it("should expose data with custom property name", async () => {
|
|
348
|
+
const useItems = createDataHook<MockItem>({
|
|
349
|
+
model: "Item",
|
|
350
|
+
dataPropertyName: "items",
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
const result = useItems();
|
|
354
|
+
await nextTick();
|
|
355
|
+
|
|
356
|
+
expect(result.items).toBeDefined();
|
|
357
|
+
expect(result.items.value).toEqual(mockItems);
|
|
358
|
+
// Original data property should still exist
|
|
359
|
+
expect(result.data.value).toEqual(mockItems);
|
|
360
|
+
});
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
describe("refetch functionality", () => {
|
|
364
|
+
it("should refetch data when refetch is called", async () => {
|
|
365
|
+
const useItems = createDataHook<MockItem>({
|
|
366
|
+
model: "Item",
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
const result = useItems();
|
|
370
|
+
await nextTick();
|
|
371
|
+
|
|
372
|
+
expect(query).toHaveBeenCalledTimes(1);
|
|
373
|
+
|
|
374
|
+
await result.refetch();
|
|
375
|
+
|
|
376
|
+
expect(query).toHaveBeenCalledTimes(2);
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
it("should update data after refetch", async () => {
|
|
380
|
+
const useItems = createDataHook<MockItem>({
|
|
381
|
+
model: "Item",
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
const result = useItems();
|
|
385
|
+
await nextTick();
|
|
386
|
+
|
|
387
|
+
const newItems = [{ id: "4", name: "New Item", status: "active" as const }];
|
|
388
|
+
(query as any).mockResolvedValue({
|
|
389
|
+
data: newItems,
|
|
390
|
+
errors: null,
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
await result.refetch();
|
|
394
|
+
|
|
395
|
+
expect(result.data.value).toEqual(newItems);
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
it("should clear previous error on successful refetch", async () => {
|
|
399
|
+
(query as any).mockResolvedValueOnce({
|
|
400
|
+
data: null,
|
|
401
|
+
errors: [{ message: "First error" }],
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
const useItems = createDataHook<MockItem>({
|
|
405
|
+
model: "Item",
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
const result = useItems();
|
|
409
|
+
await nextTick();
|
|
410
|
+
|
|
411
|
+
expect(result.error.value).not.toBeNull();
|
|
412
|
+
|
|
413
|
+
(query as any).mockResolvedValue({
|
|
414
|
+
data: mockItems,
|
|
415
|
+
errors: null,
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
await result.refetch();
|
|
419
|
+
|
|
420
|
+
expect(result.error.value).toBeNull();
|
|
421
|
+
expect(result.data.value).toEqual(mockItems);
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
it("should set loading state during refetch", async () => {
|
|
425
|
+
let resolveQuery: (value: any) => void;
|
|
426
|
+
(query as any).mockReturnValue(
|
|
427
|
+
new Promise((resolve) => {
|
|
428
|
+
resolveQuery = resolve;
|
|
429
|
+
}),
|
|
430
|
+
);
|
|
431
|
+
|
|
432
|
+
const useItems = createDataHook<MockItem>({
|
|
433
|
+
model: "Item",
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
const result = useItems({ autoFetch: false });
|
|
437
|
+
|
|
438
|
+
expect(result.loading.value).toBe(false);
|
|
439
|
+
|
|
440
|
+
const refetchPromise = result.refetch();
|
|
441
|
+
|
|
442
|
+
expect(result.loading.value).toBe(true);
|
|
443
|
+
|
|
444
|
+
resolveQuery!({ data: mockItems, errors: null });
|
|
445
|
+
await refetchPromise;
|
|
446
|
+
|
|
447
|
+
expect(result.loading.value).toBe(false);
|
|
448
|
+
});
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
describe("resetClientInstance", () => {
|
|
452
|
+
it("should be a no-op function (proxy-based approach)", () => {
|
|
453
|
+
// Should not throw
|
|
454
|
+
expect(() => resetClientInstance()).not.toThrow();
|
|
455
|
+
});
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
describe("edge cases", () => {
|
|
459
|
+
it("should handle empty data response", async () => {
|
|
460
|
+
(query as any).mockResolvedValue({
|
|
461
|
+
data: [],
|
|
462
|
+
errors: null,
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
const useItems = createDataHook<MockItem>({
|
|
466
|
+
model: "Item",
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
const result = useItems();
|
|
470
|
+
await nextTick();
|
|
471
|
+
|
|
472
|
+
expect(result.data.value).toEqual([]);
|
|
473
|
+
expect(result.error.value).toBeNull();
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
it("should handle null data response", async () => {
|
|
477
|
+
(query as any).mockResolvedValue({
|
|
478
|
+
data: null,
|
|
479
|
+
errors: null,
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
const useItems = createDataHook<MockItem>({
|
|
483
|
+
model: "Item",
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
const result = useItems();
|
|
487
|
+
await nextTick();
|
|
488
|
+
|
|
489
|
+
expect(result.data.value).toEqual([]);
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
it("should handle undefined data response", async () => {
|
|
493
|
+
(query as any).mockResolvedValue({
|
|
494
|
+
data: undefined,
|
|
495
|
+
errors: null,
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
const useItems = createDataHook<MockItem>({
|
|
499
|
+
model: "Item",
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
const result = useItems();
|
|
503
|
+
await nextTick();
|
|
504
|
+
|
|
505
|
+
expect(result.data.value).toEqual([]);
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
it("should not pass empty filter object", async () => {
|
|
509
|
+
const useItems = createDataHook<MockItem>({
|
|
510
|
+
model: "Item",
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
useItems({ filter: {} });
|
|
514
|
+
|
|
515
|
+
expect(query).toHaveBeenCalledWith("Item", "list", {});
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
it("should generate default error message when getErrorMessage returns null", async () => {
|
|
519
|
+
(query as any).mockResolvedValue({
|
|
520
|
+
data: null,
|
|
521
|
+
errors: [{}], // Error without message
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
const useItems = createDataHook<MockItem>({
|
|
525
|
+
model: "TestModel",
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
const result = useItems();
|
|
529
|
+
await nextTick();
|
|
530
|
+
|
|
531
|
+
expect(result.error.value?.message).toBe("Failed to fetch TestModel");
|
|
532
|
+
});
|
|
533
|
+
});
|
|
534
|
+
});
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { ref, computed, onMounted, type Ref, type ComputedRef } from "vue";
|
|
9
|
-
import {
|
|
9
|
+
import { query, hasErrors, getErrorMessage } from "../client/proxy";
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Configuration options for creating a data hook
|
|
@@ -56,8 +56,10 @@ export interface DataHookReturn<T, TComputed extends Record<string, any> = Recor
|
|
|
56
56
|
computed: { [K in keyof TComputed]: ComputedRef<TComputed[K]> };
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
//
|
|
60
|
-
export
|
|
59
|
+
// Reset function for testing convenience (no-op with proxy-based approach)
|
|
60
|
+
export function resetClientInstance(): void {
|
|
61
|
+
// No-op: proxy-based approach doesn't need client reset
|
|
62
|
+
}
|
|
61
63
|
|
|
62
64
|
/**
|
|
63
65
|
* Creates a reusable data hook for a specific model
|
|
@@ -132,29 +134,34 @@ export function createDataHook<
|
|
|
132
134
|
return baseFilter && Object.keys(baseFilter).length > 0 ? baseFilter : undefined;
|
|
133
135
|
};
|
|
134
136
|
|
|
135
|
-
// Fetch function
|
|
137
|
+
// Fetch function using server proxy
|
|
136
138
|
async function fetch() {
|
|
137
139
|
loading.value = true;
|
|
138
140
|
error.value = null;
|
|
139
141
|
|
|
140
142
|
try {
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
143
|
+
const queryOptions: Record<string, any> = {};
|
|
144
|
+
|
|
145
|
+
const filter = getFilter();
|
|
146
|
+
if (filter) {
|
|
147
|
+
queryOptions.filter = filter;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (limit) {
|
|
151
|
+
queryOptions.limit = limit;
|
|
152
|
+
}
|
|
146
153
|
|
|
147
154
|
if (selectionSet) {
|
|
148
155
|
queryOptions.selectionSet = selectionSet;
|
|
149
156
|
}
|
|
150
157
|
|
|
151
|
-
const
|
|
158
|
+
const response = await query(model, "list", queryOptions);
|
|
152
159
|
|
|
153
|
-
if (
|
|
154
|
-
throw new Error(
|
|
160
|
+
if (hasErrors(response)) {
|
|
161
|
+
throw new Error(getErrorMessage(response) || `Failed to fetch ${model}`);
|
|
155
162
|
}
|
|
156
163
|
|
|
157
|
-
const items =
|
|
164
|
+
const items = response.data || [];
|
|
158
165
|
data.value = transform ? items.map(transform) : items;
|
|
159
166
|
} catch (e) {
|
|
160
167
|
error.value = e as Error;
|
package/src/hooks/index.ts
CHANGED
|
@@ -20,3 +20,5 @@ export { useAccounts, type UseAccountsOptions, type UseAccountsReturn } from "./
|
|
|
20
20
|
export { useUsers, type UseUsersOptions, type UseUsersReturn } from "./useUsers";
|
|
21
21
|
export { useProducts, type UseProductsOptions, type UseProductsReturn } from "./useProducts";
|
|
22
22
|
export { useProductInstances, type UseProductInstancesOptions, type UseProductInstancesReturn } from "./useProductInstances";
|
|
23
|
+
export { useReservations, type UseReservationsOptions, type UseReservationsReturn } from "./useReservations";
|
|
24
|
+
export { useContacts, type UseContactsOptions, type UseContactsReturn } from "./useContacts";
|