@proofkit/fmodata 0.1.0-alpha.9 → 0.1.0-beta.24
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/LICENSE.md +21 -0
- package/README.md +655 -453
- package/dist/esm/client/batch-builder.d.ts +10 -9
- package/dist/esm/client/batch-builder.js +119 -56
- package/dist/esm/client/batch-builder.js.map +1 -1
- package/dist/esm/client/batch-request.js +16 -21
- package/dist/esm/client/batch-request.js.map +1 -1
- package/dist/esm/client/builders/default-select.d.ts +10 -0
- package/dist/esm/client/builders/default-select.js +41 -0
- package/dist/esm/client/builders/default-select.js.map +1 -0
- package/dist/esm/client/builders/expand-builder.d.ts +45 -0
- package/dist/esm/client/builders/expand-builder.js +185 -0
- package/dist/esm/client/builders/expand-builder.js.map +1 -0
- package/dist/esm/client/builders/index.d.ts +9 -0
- package/dist/esm/client/builders/query-string-builder.d.ts +18 -0
- package/dist/esm/client/builders/query-string-builder.js +21 -0
- package/dist/esm/client/builders/query-string-builder.js.map +1 -0
- package/dist/esm/client/builders/response-processor.d.ts +43 -0
- package/dist/esm/client/builders/response-processor.js +175 -0
- package/dist/esm/client/builders/response-processor.js.map +1 -0
- package/dist/esm/client/builders/select-mixin.d.ts +25 -0
- package/dist/esm/client/builders/select-mixin.js +28 -0
- package/dist/esm/client/builders/select-mixin.js.map +1 -0
- package/dist/esm/client/builders/select-utils.d.ts +18 -0
- package/dist/esm/client/builders/select-utils.js +30 -0
- package/dist/esm/client/builders/select-utils.js.map +1 -0
- package/dist/esm/client/builders/shared-types.d.ts +40 -0
- package/dist/esm/client/builders/table-utils.d.ts +35 -0
- package/dist/esm/client/builders/table-utils.js +44 -0
- package/dist/esm/client/builders/table-utils.js.map +1 -0
- package/dist/esm/client/database.d.ts +34 -22
- package/dist/esm/client/database.js +48 -84
- package/dist/esm/client/database.js.map +1 -1
- package/dist/esm/client/delete-builder.d.ts +25 -30
- package/dist/esm/client/delete-builder.js +45 -30
- package/dist/esm/client/delete-builder.js.map +1 -1
- package/dist/esm/client/entity-set.d.ts +35 -43
- package/dist/esm/client/entity-set.js +126 -52
- package/dist/esm/client/entity-set.js.map +1 -1
- package/dist/esm/client/error-parser.d.ts +12 -0
- package/dist/esm/client/error-parser.js +25 -0
- package/dist/esm/client/error-parser.js.map +1 -0
- package/dist/esm/client/filemaker-odata.d.ts +26 -7
- package/dist/esm/client/filemaker-odata.js +65 -42
- package/dist/esm/client/filemaker-odata.js.map +1 -1
- package/dist/esm/client/insert-builder.d.ts +19 -24
- package/dist/esm/client/insert-builder.js +94 -58
- package/dist/esm/client/insert-builder.js.map +1 -1
- package/dist/esm/client/query/expand-builder.d.ts +35 -0
- package/dist/esm/client/query/index.d.ts +4 -0
- package/dist/esm/client/query/query-builder.d.ts +132 -0
- package/dist/esm/client/query/query-builder.js +456 -0
- package/dist/esm/client/query/query-builder.js.map +1 -0
- package/dist/esm/client/query/response-processor.d.ts +25 -0
- package/dist/esm/client/query/types.d.ts +77 -0
- package/dist/esm/client/query/url-builder.d.ts +71 -0
- package/dist/esm/client/query/url-builder.js +100 -0
- package/dist/esm/client/query/url-builder.js.map +1 -0
- package/dist/esm/client/query-builder.d.ts +2 -115
- package/dist/esm/client/record-builder.d.ts +108 -36
- package/dist/esm/client/record-builder.js +284 -119
- package/dist/esm/client/record-builder.js.map +1 -1
- package/dist/esm/client/response-processor.d.ts +4 -9
- package/dist/esm/client/sanitize-json.d.ts +35 -0
- package/dist/esm/client/sanitize-json.js +27 -0
- package/dist/esm/client/sanitize-json.js.map +1 -0
- package/dist/esm/client/schema-manager.d.ts +5 -5
- package/dist/esm/client/schema-manager.js +45 -31
- package/dist/esm/client/schema-manager.js.map +1 -1
- package/dist/esm/client/update-builder.d.ts +34 -40
- package/dist/esm/client/update-builder.js +99 -58
- package/dist/esm/client/update-builder.js.map +1 -1
- package/dist/esm/client/webhook-builder.d.ts +126 -0
- package/dist/esm/client/webhook-builder.js +189 -0
- package/dist/esm/client/webhook-builder.js.map +1 -0
- package/dist/esm/errors.d.ts +19 -2
- package/dist/esm/errors.js +39 -4
- package/dist/esm/errors.js.map +1 -1
- package/dist/esm/index.d.ts +10 -8
- package/dist/esm/index.js +40 -10
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/logger.d.ts +47 -0
- package/dist/esm/logger.js +69 -0
- package/dist/esm/logger.js.map +1 -0
- package/dist/esm/logger.test.d.ts +1 -0
- package/dist/esm/orm/column.d.ts +62 -0
- package/dist/esm/orm/column.js +63 -0
- package/dist/esm/orm/column.js.map +1 -0
- package/dist/esm/orm/field-builders.d.ts +164 -0
- package/dist/esm/orm/field-builders.js +158 -0
- package/dist/esm/orm/field-builders.js.map +1 -0
- package/dist/esm/orm/index.d.ts +5 -0
- package/dist/esm/orm/operators.d.ts +173 -0
- package/dist/esm/orm/operators.js +260 -0
- package/dist/esm/orm/operators.js.map +1 -0
- package/dist/esm/orm/table.d.ts +355 -0
- package/dist/esm/orm/table.js +202 -0
- package/dist/esm/orm/table.js.map +1 -0
- package/dist/esm/transform.d.ts +20 -21
- package/dist/esm/transform.js +44 -45
- package/dist/esm/transform.js.map +1 -1
- package/dist/esm/types.d.ts +96 -30
- package/dist/esm/types.js +7 -0
- package/dist/esm/types.js.map +1 -0
- package/dist/esm/validation.d.ts +22 -12
- package/dist/esm/validation.js +132 -85
- package/dist/esm/validation.js.map +1 -1
- package/package.json +34 -29
- package/src/client/batch-builder.ts +153 -89
- package/src/client/batch-request.ts +25 -41
- package/src/client/builders/default-select.ts +75 -0
- package/src/client/builders/expand-builder.ts +246 -0
- package/src/client/builders/index.ts +11 -0
- package/src/client/builders/query-string-builder.ts +46 -0
- package/src/client/builders/response-processor.ts +279 -0
- package/src/client/builders/select-mixin.ts +65 -0
- package/src/client/builders/select-utils.ts +59 -0
- package/src/client/builders/shared-types.ts +45 -0
- package/src/client/builders/table-utils.ts +83 -0
- package/src/client/database.ts +89 -183
- package/src/client/delete-builder.ts +74 -84
- package/src/client/entity-set.ts +286 -293
- package/src/client/error-parser.ts +41 -0
- package/src/client/filemaker-odata.ts +98 -66
- package/src/client/insert-builder.ts +157 -118
- package/src/client/query/expand-builder.ts +160 -0
- package/src/client/query/index.ts +14 -0
- package/src/client/query/query-builder.ts +729 -0
- package/src/client/query/response-processor.ts +226 -0
- package/src/client/query/types.ts +126 -0
- package/src/client/query/url-builder.ts +151 -0
- package/src/client/query-builder.ts +10 -1455
- package/src/client/record-builder.ts +575 -240
- package/src/client/response-processor.ts +15 -42
- package/src/client/sanitize-json.ts +64 -0
- package/src/client/schema-manager.ts +61 -76
- package/src/client/update-builder.ts +161 -143
- package/src/client/webhook-builder.ts +265 -0
- package/src/errors.ts +49 -16
- package/src/index.ts +99 -54
- package/src/logger.test.ts +34 -0
- package/src/logger.ts +116 -0
- package/src/orm/column.ts +106 -0
- package/src/orm/field-builders.ts +250 -0
- package/src/orm/index.ts +61 -0
- package/src/orm/operators.ts +473 -0
- package/src/orm/table.ts +741 -0
- package/src/transform.ts +90 -70
- package/src/types.ts +154 -113
- package/src/validation.ts +200 -115
- package/dist/esm/client/base-table.d.ts +0 -125
- package/dist/esm/client/base-table.js +0 -57
- package/dist/esm/client/base-table.js.map +0 -1
- package/dist/esm/client/query-builder.js +0 -896
- package/dist/esm/client/query-builder.js.map +0 -1
- package/dist/esm/client/table-occurrence.d.ts +0 -72
- package/dist/esm/client/table-occurrence.js +0 -74
- package/dist/esm/client/table-occurrence.js.map +0 -1
- package/dist/esm/filter-types.d.ts +0 -76
- package/src/client/base-table.ts +0 -175
- package/src/client/query-builder.ts.bak +0 -1457
- package/src/client/table-occurrence.ts +0 -175
- package/src/filter-types.ts +0 -97
|
@@ -1,66 +1,56 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
} from "../
|
|
8
|
-
import
|
|
9
|
-
import type { BaseTable } from "./base-table";
|
|
1
|
+
import type { FFetchOptions } from "@fetchkit/ffetch";
|
|
2
|
+
import type { FMTable, InferSchemaOutputFromFMTable } from "../orm/table";
|
|
3
|
+
import { getBaseTableConfig, getTableId as getTableIdHelper, getTableName, isUsingEntityIds } from "../orm/table";
|
|
4
|
+
import { transformFieldNamesToIds } from "../transform";
|
|
5
|
+
import type { ExecutableBuilder, ExecuteMethodOptions, ExecuteOptions, ExecutionContext, Result } from "../types";
|
|
6
|
+
import { getAcceptHeader } from "../types";
|
|
7
|
+
import { validateAndTransformInput } from "../validation";
|
|
8
|
+
import { parseErrorResponse } from "./error-parser";
|
|
10
9
|
import { QueryBuilder } from "./query-builder";
|
|
11
|
-
import { type FFetchOptions } from "@fetchkit/ffetch";
|
|
12
|
-
import {
|
|
13
|
-
transformFieldNamesToIds,
|
|
14
|
-
transformTableName,
|
|
15
|
-
getTableIdentifiers,
|
|
16
|
-
} from "../transform";
|
|
17
10
|
|
|
18
11
|
/**
|
|
19
12
|
* Initial update builder returned from EntitySet.update(data)
|
|
20
13
|
* Requires calling .byId() or .where() before .execute() is available
|
|
21
14
|
*/
|
|
22
15
|
export class UpdateBuilder<
|
|
23
|
-
|
|
24
|
-
|
|
16
|
+
// biome-ignore lint/suspicious/noExplicitAny: Accepts any FMTable configuration
|
|
17
|
+
Occ extends FMTable<any, any>,
|
|
25
18
|
ReturnPreference extends "minimal" | "representation" = "minimal",
|
|
26
19
|
> {
|
|
27
|
-
private
|
|
28
|
-
private
|
|
29
|
-
private
|
|
30
|
-
private
|
|
31
|
-
private
|
|
32
|
-
private returnPreference: ReturnPreference;
|
|
20
|
+
private readonly databaseName: string;
|
|
21
|
+
private readonly context: ExecutionContext;
|
|
22
|
+
private readonly table: Occ;
|
|
23
|
+
private readonly data: Partial<InferSchemaOutputFromFMTable<Occ>>;
|
|
24
|
+
private readonly returnPreference: ReturnPreference;
|
|
33
25
|
|
|
34
|
-
private databaseUseEntityIds: boolean;
|
|
26
|
+
private readonly databaseUseEntityIds: boolean;
|
|
27
|
+
private readonly databaseIncludeSpecialColumns: boolean;
|
|
35
28
|
|
|
36
29
|
constructor(config: {
|
|
37
|
-
occurrence
|
|
38
|
-
tableName: string;
|
|
30
|
+
occurrence: Occ;
|
|
39
31
|
databaseName: string;
|
|
40
32
|
context: ExecutionContext;
|
|
41
|
-
data: Partial<
|
|
33
|
+
data: Partial<InferSchemaOutputFromFMTable<Occ>>;
|
|
42
34
|
returnPreference: ReturnPreference;
|
|
43
35
|
databaseUseEntityIds?: boolean;
|
|
36
|
+
databaseIncludeSpecialColumns?: boolean;
|
|
44
37
|
}) {
|
|
45
|
-
this.
|
|
46
|
-
this.tableName = config.tableName;
|
|
38
|
+
this.table = config.occurrence;
|
|
47
39
|
this.databaseName = config.databaseName;
|
|
48
40
|
this.context = config.context;
|
|
49
41
|
this.data = config.data;
|
|
50
42
|
this.returnPreference = config.returnPreference;
|
|
51
43
|
this.databaseUseEntityIds = config.databaseUseEntityIds ?? false;
|
|
44
|
+
this.databaseIncludeSpecialColumns = config.databaseIncludeSpecialColumns ?? false;
|
|
52
45
|
}
|
|
53
46
|
|
|
54
47
|
/**
|
|
55
48
|
* Update a single record by ID
|
|
56
49
|
* Returns updated count by default, or full record if returnFullRecord was set to true
|
|
57
50
|
*/
|
|
58
|
-
byId(
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
return new ExecutableUpdateBuilder<T, true, ReturnPreference>({
|
|
62
|
-
occurrence: this.occurrence,
|
|
63
|
-
tableName: this.tableName,
|
|
51
|
+
byId(id: string | number): ExecutableUpdateBuilder<Occ, true, ReturnPreference> {
|
|
52
|
+
return new ExecutableUpdateBuilder<Occ, true, ReturnPreference>({
|
|
53
|
+
occurrence: this.table,
|
|
64
54
|
databaseName: this.databaseName,
|
|
65
55
|
context: this.context,
|
|
66
56
|
data: this.data,
|
|
@@ -76,21 +66,10 @@ export class UpdateBuilder<
|
|
|
76
66
|
* Returns updated count by default, or full record if returnFullRecord was set to true
|
|
77
67
|
* @param fn Callback that receives a QueryBuilder for building the filter
|
|
78
68
|
*/
|
|
79
|
-
where(
|
|
80
|
-
fn: (
|
|
81
|
-
q: QueryBuilder<WithSystemFields<T>>,
|
|
82
|
-
) => QueryBuilder<WithSystemFields<T>>,
|
|
83
|
-
): ExecutableUpdateBuilder<T, true, ReturnPreference> {
|
|
69
|
+
where(fn: (q: QueryBuilder<Occ>) => QueryBuilder<Occ>): ExecutableUpdateBuilder<Occ, true, ReturnPreference> {
|
|
84
70
|
// Create a QueryBuilder for the user to configure
|
|
85
|
-
const queryBuilder = new QueryBuilder<
|
|
86
|
-
|
|
87
|
-
keyof WithSystemFields<T>,
|
|
88
|
-
false,
|
|
89
|
-
false,
|
|
90
|
-
undefined
|
|
91
|
-
>({
|
|
92
|
-
occurrence: undefined,
|
|
93
|
-
tableName: this.tableName,
|
|
71
|
+
const queryBuilder = new QueryBuilder<Occ>({
|
|
72
|
+
occurrence: this.table,
|
|
94
73
|
databaseName: this.databaseName,
|
|
95
74
|
context: this.context,
|
|
96
75
|
});
|
|
@@ -98,9 +77,8 @@ export class UpdateBuilder<
|
|
|
98
77
|
// Let the user configure it
|
|
99
78
|
const configuredBuilder = fn(queryBuilder);
|
|
100
79
|
|
|
101
|
-
return new ExecutableUpdateBuilder<
|
|
102
|
-
occurrence: this.
|
|
103
|
-
tableName: this.tableName,
|
|
80
|
+
return new ExecutableUpdateBuilder<Occ, true, ReturnPreference>({
|
|
81
|
+
occurrence: this.table,
|
|
104
82
|
databaseName: this.databaseName,
|
|
105
83
|
context: this.context,
|
|
106
84
|
data: this.data,
|
|
@@ -118,39 +96,35 @@ export class UpdateBuilder<
|
|
|
118
96
|
* Can return either updated count or full record based on returnFullRecord option
|
|
119
97
|
*/
|
|
120
98
|
export class ExecutableUpdateBuilder<
|
|
121
|
-
|
|
122
|
-
|
|
99
|
+
// biome-ignore lint/suspicious/noExplicitAny: Accepts any FMTable configuration
|
|
100
|
+
Occ extends FMTable<any, any>,
|
|
101
|
+
_IsByFilter extends boolean,
|
|
123
102
|
ReturnPreference extends "minimal" | "representation" = "minimal",
|
|
124
103
|
> implements
|
|
125
|
-
ExecutableBuilder<
|
|
126
|
-
ReturnPreference extends "minimal" ? { updatedCount: number } : T
|
|
127
|
-
>
|
|
104
|
+
ExecutableBuilder<ReturnPreference extends "minimal" ? { updatedCount: number } : InferSchemaOutputFromFMTable<Occ>>
|
|
128
105
|
{
|
|
129
|
-
private
|
|
130
|
-
private
|
|
131
|
-
private
|
|
132
|
-
private
|
|
133
|
-
private
|
|
134
|
-
private
|
|
135
|
-
private
|
|
136
|
-
private
|
|
137
|
-
private
|
|
138
|
-
private databaseUseEntityIds: boolean;
|
|
106
|
+
private readonly databaseName: string;
|
|
107
|
+
private readonly context: ExecutionContext;
|
|
108
|
+
private readonly table: Occ;
|
|
109
|
+
private readonly data: Partial<InferSchemaOutputFromFMTable<Occ>>;
|
|
110
|
+
private readonly mode: "byId" | "byFilter";
|
|
111
|
+
private readonly recordId?: string | number;
|
|
112
|
+
private readonly queryBuilder?: QueryBuilder<Occ>;
|
|
113
|
+
private readonly returnPreference: ReturnPreference;
|
|
114
|
+
private readonly databaseUseEntityIds: boolean;
|
|
139
115
|
|
|
140
116
|
constructor(config: {
|
|
141
|
-
occurrence
|
|
142
|
-
tableName: string;
|
|
117
|
+
occurrence: Occ;
|
|
143
118
|
databaseName: string;
|
|
144
119
|
context: ExecutionContext;
|
|
145
|
-
data: Partial<
|
|
120
|
+
data: Partial<InferSchemaOutputFromFMTable<Occ>>;
|
|
146
121
|
mode: "byId" | "byFilter";
|
|
147
122
|
recordId?: string | number;
|
|
148
|
-
queryBuilder?: QueryBuilder<
|
|
123
|
+
queryBuilder?: QueryBuilder<Occ>;
|
|
149
124
|
returnPreference: ReturnPreference;
|
|
150
125
|
databaseUseEntityIds?: boolean;
|
|
151
126
|
}) {
|
|
152
|
-
this.
|
|
153
|
-
this.tableName = config.tableName;
|
|
127
|
+
this.table = config.occurrence;
|
|
154
128
|
this.databaseName = config.databaseName;
|
|
155
129
|
this.context = config.context;
|
|
156
130
|
this.data = config.data;
|
|
@@ -179,30 +153,25 @@ export class ExecutableUpdateBuilder<
|
|
|
179
153
|
* @param useEntityIds - Optional override for entity ID usage
|
|
180
154
|
*/
|
|
181
155
|
private getTableId(useEntityIds?: boolean): string {
|
|
182
|
-
if (!this.occurrence) {
|
|
183
|
-
return this.tableName;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
156
|
const contextDefault = this.context._getUseEntityIds?.() ?? false;
|
|
187
157
|
const shouldUseIds = useEntityIds ?? contextDefault;
|
|
188
158
|
|
|
189
159
|
if (shouldUseIds) {
|
|
190
|
-
|
|
191
|
-
if (!identifiers.id) {
|
|
160
|
+
if (!isUsingEntityIds(this.table)) {
|
|
192
161
|
throw new Error(
|
|
193
|
-
`useEntityIds is true but
|
|
162
|
+
`useEntityIds is true but table "${getTableName(this.table)}" does not have entity IDs configured`,
|
|
194
163
|
);
|
|
195
164
|
}
|
|
196
|
-
return
|
|
165
|
+
return getTableIdHelper(this.table);
|
|
197
166
|
}
|
|
198
167
|
|
|
199
|
-
return this.
|
|
168
|
+
return getTableName(this.table);
|
|
200
169
|
}
|
|
201
170
|
|
|
202
171
|
async execute(
|
|
203
|
-
options?:
|
|
172
|
+
options?: ExecuteMethodOptions<ExecuteOptions>,
|
|
204
173
|
): Promise<
|
|
205
|
-
Result<ReturnPreference extends "minimal" ? { updatedCount: number } :
|
|
174
|
+
Result<ReturnPreference extends "minimal" ? { updatedCount: number } : InferSchemaOutputFromFMTable<Occ>>
|
|
206
175
|
> {
|
|
207
176
|
// Merge database-level useEntityIds with per-request options
|
|
208
177
|
const mergedOptions = this.mergeExecuteOptions(options);
|
|
@@ -210,14 +179,30 @@ export class ExecutableUpdateBuilder<
|
|
|
210
179
|
// Get table identifier with override support
|
|
211
180
|
const tableId = this.getTableId(mergedOptions.useEntityIds);
|
|
212
181
|
|
|
182
|
+
// Validate and transform input data using input validators (writeValidators)
|
|
183
|
+
let validatedData = this.data;
|
|
184
|
+
if (this.table) {
|
|
185
|
+
const baseTableConfig = getBaseTableConfig(this.table);
|
|
186
|
+
const inputSchema = baseTableConfig.inputSchema;
|
|
187
|
+
|
|
188
|
+
try {
|
|
189
|
+
validatedData = await validateAndTransformInput(this.data, inputSchema);
|
|
190
|
+
} catch (error) {
|
|
191
|
+
// If validation fails, return error immediately
|
|
192
|
+
return {
|
|
193
|
+
data: undefined,
|
|
194
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
195
|
+
// biome-ignore lint/suspicious/noExplicitAny: Type assertion for generic return type
|
|
196
|
+
} as any;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
213
200
|
// Transform field names to FMFIDs if using entity IDs
|
|
214
201
|
// Only transform if useEntityIds resolves to true (respects per-request override)
|
|
215
202
|
const shouldUseIds = mergedOptions.useEntityIds ?? false;
|
|
216
203
|
|
|
217
204
|
const transformedData =
|
|
218
|
-
this.
|
|
219
|
-
? transformFieldNamesToIds(this.data, this.occurrence.baseTable)
|
|
220
|
-
: this.data;
|
|
205
|
+
this.table && shouldUseIds ? transformFieldNamesToIds(validatedData, this.table) : validatedData;
|
|
221
206
|
|
|
222
207
|
let url: string;
|
|
223
208
|
|
|
@@ -234,11 +219,15 @@ export class ExecutableUpdateBuilder<
|
|
|
234
219
|
const queryString = this.queryBuilder.getQueryString();
|
|
235
220
|
// The query string will have the tableId already transformed by QueryBuilder
|
|
236
221
|
// Remove the leading "/" and table name from the query string as we'll build our own URL
|
|
237
|
-
const
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
222
|
+
const tableName = getTableName(this.table);
|
|
223
|
+
let queryParams: string;
|
|
224
|
+
if (queryString.startsWith(`/${tableId}`)) {
|
|
225
|
+
queryParams = queryString.slice(`/${tableId}`.length);
|
|
226
|
+
} else if (queryString.startsWith(`/${tableName}`)) {
|
|
227
|
+
queryParams = queryString.slice(`/${tableName}`.length);
|
|
228
|
+
} else {
|
|
229
|
+
queryParams = queryString;
|
|
230
|
+
}
|
|
242
231
|
|
|
243
232
|
url = `/${this.databaseName}/${tableId}${queryParams}`;
|
|
244
233
|
}
|
|
@@ -249,7 +238,7 @@ export class ExecutableUpdateBuilder<
|
|
|
249
238
|
};
|
|
250
239
|
|
|
251
240
|
if (this.returnPreference === "representation") {
|
|
252
|
-
headers
|
|
241
|
+
headers.Prefer = "return=representation";
|
|
253
242
|
}
|
|
254
243
|
|
|
255
244
|
// Make PATCH request with JSON body
|
|
@@ -272,38 +261,38 @@ export class ExecutableUpdateBuilder<
|
|
|
272
261
|
return {
|
|
273
262
|
data: response as ReturnPreference extends "minimal"
|
|
274
263
|
? { updatedCount: number }
|
|
275
|
-
:
|
|
276
|
-
error: undefined,
|
|
277
|
-
};
|
|
278
|
-
} else {
|
|
279
|
-
// Return updated count (minimal)
|
|
280
|
-
let updatedCount = 0;
|
|
281
|
-
|
|
282
|
-
if (typeof response === "number") {
|
|
283
|
-
updatedCount = response;
|
|
284
|
-
} else if (response && typeof response === "object") {
|
|
285
|
-
// Check if the response has a count property (fallback)
|
|
286
|
-
updatedCount = (response as any).updatedCount || 0;
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
return {
|
|
290
|
-
data: { updatedCount } as ReturnPreference extends "minimal"
|
|
291
|
-
? { updatedCount: number }
|
|
292
|
-
: T,
|
|
264
|
+
: InferSchemaOutputFromFMTable<Occ>,
|
|
293
265
|
error: undefined,
|
|
294
266
|
};
|
|
295
267
|
}
|
|
268
|
+
// Return updated count (minimal)
|
|
269
|
+
let updatedCount = 0;
|
|
270
|
+
|
|
271
|
+
if (typeof response === "number") {
|
|
272
|
+
updatedCount = response;
|
|
273
|
+
} else if (response && typeof response === "object") {
|
|
274
|
+
// Check if the response has a count property (fallback)
|
|
275
|
+
// biome-ignore lint/suspicious/noExplicitAny: Dynamic response type from OData API
|
|
276
|
+
updatedCount = (response as any).updatedCount || 0;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return {
|
|
280
|
+
data: { updatedCount } as ReturnPreference extends "minimal"
|
|
281
|
+
? { updatedCount: number }
|
|
282
|
+
: InferSchemaOutputFromFMTable<Occ>,
|
|
283
|
+
error: undefined,
|
|
284
|
+
};
|
|
296
285
|
}
|
|
297
286
|
|
|
287
|
+
// biome-ignore lint/suspicious/noExplicitAny: Request body can be any JSON-serializable value
|
|
298
288
|
getRequestConfig(): { method: string; url: string; body?: any } {
|
|
299
289
|
// For batch operations, use database-level setting (no per-request override available here)
|
|
290
|
+
// Note: Input validation happens in execute() and processResponse() for batch operations
|
|
300
291
|
const tableId = this.getTableId(this.databaseUseEntityIds);
|
|
301
292
|
|
|
302
293
|
// Transform field names to FMFIDs if using entity IDs
|
|
303
294
|
const transformedData =
|
|
304
|
-
this.
|
|
305
|
-
? transformFieldNamesToIds(this.data, this.occurrence.baseTable)
|
|
306
|
-
: this.data;
|
|
295
|
+
this.table && this.databaseUseEntityIds ? transformFieldNamesToIds(this.data, this.table) : this.data;
|
|
307
296
|
|
|
308
297
|
let url: string;
|
|
309
298
|
|
|
@@ -315,11 +304,15 @@ export class ExecutableUpdateBuilder<
|
|
|
315
304
|
}
|
|
316
305
|
|
|
317
306
|
const queryString = this.queryBuilder.getQueryString();
|
|
318
|
-
const
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
307
|
+
const tableName = getTableName(this.table);
|
|
308
|
+
let queryParams: string;
|
|
309
|
+
if (queryString.startsWith(`/${tableId}`)) {
|
|
310
|
+
queryParams = queryString.slice(`/${tableId}`.length);
|
|
311
|
+
} else if (queryString.startsWith(`/${tableName}`)) {
|
|
312
|
+
queryParams = queryString.slice(`/${tableName}`.length);
|
|
313
|
+
} else {
|
|
314
|
+
queryParams = queryString;
|
|
315
|
+
}
|
|
323
316
|
|
|
324
317
|
url = `/${this.databaseName}/${tableId}${queryParams}`;
|
|
325
318
|
}
|
|
@@ -331,7 +324,7 @@ export class ExecutableUpdateBuilder<
|
|
|
331
324
|
};
|
|
332
325
|
}
|
|
333
326
|
|
|
334
|
-
toRequest(baseUrl: string): Request {
|
|
327
|
+
toRequest(baseUrl: string, options?: ExecuteOptions): Request {
|
|
335
328
|
const config = this.getRequestConfig();
|
|
336
329
|
const fullUrl = `${baseUrl}${config.url}`;
|
|
337
330
|
|
|
@@ -339,7 +332,7 @@ export class ExecutableUpdateBuilder<
|
|
|
339
332
|
method: config.method,
|
|
340
333
|
headers: {
|
|
341
334
|
"Content-Type": "application/json",
|
|
342
|
-
Accept:
|
|
335
|
+
Accept: getAcceptHeader(options?.includeODataAnnotations),
|
|
343
336
|
},
|
|
344
337
|
body: config.body,
|
|
345
338
|
});
|
|
@@ -347,52 +340,77 @@ export class ExecutableUpdateBuilder<
|
|
|
347
340
|
|
|
348
341
|
async processResponse(
|
|
349
342
|
response: Response,
|
|
350
|
-
|
|
343
|
+
_options?: ExecuteOptions,
|
|
351
344
|
): Promise<
|
|
352
|
-
Result<ReturnPreference extends "minimal" ? { updatedCount: number } :
|
|
345
|
+
Result<ReturnPreference extends "minimal" ? { updatedCount: number } : InferSchemaOutputFromFMTable<Occ>>
|
|
353
346
|
> {
|
|
347
|
+
// Check for error responses (important for batch operations)
|
|
348
|
+
if (!response.ok) {
|
|
349
|
+
const tableName = getTableName(this.table);
|
|
350
|
+
const error = await parseErrorResponse(response, response.url || `/${this.databaseName}/${tableName}`);
|
|
351
|
+
return { data: undefined, error };
|
|
352
|
+
}
|
|
353
|
+
|
|
354
354
|
// Check for empty response (204 No Content)
|
|
355
355
|
const text = await response.text();
|
|
356
356
|
if (!text || text.trim() === "") {
|
|
357
357
|
// For 204 No Content, check the fmodata.affected_rows header
|
|
358
358
|
const affectedRows = response.headers.get("fmodata.affected_rows");
|
|
359
|
-
const updatedCount = affectedRows ? parseInt(affectedRows, 10) : 1;
|
|
359
|
+
const updatedCount = affectedRows ? Number.parseInt(affectedRows, 10) : 1;
|
|
360
360
|
return {
|
|
361
361
|
data: { updatedCount } as ReturnPreference extends "minimal"
|
|
362
362
|
? { updatedCount: number }
|
|
363
|
-
:
|
|
363
|
+
: InferSchemaOutputFromFMTable<Occ>,
|
|
364
364
|
error: undefined,
|
|
365
365
|
};
|
|
366
366
|
}
|
|
367
367
|
|
|
368
368
|
const rawResponse = JSON.parse(text);
|
|
369
369
|
|
|
370
|
+
// Validate and transform input data using input validators (writeValidators)
|
|
371
|
+
// This is needed for processResponse because it's called from batch operations
|
|
372
|
+
// where the data hasn't been validated yet
|
|
373
|
+
let _validatedData = this.data;
|
|
374
|
+
if (this.table) {
|
|
375
|
+
const baseTableConfig = getBaseTableConfig(this.table);
|
|
376
|
+
const inputSchema = baseTableConfig.inputSchema;
|
|
377
|
+
try {
|
|
378
|
+
_validatedData = await validateAndTransformInput(this.data, inputSchema);
|
|
379
|
+
} catch (error) {
|
|
380
|
+
return {
|
|
381
|
+
data: undefined,
|
|
382
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
383
|
+
// biome-ignore lint/suspicious/noExplicitAny: Type assertion for generic return type
|
|
384
|
+
} as any;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
370
388
|
// Handle based on return preference
|
|
371
389
|
if (this.returnPreference === "representation") {
|
|
372
390
|
// Return the full updated record
|
|
373
391
|
return {
|
|
374
392
|
data: rawResponse as ReturnPreference extends "minimal"
|
|
375
393
|
? { updatedCount: number }
|
|
376
|
-
:
|
|
377
|
-
error: undefined,
|
|
378
|
-
};
|
|
379
|
-
} else {
|
|
380
|
-
// Return updated count (minimal)
|
|
381
|
-
let updatedCount = 0;
|
|
382
|
-
|
|
383
|
-
if (typeof rawResponse === "number") {
|
|
384
|
-
updatedCount = rawResponse;
|
|
385
|
-
} else if (rawResponse && typeof rawResponse === "object") {
|
|
386
|
-
// Check if the response has a count property (fallback)
|
|
387
|
-
updatedCount = (rawResponse as any).updatedCount || 0;
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
return {
|
|
391
|
-
data: { updatedCount } as ReturnPreference extends "minimal"
|
|
392
|
-
? { updatedCount: number }
|
|
393
|
-
: T,
|
|
394
|
+
: InferSchemaOutputFromFMTable<Occ>,
|
|
394
395
|
error: undefined,
|
|
395
396
|
};
|
|
396
397
|
}
|
|
398
|
+
// Return updated count (minimal)
|
|
399
|
+
let updatedCount = 0;
|
|
400
|
+
|
|
401
|
+
if (typeof rawResponse === "number") {
|
|
402
|
+
updatedCount = rawResponse;
|
|
403
|
+
} else if (rawResponse && typeof rawResponse === "object") {
|
|
404
|
+
// Check if the response has a count property (fallback)
|
|
405
|
+
// biome-ignore lint/suspicious/noExplicitAny: Dynamic response type from OData API
|
|
406
|
+
updatedCount = (rawResponse as any).updatedCount || 0;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
return {
|
|
410
|
+
data: { updatedCount } as ReturnPreference extends "minimal"
|
|
411
|
+
? { updatedCount: number }
|
|
412
|
+
: InferSchemaOutputFromFMTable<Occ>,
|
|
413
|
+
error: undefined,
|
|
414
|
+
};
|
|
397
415
|
}
|
|
398
416
|
}
|