@ryuzaki13/react-foundation-api 1.1.16 → 1.1.17
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/README.md +32 -43
- package/dist/chunks/{odataFetchFn-vnAXC-c0.js → odataFetchFn-B9wSQpUS.js} +11 -11
- package/dist/chunks/{odataFetchFn-vnAXC-c0.js.map → odataFetchFn-B9wSQpUS.js.map} +1 -1
- package/dist/odata/fetchCollectionData.d.ts +1 -1
- package/dist/odata/fetchCollectionData.d.ts.map +1 -1
- package/dist/odata/index.js +111 -112
- package/dist/odata/index.js.map +1 -1
- package/dist/odata/projectODataCollectionSort.d.ts +1 -1
- package/dist/odata/projectODataCollectionSort.d.ts.map +1 -1
- package/dist/odata/types.d.ts +1 -2
- package/dist/odata/types.d.ts.map +1 -1
- package/dist/odata/useODataCollection.d.ts +1 -1
- package/dist/odata/useODataCollection.d.ts.map +1 -1
- package/dist/odata/useODataCollectionQuery.d.ts +1 -1
- package/dist/odata/useODataCollectionQuery.d.ts.map +1 -1
- package/dist/odata/useODataEntity.d.ts +1 -1
- package/dist/odata/useODataEntity.d.ts.map +1 -1
- package/dist/persisted/index.js +1 -1
- package/package.json +2 -2
- package/src/adt/README.mdx +164 -0
- package/src/async/README.mdx +253 -0
- package/src/error-report/README.mdx +148 -0
- package/src/foundationApi.mdx +123 -0
- package/src/http/README.mdx +221 -0
- package/src/odata/README.mdx +790 -0
- package/src/persisted/README.mdx +454 -0
- package/src/resource/README.mdx +358 -0
- package/src/server-fn/README.mdx +194 -0
- package/src/transport/README.mdx +183 -0
- package/src/README.md +0 -937
- package/src/async/README.md +0 -623
- package/src/async/async.mdx +0 -6
- package/src/odata/README.md +0 -761
- package/src/odata/odataFetchFn.mdx +0 -6
- package/src/persisted/README.md +0 -598
- package/src/persisted/persisted.mdx +0 -6
|
@@ -0,0 +1,790 @@
|
|
|
1
|
+
import { Meta } from "@storybook/addon-docs/blocks";
|
|
2
|
+
|
|
3
|
+
<Meta title="Foundation API/OData/OData V2 and SAP Gateway" />
|
|
4
|
+
|
|
5
|
+
# OData V2 и SAP Gateway через `@ryuzaki13/react-foundation-api/odata`
|
|
6
|
+
|
|
7
|
+
Это основной entrypoint пакета для SAP/OData: transport с cookies, SSO и X-CSRF; OData V2 envelope; metadata-aware read/write operations; TanStack Query metadata; справочники; зависимые сегменты и table columns.
|
|
8
|
+
|
|
9
|
+
Документ описывает только публичный subpath. Внутренние файлы `odataFetchFn`, parser-ы и test utils не являются отдельными import paths.
|
|
10
|
+
|
|
11
|
+
## Содержание
|
|
12
|
+
|
|
13
|
+
- [Что решает модуль](#что-решает-модуль)
|
|
14
|
+
- [Подготовка приложения](#подготовка-приложения)
|
|
15
|
+
- [Project adapter и base URLs](#project-adapter-и-base-urls)
|
|
16
|
+
- [Низкоуровневый transport](#низкоуровневый-transport)
|
|
17
|
+
- [`odataFetch` и query options](#odatafetch-и-query-options)
|
|
18
|
+
- [Metadata-aware operations](#metadata-aware-operations)
|
|
19
|
+
- [TanStack Query и mutations](#tanstack-query-и-mutations)
|
|
20
|
+
- [DEV dry-run helpers](#dev-dry-run-helpers)
|
|
21
|
+
- [Metadata queries](#metadata-queries)
|
|
22
|
+
- [OData collections](#odata-collections)
|
|
23
|
+
- [Зависимые segments](#зависимые-segments)
|
|
24
|
+
- [Table columns](#table-columns)
|
|
25
|
+
- [SSO и X-CSRF lifecycle](#sso-и-x-csrf-lifecycle)
|
|
26
|
+
- [Ошибки и ограничения](#ошибки-и-ограничения)
|
|
27
|
+
- [Полный публичный API](#полный-публичный-api)
|
|
28
|
+
|
|
29
|
+
## Что решает модуль
|
|
30
|
+
|
|
31
|
+
```text
|
|
32
|
+
application operation
|
|
33
|
+
│
|
|
34
|
+
▼
|
|
35
|
+
OData metadata ──► target validation + URL + value parsing
|
|
36
|
+
│
|
|
37
|
+
▼
|
|
38
|
+
SAP transport ───► cookies + sap-client + SSO + X-CSRF
|
|
39
|
+
│
|
|
40
|
+
▼
|
|
41
|
+
OData V2 response ─► { data, totalCount? }
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Модуль не содержит бизнес-mapping конкретной entity, UI-компоненты и authorization rules backend-а. Фильтры/parameters/metadata types строятся pure helpers из `@ryuzaki13/react-foundation-lib/odata-service`.
|
|
45
|
+
|
|
46
|
+
Для обычного REST без SAP side effects используйте [`@ryuzaki13/react-foundation-api/http`](../http/README.mdx).
|
|
47
|
+
|
|
48
|
+
## Подготовка приложения
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
npm install @ryuzaki13/react-foundation-api @ryuzaki13/react-foundation-lib @tanstack/react-query react
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Для table adapter также нужен совместимый `@tanstack/react-table`.
|
|
55
|
+
|
|
56
|
+
Hooks работают внутри `QueryClientProvider`:
|
|
57
|
+
|
|
58
|
+
```tsx
|
|
59
|
+
import { QueryClientProvider } from "@tanstack/react-query";
|
|
60
|
+
import { createQueryClient } from "@ryuzaki13/react-foundation-lib/query-client";
|
|
61
|
+
|
|
62
|
+
const queryClient = createQueryClient();
|
|
63
|
+
|
|
64
|
+
export function App() {
|
|
65
|
+
return <QueryClientProvider client={queryClient}>{/* приложение */}</QueryClientProvider>;
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Пакет рассчитан на host-сборку, которая определяет используемые compile-time constants (`__DEV__`, `__PREVIEW__`, SAP client, app id/origins/base config URL и build id). Их точная доставка относится к build-конфигурации приложения; это не runtime options одного запроса.
|
|
70
|
+
|
|
71
|
+
## Project adapter и base URLs
|
|
72
|
+
|
|
73
|
+
До первого OData hook/fetch настройте глобальный adapter один раз при bootstrap:
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
import { configureODataProjectAdapter } from "@ryuzaki13/react-foundation-api/odata";
|
|
77
|
+
|
|
78
|
+
configureODataProjectAdapter({
|
|
79
|
+
devDp0Service: "Z_SPECIAL_SRV",
|
|
80
|
+
resolveSapClient: () => appConfig.sapClient,
|
|
81
|
+
metadataVersion: {
|
|
82
|
+
service: "Z_TECHNICAL_SRV",
|
|
83
|
+
target: "MetadataVersionSet"
|
|
84
|
+
},
|
|
85
|
+
collectionUpdates: {
|
|
86
|
+
service: "Z_TECHNICAL_SRV",
|
|
87
|
+
target: "CollectionUpdatesSet"
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
type ODataProjectAdapter = {
|
|
94
|
+
devDp0Service?: string;
|
|
95
|
+
resolveSapClient?: () => string | null | undefined;
|
|
96
|
+
metadataVersion?: ODataProjectTechnicalEndpoint;
|
|
97
|
+
collectionUpdates?: ODataProjectTechnicalEndpoint;
|
|
98
|
+
};
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
| Поле | Назначение |
|
|
102
|
+
| --- | --- |
|
|
103
|
+
| `devDp0Service` | Только этот service в dev/preview переводится на `odataDp0` |
|
|
104
|
+
| `resolveSapClient` | Production/общий runtime SAP client |
|
|
105
|
+
| `metadataVersion` | Technical entity со временем генерации metadata |
|
|
106
|
+
| `collectionUpdates` | Technical entity со справочниками, изменёнными за неделю |
|
|
107
|
+
|
|
108
|
+
Если technical endpoint не задан, соответствующий version/update query возвращает безопасный пустой результат без network request.
|
|
109
|
+
|
|
110
|
+
Adapter хранится в module-level state. Повторный вызов полностью заменяет object; merge не выполняется. `getODataProjectAdapter` возвращает текущий object.
|
|
111
|
+
|
|
112
|
+
### Base URL types
|
|
113
|
+
|
|
114
|
+
```ts
|
|
115
|
+
type BaseURLType = "odata" | "odataDp0" | "odataUi2" | "config" | "";
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
| Type | Base path |
|
|
119
|
+
| --- | --- |
|
|
120
|
+
| `odata` | `/sap/opu/odata/sap` |
|
|
121
|
+
| `odataDp0` | dev/preview DP0 proxy, production обычный SAP OData path |
|
|
122
|
+
| `odataUi2` | `/sap/opu/odata/UI2` или dev/preview proxy |
|
|
123
|
+
| `config` | compile-time app config base URL |
|
|
124
|
+
| `""` | Ничего не добавлять к input path |
|
|
125
|
+
|
|
126
|
+
`resolveODataBaseUrl(service, baseUrl?)` учитывает `devDp0Service`, иначе возвращает явный `baseUrl` или `odata`. Service name нормализуется: suffix после `;` отбрасывается `normalizeODataServiceName`.
|
|
127
|
+
|
|
128
|
+
Низкоуровневые constants доступны как `BaseODataURL`, `BaseODataDp0URL`, `BaseODataUI2URL`, `BaseAppConfigURL`, `BaseUrlMap`. Рассматривайте `BaseUrlMap` как readonly library configuration.
|
|
129
|
+
|
|
130
|
+
## Низкоуровневый transport
|
|
131
|
+
|
|
132
|
+
### `fetchBase`
|
|
133
|
+
|
|
134
|
+
```ts
|
|
135
|
+
const response = await fetchBase(
|
|
136
|
+
"/Z_SERVICE_SRV/Ping",
|
|
137
|
+
{ method: "GET" },
|
|
138
|
+
"odata"
|
|
139
|
+
);
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Возвращает успешный raw `Response`. Для SAP request transport:
|
|
143
|
+
|
|
144
|
+
- добавляет `sap-language: ru`;
|
|
145
|
+
- добавляет `sap-contextid-accept: header`;
|
|
146
|
+
- добавляет `sap-client`, если client определён;
|
|
147
|
+
- ставит `Accept: application/json`, если consumer его не задал;
|
|
148
|
+
- всегда использует `credentials: include`;
|
|
149
|
+
- для write operation получает X-CSRF token;
|
|
150
|
+
- распознаёт redirect/HTML SSO и пытается восстановить session;
|
|
151
|
+
- при `401/403` write один раз очищает CSRF cache, получает новый token и повторяет запрос;
|
|
152
|
+
- на non-ok строит и бросает Error.
|
|
153
|
+
|
|
154
|
+
`RequestInitType` расширяет `RequestInit` внутренними флагами retry. Не выставляйте `retried`/`ssoRetried` в обычном прикладном коде.
|
|
155
|
+
|
|
156
|
+
### `fetchODataJson`
|
|
157
|
+
|
|
158
|
+
```ts
|
|
159
|
+
const response = await fetchODataJson<Order[]>(
|
|
160
|
+
"/Z_ORDER_SRV/OrderSet?$top=20",
|
|
161
|
+
{},
|
|
162
|
+
"odata"
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
console.log(response.data);
|
|
166
|
+
console.log(response.totalCount);
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Дополнительно выставляет OData V2 headers `DataServiceVersion: 2.0` и `MaxDataServiceVersion: 2.0`, разбирает JSON и снимает envelope:
|
|
170
|
+
|
|
171
|
+
| Server payload | Result |
|
|
172
|
+
| --- | --- |
|
|
173
|
+
| `{ d: { results: rows, __count } }` | `{ data: rows, totalCount }` |
|
|
174
|
+
| `{ d: record }` | `{ data: record }` |
|
|
175
|
+
| raw JSON | `{ data: raw }` |
|
|
176
|
+
|
|
177
|
+
Поле `__metadata` удаляется из каждого top-level record/одиночного `d`. Вложенные navigation records не очищаются рекурсивно.
|
|
178
|
+
|
|
179
|
+
Для `204/205` data содержит специальный marker. Проверяйте именно `response.data`:
|
|
180
|
+
|
|
181
|
+
```ts
|
|
182
|
+
if (isNoContentResponse(response.data)) {
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### `fetchJson`
|
|
188
|
+
|
|
189
|
+
```ts
|
|
190
|
+
const rows = await fetchJson<Order[]>("/Z_ORDER_SRV/OrderSet");
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Возвращает только `data`, поэтому `totalCount` теряется. Generic задаёт ожидаемый compile-time type, но не валидирует server payload.
|
|
194
|
+
|
|
195
|
+
### `fetchQueryFn`
|
|
196
|
+
|
|
197
|
+
Factory для raw `Response` с обязательным async transform:
|
|
198
|
+
|
|
199
|
+
```ts
|
|
200
|
+
const loadXml = fetchQueryFn("/sap/bc/example", {
|
|
201
|
+
baseUrl: "",
|
|
202
|
+
init: { headers: { Accept: "application/xml" } },
|
|
203
|
+
transform: (response) => response.text()
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
const xml = await loadXml({ signal });
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Переданный query signal используется только если `init.signal` отсутствует. Optional `swCache` добавляет `x-sw-cache`.
|
|
210
|
+
|
|
211
|
+
`fetchJsonQueryFn`, `fetchJsonMutationFn` и `fetchDeleteFn` экспортируются для существующих consumers, но помечены deprecated. Новый generic REST код должен использовать `/http`, metadata-aware OData код — специализированные operations ниже.
|
|
212
|
+
|
|
213
|
+
## `odataFetch` и query options
|
|
214
|
+
|
|
215
|
+
`odataFetch` строит URL OData query, но не загружает metadata и не проверяет target:
|
|
216
|
+
|
|
217
|
+
```ts
|
|
218
|
+
const response = await odataFetch<Order, Order[]>(
|
|
219
|
+
"/Z_ORDER_SRV/OrderSet",
|
|
220
|
+
{
|
|
221
|
+
select: ["Id", "Title"],
|
|
222
|
+
top: 50,
|
|
223
|
+
skip: 0,
|
|
224
|
+
sorts: [{ key: "Title" }],
|
|
225
|
+
expression: createFilterEqual("Status", "OPEN"),
|
|
226
|
+
inlinecount: "allpages",
|
|
227
|
+
swCache: "ttl=10m;name=orders"
|
|
228
|
+
},
|
|
229
|
+
{ signal }
|
|
230
|
+
);
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
`entitySetPath` должен содержать начальный `/`. Если в нём уже есть query string, новые options объединяются через `URLSearchParams`.
|
|
234
|
+
|
|
235
|
+
### `ODataFetchOptions`
|
|
236
|
+
|
|
237
|
+
| Поле | OData option |
|
|
238
|
+
| --- | --- |
|
|
239
|
+
| `expression` | `$filter`, строится `buildODataFilter` |
|
|
240
|
+
| `sorts` | `$orderby`, строится `buildODataOrder` |
|
|
241
|
+
| `select` | `$select`, ключи через запятую |
|
|
242
|
+
| `expand` | `$expand`, ключи через запятую |
|
|
243
|
+
| `top` | `$top` |
|
|
244
|
+
| `skip` | `$skip` |
|
|
245
|
+
| `inlinecount` | `$inlinecount` |
|
|
246
|
+
| `baseUrl` | Выбор base URL |
|
|
247
|
+
| `swCache` | Header `x-sw-cache` |
|
|
248
|
+
|
|
249
|
+
Поле `format?: "json"` присутствует в текущем type, но request builder его не добавляет в URL. Не полагайтесь на него; JSON задаёт transport через Accept/OData parsing.
|
|
250
|
+
|
|
251
|
+
Arrays `select`/`expand` и path values не проходят metadata validation. Для filter values используйте фабрики из `foundation-lib/odata-service`, а не ручную конкатенацию.
|
|
252
|
+
|
|
253
|
+
## Metadata-aware operations
|
|
254
|
+
|
|
255
|
+
Публичные factory-функции возвращают runner:
|
|
256
|
+
|
|
257
|
+
```ts
|
|
258
|
+
type Runner<T> = (context: {
|
|
259
|
+
client: QueryClient;
|
|
260
|
+
signal?: AbortSignal;
|
|
261
|
+
}) => Promise<{ data: T; totalCount?: number }>;
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
Каждый runner:
|
|
265
|
+
|
|
266
|
+
1. загружает/cache-ит `$metadata` через QueryClient;
|
|
267
|
+
2. находит target в entities или function imports;
|
|
268
|
+
3. проверяет совместимость operation;
|
|
269
|
+
4. строит target path по metadata;
|
|
270
|
+
5. выполняет transport;
|
|
271
|
+
6. optional парсит значения по metadata;
|
|
272
|
+
7. optional вызывает `transform`.
|
|
273
|
+
|
|
274
|
+
### Выбор operation
|
|
275
|
+
|
|
276
|
+
| Factory | Target | HTTP | Params | Body | Result |
|
|
277
|
+
| --- | --- | --- | --- | --- | --- |
|
|
278
|
+
| `odataQueryFn` | plain/parameterized Entity | GET | optional; у plain Entity игнорируются | нет | array или transform result |
|
|
279
|
+
| `odataReadFn` | non-parameterized Entity | GET | обязательны | нет | single |
|
|
280
|
+
| `odataCreateFn` | Entity | POST | запрещены | обязателен | single |
|
|
281
|
+
| `odataUpdateFn` | Entity | PUT | обязательны | обязателен | single |
|
|
282
|
+
| `odataDeleteFn` | Entity | DELETE | обязательны | нет | single/no-content contract backend-а |
|
|
283
|
+
| `odataFunctionImportFn` | FunctionImport | из metadata | обязательны | нет | single |
|
|
284
|
+
|
|
285
|
+
Body реально сериализуется только для truthy object/value. Для create/update используйте JSON object; `false`, `0`, `""` или `null` не подходят как body текущего helper-а.
|
|
286
|
+
|
|
287
|
+
### Query списка
|
|
288
|
+
|
|
289
|
+
```ts
|
|
290
|
+
import { odataQueryFn } from "@ryuzaki13/react-foundation-api/odata";
|
|
291
|
+
import { wrapODataParams } from "@ryuzaki13/react-foundation-lib/odata-service";
|
|
292
|
+
|
|
293
|
+
const loadOrders = odataQueryFn<OrderRaw, Order>({
|
|
294
|
+
odata: { service: "Z_ORDER_SRV", target: "OrderQuery" },
|
|
295
|
+
params: wrapODataParams({
|
|
296
|
+
CompanyId: "1000",
|
|
297
|
+
DateFrom: new Date("2026-01-01")
|
|
298
|
+
}),
|
|
299
|
+
options: {
|
|
300
|
+
top: 100,
|
|
301
|
+
select: ["Id", "Title", "CreatedAt"]
|
|
302
|
+
},
|
|
303
|
+
autoParse: true,
|
|
304
|
+
transform: (rows) => rows.map(mapOrder)
|
|
305
|
+
});
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
Для parameterized entity metadata определяет suffix `/Set` или `/Results`. Для plain entity `params` игнорируются; в dev выводится warning. Чтение конкретного key делайте `odataReadFn`.
|
|
309
|
+
|
|
310
|
+
`transform` query может вернуть новый array или одно агрегированное значение:
|
|
311
|
+
|
|
312
|
+
```ts
|
|
313
|
+
const countOrders = odataQueryFn<OrderRaw, number>({
|
|
314
|
+
odata,
|
|
315
|
+
transform: (rows) => rows.length
|
|
316
|
+
});
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### Read, create, update, delete
|
|
320
|
+
|
|
321
|
+
```ts
|
|
322
|
+
const readOrder = odataReadFn<OrderRaw, Order>({
|
|
323
|
+
odata: { service: "Z_ORDER_SRV", target: "OrderSet" },
|
|
324
|
+
params: wrapODataParams({ Id: "42" }),
|
|
325
|
+
autoParse: true,
|
|
326
|
+
transform: mapOrder
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
const createOrder = odataCreateFn<OrderRaw, Order, CreateOrderBody>({
|
|
330
|
+
odata: { service: "Z_ORDER_SRV", target: "OrderSet" },
|
|
331
|
+
body: { Title: "Новый заказ" },
|
|
332
|
+
transform: mapOrder
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
const updateOrder = odataUpdateFn<OrderRaw, Order, UpdateOrderBody>({
|
|
336
|
+
odata: { service: "Z_ORDER_SRV", target: "OrderSet" },
|
|
337
|
+
params: wrapODataParams({ Id: "42" }),
|
|
338
|
+
body: { Title: "Изменённый заказ" },
|
|
339
|
+
transform: mapOrder
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
const deleteOrder = odataDeleteFn<unknown>({
|
|
343
|
+
odata: { service: "Z_ORDER_SRV", target: "OrderSet" },
|
|
344
|
+
params: wrapODataParams({ Id: "42" })
|
|
345
|
+
});
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
### FunctionImport
|
|
349
|
+
|
|
350
|
+
```ts
|
|
351
|
+
const runAction = odataFunctionImportFn<ActionRaw, ActionResult>({
|
|
352
|
+
odata: { service: "Z_ORDER_SRV", target: "CloseOrder" },
|
|
353
|
+
params: wrapODataParams({ Id: "42" }),
|
|
354
|
+
transform: mapActionResult
|
|
355
|
+
});
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
Target обязан находиться в metadata как FunctionImport. HTTP method берётся оттуда; отсутствие method приводит к ошибке. Entity нельзя вызвать через `odataFunctionImportFn`, FunctionImport — через entity factory.
|
|
359
|
+
|
|
360
|
+
### `autoParse`
|
|
361
|
+
|
|
362
|
+
При `autoParse: true` top-level fields преобразуются с помощью metadata columns, например OData date/number/ABAP-boolean-like значения. Для FunctionImport columns берутся из `resultEntity`, если она указана.
|
|
363
|
+
|
|
364
|
+
Parser:
|
|
365
|
+
|
|
366
|
+
- не валидирует наличие всех полей;
|
|
367
|
+
- не делает deep-recursive parsing navigation objects;
|
|
368
|
+
- сохраняет исходный object, если ни одно значение не изменилось;
|
|
369
|
+
- не заменяет domain validation/mapping.
|
|
370
|
+
|
|
371
|
+
### `options`, `init`, `swCache`
|
|
372
|
+
|
|
373
|
+
Общие fields operation:
|
|
374
|
+
|
|
375
|
+
- `odata: { service, target }`;
|
|
376
|
+
- `options?: ODataFetchOptions`;
|
|
377
|
+
- `init?: RequestInit` без `signal/method/body`;
|
|
378
|
+
- `autoParse?: boolean`;
|
|
379
|
+
- `swCache?: string`;
|
|
380
|
+
- operation-specific `params`/`body`;
|
|
381
|
+
- optional `transform`.
|
|
382
|
+
|
|
383
|
+
Отдельный top-level `swCache` имеет приоритет добавления в итоговые query options. Signal приходит только в runner context.
|
|
384
|
+
|
|
385
|
+
## TanStack Query и mutations
|
|
386
|
+
|
|
387
|
+
### Query hook
|
|
388
|
+
|
|
389
|
+
Runner структурно совместим с TanStack Query queryFn:
|
|
390
|
+
|
|
391
|
+
```tsx
|
|
392
|
+
const ordersQuery = useQuery({
|
|
393
|
+
queryKey: ["orders", companyId],
|
|
394
|
+
queryFn: odataQueryFn<OrderRaw, Order>({
|
|
395
|
+
odata: { service: "Z_ORDER_SRV", target: "OrderSet" },
|
|
396
|
+
options: {
|
|
397
|
+
expression: createFilterEqual("CompanyId", companyId)
|
|
398
|
+
},
|
|
399
|
+
transform: (rows) => rows.map(mapOrder)
|
|
400
|
+
})
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
const orders = ordersQuery.data?.data ?? [];
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
Query data — object `{ data, totalCount? }`, не сам array. Query key обязан включать все значения, влияющие на params/options/result.
|
|
407
|
+
|
|
408
|
+
### Mutation hook
|
|
409
|
+
|
|
410
|
+
Runner не является обычной `mutationFn(input)`: ему нужен `QueryClient`. Создайте operation из mutation input и вызовите runner явно:
|
|
411
|
+
|
|
412
|
+
```tsx
|
|
413
|
+
const queryClient = useQueryClient();
|
|
414
|
+
|
|
415
|
+
const updateMutation = useMutation({
|
|
416
|
+
mutationFn: (input: UpdateOrderInput) =>
|
|
417
|
+
odataUpdateFn<OrderRaw, Order, UpdateOrderBody>({
|
|
418
|
+
odata: { service: "Z_ORDER_SRV", target: "OrderSet" },
|
|
419
|
+
params: wrapODataParams({ Id: input.id }),
|
|
420
|
+
body: mapUpdateBody(input),
|
|
421
|
+
transform: mapOrder
|
|
422
|
+
})({ client: queryClient }),
|
|
423
|
+
onSuccess: () => queryClient.invalidateQueries({ queryKey: ["orders"] })
|
|
424
|
+
});
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
Mutation cache не синхронизируется автоматически. Инвалидируйте/обновляйте query по policy ресурса.
|
|
428
|
+
|
|
429
|
+
## DEV dry-run helpers
|
|
430
|
+
|
|
431
|
+
Экспортируются:
|
|
432
|
+
|
|
433
|
+
- `odataQueryFnDev`;
|
|
434
|
+
- `odataReadFnDev`;
|
|
435
|
+
- `odataCreateFnDev`;
|
|
436
|
+
- `odataUpdateFnDev`;
|
|
437
|
+
- `odataDeleteFnDev`;
|
|
438
|
+
- `odataFunctionImportFnDev`.
|
|
439
|
+
|
|
440
|
+
В `__DEV__` они:
|
|
441
|
+
|
|
442
|
+
1. реально загружают metadata, если её нет в cache;
|
|
443
|
+
2. валидируют target/operation;
|
|
444
|
+
3. строят итоговый URL, headers preview и body;
|
|
445
|
+
4. показывают warning notification и console group;
|
|
446
|
+
5. не выполняют целевой data/write request;
|
|
447
|
+
6. возвращают `{ data: [], totalCount: 0 }` для query или `{ data: undefined }` для single operation;
|
|
448
|
+
7. не вызывают `transform`, потому что backend response отсутствует.
|
|
449
|
+
|
|
450
|
+
В non-dev build Dev helper делегирует обычной реальной operation. Поэтому это инструмент предварительной проверки запроса, а не mock API и не безопасная замена backend test.
|
|
451
|
+
|
|
452
|
+
Низкоуровневые `odataFetchFn`/`odataFetchFnDev` существуют внутри реализации, но не экспортируются публичным `/odata` entrypoint. Используйте специализированные helpers.
|
|
453
|
+
|
|
454
|
+
## Metadata queries
|
|
455
|
+
|
|
456
|
+
### `useODataMetadataQuery`
|
|
457
|
+
|
|
458
|
+
```tsx
|
|
459
|
+
const metadataQuery = useODataMetadataQuery({
|
|
460
|
+
service: "Z_ORDER_SRV"
|
|
461
|
+
});
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
Загружает `/<service>/$metadata`, разбирает XML через `foundation-lib/odata-service` и хранит metadata с `staleTime: Infinity`, `gcTime: Infinity`, persisted query meta.
|
|
465
|
+
|
|
466
|
+
Актуальность контролирует отдельный technical version query, если `metadataVersion` настроен в project adapter. Версия сравнивается с `dataUpdatedAt`; более новая server version инвалидирует только metadata нужного service.
|
|
467
|
+
|
|
468
|
+
### `useODataMetadata`
|
|
469
|
+
|
|
470
|
+
```tsx
|
|
471
|
+
const { metadata, metadataUpdatedAt, isLoading } = useODataMetadata({
|
|
472
|
+
service: "Z_ORDER_SRV",
|
|
473
|
+
target: "OrderSet"
|
|
474
|
+
});
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
Возвращает metadata одной Entity. Если target является FunctionImport, после загрузки hook бросит error. Ошибка metadata query также преобразуется в error во время render; используйте подходящий Error Boundary.
|
|
478
|
+
|
|
479
|
+
### Imperative и options API
|
|
480
|
+
|
|
481
|
+
| Export | Назначение |
|
|
482
|
+
| --- | --- |
|
|
483
|
+
| `fetchMetadata` | Query function raw metadata fetch/parse |
|
|
484
|
+
| `odataMetadataQueryOptions` | Общие TanStack query options |
|
|
485
|
+
| `getODataMetadataData` | `fetchQuery`, optional version sync, metadata result |
|
|
486
|
+
| `useODataMetadataVersionQuery` | Только technical version query |
|
|
487
|
+
| `applyODataMetadataVersion` | Применить уже полученную version к cache |
|
|
488
|
+
|
|
489
|
+
Ошибка optional technical version-check не лишает `getODataMetadataData` уже сохранённых metadata: sync error подавляется, затем metadata читается снова.
|
|
490
|
+
|
|
491
|
+
## OData collections
|
|
492
|
+
|
|
493
|
+
Collections предназначены для code/text справочников с metadata semantic links.
|
|
494
|
+
|
|
495
|
+
### Config
|
|
496
|
+
|
|
497
|
+
```ts
|
|
498
|
+
const organizationOData = {
|
|
499
|
+
service: "Z_REFERENCE_SRV",
|
|
500
|
+
target: "OrganizationSet",
|
|
501
|
+
limitedKeys: ["OrganizationCode"],
|
|
502
|
+
sortByCode: true,
|
|
503
|
+
excludeEmpty: true,
|
|
504
|
+
swCache: "ttl=forever;name=ref"
|
|
505
|
+
} satisfies ODataCollectionConfig;
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
| Поле | Назначение |
|
|
509
|
+
| --- | --- |
|
|
510
|
+
| `service`, `target` | OData entity |
|
|
511
|
+
| `limitedKeys` | Ограничить выбранные code fields; linked text fields добавляются автоматически |
|
|
512
|
+
| `serverFilter` | `$filter` на сервере |
|
|
513
|
+
| `clientFilter` | Filter после загрузки |
|
|
514
|
+
| `excludeEmpty` | Исключение пустых code values в separated arrays |
|
|
515
|
+
| `sortByCode` | `false` создаёт observer projection по text; cache остаётся canonical по code |
|
|
516
|
+
| `hideCode` | UI contract для consumer controls |
|
|
517
|
+
| `swCache` | SW policy, default `ttl=forever;name=ref` |
|
|
518
|
+
|
|
519
|
+
### `useODataCollectionQuery`
|
|
520
|
+
|
|
521
|
+
```tsx
|
|
522
|
+
const collectionQuery = useODataCollectionQuery<OrganizationItem>(organizationOData);
|
|
523
|
+
|
|
524
|
+
const items = collectionQuery.data?.items ?? [];
|
|
525
|
+
const textKey = collectionQuery.data?.keyPairsMap.OrganizationCode;
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
Результат:
|
|
529
|
+
|
|
530
|
+
```ts
|
|
531
|
+
interface ODataCollectionResult<T> {
|
|
532
|
+
items: T[];
|
|
533
|
+
keyPairs: CollectionPair[];
|
|
534
|
+
keyPairsMap: Record<string, string>;
|
|
535
|
+
separated: Record<string, T[]>;
|
|
536
|
+
chain: { codeKey: string; count: number }[];
|
|
537
|
+
count: number;
|
|
538
|
+
cacheUpdatedAt: number;
|
|
539
|
+
}
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
При загрузке служебные top-level fields `ID`, `CNT`, `__metadata`, `__Parameters` удаляются.
|
|
543
|
+
|
|
544
|
+
Критические cache rules:
|
|
545
|
+
|
|
546
|
+
- query key включает `service`, `target`, `limitedKeys`, `serverFilter` и внутреннюю version;
|
|
547
|
+
- `sortByCode` не входит в key намеренно: text ordering — observer `select`;
|
|
548
|
+
- `clientFilter` и `excludeEmpty` тоже не входят в key, хотя влияют на cached snapshot;
|
|
549
|
+
- поэтому для одинакового service/target/limitedKeys/serverFilter все consumers должны использовать совместимые `clientFilter`/`excludeEmpty`; иначе первый fetch может задать общий snapshot с неожиданным составом;
|
|
550
|
+
- `swCache` не является data identity.
|
|
551
|
+
|
|
552
|
+
Внутренняя collection loader сейчас логирует transport/parse error и возвращает пустой array. Поэтому query может иметь `isSuccess === true` и `items.length === 0` даже после ошибки сети. Если UI обязан отличать «пусто» от «ошибка», не используйте collection facade как единственный error signal или добавьте отдельную диагностику владельца.
|
|
553
|
+
|
|
554
|
+
### SW cache и update list
|
|
555
|
+
|
|
556
|
+
Default policies:
|
|
557
|
+
|
|
558
|
+
```text
|
|
559
|
+
collection data: ttl=forever;name=ref
|
|
560
|
+
updates list: ttl=4h;name=ref-updates
|
|
561
|
+
bust: bust=forever;name=ref
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
Technical `collectionUpdates` возвращает entities, изменённые за последние семь дней. Collection сравнивает `lastChangedAt`, metadata update time, coverage window и `cacheUpdatedAt`. Если snapshot устарел, active query инвалидируется, а TTL policy преобразуется в bust.
|
|
565
|
+
|
|
566
|
+
Экспортированы `useODataCollectionUpdatesQuery`, `odataCollectionUpdatesQueryOptions`, `fetchODataCollectionUpdates`, key factory, constants, `resolveODataCollectionSwCachePolicy` и `shouldRefreshODataCollectionCache` для тестов/диагностики.
|
|
567
|
+
|
|
568
|
+
### `useODataCollectionModel`
|
|
569
|
+
|
|
570
|
+
```tsx
|
|
571
|
+
const model = useODataCollectionModel({
|
|
572
|
+
codeKey: "OrganizationCode",
|
|
573
|
+
minSearchTextLength: 2,
|
|
574
|
+
maxVisibleItems: 100
|
|
575
|
+
});
|
|
576
|
+
```
|
|
577
|
+
|
|
578
|
+
Заполняет отсутствующие значения default-ами store из `foundation-lib/odata` и фиксирует snapshot при первом render. Изменение входного model после mount не пересоздаёт normalized model.
|
|
579
|
+
|
|
580
|
+
### `useODataCollection`
|
|
581
|
+
|
|
582
|
+
```tsx
|
|
583
|
+
const collection = useODataCollection({
|
|
584
|
+
odata: organizationOData,
|
|
585
|
+
model,
|
|
586
|
+
pageSize: 50
|
|
587
|
+
});
|
|
588
|
+
|
|
589
|
+
const visible = collection.getItems(
|
|
590
|
+
(code, text) => code.includes(search) || text.includes(search),
|
|
591
|
+
selectedItems
|
|
592
|
+
);
|
|
593
|
+
```
|
|
594
|
+
|
|
595
|
+
Возвращает indexes и UI helpers:
|
|
596
|
+
|
|
597
|
+
- `itemsMap`, `orderedKeys`, `separatedItems`;
|
|
598
|
+
- `getItems(predicate?, selected?)` с `maxVisibleItems`;
|
|
599
|
+
- `getPage(pageIndex)`;
|
|
600
|
+
- `findSourceItemsByKeys(field, keys)`;
|
|
601
|
+
- `setFilteredItems` для dependency filter;
|
|
602
|
+
- `buildSearchIndex(field)` по prefixes длиной 1–5;
|
|
603
|
+
- `debounce(key, fn, delay)`;
|
|
604
|
+
- `codeKey`, `textKey`, `totalCount`, status.
|
|
605
|
+
|
|
606
|
+
Параметр `filter` присутствует в type props, но текущая реализация его не применяет. Не полагайтесь на него; используйте `clientFilter` config или predicate `getItems`. `refetch` и `invalidate` результата deprecated.
|
|
607
|
+
|
|
608
|
+
`useODataCollectionChains` подписывается на QueryCache для набора collection keys и возвращает map chain по `service.target`.
|
|
609
|
+
|
|
610
|
+
### Query key factories
|
|
611
|
+
|
|
612
|
+
```ts
|
|
613
|
+
createODataCollectionBaseQueryKey();
|
|
614
|
+
createODataCollectionQueryKey(config);
|
|
615
|
+
```
|
|
616
|
+
|
|
617
|
+
Используйте их для точной инвалидации вместо ручного копирования array key.
|
|
618
|
+
|
|
619
|
+
## Зависимые segments
|
|
620
|
+
|
|
621
|
+
Типы `ODataDependentBaseProps`, `ODataDependentSegment`, `ODataDependentSegmentItem` описывают несколько связанных справочников/levels.
|
|
622
|
+
|
|
623
|
+
```ts
|
|
624
|
+
const items = flattenODataDependentServices(services);
|
|
625
|
+
```
|
|
626
|
+
|
|
627
|
+
Каждый segment превращается в отдельный item с:
|
|
628
|
+
|
|
629
|
+
- `id` = codeKey;
|
|
630
|
+
- `serviceKey` = `service.target`;
|
|
631
|
+
- исходными `serviceIndex`/`segmentIndex`;
|
|
632
|
+
- normalized model с `codeKey`;
|
|
633
|
+
- `panelVisibility`, default `user`.
|
|
634
|
+
|
|
635
|
+
```ts
|
|
636
|
+
const ordered = sortODataDependentSegmentItemsByChains(
|
|
637
|
+
items,
|
|
638
|
+
chains,
|
|
639
|
+
userOrderIds
|
|
640
|
+
);
|
|
641
|
+
```
|
|
642
|
+
|
|
643
|
+
Сначала учитывается service order и server chain, затем optional user override поднимает валидные уникальные IDs вверх, сохраняя относительный порядок остальных.
|
|
644
|
+
|
|
645
|
+
`sortODataDependentServicesByChains` выполняет старую service-level форму и deprecated. Для нового кода используйте flatten + `sortODataDependentSegmentItemsByChains`.
|
|
646
|
+
|
|
647
|
+
## Table columns
|
|
648
|
+
|
|
649
|
+
`useODataTableColumns` связывает OData entity metadata с table helpers из `foundation-lib/table`.
|
|
650
|
+
|
|
651
|
+
### Build mode
|
|
652
|
+
|
|
653
|
+
```tsx
|
|
654
|
+
const { columns, defaultColumnVisibility, isLoading } = useODataTableColumns<Order>({
|
|
655
|
+
mode: "build",
|
|
656
|
+
service: "Z_ORDER_SRV",
|
|
657
|
+
target: "OrderSet",
|
|
658
|
+
resolveHeader: (column) => column.label ?? column.id,
|
|
659
|
+
resolveVisible: (column) => !column.hidden,
|
|
660
|
+
resolveFormatting: (column) => resolveFormatting(column)
|
|
661
|
+
});
|
|
662
|
+
```
|
|
663
|
+
|
|
664
|
+
Генерирует leaf columns по metadata и стартовую visibility map.
|
|
665
|
+
|
|
666
|
+
### Enrich mode
|
|
667
|
+
|
|
668
|
+
```tsx
|
|
669
|
+
const result = useODataTableColumns<Order>({
|
|
670
|
+
mode: "enrich",
|
|
671
|
+
service: "Z_ORDER_SRV",
|
|
672
|
+
target: "OrderSet",
|
|
673
|
+
columns: applicationColumns
|
|
674
|
+
});
|
|
675
|
+
```
|
|
676
|
+
|
|
677
|
+
Сохраняет существующую структуру columns, добавляет metadata formatting role/type и возвращает visibility только для совпавших stable leaf IDs. Пока metadata грузятся, build mode возвращает `[]`, enrich mode — shallow copy исходных columns.
|
|
678
|
+
|
|
679
|
+
## SSO и X-CSRF lifecycle
|
|
680
|
+
|
|
681
|
+
### X-CSRF
|
|
682
|
+
|
|
683
|
+
Для SAP write method (не GET/HEAD) transport:
|
|
684
|
+
|
|
685
|
+
1. определяет CSRF scope по service/base URL/SAP client;
|
|
686
|
+
2. переиспользует token из module cache;
|
|
687
|
+
3. объединяет параллельные token fetch одного scope;
|
|
688
|
+
4. делает GET с `x-csrf-token: Fetch`;
|
|
689
|
+
5. добавляет token к write;
|
|
690
|
+
6. на `401/403` один раз сбрасывает token и повторяет write.
|
|
691
|
+
|
|
692
|
+
Token cache живёт в памяти tab/process. Публичного reset API нет.
|
|
693
|
+
|
|
694
|
+
### SSO
|
|
695
|
+
|
|
696
|
+
Transport распознаёт opaque redirect или HTML form с `SAMLRequest`/`SAMLResponse`, создаёт `SsoRequiredError` и пытается восстановить session в hidden iframe. Параллельные requests ждут общий recovery gate.
|
|
697
|
+
|
|
698
|
+
```ts
|
|
699
|
+
try {
|
|
700
|
+
await fetchJson("/Z_SERVICE_SRV/EntitySet");
|
|
701
|
+
} catch (error) {
|
|
702
|
+
if (error instanceof SsoRequiredError) {
|
|
703
|
+
showLoginExpired(error.message);
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
```
|
|
707
|
+
|
|
708
|
+
`SsoRequiredError` содержит `form?`, `recoveryUrl?`, `recoverable`. После неуспешного automatic recovery ошибка становится unrecoverable до успешного общего recovery/session reset flow.
|
|
709
|
+
|
|
710
|
+
Низкоуровневые helpers:
|
|
711
|
+
|
|
712
|
+
- `isSsoForm(form)` проверяет наличие SAML input;
|
|
713
|
+
- `submitSsoForm(form)` вручную submit-ит форму в hidden iframe и возвращает boolean;
|
|
714
|
+
- `recoverSsoSession({ form, recoveryUrl })` дедуплицирует recovery Promise.
|
|
715
|
+
|
|
716
|
+
Они требуют browser `window`/`document`; на server recovery возвращает `false`.
|
|
717
|
+
|
|
718
|
+
## Ошибки и ограничения
|
|
719
|
+
|
|
720
|
+
### HTTP/error body
|
|
721
|
+
|
|
722
|
+
- JSON error пытается собрать уникальные `error.code`, `error.message`, `details`, `message`.
|
|
723
|
+
- Непустой text становится Error message.
|
|
724
|
+
- HTML SAML form запускает SSO flow.
|
|
725
|
+
- Неожиданный HTML без SAML form регистрируется как error report и отклоняется.
|
|
726
|
+
- Unsupported non-JSON text/content type отклоняется.
|
|
727
|
+
- Network/CORS/abort errors пробрасываются.
|
|
728
|
+
|
|
729
|
+
### Практические ограничения
|
|
730
|
+
|
|
731
|
+
- Generic types не проверяют payload; используйте runtime validator на boundary.
|
|
732
|
+
- Metadata cache бесконечный по времени и зависит от optional version endpoint или ручной invalidation.
|
|
733
|
+
- OData transport в первую очередь browser/SAP-oriented: cookies, proxy, compile-time constants и iframe SSO должны быть настроены host-ом.
|
|
734
|
+
- Не помещайте персональные/секретные response в общий SW cache без изоляции.
|
|
735
|
+
- FunctionImport side effects определяются metadata HTTP method; query/mutation cache policy задаёт consumer.
|
|
736
|
+
- `odataFetch` не знает metadata и не заменяет specialized operations для сложных entity paths.
|
|
737
|
+
- Collection facade подавляет loader errors в пустой result — учитывайте это в UX.
|
|
738
|
+
- `submitSsoForm` — низкоуровневый browser side effect; обычный consumer должен позволить transport самому управлять recovery.
|
|
739
|
+
|
|
740
|
+
## Полный публичный API
|
|
741
|
+
|
|
742
|
+
### Transport и URL
|
|
743
|
+
|
|
744
|
+
| Группа | Exports |
|
|
745
|
+
| --- | --- |
|
|
746
|
+
| Fetch | `fetchBase`, `fetchODataJson`, `fetchJson`, `fetchQueryFn`, `odataFetch`, `isNoContentResponse` |
|
|
747
|
+
| Deprecated fetch factories | `fetchJsonQueryFn`, `fetchJsonMutationFn`, `fetchDeleteFn` |
|
|
748
|
+
| Base URL | `BaseODataURL`, `BaseODataDp0URL`, `BaseODataUI2URL`, `BaseAppConfigURL`, `BaseUrlMap`, `BaseURLType`, `resolveODataBaseUrl`, `normalizeODataServiceName`, `getInputUrl`, `normalizeRelativePath` |
|
|
749
|
+
| Request types | `RequestInitType`, `FetchErrorReportContext`, `ODataFetchOptions` |
|
|
750
|
+
|
|
751
|
+
### SSO и project adapter
|
|
752
|
+
|
|
753
|
+
| Группа | Exports |
|
|
754
|
+
| --- | --- |
|
|
755
|
+
| SSO | `SsoRequiredError`, `SsoForm`, `SsoRecoveryArgs`, `SsoRequiredErrorOptions`, `isSsoForm`, `submitSsoForm`, `recoverSsoSession` |
|
|
756
|
+
| Adapter | `configureODataProjectAdapter`, `getODataProjectAdapter`, `resolveODataSapClient`, `ODataProjectAdapter`, `ODataProjectTechnicalEndpoint` |
|
|
757
|
+
|
|
758
|
+
### Metadata-aware operations
|
|
759
|
+
|
|
760
|
+
| Группа | Exports |
|
|
761
|
+
| --- | --- |
|
|
762
|
+
| Real | `odataQueryFn`, `odataReadFn`, `odataCreateFn`, `odataUpdateFn`, `odataDeleteFn`, `odataFunctionImportFn` |
|
|
763
|
+
| DEV dry-run | `odataQueryFnDev`, `odataReadFnDev`, `odataCreateFnDev`, `odataUpdateFnDev`, `odataDeleteFnDev`, `odataFunctionImportFnDev` |
|
|
764
|
+
| Public request type | `ODataFetchFnRequest` |
|
|
765
|
+
|
|
766
|
+
`odataFetchFn` и `odataFetchFnDev` намеренно не находятся в public index.
|
|
767
|
+
|
|
768
|
+
### Metadata
|
|
769
|
+
|
|
770
|
+
| Exports | Назначение |
|
|
771
|
+
| --- | --- |
|
|
772
|
+
| `fetchMetadata`, `odataMetadataQueryOptions`, `useODataMetadataQuery`, `getODataMetadataData` | Service metadata lifecycle |
|
|
773
|
+
| `useODataMetadata`, `ODataEntity`, `useODataEntity` | Entity metadata/actions |
|
|
774
|
+
| `useODataMetadataVersionQuery`, `applyODataMetadataVersion` | Technical version integration |
|
|
775
|
+
|
|
776
|
+
`useODataEntity` возвращает actions `getEntityPath`, `getKeys`, `getKeyPairs`, `getKeyPairsMap`; actions нельзя вызывать до окончания metadata loading.
|
|
777
|
+
|
|
778
|
+
### Collections и dependent UI contracts
|
|
779
|
+
|
|
780
|
+
| Группа | Exports |
|
|
781
|
+
| --- | --- |
|
|
782
|
+
| Query | `useODataCollectionQuery`, `createODataCollectionBaseQueryKey`, `createODataCollectionQueryKey`, `resolveODataCollectionSwCachePolicy`, `shouldRefreshODataCollectionCache` |
|
|
783
|
+
| Model | `useODataCollection`, `useODataCollectionModel`, `useODataCollectionChains` |
|
|
784
|
+
| Updates | `useODataCollectionUpdatesQuery`, `odataCollectionUpdatesQueryOptions`, `fetchODataCollectionUpdates`, `createODataCollectionUpdatesQueryKey`, `ODATA_COLLECTION_UPDATES_LOOKBACK_MS`, `ODATA_COLLECTION_DEFAULT_SW_CACHE`, `ODATA_COLLECTION_BUST_SW_CACHE`, `ODATA_COLLECTION_UPDATES_SW_CACHE` |
|
|
785
|
+
| Dependent | `flattenODataDependentServices`, `sortODataDependentSegmentItemsByChains`, deprecated `sortODataDependentServicesByChains` |
|
|
786
|
+
| Types | `ControlPanelVisibility`, `ODataServiceCollectionConfig`, `ODataCollectionConfig`, `ODataCollectionResult`, `ODataCollectionSegment`, `ODataCollectionModel`, `ODataCollectionProps`, `ODataSelectBaseProps`, `ODataSingleSelectProps`, `ODataDependentSegment`, `ODataDependentSegments`, `ODataDependentBaseProps`, `ODataDependentSegmentItem`, `ODataCollectionUpdateItem`, `ODataCollectionUpdatesResult`, `ODataCollectionUpdatesOptions` |
|
|
787
|
+
|
|
788
|
+
### Tables
|
|
789
|
+
|
|
790
|
+
`useODataTableColumns`, `UseODataTableColumnsBuildConfig`, `UseODataTableColumnsEnrichConfig`, `UseODataTableColumnsConfig`, `UseODataTableColumnsResult`.
|