feat: 🎸 plan_id in tb schemas

This commit is contained in:
amianthus
2026-05-15 12:58:30 +01:00
parent c13e34c6ea
commit 722d0f464a
13 changed files with 35 additions and 13 deletions

View File

@@ -6,6 +6,7 @@ import {
buildEventInfo, buildEventInfo,
initEvent, initEvent,
} from "@/internal/balances/events/initEvent.js"; } from "@/internal/balances/events/initEvent.js";
import { resolveInternalProductIdForEvent } from "../../events/resolveInternalProductIdForEvent.js";
import { import {
deductionToTrackResponseV2, deductionToTrackResponseV2,
executePostgresDeductionV2, executePostgresDeductionV2,
@@ -53,6 +54,11 @@ export const runPostgresTrackV3 = async ({
mutationLogs, mutationLogs,
}); });
const internalProductId = resolveInternalProductIdForEvent({
fullSubject: updatedFullSubject,
mutationLogs,
});
if (!body.skip_event && !body.idempotency_key) { if (!body.skip_event && !body.idempotency_key) {
const eventInfo = buildEventInfo(body); const eventInfo = buildEventInfo(body);
const event = initEvent({ const event = initEvent({
@@ -62,6 +68,7 @@ export const runPostgresTrackV3 = async ({
internalEntityId: updatedFullSubject.internalEntityId, internalEntityId: updatedFullSubject.internalEntityId,
customerId: body.customer_id, customerId: body.customer_id,
entityId: body.entity_id, entityId: body.entity_id,
internalProductId,
deductions, deductions,
}); });

View File

@@ -7,6 +7,7 @@ import type {
import { tryCatch } from "@autumn/shared"; import { tryCatch } from "@autumn/shared";
import type { AutumnContext } from "@/honoUtils/HonoEnv.js"; import type { AutumnContext } from "@/honoUtils/HonoEnv.js";
import { globalEventBatchingManager } from "@/internal/balances/events/EventBatchingManager.js"; import { globalEventBatchingManager } from "@/internal/balances/events/EventBatchingManager.js";
import { resolveInternalProductIdForEvent } from "@/internal/balances/events/resolveInternalProductIdForEvent.js";
import { import {
buildEventInfo, buildEventInfo,
initEvent, initEvent,
@@ -56,11 +57,13 @@ const queueEvent = ({
body, body,
fullSubject, fullSubject,
deductions, deductions,
internalProductId,
}: { }: {
ctx: AutumnContext; ctx: AutumnContext;
body: TrackParams; body: TrackParams;
fullSubject: FullSubject; fullSubject: FullSubject;
deductions: TrackDeduction[]; deductions: TrackDeduction[];
internalProductId: string | null;
}): void => { }): void => {
if (body.skip_event) return; if (body.skip_event) return;
@@ -75,6 +78,7 @@ const queueEvent = ({
customerId: body.customer_id, customerId: body.customer_id,
entityId: body.entity_id, entityId: body.entity_id,
deductions, deductions,
internalProductId,
}), }),
); );
}; };
@@ -139,7 +143,12 @@ export const runRedisTrackV3 = async ({
mutationLogs, mutationLogs,
}); });
queueEvent({ ctx, body, fullSubject, deductions }); const internalProductId = resolveInternalProductIdForEvent({
fullSubject: updatedFullSubject,
mutationLogs,
});
queueEvent({ ctx, body, fullSubject, deductions, internalProductId });
const { balance, balances } = await deductionToTrackResponseV2({ const { balance, balances } = await deductionToTrackResponseV2({
ctx, ctx,

View File

@@ -24,7 +24,6 @@ export const handleExternalAggregateEvents = createRoute({
body: EventsAggregateParamsSchema, body: EventsAggregateParamsSchema,
handler: async (c) => { handler: async (c) => {
const ctx = c.get("ctx"); const ctx = c.get("ctx");
const { db, org, env } = ctx;
const { const {
customer_id, customer_id,
entity_id, entity_id,
@@ -56,12 +55,13 @@ export const handleExternalAggregateEvents = createRoute({
const featureIds = Array.isArray(feature_id) ? feature_id : [feature_id]; const featureIds = Array.isArray(feature_id) ? feature_id : [feature_id];
// Map special $-prefixed group_by operators to their column names
let resolvedGroupBy = group_by; let resolvedGroupBy = group_by;
if (group_by === "$customer_id") { if (group_by === "$customer_id") {
resolvedGroupBy = "customer_id"; resolvedGroupBy = "customer_id";
} else if (group_by === "$entity_id") { } else if (group_by === "$entity_id") {
resolvedGroupBy = "entity_id"; resolvedGroupBy = "entity_id";
} else if (group_by === "$plan_id") {
resolvedGroupBy = "plan_id";
} }
const [eventsResult, total] = await Promise.all([ const [eventsResult, total] = await Promise.all([

View File

@@ -1,4 +0,0 @@
TYPE s3
S3_ARN {{ tb_secret("s3_role_arn_firecrawl_backfill_apikeyid_75f684b3") }}
S3_REGION us-west-2
# Learn more at https://www.tinybird.co/docs/forward/get-data-in/connectors/s3#s3-connection-settings

View File

@@ -15,6 +15,7 @@ SCHEMA >
`set_usage` Nullable(UInt8) `json:$.set_usage`, `set_usage` Nullable(UInt8) `json:$.set_usage`,
`entity_id` Nullable(String) `json:$.entity_id`, `entity_id` Nullable(String) `json:$.entity_id`,
`internal_entity_id` Nullable(String) `json:$.internal_entity_id`, `internal_entity_id` Nullable(String) `json:$.internal_entity_id`,
`internal_product_id` Nullable(String) `json:$.internal_product_id`,
`customer_id` String `json:$.customer_id`, `customer_id` String `json:$.customer_id`,
`properties` JSON `json:$.properties`, `properties` JSON `json:$.properties`,
`deductions` JSON `json:$.deductions` `deductions` JSON `json:$.deductions`

View File

@@ -14,6 +14,7 @@ SCHEMA >
`properties` Nullable(String), `properties` Nullable(String),
`idempotency_key` Nullable(String), `idempotency_key` Nullable(String),
`entity_id` String DEFAULT '', `entity_id` String DEFAULT '',
`internal_product_id` Nullable(String),
`deductions` Nullable(String) `deductions` Nullable(String)
ENGINE "MergeTree" ENGINE "MergeTree"

View File

@@ -15,6 +15,7 @@ SQL >
properties, properties,
idempotency_key, idempotency_key,
coalesce(entity_id, '') as entity_id, coalesce(entity_id, '') as entity_id,
internal_product_id,
toString(deductions) as deductions toString(deductions) as deductions
FROM events FROM events
ORDER BY timestamp DESC, id DESC ORDER BY timestamp DESC, id DESC

View File

@@ -11,6 +11,7 @@ SCHEMA >
`customer_id` String, `customer_id` String,
`event_name` String, `event_name` String,
`entity_id` String DEFAULT '', `entity_id` String DEFAULT '',
`internal_product_id` Nullable(String),
`hour` DateTime, `hour` DateTime,
`properties` JSON, `properties` JSON,
`total_value` Float64, `total_value` Float64,

View File

@@ -10,12 +10,13 @@ SQL >
customer_id, customer_id,
event_name, event_name,
coalesce(entity_id, '') as entity_id, coalesce(entity_id, '') as entity_id,
internal_product_id,
toStartOfHour(timestamp) as hour, toStartOfHour(timestamp) as hour,
CAST(coalesce(properties, '{}') AS JSON) as properties, CAST(coalesce(properties, '{}') AS JSON) as properties,
sum(toFloat64(coalesce(value, 1))) as total_value, sum(toFloat64(coalesce(value, 1))) as total_value,
count() as event_count count() as event_count
FROM events FROM events
GROUP BY org_id, env, customer_id, event_name, entity_id, hour, properties GROUP BY org_id, env, customer_id, event_name, entity_id, internal_product_id, hour, properties
TYPE materialized TYPE materialized
DATASOURCE events_hourly_mv DATASOURCE events_hourly_mv

View File

@@ -11,6 +11,7 @@ SCHEMA >
`customer_id` String, `customer_id` String,
`event_name` String, `event_name` String,
`entity_id` String DEFAULT '', `entity_id` String DEFAULT '',
`internal_product_id` Nullable(String),
`hour` DateTime, `hour` DateTime,
`total_value` Float64, `total_value` Float64,
`event_count` UInt64 `event_count` UInt64

View File

@@ -11,11 +11,12 @@ SQL >
customer_id, customer_id,
event_name, event_name,
coalesce(entity_id, '') as entity_id, coalesce(entity_id, '') as entity_id,
internal_product_id,
toStartOfHour(timestamp) as hour, toStartOfHour(timestamp) as hour,
sum(toFloat64(coalesce(value, 1))) as total_value, sum(toFloat64(coalesce(value, 1))) as total_value,
count() as event_count count() as event_count
FROM events FROM events
GROUP BY org_id, env, customer_id, event_name, entity_id, hour GROUP BY org_id, env, customer_id, event_name, entity_id, internal_product_id, hour
TYPE materialized TYPE materialized
DATASOURCE events_hourly_no_properties_two_mv DATASOURCE events_hourly_no_properties_two_mv

View File

@@ -15,7 +15,7 @@ DESCRIPTION >
SQL > SQL >
% %
{% set no_property_filters = (not defined(filter_key_0) or String(filter_key_0, '') == '') and (not defined(filter_key_1) or String(filter_key_1, '') == '') and (not defined(filter_key_2) or String(filter_key_2, '') == '') and (not defined(filter_key_3) or String(filter_key_3, '') == '') and (not defined(filter_key_4) or String(filter_key_4, '') == '') %} {% set no_property_filters = (not defined(filter_key_0) or String(filter_key_0, '') == '') and (not defined(filter_key_1) or String(filter_key_1, '') == '') and (not defined(filter_key_2) or String(filter_key_2, '') == '') and (not defined(filter_key_3) or String(filter_key_3, '') == '') and (not defined(filter_key_4) or String(filter_key_4, '') == '') %}
{% set use_no_props = (String(group_column, 'property') in ['customer_id', 'entity_id']) and no_property_filters %} {% set use_no_props = (String(group_column, 'property') in ['customer_id', 'entity_id', 'plan_id']) and no_property_filters %}
SELECT SELECT
{% if String(bin_size, 'day') == 'hour' %} {% if String(bin_size, 'day') == 'hour' %}
@@ -30,6 +30,8 @@ SQL >
customer_id as group_value, customer_id as group_value,
{% elif String(group_column, 'property') == 'entity_id' %} {% elif String(group_column, 'property') == 'entity_id' %}
entity_id as group_value, entity_id as group_value,
{% elif String(group_column, 'property') == 'plan_id' %}
coalesce(internal_product_id, '') as group_value,
{% else %} {% else %}
{{ column('properties.' + String(property_key, '')) }}::String as group_value, {{ column('properties.' + String(property_key, '')) }}::String as group_value,
{% end %} {% end %}

View File

@@ -23,16 +23,17 @@ export const ExtEventsAggregateParamsSchema = z.object({
(val) => (val) =>
val.startsWith("properties.") || val.startsWith("properties.") ||
val === "$customer_id" || val === "$customer_id" ||
val === "$entity_id", val === "$entity_id" ||
val === "$plan_id",
{ {
message: message:
'group_by must start with "properties." or be "$customer_id" or "$entity_id"', 'group_by must start with "properties." or be "$customer_id", "$entity_id", or "$plan_id"',
}, },
) )
.optional() .optional()
.meta({ .meta({
description: description:
'Property to group events by (e.g. "properties.region"), or "$customer_id" / "$entity_id" to group by those columns', 'Property to group events by (e.g. "properties.region"), or "$customer_id" / "$entity_id" / "$plan_id" to group by those columns',
}), }),
range: RangeEnum.optional().meta({ range: RangeEnum.optional().meta({
description: description: