chore: improved schema
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { createToken, Lexer, EmbeddedActionsParser } from 'chevrotain';
|
||||
import type { ZodType } from 'zod';
|
||||
|
||||
import type { QueryFilter, QueryCondition } from './query-parser.schemas.js';
|
||||
import { type QueryFilter, type QueryCondition, queryFilterSchema } from './query-parser.schemas.js';
|
||||
|
||||
// ----------------- Lexer -----------------
|
||||
|
||||
@@ -426,7 +427,10 @@ class QueryParserParser extends EmbeddedActionsParser {
|
||||
return this.SUBRULE(this.#orExpression);
|
||||
});
|
||||
|
||||
public parse = (input: string): QueryFilter => {
|
||||
public parse = <T extends typeof queryFilterSchema>(
|
||||
input: string,
|
||||
schema: T = queryFilterSchema as unknown as T,
|
||||
): QueryFilter => {
|
||||
const lexResult = QueryLexer.tokenize(input);
|
||||
|
||||
if (lexResult.errors.length > 0) {
|
||||
@@ -450,7 +454,7 @@ class QueryParserParser extends EmbeddedActionsParser {
|
||||
throw new Error(`Parse error: ${error.message}`);
|
||||
}
|
||||
|
||||
return result;
|
||||
return schema.parse(result);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,85 +1,65 @@
|
||||
import { z } from 'zod';
|
||||
import { z, ZodArray } from 'zod';
|
||||
|
||||
const queryConditionTextSchema = z.object({
|
||||
type: z.literal('text'),
|
||||
tableName: z.string().optional(),
|
||||
field: z.array(z.string()),
|
||||
conditions: z.object({
|
||||
equal: z.string().nullish(),
|
||||
notEqual: z.string().optional(),
|
||||
like: z.string().optional(),
|
||||
notLike: z.string().optional(),
|
||||
in: z.array(z.string()).optional(),
|
||||
notIn: z.array(z.string()).optional(),
|
||||
}),
|
||||
});
|
||||
const queryConditionTextSchema = z
|
||||
.object({
|
||||
type: z.literal('text'),
|
||||
tableName: z.string().optional(),
|
||||
field: z.array(z.string()),
|
||||
conditions: z.object({
|
||||
equal: z.string().nullish(),
|
||||
notEqual: z.string().optional(),
|
||||
like: z.string().optional(),
|
||||
notLike: z.string().optional(),
|
||||
in: z.array(z.string()).optional(),
|
||||
notIn: z.array(z.string()).optional(),
|
||||
}),
|
||||
})
|
||||
.meta({ id: 'QueryConditionText' });
|
||||
|
||||
type QueryConditionText = z.infer<typeof queryConditionTextSchema>;
|
||||
|
||||
const queryConditionNumberSchema = z.object({
|
||||
type: z.literal('number'),
|
||||
tableName: z.string().optional(),
|
||||
field: z.array(z.string()),
|
||||
conditions: z.object({
|
||||
equals: z.number().nullish(),
|
||||
notEquals: z.number().nullish(),
|
||||
greaterThan: z.number().optional(),
|
||||
greaterThanOrEqual: z.number().optional(),
|
||||
lessThan: z.number().optional(),
|
||||
lessThanOrEqual: z.number().optional(),
|
||||
in: z.array(z.number()).optional(),
|
||||
notIn: z.array(z.number()).optional(),
|
||||
}),
|
||||
});
|
||||
const queryConditionNumberSchema = z
|
||||
.object({
|
||||
type: z.literal('number'),
|
||||
tableName: z.string().optional(),
|
||||
field: z.array(z.string()),
|
||||
conditions: z.object({
|
||||
equals: z.number().nullish(),
|
||||
notEquals: z.number().nullish(),
|
||||
greaterThan: z.number().optional(),
|
||||
greaterThanOrEqual: z.number().optional(),
|
||||
lessThan: z.number().optional(),
|
||||
lessThanOrEqual: z.number().optional(),
|
||||
in: z.array(z.number()).optional(),
|
||||
notIn: z.array(z.number()).optional(),
|
||||
}),
|
||||
})
|
||||
.meta({ id: 'QueryConditionNumber' });
|
||||
|
||||
type QueryConditionNumber = z.infer<typeof queryConditionNumberSchema>;
|
||||
|
||||
const queryConditionSchema = z.discriminatedUnion('type', [queryConditionTextSchema, queryConditionNumberSchema]);
|
||||
const queryConditionSchema = z
|
||||
.discriminatedUnion('type', [queryConditionTextSchema, queryConditionNumberSchema])
|
||||
.meta({ id: 'QueryCondition' });
|
||||
|
||||
type QueryCondition = z.infer<typeof queryConditionSchema>;
|
||||
|
||||
type QueryFilter = QueryCondition | QueryOperator;
|
||||
|
||||
type QueryOperator = {
|
||||
type: 'operator';
|
||||
operator: 'and' | 'or';
|
||||
conditions: QueryFilter[];
|
||||
};
|
||||
|
||||
// Create a depth-limited recursive schema for OpenAPI compatibility
|
||||
// This supports up to 3 levels of nesting, which should be sufficient for most use cases
|
||||
// OpenAPI cannot handle z.lazy(), so we manually define the nesting
|
||||
// If you need deeper nesting, you can add more levels (Level3, Level4, etc.)
|
||||
const queryFilterSchemaLevel0: z.ZodType<QueryFilter> = z.union([
|
||||
queryConditionSchema,
|
||||
z.object({
|
||||
const queryOperatorSchema = z
|
||||
.object({
|
||||
type: z.literal('operator'),
|
||||
operator: z.enum(['and', 'or']),
|
||||
conditions: z.array(queryConditionSchema),
|
||||
}),
|
||||
]);
|
||||
get conditions(): ZodArray<typeof queryOperatorSchema | typeof queryConditionSchema> {
|
||||
// eslint-disable-next-line
|
||||
return z.array(queryFilterSchema) as any;
|
||||
},
|
||||
})
|
||||
.meta({ id: 'QueryOperator' });
|
||||
|
||||
const queryFilterSchemaLevel1: z.ZodType<QueryFilter> = z.union([
|
||||
queryConditionSchema,
|
||||
z.object({
|
||||
type: z.literal('operator'),
|
||||
operator: z.enum(['and', 'or']),
|
||||
conditions: z.array(queryFilterSchemaLevel0),
|
||||
}),
|
||||
]);
|
||||
type QueryOperator = z.infer<typeof queryOperatorSchema>;
|
||||
|
||||
const queryFilterSchemaLevel2: z.ZodType<QueryFilter> = z.union([
|
||||
queryConditionSchema,
|
||||
z.object({
|
||||
type: z.literal('operator'),
|
||||
operator: z.enum(['and', 'or']),
|
||||
conditions: z.array(queryFilterSchemaLevel1),
|
||||
}),
|
||||
]);
|
||||
const queryFilterSchema = z.union([queryOperatorSchema, queryConditionSchema]).meta({ id: 'QueryFilter' });
|
||||
|
||||
// Export the depth-limited schema (supports 3 levels of nesting)
|
||||
// This works with OpenAPI schema generation
|
||||
const queryFilterSchema = queryFilterSchemaLevel2;
|
||||
type QueryFilter = z.infer<typeof queryFilterSchema>;
|
||||
|
||||
export type { QueryConditionText, QueryConditionNumber, QueryOperator, QueryCondition, QueryFilter };
|
||||
export { queryConditionSchema, queryFilterSchema };
|
||||
|
||||
Reference in New Issue
Block a user