feat(elasticsearch): add custom checks and tabular REST results

This commit is contained in:
weiyong authored and GitHub committed 2026-07-27 18:55:37 +08:00
1 parent e2b35944ce
commit a49137ae8b
44 files changed
+917 -74

No files matched your search

@@ -122,7 +122,7 @@ function createExportState(
context: computed(() => "table-data"),
sourceColumns: computed(() => columns),
visibleColumnIndexes: computed(() => visibleColumnIndexes ?? columns.map((_, index) => index)),
columnTypes: computed(() => columns.map(() => "varchar")),
columnTypes: computed(() => columns.map((column) => tableMeta.columns?.find((item) => item.name === column)?.data_type ?? "varchar")),
extractorOptions: computed(() => extractorOptions),
whereInput: computed(() => undefined),
orderBy: computed(() => undefined),
@@ -547,6 +547,37 @@ describe("useDataGridExport prepared row statements", () => {
);
});
it("keeps JSON cells structured in JSON and SQL extractor requests", async () => {
const tableMeta: DataGridTableMeta = {
tableName: "events",
primaryKeys: ["id"],
columns: [
{ name: "id", data_type: "int", is_nullable: false, is_primary_key: true },
{ name: "payload", data_type: "json", is_nullable: true },
],
};
const matrix: CellSelectionMatrix = {
rowIndexes: [0],
columnIndexes: [0, 1],
columns: ["id", "payload"],
rows: [[7, '{"name":"Ada","tags":["admin"]}']],
};
vi.mocked(extractDataGridSelection).mockResolvedValue({
text: "copied",
mimeType: "application/json",
fileExtension: "json",
rowCount: 1,
columnCount: 2,
});
const state = createExportState(tableMeta, ["id", "payload"], matrix, [7, '{"name":"Ada","tags":["admin"]}']);
await expect(state.copyWithExtractor("json")).resolves.toBe(true);
await expect(state.copyWithExtractor("sql-inserts")).resolves.toBe(true);
expect(extractDataGridSelection).toHaveBeenNthCalledWith(1, expect.objectContaining({ rows: [[7, { name: "Ada", tags: ["admin"] }]] }));
expect(extractDataGridSelection).toHaveBeenNthCalledWith(2, expect.objectContaining({ rows: [[7, { name: "Ada", tags: ["admin"] }]] }));
});
it("rejects SQL UPDATE instead of silently skipping a row with a null primary key", async () => {
const matrix: CellSelectionMatrix = {
rowIndexes: [0],
@@ -348,7 +348,14 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
}
const obj: Record<string, unknown> = {};
columns.value.forEach((col, i) => {
obj[col] = item.data[i];
const value = item.data[i];
if (typeof value === "string" && columnTypes.value?.[i]?.trim().toLowerCase() === "json") {
try {
obj[col] = JSON.parse(value);
return;
} catch {}
}
obj[col] = value;
});
return obj;
}
@@ -443,6 +450,7 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
allDisplayItems,
allSourceColumns,
visibleColumnIndexes,
columnTypes,
extractorOptions: extractorOptionsOption,
databaseType,
tableMeta,
@@ -32,6 +32,7 @@ interface UseDataGridExtractorOptions {
allDisplayItems: ComputedRef<ExtractorRowItem[]>;
allSourceColumns: ComputedRef<Array<string | undefined> | undefined>;
visibleColumnIndexes: ComputedRef<number[]>;
columnTypes: ComputedRef<Array<string | undefined> | undefined>;
extractorOptions?: ComputedRef<DataGridExtractorOptions>;
databaseType: ComputedRef<DatabaseType | undefined>;
tableMeta: ComputedRef<DataGridTableMeta | undefined>;
@@ -54,6 +55,15 @@ export function useDataGridExtractor(options: UseDataGridExtractorOptions) {
const { toast } = useToast();
const hasUnsupportedDiscreteSelection = computed(() => options.hasCellSelection.value && options.selectedCellMatrix.value === null);
function normalizeCellValue(value: unknown, columnType: string | undefined): unknown {
if (typeof value !== "string" || columnType?.trim().toLowerCase() !== "json") return value;
try {
return JSON.parse(value);
} catch {
return value;
}
}
function selectionData(): SelectionData | null {
if (options.hasRowSelection.value && options.selectedRowIds.value.size > 0) {
const rows = options.displayItems.value.filter((item) => options.selectedRowIds.value.has(item.id) && !item.isDraft).map((item) => item.data);
@@ -116,13 +126,14 @@ export function useDataGridExtractor(options: UseDataGridExtractorOptions) {
}
}
const compactIndexBySource = new Map(requiredSourceIndexes.map((sourceIndex, compactIndex) => [sourceIndex, compactIndex]));
const columnTypesBySource = new Map(visibleIndexes.map((sourceIndex, visibleIndex) => [sourceIndex, options.columnTypes.value?.[visibleIndex]]));
const columns = requiredSourceIndexes.map((sourceIndex, compactIndex) => ({
displayName: fullColumns[sourceIndex],
sourceName: sourceNames?.[sourceIndex],
sourceIndex: compactIndex,
}));
const selectedColumnIndexes = selectedSourceIndexes.map((sourceIndex) => compactIndexBySource.get(sourceIndex)).filter((index): index is number => index !== undefined);
const rows = sourceRows.map((row) => requiredSourceIndexes.map((sourceIndex) => row[sourceIndex]));
const rows = sourceRows.map((row) => requiredSourceIndexes.map((sourceIndex) => normalizeCellValue(row[sourceIndex], columnTypesBySource.get(sourceIndex))));
const descriptor = DATA_GRID_COPY_EXTRACTOR_DESCRIPTORS[extractor];
const tableMeta =
descriptor.category === "sql"