Skip to content

Commit 7dd5cf5

Browse files
authored
feat: dimension validator (#118)
* add: dimension validator * some cleanup * update: structure * add: tests * remove: testing * update: test * add: more test * fix: build
1 parent 05009ca commit 7dd5cf5

File tree

15 files changed

+678
-7
lines changed

15 files changed

+678
-7
lines changed

meerkat-browser/src/ast/dimension.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { validateDimension } from '@devrev/meerkat-core';
2+
import { AsyncDuckDBConnection } from '@duckdb/duckdb-wasm';
3+
import { parseQueryToAST } from './query-to-ast';
4+
import { getAvailableFunctions, isParseError } from './utils';
5+
6+
/**
7+
* Validates the query can be used as a dimension by parsing it to an AST and checking its structure
8+
* @param connection - DuckDB connection instance
9+
* @param query - The query string to validate
10+
* @returns Promise<boolean> - Whether the dimension is valid
11+
*/
12+
export const validateDimensionQuery = async ({
13+
connection,
14+
query,
15+
validFunctions,
16+
}: {
17+
connection: AsyncDuckDBConnection;
18+
query: string;
19+
validFunctions?: string[];
20+
}): Promise<boolean> => {
21+
const parsedSerialization = await parseQueryToAST(query, connection);
22+
23+
if (isParseError(parsedSerialization)) {
24+
throw new Error(parsedSerialization.error_message ?? 'Unknown error');
25+
}
26+
27+
// Only fetch valid functions if not provided
28+
const availableFunctions =
29+
validFunctions ?? (await getAvailableFunctions(connection, 'scalar'));
30+
31+
return validateDimension(parsedSerialization, availableFunctions);
32+
};

meerkat-browser/src/ast/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './dimension';
2+
export * from './query-to-ast';
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import {
2+
astSerializerQuery,
3+
deserializeQuery,
4+
ParsedSerialization,
5+
} from '@devrev/meerkat-core';
6+
import { AsyncDuckDBConnection } from '@duckdb/duckdb-wasm';
7+
8+
/**
9+
* Converts a query to an AST
10+
* @param query - The query string to convert
11+
* @param connection - The DuckDB connection instance
12+
* @returns The AST as a JSON object
13+
*/
14+
export const parseQueryToAST = async (
15+
query: string,
16+
connection: AsyncDuckDBConnection
17+
): Promise<ParsedSerialization> => {
18+
try {
19+
const serializedQuery = astSerializerQuery(query);
20+
const arrowResult = await connection.query(serializedQuery);
21+
22+
const parsedOutputQuery = arrowResult.toArray().map((row) => row.toJSON());
23+
const deserializedQuery = deserializeQuery(parsedOutputQuery);
24+
25+
return JSON.parse(deserializedQuery);
26+
} catch (error) {
27+
throw new Error('Failed to parse query to AST');
28+
}
29+
};

meerkat-browser/src/ast/utils.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { ParsedSerialization } from '@devrev/meerkat-core';
2+
import { AsyncDuckDBConnection } from '@duckdb/duckdb-wasm';
3+
4+
export const isParseError = (data: ParsedSerialization): boolean => {
5+
return !!data.error;
6+
};
7+
8+
// Helper function to get available functions from DuckDB based on function type
9+
export const getAvailableFunctions = async (
10+
connection: AsyncDuckDBConnection,
11+
function_type: 'scalar' | 'aggregate'
12+
): Promise<string[]> => {
13+
const result = await connection.query(
14+
`SELECT distinct function_name FROM duckdb_functions() WHERE function_type = '${function_type}'`
15+
);
16+
17+
return result.toArray().map((row) => row.toJSON().function_name);
18+
};

meerkat-browser/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export * from './browser-cube-to-sql/browser-cube-to-sql';
22
export { convertCubeStringToTableSchema };
33
import { convertCubeStringToTableSchema } from '@devrev/meerkat-core';
4+
export { validateDimensionQuery } from './ast';
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export const astSerializerQuery = (query: string) => {
2+
return `SELECT json_serialize_sql('${query}')`;
3+
};

0 commit comments

Comments
 (0)