Files
cfw-autumn/shared/api/events/aggregate/eventsAggregateParams.ts
2026-03-28 14:15:12 +00:00

107 lines
3.1 KiB
TypeScript

import { z } from "zod/v4";
import { BinSizeEnum } from "../components/binsizeEnum";
import { RangeEnum } from "../components/rangeEnum";
export const ExtEventsAggregateParamsSchema = z.object({
customer_id: z
.string()
.min(1)
.optional()
.meta({ description: "Customer ID to aggregate events for" }),
entity_id: z.string().min(1).optional().meta({
description:
"Entity ID to filter aggregated events for (e.g., per-seat or per-resource limits)",
}),
feature_id: z
.string()
.min(1)
.or(z.array(z.string().min(1)))
.meta({ description: "Feature ID(s) to aggregate events for" }),
group_by: z
.string()
.refine(
(val) =>
val.startsWith("properties.") ||
val === "$customer_id" ||
val === "$entity_id",
{
message:
'group_by must start with "properties." or be "$customer_id" or "$entity_id"',
},
)
.optional()
.meta({
description:
'Property to group events by (e.g. "properties.region"), or "$customer_id" / "$entity_id" to group by those columns',
}),
range: RangeEnum.optional().meta({
description:
"Time range to aggregate events for. Either range or custom_range must be provided",
}),
bin_size: BinSizeEnum.optional().meta({
description:
"Size of the time bins to aggregate events for. Defaults to hour if range is 24h, otherwise day",
}),
custom_range: z
.object({
start: z.number(),
end: z.number(),
})
.refine((data) => data.start < data.end, {
message: "start must be before end",
})
.optional()
.meta({
description:
"Custom time range to aggregate events for. If provided, range must not be provided",
}),
filter_by: z.record(z.string(), z.string()).optional().meta({
description:
'Filter events by property values, e.g. {"model": "gpt-4", "region": "us"}. Maximum 5 filters.',
}),
max_groups: z.number().int().min(1).max(250).optional().meta({
description:
"Maximum number of distinct group values to return per time bin when using group_by. Remaining values are bundled into an 'Other' bucket. Defaults to 9",
}),
});
export const EventsAggregateParamsSchema =
ExtEventsAggregateParamsSchema.refine(
(data) => {
const customRangeExists =
!!data.custom_range?.end && !!data.custom_range?.start;
const rangeExists = data.range !== undefined;
return !customRangeExists || !rangeExists;
},
{
message: "Only one of range or custom_range may be provided",
path: ["custom_range", "range"],
},
)
.refine(
(data) => !data.filter_by || Object.keys(data.filter_by).length <= 5,
{
message: "filter_by supports a maximum of 5 filters",
path: ["filter_by"],
},
)
.transform((data) => {
if (!data.range && !data.custom_range) {
return { ...data, range: "1bc" as const };
}
return data;
})
.transform((data) => {
if (!data.bin_size) {
// If range is 24h, set bin_size to hour, otherwise set bin_size to day
if (data.range === "24h") {
return { ...data, bin_size: "hour" as const };
}
return { ...data, bin_size: "day" as const };
}
return data;
});
export type EventsAggregateParams = z.infer<typeof EventsAggregateParamsSchema>;