@burdenoff/microfe-bigconsole 2026.924.3 → 2026.925.1

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.
@@ -1,8 +1,9 @@
1
1
  import { CreateDataSinkDocument as e, GetDataSinkDocument as t, UpdateDataSinkDocument as n } from "../../generated/wspace-operations.js";
2
- import { useCallback as r, useState as i } from "react";
3
- import { useApolloClient as a } from "@apollo/client/react";
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 o(e) {
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 s(e) {
15
+ function l(e) {
15
16
  return typeof e == "object" && !!e && !Array.isArray(e);
16
17
  }
17
- function c(e) {
18
+ function u(e) {
18
19
  let t = e;
19
- return s(t.dataSink) ? t.dataSink : typeof e.key == "string" ? e : null;
20
+ return l(t.dataSink) ? t.dataSink : typeof e.key == "string" ? e : null;
20
21
  }
21
- function l(e) {
22
+ function d(e) {
22
23
  if (e) return {
23
24
  type: e.type,
24
- ...e.schedule ? { schedule: u(e.schedule) } : {}
25
+ ...e.schedule ? { schedule: f(e.schedule) } : {}
25
26
  };
26
27
  }
27
- function u(e) {
28
+ function f(e) {
28
29
  if (e == null) return e;
29
- if (Array.isArray(e)) return e.map((e) => u(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] = u(r));
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 d(e) {
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 f(e) {
46
+ function m(e) {
46
47
  let t = [];
47
48
  try {
48
- let n = JSON.parse(e), r = n, i = c(n);
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 p() {
92
- let [s, p] = i(!1), [m, h] = i(null), [g, _] = i(null), [v, y] = i(null), b = a(), x = r((e) => {
93
- let t = f(e);
94
- return _(t), t;
95
- }, []), S = r((e) => d(e), []), C = r(async (r, i = {}) => {
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
- p(!0), h(null);
98
- let a = f(r);
99
- if (!a.isValid || !a.dataSinkInfo) throw Error(a.errors[0]?.message || "Invalid import content");
100
- let o = c(JSON.parse(r));
101
- if (!o) throw Error("Missing DataSink payload in import content");
102
- let s = Object.prototype.hasOwnProperty.call(o, "data"), m = d(r), g = !1, _;
103
- if (m) try {
104
- let e = (await b.query({
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: m },
107
+ variables: { id: g },
107
108
  fetchPolicy: "network-only"
108
109
  })).data?.getDataSink;
109
110
  if (e) {
110
- if (s && JSON.stringify(e.data) !== JSON.stringify(o.data) || (e.description || "") !== (o.description || "") || e.ttl !== o.ttl || e.fieldMaskingEnabled !== (o.fieldMaskingEnabled ?? !1) || JSON.stringify(e.fieldMaskingRules) !== JSON.stringify(o.fieldMaskingRules) || JSON.stringify(e.refresh) !== JSON.stringify(o.refresh) || JSON.stringify(e.exports) !== JSON.stringify(o.exports) || JSON.stringify(e.alerts) !== JSON.stringify(o.alerts) || JSON.stringify(e.quota) !== JSON.stringify(o.quota) || JSON.stringify(e.metadata) !== JSON.stringify(o.metadata)) {
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: m,
113
+ id: g,
113
114
  version: e.version,
114
115
  name: e.name,
115
- description: o.description || void 0,
116
- ...s && { data: u(o.data) },
117
- ttl: o.ttl ?? void 0,
118
- fieldMaskingEnabled: o.fieldMaskingEnabled ?? !1,
119
- fieldMaskingRules: u(o.fieldMaskingRules) || void 0,
120
- refresh: l(o.refresh),
121
- exports: u(o.exports) || void 0,
122
- alerts: u(o.alerts) || void 0,
123
- quota: u(o.quota) || void 0,
124
- metadata: u(o.metadata) || void 0
125
- }, r = (await b.mutate({
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 && (g = !0, _ = {
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 y(t), t;
148
+ return x(t), t;
148
149
  }
149
150
  let t = {
150
151
  success: !0,
151
- dataSink: _,
152
- originalUpdated: g,
153
- originalDataSink: _,
152
+ dataSink: y,
153
+ originalUpdated: v,
154
+ originalDataSink: y,
154
155
  alreadyExists: !1
155
156
  };
156
- return y(t), t;
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 v = {
162
- name: i.name || o.name || "Imported DataSink",
163
- key: i.key || o.key || "",
164
- description: o.description || void 0,
165
- config: u(o.config) || void 0,
166
- ...s && { data: u(o.data) },
167
- ttl: o.ttl ?? void 0,
168
- fieldMaskingEnabled: o.fieldMaskingEnabled ?? !1,
169
- fieldMaskingRules: u(o.fieldMaskingRules) || void 0,
170
- refresh: l(o.refresh),
171
- exports: u(o.exports) || void 0,
172
- alerts: u(o.alerts) || void 0,
173
- quota: u(o.quota) || void 0,
174
- metadata: u(o.metadata) || void 0
175
- }, x = (await b.mutate({
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: v }
178
- })).data?.createDataSink;
179
- if (x) {
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: x.id,
184
- name: x.name,
185
- key: x.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 y(e), e;
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
- h(t);
199
+ _(t);
196
200
  let n = {
197
201
  success: !1,
198
202
  errorMessage: t.message
199
203
  };
200
- return y(n), n;
204
+ return x(n), n;
201
205
  } finally {
202
- p(!1);
206
+ h(!1);
203
207
  }
204
- }, [b]);
208
+ }, [S]);
205
209
  return {
206
- loading: s,
207
- error: m,
208
- validationResult: g,
209
- importResult: v,
210
- validateImport: x,
211
- importDataSink: C,
212
- importFromFile: r(async (e, t = {}) => {
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 o(e);
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 C(n, t);
225
+ return await T(n, t);
222
226
  } catch (e) {
223
- return h(e instanceof Error ? e : /* @__PURE__ */ Error("Failed to import from file")), null;
227
+ if (i(e)) throw e;
228
+ return _(e instanceof Error ? e : /* @__PURE__ */ Error("Failed to import from file")), null;
224
229
  }
225
- }, [C]),
226
- extractDataSinkId: S,
227
- clearState: r(() => {
228
- h(null), _(null), y(null);
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 { p as default };
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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"}