@osdk/react 0.9.0-beta.1 → 0.9.0-beta.10
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/AGENTS.md +253 -0
- package/CHANGELOG.md +118 -0
- package/build/browser/intellisense.test.js +1 -1
- package/build/browser/intellisense.test.js.map +1 -1
- package/build/browser/new/makeExternalStore.js +2 -2
- package/build/browser/new/makeExternalStore.js.map +1 -1
- package/build/browser/new/platform-apis/admin/useCurrentFoundryUser.js +44 -0
- package/build/browser/new/platform-apis/admin/useCurrentFoundryUser.js.map +1 -0
- package/build/browser/new/platform-apis/admin/useFoundryUser.js +50 -0
- package/build/browser/new/platform-apis/admin/useFoundryUser.js.map +1 -0
- package/build/browser/new/platform-apis/admin/useFoundryUsersList.js +54 -0
- package/build/browser/new/platform-apis/admin/useFoundryUsersList.js.map +1 -0
- package/build/browser/new/useLinks.js +15 -8
- package/build/browser/new/useLinks.js.map +1 -1
- package/build/browser/new/useObjectSet.js +20 -4
- package/build/browser/new/useObjectSet.js.map +1 -1
- package/build/browser/new/useOsdkAction.js.map +1 -1
- package/build/browser/new/useOsdkAggregation.js +1 -1
- package/build/browser/new/useOsdkAggregation.js.map +1 -1
- package/build/browser/new/useOsdkFunction.js +101 -0
- package/build/browser/new/useOsdkFunction.js.map +1 -0
- package/build/browser/new/useOsdkObject.js +1 -1
- package/build/browser/new/useOsdkObject.js.map +1 -1
- package/build/browser/new/useOsdkObjects.js +4 -3
- package/build/browser/new/useOsdkObjects.js.map +1 -1
- package/build/browser/public/experimental.js +4 -0
- package/build/browser/public/experimental.js.map +1 -1
- package/build/browser/utils/usePlatformQuery.js +74 -0
- package/build/browser/utils/usePlatformQuery.js.map +1 -0
- package/build/cjs/{chunk-OVBG5VXE.cjs → chunk-V32JHU3O.cjs} +8 -3
- package/build/cjs/chunk-V32JHU3O.cjs.map +1 -0
- package/build/cjs/index.cjs +4 -4
- package/build/cjs/public/experimental.cjs +413 -67
- package/build/cjs/public/experimental.cjs.map +1 -1
- package/build/cjs/public/experimental.d.cts +279 -32
- package/build/esm/intellisense.test.js +1 -1
- package/build/esm/intellisense.test.js.map +1 -1
- package/build/esm/new/makeExternalStore.js +2 -2
- package/build/esm/new/makeExternalStore.js.map +1 -1
- package/build/esm/new/platform-apis/admin/useCurrentFoundryUser.js +44 -0
- package/build/esm/new/platform-apis/admin/useCurrentFoundryUser.js.map +1 -0
- package/build/esm/new/platform-apis/admin/useFoundryUser.js +50 -0
- package/build/esm/new/platform-apis/admin/useFoundryUser.js.map +1 -0
- package/build/esm/new/platform-apis/admin/useFoundryUsersList.js +54 -0
- package/build/esm/new/platform-apis/admin/useFoundryUsersList.js.map +1 -0
- package/build/esm/new/useLinks.js +15 -8
- package/build/esm/new/useLinks.js.map +1 -1
- package/build/esm/new/useObjectSet.js +20 -4
- package/build/esm/new/useObjectSet.js.map +1 -1
- package/build/esm/new/useOsdkAction.js.map +1 -1
- package/build/esm/new/useOsdkAggregation.js +1 -1
- package/build/esm/new/useOsdkAggregation.js.map +1 -1
- package/build/esm/new/useOsdkFunction.js +101 -0
- package/build/esm/new/useOsdkFunction.js.map +1 -0
- package/build/esm/new/useOsdkObject.js +1 -1
- package/build/esm/new/useOsdkObject.js.map +1 -1
- package/build/esm/new/useOsdkObjects.js +4 -3
- package/build/esm/new/useOsdkObjects.js.map +1 -1
- package/build/esm/public/experimental.js +4 -0
- package/build/esm/public/experimental.js.map +1 -1
- package/build/esm/utils/usePlatformQuery.js +74 -0
- package/build/esm/utils/usePlatformQuery.js.map +1 -0
- package/build/types/new/makeExternalStore.d.ts +1 -1
- package/build/types/new/makeExternalStore.d.ts.map +1 -1
- package/build/types/new/platform-apis/admin/useCurrentFoundryUser.d.ts +28 -0
- package/build/types/new/platform-apis/admin/useCurrentFoundryUser.d.ts.map +1 -0
- package/build/types/new/platform-apis/admin/useFoundryUser.d.ts +36 -0
- package/build/types/new/platform-apis/admin/useFoundryUser.d.ts.map +1 -0
- package/build/types/new/platform-apis/admin/useFoundryUsersList.d.ts +52 -0
- package/build/types/new/platform-apis/admin/useFoundryUsersList.d.ts.map +1 -0
- package/build/types/new/useLinks.d.ts +5 -5
- package/build/types/new/useLinks.d.ts.map +1 -1
- package/build/types/new/useObjectSet.d.ts +4 -0
- package/build/types/new/useObjectSet.d.ts.map +1 -1
- package/build/types/new/useOsdkAction.d.ts +3 -3
- package/build/types/new/useOsdkAction.d.ts.map +1 -1
- package/build/types/new/useOsdkAggregation.d.ts +10 -12
- package/build/types/new/useOsdkAggregation.d.ts.map +1 -1
- package/build/types/new/useOsdkFunction.d.ts +112 -0
- package/build/types/new/useOsdkFunction.d.ts.map +1 -0
- package/build/types/new/useOsdkObjects.d.ts +31 -13
- package/build/types/new/useOsdkObjects.d.ts.map +1 -1
- package/build/types/public/experimental.d.ts +5 -0
- package/build/types/public/experimental.d.ts.map +1 -1
- package/build/types/utils/usePlatformQuery.d.ts +25 -0
- package/build/types/utils/usePlatformQuery.d.ts.map +1 -0
- package/docs/actions.md +414 -0
- package/docs/advanced-queries.md +663 -0
- package/docs/cache-management.md +213 -0
- package/docs/getting-started.md +382 -0
- package/docs/platform-apis.md +203 -0
- package/docs/querying-data.md +648 -0
- package/package.json +8 -4
- package/build/browser/new/types.js +0 -2
- package/build/browser/new/types.js.map +0 -1
- package/build/cjs/chunk-OVBG5VXE.cjs.map +0 -1
- package/build/esm/new/types.js +0 -2
- package/build/esm/new/types.js.map +0 -1
- package/build/types/new/types.d.ts +0 -5
- package/build/types/new/types.d.ts.map +0 -1
|
@@ -0,0 +1,663 @@
|
|
|
1
|
+
---
|
|
2
|
+
sidebar_position: 4
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Advanced Queries
|
|
6
|
+
|
|
7
|
+
This guide covers advanced querying patterns including useObjectSet, derived properties, aggregations, and metadata.
|
|
8
|
+
|
|
9
|
+
## useObjectSet
|
|
10
|
+
|
|
11
|
+
*Experimental - import from `@osdk/react/experimental`*
|
|
12
|
+
|
|
13
|
+
Advanced querying with set operations, derived properties, and link traversal.
|
|
14
|
+
|
|
15
|
+
### When to Use useObjectSet vs useOsdkObjects
|
|
16
|
+
|
|
17
|
+
Both hooks support where, orderBy, pagination, withProperties, pivotTo, autoFetchMore, and streamUpdates.
|
|
18
|
+
|
|
19
|
+
**Use useOsdkObjects when:**
|
|
20
|
+
- Passing an ObjectType or Interface directly (`Todo`)
|
|
21
|
+
|
|
22
|
+
**Use useObjectSet when:**
|
|
23
|
+
- Starting from an ObjectSet instance (`$(Todo)`)
|
|
24
|
+
- Need set operations (`union`, `intersect`, `subtract`) with other ObjectSets
|
|
25
|
+
|
|
26
|
+
:::note
|
|
27
|
+
`useOsdkObjects` is in active development to reach full OSDK TypeScript client parity as it has more performance enhancements. `useObjectSet` currently supports everything and should be used if a feature you need isn't present in `useOsdkObjects`. Check the JSDoc for current feature support.
|
|
28
|
+
:::
|
|
29
|
+
|
|
30
|
+
```tsx
|
|
31
|
+
import { $, Todo } from "@my/osdk";
|
|
32
|
+
import { useObjectSet, useOsdkObjects } from "@osdk/react/experimental";
|
|
33
|
+
|
|
34
|
+
// Simple query - use useOsdkObjects
|
|
35
|
+
const { data } = useOsdkObjects(Todo, {
|
|
36
|
+
where: { isComplete: false },
|
|
37
|
+
orderBy: { createdAt: "desc" },
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// Set operations - use useObjectSet
|
|
41
|
+
const urgentTodos = $(Todo).where({ isUrgent: true });
|
|
42
|
+
const completedTodos = $(Todo).where({ isComplete: true });
|
|
43
|
+
|
|
44
|
+
const { data } = useObjectSet($(Todo), {
|
|
45
|
+
union: [urgentTodos],
|
|
46
|
+
subtract: [completedTodos],
|
|
47
|
+
});
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
:::note The `$` function
|
|
51
|
+
The `$` function from your generated SDK creates an ObjectSet from an object type. `$(Todo)` creates an ObjectSet containing all Todo objects that you can then filter, union, intersect, or subtract with other ObjectSets.
|
|
52
|
+
:::
|
|
53
|
+
|
|
54
|
+
### Basic Usage
|
|
55
|
+
|
|
56
|
+
```tsx
|
|
57
|
+
import { $, Todo } from "@my/osdk";
|
|
58
|
+
import { useObjectSet } from "@osdk/react/experimental";
|
|
59
|
+
|
|
60
|
+
function TodosWithSetOperations() {
|
|
61
|
+
const allTodos = $(Todo);
|
|
62
|
+
const completedTodos = $(Todo).where({ isComplete: true });
|
|
63
|
+
|
|
64
|
+
const { data, isLoading, fetchMore } = useObjectSet(allTodos, {
|
|
65
|
+
subtract: [completedTodos],
|
|
66
|
+
where: { priority: "high" },
|
|
67
|
+
orderBy: { createdAt: "desc" },
|
|
68
|
+
pageSize: 20,
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<div>
|
|
73
|
+
{data?.map(todo => (
|
|
74
|
+
<div key={todo.$primaryKey}>
|
|
75
|
+
{todo.title}
|
|
76
|
+
</div>
|
|
77
|
+
))}
|
|
78
|
+
</div>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Set Operations
|
|
84
|
+
|
|
85
|
+
#### Union
|
|
86
|
+
|
|
87
|
+
Combine multiple object sets:
|
|
88
|
+
|
|
89
|
+
```tsx
|
|
90
|
+
import { $, Todo } from "@my/osdk";
|
|
91
|
+
import { useObjectSet } from "@osdk/react/experimental";
|
|
92
|
+
|
|
93
|
+
function CombinedTodoQuery() {
|
|
94
|
+
const highPriorityTodos = $(Todo).where({ priority: "high" });
|
|
95
|
+
const urgentTodos = $(Todo).where({ isUrgent: true });
|
|
96
|
+
|
|
97
|
+
const { data } = useObjectSet(highPriorityTodos, {
|
|
98
|
+
union: [urgentTodos], // High priority OR urgent
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
return <div>High priority or urgent: {data?.length}</div>;
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
#### Intersect
|
|
106
|
+
|
|
107
|
+
Find objects that exist in all sets:
|
|
108
|
+
|
|
109
|
+
```tsx
|
|
110
|
+
import { $, Employee } from "@my/osdk";
|
|
111
|
+
import { useObjectSet } from "@osdk/react/experimental";
|
|
112
|
+
|
|
113
|
+
function SharedProjects({ employee1, employee2 }: {
|
|
114
|
+
employee1: Employee.OsdkInstance;
|
|
115
|
+
employee2: Employee.OsdkInstance;
|
|
116
|
+
}) {
|
|
117
|
+
const set1 = $(Employee).where({ id: employee1.id });
|
|
118
|
+
const set2 = $(Employee).where({ id: employee2.id });
|
|
119
|
+
|
|
120
|
+
const { data } = useObjectSet(set1, {
|
|
121
|
+
pivotTo: "projects",
|
|
122
|
+
intersect: [set2.$pivotTo("projects")],
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
return (
|
|
126
|
+
<div>
|
|
127
|
+
<h3>Shared Projects</h3>
|
|
128
|
+
{data?.map(project => (
|
|
129
|
+
<div key={project.$primaryKey}>{project.name}</div>
|
|
130
|
+
))}
|
|
131
|
+
</div>
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
#### Subtract
|
|
137
|
+
|
|
138
|
+
Remove objects that exist in another set:
|
|
139
|
+
|
|
140
|
+
```tsx
|
|
141
|
+
import { $, Todo } from "@my/osdk";
|
|
142
|
+
import { useObjectSet } from "@osdk/react/experimental";
|
|
143
|
+
|
|
144
|
+
function ActiveTodos() {
|
|
145
|
+
const allTodos = $(Todo);
|
|
146
|
+
const completedTodos = $(Todo).where({ isComplete: true });
|
|
147
|
+
|
|
148
|
+
const { data } = useObjectSet(allTodos, {
|
|
149
|
+
subtract: [completedTodos],
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
return <div>Active todos: {data?.length}</div>;
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
#### Combined Operations
|
|
157
|
+
|
|
158
|
+
```tsx
|
|
159
|
+
import { $, Todo } from "@my/osdk";
|
|
160
|
+
import { useObjectSet } from "@osdk/react/experimental";
|
|
161
|
+
|
|
162
|
+
function ComplexTodoQuery() {
|
|
163
|
+
const highPriorityTodos = $(Todo).where({ priority: "high" });
|
|
164
|
+
const urgentTodos = $(Todo).where({ isUrgent: true });
|
|
165
|
+
const completedTodos = $(Todo).where({ isComplete: true });
|
|
166
|
+
|
|
167
|
+
const { data } = useObjectSet(highPriorityTodos, {
|
|
168
|
+
union: [urgentTodos], // High priority OR urgent
|
|
169
|
+
subtract: [completedTodos], // But not completed
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
return <div>High priority or urgent (but not completed): {data?.length}</div>;
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Link Traversal with pivotTo
|
|
177
|
+
|
|
178
|
+
Navigate to linked objects:
|
|
179
|
+
|
|
180
|
+
```tsx
|
|
181
|
+
import { $, Employee } from "@my/osdk";
|
|
182
|
+
import { useObjectSet } from "@osdk/react/experimental";
|
|
183
|
+
|
|
184
|
+
function EmployeeDepartments({ employee }: { employee: Employee.OsdkInstance }) {
|
|
185
|
+
const employeeSet = $(Employee).where({ id: employee.id });
|
|
186
|
+
|
|
187
|
+
const { data } = useObjectSet(employeeSet, {
|
|
188
|
+
pivotTo: "department",
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
return (
|
|
192
|
+
<div>
|
|
193
|
+
Departments: {data?.map(dept => dept.name).join(", ")}
|
|
194
|
+
</div>
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### Auto-Fetching and Streaming
|
|
200
|
+
|
|
201
|
+
```tsx
|
|
202
|
+
import { $, Todo } from "@my/osdk";
|
|
203
|
+
import { useObjectSet } from "@osdk/react/experimental";
|
|
204
|
+
|
|
205
|
+
const { data, isLoading } = useObjectSet($(Todo), {
|
|
206
|
+
where: { isComplete: false },
|
|
207
|
+
autoFetchMore: 200, // Fetch at least 200 items
|
|
208
|
+
streamUpdates: true, // Real-time WebSocket updates
|
|
209
|
+
});
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### All Options
|
|
213
|
+
|
|
214
|
+
- `where` - Filter objects
|
|
215
|
+
- `withProperties` - Add derived/computed properties
|
|
216
|
+
- `union` - Combine with other ObjectSets
|
|
217
|
+
- `intersect` - Find common objects with other ObjectSets
|
|
218
|
+
- `subtract` - Remove objects that exist in other ObjectSets
|
|
219
|
+
- `pivotTo` - Traverse to linked objects (changes result type)
|
|
220
|
+
- `pageSize` - Number of objects per page
|
|
221
|
+
- `orderBy` - Sort order
|
|
222
|
+
- `dedupeIntervalMs` - Minimum time between re-fetches (default: 2000ms)
|
|
223
|
+
- `streamUpdates` - Enable real-time websocket updates (default: false)
|
|
224
|
+
- `autoFetchMore` - Auto-fetch additional pages
|
|
225
|
+
- `enabled` - Enable/disable the query
|
|
226
|
+
|
|
227
|
+
### Return Values
|
|
228
|
+
|
|
229
|
+
- `data` - Array of objects with derived properties
|
|
230
|
+
- `isLoading` - True while fetching
|
|
231
|
+
- `error` - Error object if fetch failed
|
|
232
|
+
- `fetchMore` - Function to load next page
|
|
233
|
+
- `objectSet` - The transformed ObjectSet after all operations
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## Derived Properties
|
|
238
|
+
|
|
239
|
+
*Available in both useOsdkObjects and useObjectSet*
|
|
240
|
+
|
|
241
|
+
Add computed properties calculated server-side using the builder pattern.
|
|
242
|
+
|
|
243
|
+
### Basic Usage
|
|
244
|
+
|
|
245
|
+
Derived properties use a builder function that receives a `DerivedProperty.Builder`:
|
|
246
|
+
|
|
247
|
+
```tsx
|
|
248
|
+
import type { DerivedProperty } from "@osdk/client";
|
|
249
|
+
import { Employee } from "@my/osdk";
|
|
250
|
+
import { useOsdkObjects } from "@osdk/react/experimental";
|
|
251
|
+
|
|
252
|
+
const { data } = useOsdkObjects(Employee, {
|
|
253
|
+
where: { department: "Engineering" },
|
|
254
|
+
withProperties: {
|
|
255
|
+
// Get manager's name via link traversal
|
|
256
|
+
managerName: (base: DerivedProperty.Builder<Employee, false>) =>
|
|
257
|
+
base.pivotTo("manager").selectProperty("fullName"),
|
|
258
|
+
|
|
259
|
+
// Count direct reports
|
|
260
|
+
reportCount: (base: DerivedProperty.Builder<Employee, false>) =>
|
|
261
|
+
base.pivotTo("reports").aggregate("$count"),
|
|
262
|
+
},
|
|
263
|
+
});
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### Builder Methods
|
|
267
|
+
|
|
268
|
+
The builder provides these methods:
|
|
269
|
+
|
|
270
|
+
- `.pivotTo(linkName)` - Navigate to linked objects
|
|
271
|
+
- `.selectProperty(propertyName)` - Select a property value
|
|
272
|
+
- `.aggregate(aggregation)` - Aggregate values (`"$count"`, `"propertyName:$avg"`, etc.)
|
|
273
|
+
- `.where(clause)` - Filter before aggregating
|
|
274
|
+
|
|
275
|
+
### Advanced Examples
|
|
276
|
+
|
|
277
|
+
```tsx
|
|
278
|
+
import type { DerivedProperty } from "@osdk/client";
|
|
279
|
+
|
|
280
|
+
const { data } = useOsdkObjects(Employee, {
|
|
281
|
+
where: { department: "Engineering" },
|
|
282
|
+
withProperties: {
|
|
283
|
+
// Chained traversal
|
|
284
|
+
departmentSize: (base: DerivedProperty.Builder<Employee, false>) =>
|
|
285
|
+
base.pivotTo("manager")
|
|
286
|
+
.pivotTo("reports")
|
|
287
|
+
.aggregate("$count"),
|
|
288
|
+
|
|
289
|
+
// Aggregate a specific property
|
|
290
|
+
avgReportSalary: (base: DerivedProperty.Builder<Employee, false>) =>
|
|
291
|
+
base.pivotTo("reports")
|
|
292
|
+
.selectProperty("salary")
|
|
293
|
+
.aggregate("$avg"),
|
|
294
|
+
},
|
|
295
|
+
});
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### Filtering on Derived Properties
|
|
299
|
+
|
|
300
|
+
You can filter on derived properties in your where clause:
|
|
301
|
+
|
|
302
|
+
```tsx
|
|
303
|
+
import type { DerivedProperty } from "@osdk/client";
|
|
304
|
+
|
|
305
|
+
const { data } = useOsdkObjects(Employee, {
|
|
306
|
+
withProperties: {
|
|
307
|
+
reportCount: (base: DerivedProperty.Builder<Employee, false>) =>
|
|
308
|
+
base.pivotTo("reports").aggregate("$count"),
|
|
309
|
+
},
|
|
310
|
+
where: {
|
|
311
|
+
department: "Engineering",
|
|
312
|
+
reportCount: { $gt: 0 }, // Only managers
|
|
313
|
+
},
|
|
314
|
+
});
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
---
|
|
318
|
+
|
|
319
|
+
## useOsdkFunction
|
|
320
|
+
|
|
321
|
+
*Experimental - import from `@osdk/react/experimental`*
|
|
322
|
+
|
|
323
|
+
Execute and observe functions with request deduplication and configurable dependency tracking for automatic refetching.
|
|
324
|
+
|
|
325
|
+
### Basic Usage
|
|
326
|
+
|
|
327
|
+
```tsx
|
|
328
|
+
import { addOne } from "@my/osdk";
|
|
329
|
+
import { useOsdkFunction } from "@osdk/react/experimental";
|
|
330
|
+
|
|
331
|
+
function AddOneDemo() {
|
|
332
|
+
const { data, isLoading, error } = useOsdkFunction(addOne, {
|
|
333
|
+
params: { n: 5 },
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
if (isLoading && data === undefined) {
|
|
337
|
+
return <div>Calculating...</div>;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
if (error) {
|
|
341
|
+
return <div>Error: {error.message}</div>;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
return <div>Result: {data}</div>;
|
|
345
|
+
}
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
### Functions Without Parameters
|
|
349
|
+
|
|
350
|
+
```tsx
|
|
351
|
+
import { getTodoCount } from "@my/osdk";
|
|
352
|
+
import { useOsdkFunction } from "@osdk/react/experimental";
|
|
353
|
+
|
|
354
|
+
function TodoCount() {
|
|
355
|
+
const { data, isLoading } = useOsdkFunction(getTodoCount);
|
|
356
|
+
|
|
357
|
+
return (
|
|
358
|
+
<div>
|
|
359
|
+
{isLoading && <span>Loading...</span>}
|
|
360
|
+
{data !== undefined && <span>Total todos: {data}</span>}
|
|
361
|
+
</div>
|
|
362
|
+
);
|
|
363
|
+
}
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
### Dependency Tracking
|
|
367
|
+
|
|
368
|
+
Automatically refetch when actions modify objects of specified types:
|
|
369
|
+
|
|
370
|
+
```tsx
|
|
371
|
+
import { Employee, getEmployeeMetrics } from "@my/osdk";
|
|
372
|
+
import { useOsdkFunction } from "@osdk/react/experimental";
|
|
373
|
+
|
|
374
|
+
function EmployeeMetrics({ departmentId }: { departmentId: string }) {
|
|
375
|
+
const { data, isLoading, refetch } = useOsdkFunction(getEmployeeMetrics, {
|
|
376
|
+
params: { departmentId },
|
|
377
|
+
dependsOn: [Employee], // Refetch when any Employee changes
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
return (
|
|
381
|
+
<div>
|
|
382
|
+
{isLoading && <span>Updating...</span>}
|
|
383
|
+
{data && <span>Headcount: {data.headcount}</span>}
|
|
384
|
+
<button onClick={refetch}>Refresh</button>
|
|
385
|
+
</div>
|
|
386
|
+
);
|
|
387
|
+
}
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
### Specific Object Dependencies
|
|
391
|
+
|
|
392
|
+
For finer-grained control, depend on specific object instances:
|
|
393
|
+
|
|
394
|
+
```tsx
|
|
395
|
+
import { Employee, getEmployeeReport } from "@my/osdk";
|
|
396
|
+
import { useOsdkFunction, useOsdkObject } from "@osdk/react/experimental";
|
|
397
|
+
|
|
398
|
+
function EmployeeReport({ employee }: { employee: Employee.OsdkInstance }) {
|
|
399
|
+
const { data, isLoading } = useOsdkFunction(getEmployeeReport, {
|
|
400
|
+
params: { employeeId: employee.$primaryKey },
|
|
401
|
+
dependsOnObjects: [employee], // Refetch only when this employee changes
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
return (
|
|
405
|
+
<div>
|
|
406
|
+
{isLoading && <span>Loading report...</span>}
|
|
407
|
+
{data && <pre>{JSON.stringify(data, null, 2)}</pre>}
|
|
408
|
+
</div>
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
### Conditional Execution
|
|
414
|
+
|
|
415
|
+
Use `enabled` to control when the function executes:
|
|
416
|
+
|
|
417
|
+
```tsx
|
|
418
|
+
import { Employee, getEmployeeReport } from "@my/osdk";
|
|
419
|
+
import { useOsdkFunction, useOsdkObject } from "@osdk/react/experimental";
|
|
420
|
+
|
|
421
|
+
function ConditionalReport({ employeeId }: { employeeId: string }) {
|
|
422
|
+
const { object: employee } = useOsdkObject(Employee, employeeId);
|
|
423
|
+
|
|
424
|
+
const { data, isLoading } = useOsdkFunction(getEmployeeReport, {
|
|
425
|
+
params: { employeeId },
|
|
426
|
+
enabled: employee !== undefined, // Wait for employee to load
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
if (!employee) {
|
|
430
|
+
return <div>Loading employee...</div>;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
return (
|
|
434
|
+
<div>
|
|
435
|
+
<h2>{employee.fullName}</h2>
|
|
436
|
+
{isLoading && <span>Loading report...</span>}
|
|
437
|
+
{data && <div>Report: {JSON.stringify(data)}</div>}
|
|
438
|
+
</div>
|
|
439
|
+
);
|
|
440
|
+
}
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
### Options
|
|
444
|
+
|
|
445
|
+
- `params` - Parameters to pass to the function (required if function has parameters)
|
|
446
|
+
- `dependsOn` - Array of object types; refetch when any object of these types changes
|
|
447
|
+
- `dependsOnObjects` - Array of specific object instances; refetch when these objects change
|
|
448
|
+
- `dedupeIntervalMs` - Milliseconds to dedupe identical calls (default: 2000)
|
|
449
|
+
- `enabled` - Enable/disable execution (default: true)
|
|
450
|
+
|
|
451
|
+
### Return Values
|
|
452
|
+
|
|
453
|
+
- `data` - Function result, or undefined if not loaded or on error
|
|
454
|
+
- `isLoading` - True while the function is executing
|
|
455
|
+
- `error` - Error object if execution failed
|
|
456
|
+
- `lastUpdated` - Timestamp (ms since epoch) when result was last fetched
|
|
457
|
+
- `refetch` - Function to manually trigger a refetch
|
|
458
|
+
|
|
459
|
+
---
|
|
460
|
+
|
|
461
|
+
## useOsdkAggregation
|
|
462
|
+
|
|
463
|
+
*Experimental - import from `@osdk/react/experimental`*
|
|
464
|
+
|
|
465
|
+
Server-side grouping and aggregation.
|
|
466
|
+
|
|
467
|
+
### Simple Aggregation
|
|
468
|
+
|
|
469
|
+
```tsx
|
|
470
|
+
import { Todo } from "@my/osdk";
|
|
471
|
+
import { useOsdkAggregation } from "@osdk/react/experimental";
|
|
472
|
+
|
|
473
|
+
function TodoStats() {
|
|
474
|
+
const { data, isLoading, error } = useOsdkAggregation(Todo, {
|
|
475
|
+
aggregate: {
|
|
476
|
+
$select: {
|
|
477
|
+
$count: "unordered",
|
|
478
|
+
"priority:avg": "unordered",
|
|
479
|
+
"dueDate:max": "unordered",
|
|
480
|
+
},
|
|
481
|
+
},
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
if (isLoading) {
|
|
485
|
+
return <div>Calculating stats...</div>;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
if (error) {
|
|
489
|
+
return <div>Error: {JSON.stringify(error)}</div>;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
// Results: "propertyName:metric" in $select becomes data.propertyName.metric
|
|
493
|
+
return (
|
|
494
|
+
<div>
|
|
495
|
+
<p>Total Todos: {data?.$count}</p>
|
|
496
|
+
<p>Average Priority: {data?.priority.avg}</p>
|
|
497
|
+
<p>Latest Due Date: {data?.dueDate.max}</p>
|
|
498
|
+
</div>
|
|
499
|
+
);
|
|
500
|
+
}
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
### Grouped Aggregations
|
|
504
|
+
|
|
505
|
+
```tsx
|
|
506
|
+
import { Todo } from "@my/osdk";
|
|
507
|
+
import { useOsdkAggregation } from "@osdk/react/experimental";
|
|
508
|
+
|
|
509
|
+
function TodosByStatus() {
|
|
510
|
+
const { data, isLoading } = useOsdkAggregation(Todo, {
|
|
511
|
+
aggregate: {
|
|
512
|
+
$groupBy: { status: "exact" },
|
|
513
|
+
$select: {
|
|
514
|
+
$count: "unordered",
|
|
515
|
+
"priority:avg": "unordered",
|
|
516
|
+
},
|
|
517
|
+
},
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
if (isLoading) {
|
|
521
|
+
return <div>Loading...</div>;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
return (
|
|
525
|
+
<div>
|
|
526
|
+
{data?.map((group, idx) => (
|
|
527
|
+
<div key={idx}>
|
|
528
|
+
<h3>Status: {group.$group.status}</h3>
|
|
529
|
+
<p>Count: {group.$count}</p>
|
|
530
|
+
<p>Avg Priority: {group.priority.avg}</p>
|
|
531
|
+
</div>
|
|
532
|
+
))}
|
|
533
|
+
</div>
|
|
534
|
+
);
|
|
535
|
+
}
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
### Filtered Aggregations
|
|
539
|
+
|
|
540
|
+
```tsx
|
|
541
|
+
import { Todo } from "@my/osdk";
|
|
542
|
+
import { useOsdkAggregation } from "@osdk/react/experimental";
|
|
543
|
+
|
|
544
|
+
function HighPriorityStats() {
|
|
545
|
+
const { data, isLoading } = useOsdkAggregation(Todo, {
|
|
546
|
+
where: { priority: "high", isComplete: false },
|
|
547
|
+
aggregate: {
|
|
548
|
+
$select: {
|
|
549
|
+
$count: "unordered",
|
|
550
|
+
"dueDate:min": "unordered",
|
|
551
|
+
},
|
|
552
|
+
},
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
if (isLoading || !data) return <div>Loading...</div>;
|
|
556
|
+
|
|
557
|
+
return (
|
|
558
|
+
<div>
|
|
559
|
+
<p>High Priority Incomplete: {data.$count}</p>
|
|
560
|
+
<p>Earliest Due: {data.dueDate.min}</p>
|
|
561
|
+
</div>
|
|
562
|
+
);
|
|
563
|
+
}
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
### Aggregation Syntax
|
|
567
|
+
|
|
568
|
+
The `$select` object uses a special key format where each key is a metric and each value is an ordering directive (`"unordered"`, `"asc"`, or `"desc"`). When using `$groupBy`, the ordering determines the order results are returned.
|
|
569
|
+
|
|
570
|
+
**Key formats:**
|
|
571
|
+
- `$count` - Count of objects
|
|
572
|
+
- `"propertyName:sum"` - Sum of a numeric property
|
|
573
|
+
- `"propertyName:avg"` - Average of a numeric property
|
|
574
|
+
- `"propertyName:min"` - Minimum value of a property
|
|
575
|
+
- `"propertyName:max"` - Maximum value of a property
|
|
576
|
+
- `"propertyName:exactDistinct"` - Exact distinct count
|
|
577
|
+
- `"propertyName:approximateDistinct"` - Approximate distinct count (more performant for large datasets)
|
|
578
|
+
|
|
579
|
+
### Options
|
|
580
|
+
|
|
581
|
+
- `where` - Filter objects before aggregation
|
|
582
|
+
- `withProperties` - Add derived properties for computed values
|
|
583
|
+
- `aggregate` - Aggregation configuration:
|
|
584
|
+
- `$select` (required) - Object mapping metric keys (e.g., `$count`, `"salary:avg"`) to ordering (`"unordered"`, `"asc"`, or `"desc"`)
|
|
585
|
+
- `$groupBy` (optional) - Object mapping property names to grouping strategy (e.g., `"exact"`, `{ $fixedWidth: 10 }`)
|
|
586
|
+
- `dedupeIntervalMs` - Minimum time between re-fetches (default: 2000ms)
|
|
587
|
+
|
|
588
|
+
### Return Values
|
|
589
|
+
|
|
590
|
+
- `data` - Aggregation result (single object for non-grouped, array for grouped). For `$count`, access via `data.$count`. For property metrics like `"salary:avg"`, access via `data.salary.avg`
|
|
591
|
+
- `isLoading` - True while fetching
|
|
592
|
+
- `error` - Error object if fetch failed
|
|
593
|
+
- `refetch` - Manual refetch function
|
|
594
|
+
|
|
595
|
+
---
|
|
596
|
+
|
|
597
|
+
## useOsdkMetadata
|
|
598
|
+
|
|
599
|
+
*Stable - import from `@osdk/react`*
|
|
600
|
+
|
|
601
|
+
Fetch metadata about object types or interfaces.
|
|
602
|
+
|
|
603
|
+
```tsx
|
|
604
|
+
import { Todo } from "@my/osdk";
|
|
605
|
+
import { useOsdkMetadata } from "@osdk/react";
|
|
606
|
+
|
|
607
|
+
function TodoMetadataViewer() {
|
|
608
|
+
const { metadata, loading, error } = useOsdkMetadata(Todo);
|
|
609
|
+
|
|
610
|
+
if (loading) {
|
|
611
|
+
return <div>Loading metadata...</div>;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
if (error) {
|
|
615
|
+
return <div>Error: {error}</div>;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
return (
|
|
619
|
+
<div>
|
|
620
|
+
<h2>{metadata?.displayName}</h2>
|
|
621
|
+
<p>Description: {metadata?.description}</p>
|
|
622
|
+
<h3>Properties:</h3>
|
|
623
|
+
<ul>
|
|
624
|
+
{Object.entries(metadata?.properties || {}).map(([key, prop]) => (
|
|
625
|
+
<li key={key}>
|
|
626
|
+
{key}: {prop.dataType.type}
|
|
627
|
+
{prop.displayName && ` (${prop.displayName})`}
|
|
628
|
+
</li>
|
|
629
|
+
))}
|
|
630
|
+
</ul>
|
|
631
|
+
</div>
|
|
632
|
+
);
|
|
633
|
+
}
|
|
634
|
+
```
|
|
635
|
+
|
|
636
|
+
### Return Values
|
|
637
|
+
|
|
638
|
+
- `metadata` - ObjectMetadata or InterfaceMetadata with type information
|
|
639
|
+
- `loading` - True while fetching metadata
|
|
640
|
+
- `error` - Error message string if fetch failed
|
|
641
|
+
|
|
642
|
+
---
|
|
643
|
+
|
|
644
|
+
## Performance Considerations
|
|
645
|
+
|
|
646
|
+
### useObjectSet
|
|
647
|
+
|
|
648
|
+
- Set operations (union, intersect, subtract) are performed on the server
|
|
649
|
+
- Each unique combination of options creates a separate cache entry
|
|
650
|
+
- Using `pivotTo` creates a new query for the linked type
|
|
651
|
+
- Consider using `pageSize` to limit initial data load
|
|
652
|
+
|
|
653
|
+
### Derived Properties
|
|
654
|
+
|
|
655
|
+
- Computed server-side, so no client-side overhead
|
|
656
|
+
- Complex derived properties with many link traversals may be slower
|
|
657
|
+
- Filtering on derived properties happens server-side
|
|
658
|
+
|
|
659
|
+
### Aggregations
|
|
660
|
+
|
|
661
|
+
- Aggregations are always computed server-side
|
|
662
|
+
- Use `where` to reduce the dataset before aggregation
|
|
663
|
+
- Group by produces array results that may be large
|