@burdenoff/microfe-bigconsole 2026.924.3 → 2026.925.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bigconsole/BigConsoleRoot.js +94 -84
- package/dist/bigconsole/BigConsoleRoot.js.map +1 -1
- package/dist/bigconsole/assistant/previewController.js +6 -6
- package/dist/bigconsole/components/dashboard/ShareDialog/EmbedTab.js +142 -145
- package/dist/bigconsole/components/dashboard/ShareDialog/EmbedTab.js.map +1 -1
- package/dist/bigconsole/components/datasink/ImportDataSinkDialog.js +73 -61
- package/dist/bigconsole/components/datasink/ImportDataSinkDialog.js.map +1 -1
- package/dist/bigconsole/components/embed/ReadOnlyDashboardRenderer.js +5 -5
- package/dist/bigconsole/components/embed/ReadOnlyDashboardRenderer.js.map +1 -1
- package/dist/bigconsole/components/embed/types.js.map +1 -1
- package/dist/bigconsole/components/preview/AiPreviewMiniPanel.js +4 -4
- package/dist/bigconsole/components/preview/AiPreviewPanel.js +5 -5
- package/dist/bigconsole/hooks/useDashboardEmbedPolicy.js +2 -4
- package/dist/bigconsole/hooks/useDashboardEmbedPolicy.js.map +1 -1
- package/dist/bigconsole/hooks/useDataSinkOperations.js +92 -89
- package/dist/bigconsole/hooks/useDataSinkOperations.js.map +1 -1
- package/dist/bigconsole/hooks/useImportDataSink.js +96 -91
- package/dist/bigconsole/hooks/useImportDataSink.js.map +1 -1
- package/dist/bigconsole/pages/DataSinkBuilderPage.js +79 -77
- package/dist/bigconsole/pages/DataSinkBuilderPage.js.map +1 -1
- package/dist/bigconsole/providers/BigConsoleQuotaProvider.js +36 -35
- package/dist/bigconsole/providers/BigConsoleQuotaProvider.js.map +1 -1
- package/dist/bigconsole/utils/quota.js +51 -23
- package/dist/bigconsole/utils/quota.js.map +1 -1
- package/dist/generated/wspace-operations.js +0 -1
- package/dist/generated/wspace-operations.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { CreateDataSinkDocument as e, GetDataSinkDocument as t, UpdateDataSinkDocument as n } from "../../generated/wspace-operations.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { createBigConsoleQuotaError as r, isBigConsoleQuotaError as i } from "../utils/quota.js";
|
|
3
|
+
import { useCallback as a, useState as o } from "react";
|
|
4
|
+
import { useApolloClient as s } from "@apollo/client/react";
|
|
4
5
|
//#region src/bigconsole/hooks/useImportDataSink.ts
|
|
5
|
-
function
|
|
6
|
+
function c(e) {
|
|
6
7
|
return new Promise((t, n) => {
|
|
7
8
|
let r = new FileReader();
|
|
8
9
|
r.onload = (e) => {
|
|
@@ -11,30 +12,30 @@ function o(e) {
|
|
|
11
12
|
}, r.onerror = () => n(/* @__PURE__ */ Error("Failed to read file")), r.readAsText(e);
|
|
12
13
|
});
|
|
13
14
|
}
|
|
14
|
-
function
|
|
15
|
+
function l(e) {
|
|
15
16
|
return typeof e == "object" && !!e && !Array.isArray(e);
|
|
16
17
|
}
|
|
17
|
-
function
|
|
18
|
+
function u(e) {
|
|
18
19
|
let t = e;
|
|
19
|
-
return
|
|
20
|
+
return l(t.dataSink) ? t.dataSink : typeof e.key == "string" ? e : null;
|
|
20
21
|
}
|
|
21
|
-
function
|
|
22
|
+
function d(e) {
|
|
22
23
|
if (e) return {
|
|
23
24
|
type: e.type,
|
|
24
|
-
...e.schedule ? { schedule:
|
|
25
|
+
...e.schedule ? { schedule: f(e.schedule) } : {}
|
|
25
26
|
};
|
|
26
27
|
}
|
|
27
|
-
function
|
|
28
|
+
function f(e) {
|
|
28
29
|
if (e == null) return e;
|
|
29
|
-
if (Array.isArray(e)) return e.map((e) =>
|
|
30
|
+
if (Array.isArray(e)) return e.map((e) => f(e));
|
|
30
31
|
if (typeof e == "object") {
|
|
31
32
|
let t = {};
|
|
32
|
-
for (let [n, r] of Object.entries(e)) n !== "__typename" && (t[n] =
|
|
33
|
+
for (let [n, r] of Object.entries(e)) n !== "__typename" && (t[n] = f(r));
|
|
33
34
|
return t;
|
|
34
35
|
}
|
|
35
36
|
return e;
|
|
36
37
|
}
|
|
37
|
-
function
|
|
38
|
+
function p(e) {
|
|
38
39
|
try {
|
|
39
40
|
let t = JSON.parse(e), n = t;
|
|
40
41
|
return n.exportedFrom?.dataSinkId || n.dataSink?.id || t.id || null;
|
|
@@ -42,10 +43,10 @@ function d(e) {
|
|
|
42
43
|
return null;
|
|
43
44
|
}
|
|
44
45
|
}
|
|
45
|
-
function
|
|
46
|
+
function m(e) {
|
|
46
47
|
let t = [];
|
|
47
48
|
try {
|
|
48
|
-
let n = JSON.parse(e), r = n, i =
|
|
49
|
+
let n = JSON.parse(e), r = n, i = u(n);
|
|
49
50
|
if (!r.schemaVersion && !i && t.push({
|
|
50
51
|
code: "MISSING_SCHEMA_VERSION",
|
|
51
52
|
message: "Missing schema version"
|
|
@@ -88,148 +89,152 @@ function f(e) {
|
|
|
88
89
|
};
|
|
89
90
|
}
|
|
90
91
|
}
|
|
91
|
-
function
|
|
92
|
-
let [
|
|
93
|
-
let t =
|
|
94
|
-
return
|
|
95
|
-
}, []),
|
|
92
|
+
function h() {
|
|
93
|
+
let [l, h] = o(!1), [g, _] = o(null), [v, y] = o(null), [b, x] = o(null), S = s(), C = a((e) => {
|
|
94
|
+
let t = m(e);
|
|
95
|
+
return y(t), t;
|
|
96
|
+
}, []), w = a((e) => p(e), []), T = a(async (a, o = {}) => {
|
|
96
97
|
try {
|
|
97
|
-
|
|
98
|
-
let
|
|
99
|
-
if (!
|
|
100
|
-
let
|
|
101
|
-
if (!
|
|
102
|
-
let
|
|
103
|
-
if (
|
|
104
|
-
let e = (await
|
|
98
|
+
h(!0), _(null);
|
|
99
|
+
let s = m(a);
|
|
100
|
+
if (!s.isValid || !s.dataSinkInfo) throw Error(s.errors[0]?.message || "Invalid import content");
|
|
101
|
+
let c = u(JSON.parse(a));
|
|
102
|
+
if (!c) throw Error("Missing DataSink payload in import content");
|
|
103
|
+
let l = Object.prototype.hasOwnProperty.call(c, "data"), g = p(a), v = !1, y;
|
|
104
|
+
if (g) try {
|
|
105
|
+
let e = (await S.query({
|
|
105
106
|
query: t,
|
|
106
|
-
variables: { id:
|
|
107
|
+
variables: { id: g },
|
|
107
108
|
fetchPolicy: "network-only"
|
|
108
109
|
})).data?.getDataSink;
|
|
109
110
|
if (e) {
|
|
110
|
-
if (
|
|
111
|
+
if (l && JSON.stringify(e.data) !== JSON.stringify(c.data) || (e.description || "") !== (c.description || "") || e.ttl !== c.ttl || e.fieldMaskingEnabled !== (c.fieldMaskingEnabled ?? !1) || JSON.stringify(e.fieldMaskingRules) !== JSON.stringify(c.fieldMaskingRules) || JSON.stringify(e.refresh) !== JSON.stringify(c.refresh) || JSON.stringify(e.exports) !== JSON.stringify(c.exports) || JSON.stringify(e.alerts) !== JSON.stringify(c.alerts) || JSON.stringify(e.quota) !== JSON.stringify(c.quota) || JSON.stringify(e.metadata) !== JSON.stringify(c.metadata)) {
|
|
111
112
|
let t = {
|
|
112
|
-
id:
|
|
113
|
+
id: g,
|
|
113
114
|
version: e.version,
|
|
114
115
|
name: e.name,
|
|
115
|
-
description:
|
|
116
|
-
...
|
|
117
|
-
ttl:
|
|
118
|
-
fieldMaskingEnabled:
|
|
119
|
-
fieldMaskingRules:
|
|
120
|
-
refresh:
|
|
121
|
-
exports:
|
|
122
|
-
alerts:
|
|
123
|
-
quota:
|
|
124
|
-
metadata:
|
|
125
|
-
}, r = (await
|
|
116
|
+
description: c.description || void 0,
|
|
117
|
+
...l && { data: f(c.data) },
|
|
118
|
+
ttl: c.ttl ?? void 0,
|
|
119
|
+
fieldMaskingEnabled: c.fieldMaskingEnabled ?? !1,
|
|
120
|
+
fieldMaskingRules: f(c.fieldMaskingRules) || void 0,
|
|
121
|
+
refresh: d(c.refresh),
|
|
122
|
+
exports: f(c.exports) || void 0,
|
|
123
|
+
alerts: f(c.alerts) || void 0,
|
|
124
|
+
quota: f(c.quota) || void 0,
|
|
125
|
+
metadata: f(c.metadata) || void 0
|
|
126
|
+
}, r = (await S.mutate({
|
|
126
127
|
mutation: n,
|
|
127
128
|
variables: { input: t }
|
|
128
129
|
})).data?.updateDataSink;
|
|
129
|
-
r && (
|
|
130
|
+
r && (v = !0, y = {
|
|
130
131
|
id: r.id,
|
|
131
132
|
name: r.name,
|
|
132
133
|
key: r.key
|
|
133
134
|
});
|
|
134
135
|
} else {
|
|
135
|
-
|
|
136
|
+
y = {
|
|
136
137
|
id: e.id,
|
|
137
138
|
name: e.name,
|
|
138
139
|
key: e.key
|
|
139
140
|
};
|
|
140
141
|
let t = {
|
|
141
142
|
success: !0,
|
|
142
|
-
dataSink:
|
|
143
|
+
dataSink: y,
|
|
143
144
|
originalUpdated: !1,
|
|
144
|
-
originalDataSink:
|
|
145
|
+
originalDataSink: y,
|
|
145
146
|
alreadyExists: !0
|
|
146
147
|
};
|
|
147
|
-
return
|
|
148
|
+
return x(t), t;
|
|
148
149
|
}
|
|
149
150
|
let t = {
|
|
150
151
|
success: !0,
|
|
151
|
-
dataSink:
|
|
152
|
-
originalUpdated:
|
|
153
|
-
originalDataSink:
|
|
152
|
+
dataSink: y,
|
|
153
|
+
originalUpdated: v,
|
|
154
|
+
originalDataSink: y,
|
|
154
155
|
alreadyExists: !1
|
|
155
156
|
};
|
|
156
|
-
return
|
|
157
|
+
return x(t), t;
|
|
157
158
|
}
|
|
158
159
|
} catch (e) {
|
|
159
160
|
console.warn("Could not fetch original DataSink:", e);
|
|
160
161
|
}
|
|
161
|
-
let
|
|
162
|
-
name:
|
|
163
|
-
key:
|
|
164
|
-
description:
|
|
165
|
-
config:
|
|
166
|
-
...
|
|
167
|
-
ttl:
|
|
168
|
-
fieldMaskingEnabled:
|
|
169
|
-
fieldMaskingRules:
|
|
170
|
-
refresh:
|
|
171
|
-
exports:
|
|
172
|
-
alerts:
|
|
173
|
-
quota:
|
|
174
|
-
metadata:
|
|
175
|
-
},
|
|
162
|
+
let b = {
|
|
163
|
+
name: o.name || c.name || "Imported DataSink",
|
|
164
|
+
key: o.key || c.key || "",
|
|
165
|
+
description: c.description || void 0,
|
|
166
|
+
config: f(c.config) || void 0,
|
|
167
|
+
...l && { data: f(c.data) },
|
|
168
|
+
ttl: c.ttl ?? void 0,
|
|
169
|
+
fieldMaskingEnabled: c.fieldMaskingEnabled ?? !1,
|
|
170
|
+
fieldMaskingRules: f(c.fieldMaskingRules) || void 0,
|
|
171
|
+
refresh: d(c.refresh),
|
|
172
|
+
exports: f(c.exports) || void 0,
|
|
173
|
+
alerts: f(c.alerts) || void 0,
|
|
174
|
+
quota: f(c.quota) || void 0,
|
|
175
|
+
metadata: f(c.metadata) || void 0
|
|
176
|
+
}, C = await S.mutate({
|
|
176
177
|
mutation: e,
|
|
177
|
-
variables: { input:
|
|
178
|
-
})
|
|
179
|
-
if (
|
|
178
|
+
variables: { input: b }
|
|
179
|
+
});
|
|
180
|
+
if (i(C)) throw r(C);
|
|
181
|
+
let w = C.data?.createDataSink;
|
|
182
|
+
if (w) {
|
|
180
183
|
let e = {
|
|
181
184
|
success: !0,
|
|
182
185
|
dataSink: {
|
|
183
|
-
id:
|
|
184
|
-
name:
|
|
185
|
-
key:
|
|
186
|
+
id: w.id,
|
|
187
|
+
name: w.name,
|
|
188
|
+
key: w.key
|
|
186
189
|
},
|
|
187
190
|
originalUpdated: !1,
|
|
188
191
|
originalDataSink: void 0
|
|
189
192
|
};
|
|
190
|
-
return
|
|
193
|
+
return x(e), e;
|
|
191
194
|
}
|
|
192
195
|
throw Error("Import returned empty result");
|
|
193
196
|
} catch (e) {
|
|
197
|
+
if (i(e)) throw e;
|
|
194
198
|
let t = e instanceof Error ? e : /* @__PURE__ */ Error("Failed to import DataSink");
|
|
195
|
-
|
|
199
|
+
_(t);
|
|
196
200
|
let n = {
|
|
197
201
|
success: !1,
|
|
198
202
|
errorMessage: t.message
|
|
199
203
|
};
|
|
200
|
-
return
|
|
204
|
+
return x(n), n;
|
|
201
205
|
} finally {
|
|
202
|
-
|
|
206
|
+
h(!1);
|
|
203
207
|
}
|
|
204
|
-
}, [
|
|
208
|
+
}, [S]);
|
|
205
209
|
return {
|
|
206
|
-
loading:
|
|
207
|
-
error:
|
|
208
|
-
validationResult:
|
|
209
|
-
importResult:
|
|
210
|
-
validateImport:
|
|
211
|
-
importDataSink:
|
|
212
|
-
importFromFile:
|
|
210
|
+
loading: l,
|
|
211
|
+
error: g,
|
|
212
|
+
validationResult: v,
|
|
213
|
+
importResult: b,
|
|
214
|
+
validateImport: C,
|
|
215
|
+
importDataSink: T,
|
|
216
|
+
importFromFile: a(async (e, t = {}) => {
|
|
213
217
|
try {
|
|
214
218
|
if (!e.name.endsWith(".json")) throw Error("Invalid file type. Please select a JSON file.");
|
|
215
|
-
let n = await
|
|
219
|
+
let n = await c(e);
|
|
216
220
|
try {
|
|
217
221
|
JSON.parse(n);
|
|
218
222
|
} catch {
|
|
219
223
|
throw Error("Invalid JSON format. Please select a valid export file.");
|
|
220
224
|
}
|
|
221
|
-
return await
|
|
225
|
+
return await T(n, t);
|
|
222
226
|
} catch (e) {
|
|
223
|
-
|
|
227
|
+
if (i(e)) throw e;
|
|
228
|
+
return _(e instanceof Error ? e : /* @__PURE__ */ Error("Failed to import from file")), null;
|
|
224
229
|
}
|
|
225
|
-
}, [
|
|
226
|
-
extractDataSinkId:
|
|
227
|
-
clearState:
|
|
228
|
-
|
|
230
|
+
}, [T]),
|
|
231
|
+
extractDataSinkId: w,
|
|
232
|
+
clearState: a(() => {
|
|
233
|
+
_(null), y(null), x(null);
|
|
229
234
|
}, [])
|
|
230
235
|
};
|
|
231
236
|
}
|
|
232
237
|
//#endregion
|
|
233
|
-
export {
|
|
238
|
+
export { h as default };
|
|
234
239
|
|
|
235
240
|
//# sourceMappingURL=useImportDataSink.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useImportDataSink.js","names":[],"sources":["../../../src/bigconsole/hooks/useImportDataSink.ts"],"sourcesContent":["/**\n * useImportDataSink Hook\n *\n * Provides DataSink import functionality with validation and conflict resolution.\n * Supports both creating new DataSinks and updating existing ones on re-import.\n */\n\nimport { useCallback, useState } from 'react';\nimport { useApolloClient } from '@apollo/client/react';\nimport { CreateDataSinkDocument, UpdateDataSinkDocument, GetDataSinkDocument } from '../../generated/wspace-operations';\nimport type { DataSink, CreateDataSinkInput } from '../types';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface ImportDataSinkInfo {\n name: string | null;\n description: string | null;\n key: string;\n status: string;\n setupState: string;\n exportedAt?: string;\n exportedFrom?: string;\n schemaVersion: string;\n}\n\nexport interface ImportDataSinkValidationError {\n code: string;\n message: string;\n path?: string;\n}\n\nexport interface ImportDataSinkValidationResult {\n isValid: boolean;\n dataSinkInfo: ImportDataSinkInfo | null;\n errors: ImportDataSinkValidationError[];\n}\n\nexport interface ImportDataSinkResult {\n success: boolean;\n dataSink?: {\n id: string;\n name: string | null;\n key: string;\n };\n errorMessage?: string;\n /** Whether the original DataSink was also updated */\n originalUpdated?: boolean;\n /** The original DataSink that was updated (if any) */\n originalDataSink?: {\n id: string;\n name: string | null;\n key: string;\n };\n /** Whether the entity already exists with no changes (show \"already exists\" popup) */\n alreadyExists?: boolean;\n}\n\nexport interface ImportDataSinkOptions {\n /** Override DataSink name (optional) */\n name?: string;\n /** Override DataSink key (optional - required to avoid conflicts) */\n key?: string;\n}\n\nexport interface UseImportDataSinkResult {\n /** Loading state */\n loading: boolean;\n /** Error state */\n error: Error | null;\n /** Last validation result */\n validationResult: ImportDataSinkValidationResult | null;\n /** Last import result */\n importResult: ImportDataSinkResult | null;\n /** Validate import content without importing */\n validateImport: (content: string) => ImportDataSinkValidationResult;\n /** Import DataSink from JSON content */\n importDataSink: (content: string, options?: ImportDataSinkOptions) => Promise<ImportDataSinkResult | null>;\n /** Import DataSink from file */\n importFromFile: (file: File, options?: ImportDataSinkOptions) => Promise<ImportDataSinkResult | null>;\n /** Extract DataSink ID from import content */\n extractDataSinkId: (content: string) => string | null;\n /** Clear state */\n clearState: () => void;\n}\n\ninterface ImportedDataSinkPayload {\n id?: string;\n name?: string | null;\n description?: string | null;\n key?: string;\n config?: Record<string, unknown> | null;\n data?: Record<string, unknown>;\n ttl?: number;\n fieldMaskingEnabled?: boolean;\n fieldMaskingRules?: Record<string, unknown> | null;\n status?: string;\n setupState?: string;\n refresh?: DataSink['refresh'];\n exports?: DataSink['exports'];\n alerts?: DataSink['alerts'];\n quota?: DataSink['quota'];\n metadata?: Record<string, unknown> | null;\n}\n\ninterface ParsedDataSinkExport {\n schemaVersion?: string;\n exportedAt?: string;\n exportedFrom?: {\n dataSinkId?: string;\n };\n dataSink?: ImportedDataSinkPayload;\n}\n\n// ============================================================================\n// Helper: Read file as text\n// ============================================================================\n\nfunction readFileAsText(file: File): Promise<string> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = (e) => {\n const result = e.target?.result;\n if (typeof result === 'string') {\n resolve(result);\n } else {\n reject(new Error('Failed to read file as text'));\n }\n };\n reader.onerror = () => reject(new Error('Failed to read file'));\n reader.readAsText(file);\n });\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction resolveDataSinkPayload(\n parsed: ParsedDataSinkExport | ImportedDataSinkPayload\n): ImportedDataSinkPayload | null {\n const wrapped = parsed as ParsedDataSinkExport;\n if (isRecord(wrapped.dataSink)) {\n return wrapped.dataSink as ImportedDataSinkPayload;\n }\n\n // Backward-compatible import for raw DataSink JSON objects.\n if (typeof (parsed as ImportedDataSinkPayload).key === 'string') {\n return parsed as ImportedDataSinkPayload;\n }\n\n return null;\n}\n\nfunction normalizeRefreshInput(refresh: DataSink['refresh'] | undefined): CreateDataSinkInput['refresh'] {\n if (!refresh) {\n return undefined;\n }\n\n return {\n type: refresh.type,\n ...(refresh.schedule ? { schedule: stripTypename(refresh.schedule) } : {}),\n };\n}\n\n/**\n * Recursively remove __typename fields from an object\n * This is necessary because exported data contains __typename fields\n * which are not accepted by GraphQL input types\n */\nfunction stripTypename<T>(obj: T): T {\n if (obj === null || obj === undefined) {\n return obj;\n }\n\n if (Array.isArray(obj)) {\n return obj.map((item) => stripTypename(item)) as T;\n }\n\n if (typeof obj === 'object') {\n const cleaned: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {\n if (key !== '__typename') {\n cleaned[key] = stripTypename(value);\n }\n }\n return cleaned as T;\n }\n\n return obj;\n}\n\n/**\n * Extract DataSink ID from imported JSON content\n * The ID is stored during export for re-import update support\n */\nfunction extractDataSinkIdFromContent(content: string): string | null {\n try {\n const parsed = JSON.parse(content) as ParsedDataSinkExport | ImportedDataSinkPayload;\n const wrapped = parsed as ParsedDataSinkExport;\n // Check both locations where ID might be stored\n return wrapped.exportedFrom?.dataSinkId || wrapped.dataSink?.id || (parsed as ImportedDataSinkPayload).id || null;\n } catch {\n return null;\n }\n}\n\n/**\n * Validate DataSink export content\n */\nfunction validateDataSinkContent(content: string): ImportDataSinkValidationResult {\n const errors: ImportDataSinkValidationError[] = [];\n\n try {\n const parsed = JSON.parse(content) as ParsedDataSinkExport | ImportedDataSinkPayload;\n const wrapped = parsed as ParsedDataSinkExport;\n const dataSink = resolveDataSinkPayload(parsed);\n\n // Check schema version\n if (!wrapped.schemaVersion && !dataSink) {\n errors.push({\n code: 'MISSING_SCHEMA_VERSION',\n message: 'Missing schema version',\n });\n }\n\n // Check dataSink object exists\n if (!dataSink) {\n errors.push({\n code: 'MISSING_DATASINK',\n message: 'This file is not a BigConsole DataSink export. Expected a top-level dataSink object.',\n });\n return { isValid: false, dataSinkInfo: null, errors };\n }\n\n // Validate required fields\n if (!dataSink.key) {\n errors.push({\n code: 'MISSING_KEY',\n message: 'DataSink key is required',\n path: 'dataSink.key',\n });\n }\n\n const dataSinkInfo: ImportDataSinkInfo = {\n name: dataSink.name || null,\n description: dataSink.description || null,\n key: dataSink.key || '',\n status: dataSink.status || 'ready',\n setupState: dataSink.setupState || 'not_started',\n exportedAt: wrapped.exportedAt,\n exportedFrom: wrapped.exportedFrom?.dataSinkId,\n schemaVersion: wrapped.schemaVersion || 'legacy',\n };\n\n return {\n isValid: errors.length === 0,\n dataSinkInfo,\n errors,\n };\n } catch {\n return {\n isValid: false,\n dataSinkInfo: null,\n errors: [\n {\n code: 'INVALID_JSON',\n message: 'Invalid JSON format',\n },\n ],\n };\n }\n}\n\n// ============================================================================\n// Hook\n// ============================================================================\n\nexport function useImportDataSink(): UseImportDataSinkResult {\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n const [validationResult, setValidationResult] = useState<ImportDataSinkValidationResult | null>(null);\n const [importResult, setImportResult] = useState<ImportDataSinkResult | null>(null);\n\n const apolloClient = useApolloClient();\n\n // Validate import content\n const validateImport = useCallback((content: string): ImportDataSinkValidationResult => {\n const result = validateDataSinkContent(content);\n setValidationResult(result);\n return result;\n }, []);\n\n // Extract DataSink ID from content\n const extractDataSinkId = useCallback((content: string): string | null => {\n return extractDataSinkIdFromContent(content);\n }, []);\n\n // Import DataSink from JSON content\n // Logic (DECLARATIVE - idempotent re-import):\n // 1. If original DataSink ID exists in JSON, check if original still exists\n // 2. If original exists → UPDATE it with the JSON data (no new copy created)\n // 3. If original doesn't exist → CREATE a new DataSink from the JSON\n // This ensures re-importing the same JSON is idempotent and updates the existing entity\n const importDataSink = useCallback(\n async (content: string, options: ImportDataSinkOptions = {}): Promise<ImportDataSinkResult | null> => {\n try {\n setLoading(true);\n setError(null);\n\n // Validate content first\n const validation = validateDataSinkContent(content);\n if (!validation.isValid || !validation.dataSinkInfo) {\n throw new Error(validation.errors[0]?.message || 'Invalid import content');\n }\n\n // Parse the content to get DataSink data\n const parsed = JSON.parse(content) as ParsedDataSinkExport | ImportedDataSinkPayload;\n const dataSinkData = resolveDataSinkPayload(parsed);\n if (!dataSinkData) {\n throw new Error('Missing DataSink payload in import content');\n }\n const hasImportedData = Object.prototype.hasOwnProperty.call(dataSinkData, 'data');\n\n // Extract original DataSink ID from the JSON (if it was exported from an existing DataSink)\n const originalDataSinkId = extractDataSinkIdFromContent(content);\n let originalUpdated = false;\n let originalDataSinkInfo: { id: string; name: string | null; key: string } | undefined;\n\n // Step 1: Check if original DataSink exists and update it\n if (originalDataSinkId) {\n try {\n const getDataSinkResult = await apolloClient.query<{ getDataSink?: DataSink }>({\n query: GetDataSinkDocument,\n variables: { id: originalDataSinkId },\n fetchPolicy: 'network-only',\n });\n\n const existingDataSink = getDataSinkResult.data?.getDataSink;\n\n if (existingDataSink) {\n // Compare to detect changes (excluding name - we don't update the original's name)\n const hasChanges =\n (hasImportedData && JSON.stringify(existingDataSink.data) !== JSON.stringify(dataSinkData.data)) ||\n (existingDataSink.description || '') !== (dataSinkData.description || '') ||\n existingDataSink.ttl !== dataSinkData.ttl ||\n existingDataSink.fieldMaskingEnabled !== (dataSinkData.fieldMaskingEnabled ?? false) ||\n JSON.stringify(existingDataSink.fieldMaskingRules) !== JSON.stringify(dataSinkData.fieldMaskingRules) ||\n JSON.stringify(existingDataSink.refresh) !== JSON.stringify(dataSinkData.refresh) ||\n JSON.stringify(existingDataSink.exports) !== JSON.stringify(dataSinkData.exports) ||\n JSON.stringify(existingDataSink.alerts) !== JSON.stringify(dataSinkData.alerts) ||\n JSON.stringify(existingDataSink.quota) !== JSON.stringify(dataSinkData.quota) ||\n JSON.stringify(existingDataSink.metadata) !== JSON.stringify(dataSinkData.metadata);\n\n // Update original if there are changes\n // Note: We intentionally do NOT update the name - the original keeps its name\n if (hasChanges) {\n const updateInput = {\n id: originalDataSinkId,\n version: existingDataSink.version,\n name: existingDataSink.name, // Keep original name\n description: dataSinkData.description || undefined,\n ...(hasImportedData && { data: stripTypename(dataSinkData.data) }),\n ttl: dataSinkData.ttl ?? undefined,\n fieldMaskingEnabled: dataSinkData.fieldMaskingEnabled ?? false,\n fieldMaskingRules: stripTypename(dataSinkData.fieldMaskingRules) || undefined,\n refresh: normalizeRefreshInput(dataSinkData.refresh),\n exports: stripTypename(dataSinkData.exports) || undefined,\n alerts: stripTypename(dataSinkData.alerts) || undefined,\n quota: stripTypename(dataSinkData.quota) || undefined,\n metadata: stripTypename(dataSinkData.metadata) || undefined,\n };\n\n const updateResult = await apolloClient.mutate<{ updateDataSink?: DataSink }>({\n mutation: UpdateDataSinkDocument,\n variables: { input: updateInput },\n });\n\n const updatedDataSink = updateResult.data?.updateDataSink;\n if (updatedDataSink) {\n originalUpdated = true;\n originalDataSinkInfo = {\n id: updatedDataSink.id,\n name: updatedDataSink.name,\n key: updatedDataSink.key,\n };\n }\n } else {\n // No changes - entity already exists with same data\n // Return with alreadyExists flag so UI can show \"already exists\" popup\n originalDataSinkInfo = {\n id: existingDataSink.id,\n name: existingDataSink.name,\n key: existingDataSink.key,\n };\n const alreadyExistsResult: ImportDataSinkResult = {\n success: true,\n dataSink: originalDataSinkInfo,\n originalUpdated: false,\n originalDataSink: originalDataSinkInfo,\n alreadyExists: true,\n };\n setImportResult(alreadyExistsResult);\n return alreadyExistsResult;\n }\n\n // If original was updated, return success with the updated DataSink info\n const finalResult: ImportDataSinkResult = {\n success: true,\n dataSink: originalDataSinkInfo,\n originalUpdated,\n originalDataSink: originalDataSinkInfo,\n alreadyExists: false,\n };\n setImportResult(finalResult);\n return finalResult;\n }\n // If original doesn't exist, continue to create a new DataSink\n } catch (fetchErr) {\n // Original DataSink fetch failed (might not exist), continue with creating new\n console.warn('Could not fetch original DataSink:', fetchErr);\n }\n }\n\n // Step 2: Create a NEW DataSink only if original doesn't exist\n // (This happens when importing for the first time, or if original was deleted)\n // Use stripTypename to remove __typename fields from nested objects\n const createInput: CreateDataSinkInput = {\n name: options.name || dataSinkData.name || 'Imported DataSink',\n key: options.key || dataSinkData.key || '',\n description: dataSinkData.description || undefined,\n config: stripTypename(dataSinkData.config) || undefined,\n ...(hasImportedData && { data: stripTypename(dataSinkData.data) }),\n ttl: dataSinkData.ttl ?? undefined,\n fieldMaskingEnabled: dataSinkData.fieldMaskingEnabled ?? false,\n fieldMaskingRules: stripTypename(dataSinkData.fieldMaskingRules) || undefined,\n refresh: normalizeRefreshInput(dataSinkData.refresh),\n exports: stripTypename(dataSinkData.exports) || undefined,\n alerts: stripTypename(dataSinkData.alerts) || undefined,\n quota: stripTypename(dataSinkData.quota) || undefined,\n metadata: stripTypename(dataSinkData.metadata) || undefined,\n };\n\n const result = await apolloClient.mutate<{ createDataSink?: DataSink }>({\n mutation: CreateDataSinkDocument,\n variables: { input: createInput },\n });\n\n const createdDataSink = result.data?.createDataSink;\n\n if (createdDataSink) {\n const finalResult: ImportDataSinkResult = {\n success: true,\n dataSink: {\n id: createdDataSink.id,\n name: createdDataSink.name,\n key: createdDataSink.key,\n },\n originalUpdated: false,\n originalDataSink: undefined,\n };\n setImportResult(finalResult);\n return finalResult;\n }\n\n throw new Error('Import returned empty result');\n } catch (err) {\n const importError = err instanceof Error ? err : new Error('Failed to import DataSink');\n setError(importError);\n const failResult: ImportDataSinkResult = {\n success: false,\n errorMessage: importError.message,\n };\n setImportResult(failResult);\n return failResult;\n } finally {\n setLoading(false);\n }\n },\n [apolloClient]\n );\n\n // Import DataSink from file\n const importFromFile = useCallback(\n async (file: File, options: ImportDataSinkOptions = {}): Promise<ImportDataSinkResult | null> => {\n try {\n // Validate file type\n if (!file.name.endsWith('.json')) {\n throw new Error('Invalid file type. Please select a JSON file.');\n }\n\n // Read file content\n const content = await readFileAsText(file);\n\n // Validate JSON format\n try {\n JSON.parse(content);\n } catch {\n throw new Error('Invalid JSON format. Please select a valid export file.');\n }\n\n // Perform import\n return await importDataSink(content, options);\n } catch (err) {\n const fileError = err instanceof Error ? err : new Error('Failed to import from file');\n setError(fileError);\n return null;\n }\n },\n [importDataSink]\n );\n\n // Clear state\n const clearState = useCallback(() => {\n setError(null);\n setValidationResult(null);\n setImportResult(null);\n }, []);\n\n return {\n loading,\n error,\n validationResult,\n importResult,\n validateImport,\n importDataSink,\n importFromFile,\n extractDataSinkId,\n clearState,\n };\n}\n\nexport default useImportDataSink;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useImportDataSink.js","names":[],"sources":["../../../src/bigconsole/hooks/useImportDataSink.ts"],"sourcesContent":["/**\n * useImportDataSink Hook\n *\n * Provides DataSink import functionality with validation and conflict resolution.\n * Supports both creating new DataSinks and updating existing ones on re-import.\n */\n\nimport { useCallback, useState } from 'react';\nimport { useApolloClient } from '@apollo/client/react';\nimport { CreateDataSinkDocument, UpdateDataSinkDocument, GetDataSinkDocument } from '../../generated/wspace-operations';\nimport type { DataSink, CreateDataSinkInput } from '../types';\nimport { createBigConsoleQuotaError, isBigConsoleQuotaError } from '../utils/quota';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface ImportDataSinkInfo {\n name: string | null;\n description: string | null;\n key: string;\n status: string;\n setupState: string;\n exportedAt?: string;\n exportedFrom?: string;\n schemaVersion: string;\n}\n\nexport interface ImportDataSinkValidationError {\n code: string;\n message: string;\n path?: string;\n}\n\nexport interface ImportDataSinkValidationResult {\n isValid: boolean;\n dataSinkInfo: ImportDataSinkInfo | null;\n errors: ImportDataSinkValidationError[];\n}\n\nexport interface ImportDataSinkResult {\n success: boolean;\n dataSink?: {\n id: string;\n name: string | null;\n key: string;\n };\n errorMessage?: string;\n /** Whether the original DataSink was also updated */\n originalUpdated?: boolean;\n /** The original DataSink that was updated (if any) */\n originalDataSink?: {\n id: string;\n name: string | null;\n key: string;\n };\n /** Whether the entity already exists with no changes (show \"already exists\" popup) */\n alreadyExists?: boolean;\n}\n\nexport interface ImportDataSinkOptions {\n /** Override DataSink name (optional) */\n name?: string;\n /** Override DataSink key (optional - required to avoid conflicts) */\n key?: string;\n}\n\nexport interface UseImportDataSinkResult {\n /** Loading state */\n loading: boolean;\n /** Error state */\n error: Error | null;\n /** Last validation result */\n validationResult: ImportDataSinkValidationResult | null;\n /** Last import result */\n importResult: ImportDataSinkResult | null;\n /** Validate import content without importing */\n validateImport: (content: string) => ImportDataSinkValidationResult;\n /** Import DataSink from JSON content */\n importDataSink: (content: string, options?: ImportDataSinkOptions) => Promise<ImportDataSinkResult | null>;\n /** Import DataSink from file */\n importFromFile: (file: File, options?: ImportDataSinkOptions) => Promise<ImportDataSinkResult | null>;\n /** Extract DataSink ID from import content */\n extractDataSinkId: (content: string) => string | null;\n /** Clear state */\n clearState: () => void;\n}\n\ninterface ImportedDataSinkPayload {\n id?: string;\n name?: string | null;\n description?: string | null;\n key?: string;\n config?: Record<string, unknown> | null;\n data?: Record<string, unknown>;\n ttl?: number;\n fieldMaskingEnabled?: boolean;\n fieldMaskingRules?: Record<string, unknown> | null;\n status?: string;\n setupState?: string;\n refresh?: DataSink['refresh'];\n exports?: DataSink['exports'];\n alerts?: DataSink['alerts'];\n quota?: DataSink['quota'];\n metadata?: Record<string, unknown> | null;\n}\n\ninterface ParsedDataSinkExport {\n schemaVersion?: string;\n exportedAt?: string;\n exportedFrom?: {\n dataSinkId?: string;\n };\n dataSink?: ImportedDataSinkPayload;\n}\n\n// ============================================================================\n// Helper: Read file as text\n// ============================================================================\n\nfunction readFileAsText(file: File): Promise<string> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = (e) => {\n const result = e.target?.result;\n if (typeof result === 'string') {\n resolve(result);\n } else {\n reject(new Error('Failed to read file as text'));\n }\n };\n reader.onerror = () => reject(new Error('Failed to read file'));\n reader.readAsText(file);\n });\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction resolveDataSinkPayload(\n parsed: ParsedDataSinkExport | ImportedDataSinkPayload\n): ImportedDataSinkPayload | null {\n const wrapped = parsed as ParsedDataSinkExport;\n if (isRecord(wrapped.dataSink)) {\n return wrapped.dataSink as ImportedDataSinkPayload;\n }\n\n // Backward-compatible import for raw DataSink JSON objects.\n if (typeof (parsed as ImportedDataSinkPayload).key === 'string') {\n return parsed as ImportedDataSinkPayload;\n }\n\n return null;\n}\n\nfunction normalizeRefreshInput(refresh: DataSink['refresh'] | undefined): CreateDataSinkInput['refresh'] {\n if (!refresh) {\n return undefined;\n }\n\n return {\n type: refresh.type,\n ...(refresh.schedule ? { schedule: stripTypename(refresh.schedule) } : {}),\n };\n}\n\n/**\n * Recursively remove __typename fields from an object\n * This is necessary because exported data contains __typename fields\n * which are not accepted by GraphQL input types\n */\nfunction stripTypename<T>(obj: T): T {\n if (obj === null || obj === undefined) {\n return obj;\n }\n\n if (Array.isArray(obj)) {\n return obj.map((item) => stripTypename(item)) as T;\n }\n\n if (typeof obj === 'object') {\n const cleaned: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {\n if (key !== '__typename') {\n cleaned[key] = stripTypename(value);\n }\n }\n return cleaned as T;\n }\n\n return obj;\n}\n\n/**\n * Extract DataSink ID from imported JSON content\n * The ID is stored during export for re-import update support\n */\nfunction extractDataSinkIdFromContent(content: string): string | null {\n try {\n const parsed = JSON.parse(content) as ParsedDataSinkExport | ImportedDataSinkPayload;\n const wrapped = parsed as ParsedDataSinkExport;\n // Check both locations where ID might be stored\n return wrapped.exportedFrom?.dataSinkId || wrapped.dataSink?.id || (parsed as ImportedDataSinkPayload).id || null;\n } catch {\n return null;\n }\n}\n\n/**\n * Validate DataSink export content\n */\nfunction validateDataSinkContent(content: string): ImportDataSinkValidationResult {\n const errors: ImportDataSinkValidationError[] = [];\n\n try {\n const parsed = JSON.parse(content) as ParsedDataSinkExport | ImportedDataSinkPayload;\n const wrapped = parsed as ParsedDataSinkExport;\n const dataSink = resolveDataSinkPayload(parsed);\n\n // Check schema version\n if (!wrapped.schemaVersion && !dataSink) {\n errors.push({\n code: 'MISSING_SCHEMA_VERSION',\n message: 'Missing schema version',\n });\n }\n\n // Check dataSink object exists\n if (!dataSink) {\n errors.push({\n code: 'MISSING_DATASINK',\n message: 'This file is not a BigConsole DataSink export. Expected a top-level dataSink object.',\n });\n return { isValid: false, dataSinkInfo: null, errors };\n }\n\n // Validate required fields\n if (!dataSink.key) {\n errors.push({\n code: 'MISSING_KEY',\n message: 'DataSink key is required',\n path: 'dataSink.key',\n });\n }\n\n const dataSinkInfo: ImportDataSinkInfo = {\n name: dataSink.name || null,\n description: dataSink.description || null,\n key: dataSink.key || '',\n status: dataSink.status || 'ready',\n setupState: dataSink.setupState || 'not_started',\n exportedAt: wrapped.exportedAt,\n exportedFrom: wrapped.exportedFrom?.dataSinkId,\n schemaVersion: wrapped.schemaVersion || 'legacy',\n };\n\n return {\n isValid: errors.length === 0,\n dataSinkInfo,\n errors,\n };\n } catch {\n return {\n isValid: false,\n dataSinkInfo: null,\n errors: [\n {\n code: 'INVALID_JSON',\n message: 'Invalid JSON format',\n },\n ],\n };\n }\n}\n\n// ============================================================================\n// Hook\n// ============================================================================\n\nexport function useImportDataSink(): UseImportDataSinkResult {\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n const [validationResult, setValidationResult] = useState<ImportDataSinkValidationResult | null>(null);\n const [importResult, setImportResult] = useState<ImportDataSinkResult | null>(null);\n\n const apolloClient = useApolloClient();\n\n // Validate import content\n const validateImport = useCallback((content: string): ImportDataSinkValidationResult => {\n const result = validateDataSinkContent(content);\n setValidationResult(result);\n return result;\n }, []);\n\n // Extract DataSink ID from content\n const extractDataSinkId = useCallback((content: string): string | null => {\n return extractDataSinkIdFromContent(content);\n }, []);\n\n // Import DataSink from JSON content\n // Logic (DECLARATIVE - idempotent re-import):\n // 1. If original DataSink ID exists in JSON, check if original still exists\n // 2. If original exists → UPDATE it with the JSON data (no new copy created)\n // 3. If original doesn't exist → CREATE a new DataSink from the JSON\n // This ensures re-importing the same JSON is idempotent and updates the existing entity\n const importDataSink = useCallback(\n async (content: string, options: ImportDataSinkOptions = {}): Promise<ImportDataSinkResult | null> => {\n try {\n setLoading(true);\n setError(null);\n\n // Validate content first\n const validation = validateDataSinkContent(content);\n if (!validation.isValid || !validation.dataSinkInfo) {\n throw new Error(validation.errors[0]?.message || 'Invalid import content');\n }\n\n // Parse the content to get DataSink data\n const parsed = JSON.parse(content) as ParsedDataSinkExport | ImportedDataSinkPayload;\n const dataSinkData = resolveDataSinkPayload(parsed);\n if (!dataSinkData) {\n throw new Error('Missing DataSink payload in import content');\n }\n const hasImportedData = Object.prototype.hasOwnProperty.call(dataSinkData, 'data');\n\n // Extract original DataSink ID from the JSON (if it was exported from an existing DataSink)\n const originalDataSinkId = extractDataSinkIdFromContent(content);\n let originalUpdated = false;\n let originalDataSinkInfo: { id: string; name: string | null; key: string } | undefined;\n\n // Step 1: Check if original DataSink exists and update it\n if (originalDataSinkId) {\n try {\n const getDataSinkResult = await apolloClient.query<{ getDataSink?: DataSink }>({\n query: GetDataSinkDocument,\n variables: { id: originalDataSinkId },\n fetchPolicy: 'network-only',\n });\n\n const existingDataSink = getDataSinkResult.data?.getDataSink;\n\n if (existingDataSink) {\n // Compare to detect changes (excluding name - we don't update the original's name)\n const hasChanges =\n (hasImportedData && JSON.stringify(existingDataSink.data) !== JSON.stringify(dataSinkData.data)) ||\n (existingDataSink.description || '') !== (dataSinkData.description || '') ||\n existingDataSink.ttl !== dataSinkData.ttl ||\n existingDataSink.fieldMaskingEnabled !== (dataSinkData.fieldMaskingEnabled ?? false) ||\n JSON.stringify(existingDataSink.fieldMaskingRules) !== JSON.stringify(dataSinkData.fieldMaskingRules) ||\n JSON.stringify(existingDataSink.refresh) !== JSON.stringify(dataSinkData.refresh) ||\n JSON.stringify(existingDataSink.exports) !== JSON.stringify(dataSinkData.exports) ||\n JSON.stringify(existingDataSink.alerts) !== JSON.stringify(dataSinkData.alerts) ||\n JSON.stringify(existingDataSink.quota) !== JSON.stringify(dataSinkData.quota) ||\n JSON.stringify(existingDataSink.metadata) !== JSON.stringify(dataSinkData.metadata);\n\n // Update original if there are changes\n // Note: We intentionally do NOT update the name - the original keeps its name\n if (hasChanges) {\n const updateInput = {\n id: originalDataSinkId,\n version: existingDataSink.version,\n name: existingDataSink.name, // Keep original name\n description: dataSinkData.description || undefined,\n ...(hasImportedData && { data: stripTypename(dataSinkData.data) }),\n ttl: dataSinkData.ttl ?? undefined,\n fieldMaskingEnabled: dataSinkData.fieldMaskingEnabled ?? false,\n fieldMaskingRules: stripTypename(dataSinkData.fieldMaskingRules) || undefined,\n refresh: normalizeRefreshInput(dataSinkData.refresh),\n exports: stripTypename(dataSinkData.exports) || undefined,\n alerts: stripTypename(dataSinkData.alerts) || undefined,\n quota: stripTypename(dataSinkData.quota) || undefined,\n metadata: stripTypename(dataSinkData.metadata) || undefined,\n };\n\n const updateResult = await apolloClient.mutate<{ updateDataSink?: DataSink }>({\n mutation: UpdateDataSinkDocument,\n variables: { input: updateInput },\n });\n\n const updatedDataSink = updateResult.data?.updateDataSink;\n if (updatedDataSink) {\n originalUpdated = true;\n originalDataSinkInfo = {\n id: updatedDataSink.id,\n name: updatedDataSink.name,\n key: updatedDataSink.key,\n };\n }\n } else {\n // No changes - entity already exists with same data\n // Return with alreadyExists flag so UI can show \"already exists\" popup\n originalDataSinkInfo = {\n id: existingDataSink.id,\n name: existingDataSink.name,\n key: existingDataSink.key,\n };\n const alreadyExistsResult: ImportDataSinkResult = {\n success: true,\n dataSink: originalDataSinkInfo,\n originalUpdated: false,\n originalDataSink: originalDataSinkInfo,\n alreadyExists: true,\n };\n setImportResult(alreadyExistsResult);\n return alreadyExistsResult;\n }\n\n // If original was updated, return success with the updated DataSink info\n const finalResult: ImportDataSinkResult = {\n success: true,\n dataSink: originalDataSinkInfo,\n originalUpdated,\n originalDataSink: originalDataSinkInfo,\n alreadyExists: false,\n };\n setImportResult(finalResult);\n return finalResult;\n }\n // If original doesn't exist, continue to create a new DataSink\n } catch (fetchErr) {\n // Original DataSink fetch failed (might not exist), continue with creating new\n console.warn('Could not fetch original DataSink:', fetchErr);\n }\n }\n\n // Step 2: Create a NEW DataSink only if original doesn't exist\n // (This happens when importing for the first time, or if original was deleted)\n // Use stripTypename to remove __typename fields from nested objects\n const createInput: CreateDataSinkInput = {\n name: options.name || dataSinkData.name || 'Imported DataSink',\n key: options.key || dataSinkData.key || '',\n description: dataSinkData.description || undefined,\n config: stripTypename(dataSinkData.config) || undefined,\n ...(hasImportedData && { data: stripTypename(dataSinkData.data) }),\n ttl: dataSinkData.ttl ?? undefined,\n fieldMaskingEnabled: dataSinkData.fieldMaskingEnabled ?? false,\n fieldMaskingRules: stripTypename(dataSinkData.fieldMaskingRules) || undefined,\n refresh: normalizeRefreshInput(dataSinkData.refresh),\n exports: stripTypename(dataSinkData.exports) || undefined,\n alerts: stripTypename(dataSinkData.alerts) || undefined,\n quota: stripTypename(dataSinkData.quota) || undefined,\n metadata: stripTypename(dataSinkData.metadata) || undefined,\n };\n\n const result = await apolloClient.mutate<{ createDataSink?: DataSink }>({\n mutation: CreateDataSinkDocument,\n variables: { input: createInput },\n });\n\n // A quota refusal (spent, or never provisioned because the org has no\n // billing account yet — BOFF-7913) is thrown to the dialog so it can\n // raise the billing popup instead of a dead-end \"import failed\".\n if (isBigConsoleQuotaError(result)) {\n throw createBigConsoleQuotaError(result);\n }\n\n const createdDataSink = result.data?.createDataSink;\n\n if (createdDataSink) {\n const finalResult: ImportDataSinkResult = {\n success: true,\n dataSink: {\n id: createdDataSink.id,\n name: createdDataSink.name,\n key: createdDataSink.key,\n },\n originalUpdated: false,\n originalDataSink: undefined,\n };\n setImportResult(finalResult);\n return finalResult;\n }\n\n throw new Error('Import returned empty result');\n } catch (err) {\n if (isBigConsoleQuotaError(err)) {\n throw err;\n }\n const importError = err instanceof Error ? err : new Error('Failed to import DataSink');\n setError(importError);\n const failResult: ImportDataSinkResult = {\n success: false,\n errorMessage: importError.message,\n };\n setImportResult(failResult);\n return failResult;\n } finally {\n setLoading(false);\n }\n },\n [apolloClient]\n );\n\n // Import DataSink from file\n const importFromFile = useCallback(\n async (file: File, options: ImportDataSinkOptions = {}): Promise<ImportDataSinkResult | null> => {\n try {\n // Validate file type\n if (!file.name.endsWith('.json')) {\n throw new Error('Invalid file type. Please select a JSON file.');\n }\n\n // Read file content\n const content = await readFileAsText(file);\n\n // Validate JSON format\n try {\n JSON.parse(content);\n } catch {\n throw new Error('Invalid JSON format. Please select a valid export file.');\n }\n\n // Perform import\n return await importDataSink(content, options);\n } catch (err) {\n if (isBigConsoleQuotaError(err)) {\n throw err;\n }\n const fileError = err instanceof Error ? err : new Error('Failed to import from file');\n setError(fileError);\n return null;\n }\n },\n [importDataSink]\n );\n\n // Clear state\n const clearState = useCallback(() => {\n setError(null);\n setValidationResult(null);\n setImportResult(null);\n }, []);\n\n return {\n loading,\n error,\n validationResult,\n importResult,\n validateImport,\n importDataSink,\n importFromFile,\n extractDataSinkId,\n clearState,\n };\n}\n\nexport default useImportDataSink;\n"],"mappings":"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"}
|