-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Filter events & analytics by metadata #2648
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
The latest updates on your projects. Learn more about Vercel for Git βοΈ
|
|
Warning Rate limit exceeded@steven-tey has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 0 minutes and 41 seconds before requesting another review. β How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. π¦ How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. π Files selected for processing (5)
WalkthroughThis update introduces support for advanced event filtering by metadata through query parsing and backend enhancements. It adds a query parser utility, updates analytics and event retrieval functions to process metadata filters, modifies schemas to include metadata fields, and extends Tinybird SQL pipes to apply dynamic metadata-based filters. UI changes display metadata in tables, and new tests validate the parsing and filtering logic. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant UI
participant Backend (API)
participant QueryParser
participant Tinybird Pipe
User->>UI: Inputs filter query (e.g., metadata.productId:premiumProductId)
UI->>Backend (API): GET /events?query=metadata.productId:premiumProductId
Backend (API)->>QueryParser: parseFiltersFromQuery(query)
QueryParser-->>Backend (API): filters array
Backend (API)->>Tinybird Pipe: Request with filters parameter (JSON)
Tinybird Pipe-->>Backend (API): Filtered events (by metadata)
Backend (API)-->>UI: Filtered events data
UI-->>User: Displays events with metadata column
Estimated code review effortπ― 3 (Moderate) | β±οΈ ~18 minutes Poem
β¨ Finishing Touches
π§ͺ Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. πͺ§ TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
π Outside diff range comments (1)
packages/tinybird/pipes/v2_events.pipe (1)
35-88: Missing filters support in click_events node.The
click_eventsnode doesn't include the new filters parameter support, whilelead_eventsandsale_eventsdo. This creates inconsistent filtering behavior.Add the filters parameter support to the click_events node:
{% if defined(url) %} AND url = {{ url }} {% end %} + + {% if defined(filters) %} + {% for item in JSON(filters, '[]') %} + {% if item.get('operator', '') == 'equals' %} + AND JSONExtractString(metadata, {{ item.get('operand', '') }}) = {{ item.get('value', '') }} + {% end %} + {% end %} + {% end %} + ORDER BY timestamp {% if order == 'asc' %} ASC {% else %} DESC {% end %}
π§Ή Nitpick comments (1)
packages/tinybird/pipes/v2_events.pipe (1)
137-143: Consider performance implications of JSON parsing in SQL.The
JSONExtractStringfunction is called for each row that matches other filters. For large datasets, this could impact query performance.Consider:
- Adding indexes on commonly filtered metadata fields
- Documenting performance characteristics
- Implementing query result caching for common filter combinations
Also applies to: 187-193
π Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
π Files selected for processing (6)
apps/web/app/(ee)/api/events/route.ts(1 hunks)apps/web/lib/analytics/get-analytics.ts(4 hunks)apps/web/lib/analytics/get-events.ts(4 hunks)apps/web/lib/analytics/utils/analytics-query-parser.ts(1 hunks)apps/web/lib/zod/schemas/analytics.ts(2 hunks)packages/tinybird/pipes/v2_events.pipe(2 hunks)
π§° Additional context used
π§ Learnings (5)
apps/web/app/(ee)/api/events/route.ts (2)
Learnt from: devkiran
PR: dubinc/dub#2637
File: apps/web/app/(ee)/api/singular/webhook/route.ts:0-0
Timestamp: 2025-07-17T06:41:45.598Z
Learning: In the Singular integration (apps/web/app/(ee)/api/singular/webhook/route.ts), the event names in the singularToDubEvent object have intentionally different casing: "Copy GAID" and "copy IDFA". This casing difference is valid and should not be changed, as these are the correct event names expected from Singular.
Learnt from: devkiran
PR: dubinc/dub#2177
File: apps/web/lib/api/links/bulk-create-links.ts:66-84
Timestamp: 2025-06-06T07:59:03.120Z
Learning: In apps/web/lib/api/links/bulk-create-links.ts, the team accepts the risk of potential undefined results from links.find() operations when building invalidLinks arrays, because existing links are fetched from the database based on the input links, so matches are expected to always exist.
apps/web/lib/analytics/get-analytics.ts (1)
Learnt from: TWilson023
PR: dubinc/dub#2519
File: apps/web/ui/analytics/utils.ts:35-37
Timestamp: 2025-06-16T19:21:23.506Z
Learning: In the `useAnalyticsFilterOption` function in `apps/web/ui/analytics/utils.ts`, the pattern `options?.context ?? useContext(AnalyticsContext)` is intentionally designed as a complete replacement strategy, not a merge. When `options.context` is provided, it should contain all required fields (`baseApiPath`, `queryString`, `selectedTab`, `requiresUpgrade`) and completely replace the React context, not be merged with it. This is used for dependency injection or testing scenarios.
apps/web/lib/analytics/get-events.ts (2)
Learnt from: TWilson023
PR: dubinc/dub#2519
File: apps/web/ui/analytics/utils.ts:35-37
Timestamp: 2025-06-16T19:21:23.506Z
Learning: In the `useAnalyticsFilterOption` function in `apps/web/ui/analytics/utils.ts`, the pattern `options?.context ?? useContext(AnalyticsContext)` is intentionally designed as a complete replacement strategy, not a merge. When `options.context` is provided, it should contain all required fields (`baseApiPath`, `queryString`, `selectedTab`, `requiresUpgrade`) and completely replace the React context, not be merged with it. This is used for dependency injection or testing scenarios.
Learnt from: devkiran
PR: dubinc/dub#2177
File: apps/web/lib/api/links/bulk-create-links.ts:66-84
Timestamp: 2025-06-06T07:59:03.120Z
Learning: In apps/web/lib/api/links/bulk-create-links.ts, the team accepts the risk of potential undefined results from links.find() operations when building invalidLinks arrays, because existing links are fetched from the database based on the input links, so matches are expected to always exist.
apps/web/lib/analytics/utils/analytics-query-parser.ts (1)
Learnt from: TWilson023
PR: dubinc/dub#2519
File: apps/web/ui/analytics/utils.ts:35-37
Timestamp: 2025-06-16T19:21:23.506Z
Learning: In the `useAnalyticsFilterOption` function in `apps/web/ui/analytics/utils.ts`, the pattern `options?.context ?? useContext(AnalyticsContext)` is intentionally designed as a complete replacement strategy, not a merge. When `options.context` is provided, it should contain all required fields (`baseApiPath`, `queryString`, `selectedTab`, `requiresUpgrade`) and completely replace the React context, not be merged with it. This is used for dependency injection or testing scenarios.
apps/web/lib/zod/schemas/analytics.ts (1)
Learnt from: TWilson023
PR: dubinc/dub#2519
File: apps/web/ui/analytics/utils.ts:35-37
Timestamp: 2025-06-16T19:21:23.506Z
Learning: In the `useAnalyticsFilterOption` function in `apps/web/ui/analytics/utils.ts`, the pattern `options?.context ?? useContext(AnalyticsContext)` is intentionally designed as a complete replacement strategy, not a merge. When `options.context` is provided, it should contain all required fields (`baseApiPath`, `queryString`, `selectedTab`, `requiresUpgrade`) and completely replace the React context, not be merged with it. This is used for dependency injection or testing scenarios.
𧬠Code Graph Analysis (3)
apps/web/lib/analytics/get-analytics.ts (1)
apps/web/lib/analytics/utils/analytics-query-parser.ts (1)
parseFiltersFromQuery(10-39)
apps/web/lib/analytics/get-events.ts (1)
apps/web/lib/analytics/utils/analytics-query-parser.ts (1)
parseFiltersFromQuery(10-39)
apps/web/lib/analytics/utils/analytics-query-parser.ts (1)
apps/web/lib/analytics/types.ts (1)
EventsFilters(47-54)
π Additional comments (12)
apps/web/lib/analytics/utils/analytics-query-parser.ts (2)
4-8: Consider expanding the Filter interface for future extensibility.The current interface is minimal but well-structured. The comment indicates the operator can be expanded later, which is good forward-thinking.
The interface design with the expandable operator field is well-thought-out for future enhancements.
15-19: Validate the zod schema for metadata filtering.The schema correctly validates that metadata is a record of string key-value pairs, which is appropriate for the filtering use case.
The zod schema properly validates the expected structure for metadata filtering.
apps/web/app/(ee)/api/events/route.ts (1)
13-13: Good documentation addition.The comment clearly identifies the API endpoint handled by this route.
apps/web/lib/analytics/get-analytics.ts (4)
16-16: Proper import of the parsing utility.The import correctly references the new parsing utility function.
36-36: Good integration of the query parameter.The query parameter is properly destructured from the params object, maintaining consistency with the existing parameter pattern.
109-109: Validate the filters parsing logic.The filters are correctly parsed using the utility function, which handles validation and returns undefined for invalid queries.
126-126: Ensure proper serialization of filters.The conditional JSON.stringify ensures that undefined filters are handled correctly while valid filters are properly serialized for the Tinybird pipe.
apps/web/lib/analytics/get-events.ts (3)
25-25: Consistent import pattern.The import follows the same pattern as in get-analytics.ts, maintaining consistency across the codebase.
43-43: Proper parameter integration.The query parameter is correctly integrated into the function signature and destructuring.
83-83: Consistent filters implementation.The filters parsing and serialization logic is identical to the analytics function, ensuring consistent behavior across both endpoints.
Also applies to: 96-96
packages/tinybird/pipes/v2_events.pipe (2)
187-193: Consistent filters implementation across event types.The filters logic is consistently implemented between lead_events and sale_events nodes, which is good for maintainability.
137-143: Ensure SQL injectionβsafe metadata filtering and complete filter support across all event pipes
- In packages/tinybird/pipes/v2_events.pipe (lines 137β143),
operandandvalueare injected directly into the SQL. Add explicit quoting or escaping (e.g. a whitelist of allowed field names and proper parameter binding) to prevent SQL injection.- Benchmark the performance impact of
JSONExtractString(metadata,β¦)in the lead_events and sale_events pipes; consider pre-extracting indexed columns if queries become slow.- Implement the same
filtersparameter logic in packages/tinybird/pipes/click_events.pipe to ensure consistency across all event nodes.
β¦ update tests accordingly
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
π§Ή Nitpick comments (2)
apps/web/lib/analytics/utils/analytics-query-parser.ts (2)
66-104: Improve operator validation and consider simplifying key extraction.The regex pattern and parsing logic work correctly, but there are some areas for improvement:
Operator validation: The pattern
([:><=!]+)allows multiple consecutive operator characters, which could lead to unexpected behavior.Key extraction complexity: The multiple replace operations for metadata key extraction could be simplified.
Consider these improvements:
// More precise operator pattern - const unifiedPattern = - /^([a-zA-Z_][a-zA-Z0-9_]*|metadata\[['"][^'"]*['"]\](?:\[['"][^'"]*['"]\])*)\s*([:><=!]+)\s*(.+)$/; + const unifiedPattern = + /^([a-zA-Z_][a-zA-Z0-9_]*|metadata\[['"][^'"]*['"]\](?:\[['"][^'"]*['"]\])*)\s*(:|>=|<=|!=|>|<|=)\s*(.+)$/;For the key extraction, consider a more direct approach:
if (fieldOrMetadata.startsWith("metadata")) { const keyPath = fieldOrMetadata.replace(/^metadata/, ""); - const extractedKey = keyPath - .replace(/^\[['"]|['"]\]$/g, "") // Remove leading [' or [" and trailing '] or "] - .replace(/\[['"]/g, ".") // Replace [' or [" with . - .replace(/['"]\]/g, ""); // Remove trailing '] or "] + // Extract keys from bracket notation: metadata['key1']['key2'] -> key1.key2 + const extractedKey = keyPath + .match(/\[['"]([^'"]*)['"]\]/g) + ?.map(match => match.slice(2, -2)) + .join('.') || ''; operand = `metadata.${extractedKey}`; }
107-126: Consider explicit error handling for unsupported operators.The function correctly maps operators, but silently defaulting to "equals" for unsupported operators might mask parsing errors.
Consider throwing an error or returning null for unsupported operators:
default: - // For unsupported operators, default to equals - return "equals"; + throw new Error(`Unsupported operator: ${operator}`);This would make parsing failures more explicit and help catch bugs during development.
π Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
π Files selected for processing (2)
apps/web/lib/analytics/utils/analytics-query-parser.ts(1 hunks)apps/web/tests/misc/analytics-query-parser.test.ts(1 hunks)
π§ Files skipped from review as they are similar to previous changes (1)
- apps/web/tests/misc/analytics-query-parser.test.ts
π§° Additional context used
π§ Learnings (1)
apps/web/lib/analytics/utils/analytics-query-parser.ts (1)
Learnt from: TWilson023
PR: dubinc/dub#2519
File: apps/web/ui/analytics/utils.ts:35-37
Timestamp: 2025-06-16T19:21:23.506Z
Learning: In the `useAnalyticsFilterOption` function in `apps/web/ui/analytics/utils.ts`, the pattern `options?.context ?? useContext(AnalyticsContext)` is intentionally designed as a complete replacement strategy, not a merge. When `options.context` is provided, it should contain all required fields (`baseApiPath`, `queryString`, `selectedTab`, `requiresUpgrade`) and completely replace the React context, not be merged with it. This is used for dependency injection or testing scenarios.
𧬠Code Graph Analysis (1)
apps/web/lib/analytics/utils/analytics-query-parser.ts (1)
apps/web/lib/analytics/types.ts (1)
EventsFilters(47-54)
β° Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build
π Additional comments (2)
apps/web/lib/analytics/utils/analytics-query-parser.ts (2)
3-13: Well-structured filter interface definition.The
InternalFilterinterface provides clear typing with a comprehensive set of comparison operators. The string union type for operators ensures type safety.
16-63: Solid implementation of query parsing logic.The function handles the parsing workflow correctly:
- Proper early return for empty queries
- Case-insensitive logical operator splitting
- Robust operand validation supporting nested metadata fields
- Clean error handling with continuation on invalid conditions
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
π§Ή Nitpick comments (2)
apps/web/lib/analytics/query-parser.ts (2)
71-72: Complex regex pattern needs documentation and testing for edge cases.The regex pattern is complex and handles multiple syntax variations. Consider adding more detailed comments explaining each part of the pattern, and ensure comprehensive test coverage for edge cases like malformed bracket notation or escaped quotes.
102-102: Value sanitization removes quotes but may affect intended quoted values.The value sanitization removes surrounding quotes, which is correct for parsing, but ensure this doesn't interfere with values that intentionally contain quotes at the beginning or end.
π Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
π Files selected for processing (11)
apps/web/app/(ee)/api/customers/[id]/activity/route.ts(0 hunks)apps/web/lib/analytics/get-analytics.ts(4 hunks)apps/web/lib/analytics/get-customer-events.ts(3 hunks)apps/web/lib/analytics/get-events.ts(5 hunks)apps/web/lib/analytics/query-parser.ts(1 hunks)apps/web/lib/zod/schemas/analytics.ts(2 hunks)apps/web/lib/zod/schemas/leads.ts(1 hunks)apps/web/lib/zod/schemas/sales.ts(1 hunks)apps/web/tests/misc/analytics-query-parser.test.ts(1 hunks)apps/web/ui/customers/customer-activity-list.tsx(2 hunks)packages/tinybird/pipes/v2_events.pipe(2 hunks)
π€ Files with no reviewable changes (1)
- apps/web/app/(ee)/api/customers/[id]/activity/route.ts
β Files skipped from review due to trivial changes (2)
- apps/web/ui/customers/customer-activity-list.tsx
- apps/web/tests/misc/analytics-query-parser.test.ts
π§ Files skipped from review as they are similar to previous changes (4)
- apps/web/lib/zod/schemas/analytics.ts
- apps/web/lib/analytics/get-events.ts
- packages/tinybird/pipes/v2_events.pipe
- apps/web/lib/analytics/get-analytics.ts
π§° Additional context used
π§ Learnings (4)
π Learning: in the singular integration (apps/web/app/(ee)/api/singular/webhook/route.ts), the event names in th...
Learnt from: devkiran
PR: dubinc/dub#2637
File: apps/web/app/(ee)/api/singular/webhook/route.ts:0-0
Timestamp: 2025-07-17T06:41:45.620Z
Learning: In the Singular integration (apps/web/app/(ee)/api/singular/webhook/route.ts), the event names in the singularToDubEvent object have intentionally different casing: "Copy GAID" and "copy IDFA". This casing difference is valid and should not be changed, as these are the correct event names expected from Singular.
Applied to files:
apps/web/lib/analytics/get-customer-events.tsapps/web/lib/zod/schemas/sales.ts
π Learning: in the `useanalyticsfilteroption` function in `apps/web/ui/analytics/utils.ts`, the pattern `options...
Learnt from: TWilson023
PR: dubinc/dub#2519
File: apps/web/ui/analytics/utils.ts:35-37
Timestamp: 2025-06-16T19:21:23.506Z
Learning: In the `useAnalyticsFilterOption` function in `apps/web/ui/analytics/utils.ts`, the pattern `options?.context ?? useContext(AnalyticsContext)` is intentionally designed as a complete replacement strategy, not a merge. When `options.context` is provided, it should contain all required fields (`baseApiPath`, `queryString`, `selectedTab`, `requiresUpgrade`) and completely replace the React context, not be merged with it. This is used for dependency injection or testing scenarios.
Applied to files:
apps/web/lib/analytics/get-customer-events.tsapps/web/lib/analytics/query-parser.ts
π Learning: in apps/web/ui/partners/rewards/rewards-logic.tsx, when setting the entity field in a reward conditi...
Learnt from: TWilson023
PR: dubinc/dub#2673
File: apps/web/ui/partners/rewards/rewards-logic.tsx:268-275
Timestamp: 2025-07-30T15:29:54.131Z
Learning: In apps/web/ui/partners/rewards/rewards-logic.tsx, when setting the entity field in a reward condition, dependent fields (attribute, operator, value) should be reset rather than preserved because different entities (customer vs sale) have different available attributes. Maintaining existing fields when the entity changes would create invalid state combinations and confusing UX.
Applied to files:
apps/web/lib/zod/schemas/sales.ts
π Learning: in apps/web/ui/partners/rewards/add-edit-reward-sheet.tsx, the form schema uses partial condition ob...
Learnt from: TWilson023
PR: dubinc/dub#2673
File: apps/web/ui/partners/rewards/add-edit-reward-sheet.tsx:56-66
Timestamp: 2025-07-30T15:25:13.936Z
Learning: In apps/web/ui/partners/rewards/add-edit-reward-sheet.tsx, the form schema uses partial condition objects to allow users to add empty/unconfigured condition fields without type errors, while submission validation uses strict schemas to ensure data integrity. This two-stage validation pattern improves UX by allowing progressive completion of complex forms.
Applied to files:
apps/web/lib/zod/schemas/sales.ts
β° Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build
π Additional comments (7)
apps/web/lib/zod/schemas/leads.ts (2)
122-122: LGTM! Consistent metadata field addition.The optional
metadatafield addition to the Tinybird endpoint schema is consistent with the string type used in other parts of the pipeline.
133-133: LGTM! Flexible metadata type for API responses.Using
z.any().nullish()for the response schema metadata field provides the necessary flexibility for parsed JSON metadata while maintaining proper nullish handling.apps/web/lib/analytics/get-customer-events.ts (2)
11-12: LGTM! Simplified schema imports.The removal of extended schemas and use of base schemas aligns with the schema simplification across the codebase.
79-81: LGTM! Consistent schema usage.The use of base schemas with
.omit({ customer: true })is clean and consistent with the schema simplification.apps/web/lib/zod/schemas/sales.ts (3)
136-136: LGTM! Consistent metadata field addition.The optional
metadatafield addition mirrors the leads schema changes and maintains consistency across event types.
144-156: LGTM! Improved schema organization.The reorganization with clear comments separating core event fields, nested objects, and deprecated fields improves code readability and maintainability.
152-152: LGTM! Flexible metadata type.Using
z.any().nullish()for metadata provides the necessary flexibility for parsed JSON data while maintaining proper nullish handling.
| const extractedKey = keyPath | ||
| .replace(/^\[['"]|['"]\]$/g, "") // Remove leading [' or [" and trailing '] or "] | ||
| .replace(/\[['"]/g, ".") // Replace [' or [" with . | ||
| .replace(/['"]\]/g, ""); // Remove trailing '] or "] | ||
|
|
||
| operand = `metadata.${extractedKey}`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential security risk with bracket notation parsing.
The bracket notation parsing logic uses multiple regex replacements that could be vulnerable to malformed input. Consider using a more robust parsing approach or adding input validation to prevent potential issues.
Apply this diff to add input validation:
if (fieldOrMetadata.startsWith("metadata")) {
+ // Validate bracket notation format
+ if (!/^metadata(\[['"][^'"]*['"]\])+$/.test(fieldOrMetadata)) {
+ return null;
+ }
const keyPath = fieldOrMetadata.replace(/^metadata/, "");Committable suggestion skipped: line range outside the PR's diff.
π€ Prompt for AI Agents
In apps/web/lib/analytics/query-parser.ts around lines 89 to 94, the current
bracket notation parsing uses regex replacements that may be vulnerable to
malformed input. To fix this, add input validation before performing the
replacements to ensure the keyPath matches expected patterns or sanitize it
properly. Consider implementing a stricter parser or validation function that
checks for allowed characters and structure in keyPath to prevent injection or
parsing errors.
|
@CodeRabbit full review |
β Actions performedFull review triggered. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
π§Ή Nitpick comments (1)
apps/web/ui/analytics/events/events-table.tsx (1)
477-492: LGTM! Consider performance implications of showing all metadata items.The metadata column implementation is well-structured with proper empty state handling. However, setting
previewItems={0}means all metadata entries will be displayed initially, which could impact table performance if events have large metadata objects.Consider using a small positive value (e.g.,
previewItems={2}) to maintain table readability and performance while still providing access to full metadata via the tooltip.- return <MetadataViewer metadata={metadata} previewItems={0} />; + return <MetadataViewer metadata={metadata} previewItems={2} />;
π Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
π Files selected for processing (3)
apps/web/ui/analytics/events/events-table.tsx(2 hunks)apps/web/ui/analytics/events/metadata-viewer.tsx(3 hunks)apps/web/ui/analytics/events/use-column-visibility.ts(4 hunks)
β Files skipped from review due to trivial changes (1)
- apps/web/ui/analytics/events/use-column-visibility.ts
π§° Additional context used
π§ Learnings (1)
π Learning: in apps/web/ui/partners/design/previews/lander-preview.tsx, the ellipsis wave animation delay calcul...
Learnt from: TWilson023
PR: dubinc/dub#2614
File: apps/web/ui/partners/design/previews/lander-preview.tsx:181-181
Timestamp: 2025-07-09T20:52:56.592Z
Learning: In apps/web/ui/partners/design/previews/lander-preview.tsx, the ellipsis wave animation delay calculation `3 - i * -0.15` is intentionally designed to create negative delays that offset each dot's animation cycle. This pattern works correctly for the intended ellipsis effect and should not be changed to positive incremental delays.
Applied to files:
apps/web/ui/analytics/events/metadata-viewer.tsx
β° Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build
π Additional comments (5)
apps/web/ui/analytics/events/events-table.tsx (1)
45-45: LGTM!The import for MetadataViewer is correctly added and properly used in the metadata column.
apps/web/ui/analytics/events/metadata-viewer.tsx (4)
9-12: LGTM! Excellent enhancement for component flexibility.The addition of the optional
previewItemsparameter with a sensible default value of 3 makes the component more reusable while maintaining backward compatibility.
2-2: LGTM!The import of
pluralizeutility is correctly added for proper grammar in the UI.
39-42: LGTM!The logic correctly uses the configurable
previewItemsparameter instead of a hardcoded value, improving component flexibility.
98-101: LGTM!The button label correctly uses the
pluralizeutility to display grammatically correct text for the number of hidden items.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
β»οΈ Duplicate comments (3)
apps/web/lib/analytics/get-customer-events.ts (1)
63-63: Add error handling for JSON parsing.The metadata JSON parsing could fail with malformed JSON data from the database. Consider adding try-catch or using a safe JSON parsing utility.
Apply this diff to add safe JSON parsing:
- metadata: evt.metadata ? JSON.parse(evt.metadata) : undefined, + metadata: evt.metadata ? (() => { + try { + return JSON.parse(evt.metadata); + } catch { + return null; + } + })() : undefined,apps/web/lib/analytics/query-parser.ts (2)
89-94: Potential security risk with bracket notation parsing.The bracket notation parsing logic uses multiple regex replacements that could be vulnerable to malformed input. Consider using a more robust parsing approach or adding input validation to prevent potential issues.
Apply this diff to add input validation:
if (fieldOrMetadata.startsWith("metadata")) { + // Validate bracket notation format + if (!/^metadata(\[['"][^'"]*['"]\])+$/.test(fieldOrMetadata)) { + return null; + } const keyPath = fieldOrMetadata.replace(/^metadata/, "");
122-125: Default fallback to 'equals' for unsupported operators may mask errors.Defaulting to 'equals' for unsupported operators could silently change user intent. Consider throwing an error or returning null instead to make parsing failures explicit.
Apply this diff to handle unsupported operators:
default: - // For unsupported operators, default to equals - return "equals"; + // Return null for unsupported operators to indicate parsing failure + return null;And update the parseCondition function to handle this:
return { operand, - operator: mapOperator(operator), + operator: mapOperator(operator) ?? "equals", // fallback only after explicit check value: value.trim().replace(/^['"`]|['"`]$/g, ""), };
π§Ή Nitpick comments (1)
apps/web/tests/misc/analytics-query-parser.test.ts (1)
45-50: Remove duplicate test case.This test is identical to the "should parse simple nested property" test on lines 5-10. Both test the same input
"metadata['key']:value"and expect the same result.- it("should parse equals operator (:) for nested property", () => { - const result = parseFiltersFromQuery("metadata['key']:value"); - expect(result).toEqual([ - { operand: "metadata.key", operator: "equals", value: "value" }, - ]); - });
π Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
π Files selected for processing (15)
apps/web/app/(ee)/api/customers/[id]/activity/route.ts(0 hunks)apps/web/app/(ee)/api/events/route.ts(1 hunks)apps/web/lib/analytics/get-analytics.ts(4 hunks)apps/web/lib/analytics/get-customer-events.ts(3 hunks)apps/web/lib/analytics/get-events.ts(5 hunks)apps/web/lib/analytics/query-parser.ts(1 hunks)apps/web/lib/zod/schemas/analytics.ts(2 hunks)apps/web/lib/zod/schemas/leads.ts(1 hunks)apps/web/lib/zod/schemas/sales.ts(1 hunks)apps/web/tests/misc/analytics-query-parser.test.ts(1 hunks)apps/web/ui/analytics/events/events-table.tsx(2 hunks)apps/web/ui/analytics/events/metadata-viewer.tsx(3 hunks)apps/web/ui/analytics/events/use-column-visibility.ts(4 hunks)apps/web/ui/customers/customer-activity-list.tsx(2 hunks)packages/tinybird/pipes/v2_events.pipe(2 hunks)
π€ Files with no reviewable changes (1)
- apps/web/app/(ee)/api/customers/[id]/activity/route.ts
π§° Additional context used
π§ Learnings (7)
π Learning: in the singular integration (apps/web/app/(ee)/api/singular/webhook/route.ts), the event names in th...
Learnt from: devkiran
PR: dubinc/dub#2637
File: apps/web/app/(ee)/api/singular/webhook/route.ts:0-0
Timestamp: 2025-07-17T06:41:45.620Z
Learning: In the Singular integration (apps/web/app/(ee)/api/singular/webhook/route.ts), the event names in the singularToDubEvent object have intentionally different casing: "Copy GAID" and "copy IDFA". This casing difference is valid and should not be changed, as these are the correct event names expected from Singular.
Applied to files:
apps/web/app/(ee)/api/events/route.tsapps/web/lib/zod/schemas/analytics.tsapps/web/lib/analytics/get-events.tsapps/web/ui/analytics/events/use-column-visibility.tsapps/web/lib/zod/schemas/sales.tsapps/web/lib/analytics/get-customer-events.ts
π Learning: in apps/web/lib/api/links/bulk-create-links.ts, the team accepts the risk of potential undefined res...
Learnt from: devkiran
PR: dubinc/dub#2177
File: apps/web/lib/api/links/bulk-create-links.ts:66-84
Timestamp: 2025-06-06T07:59:03.120Z
Learning: In apps/web/lib/api/links/bulk-create-links.ts, the team accepts the risk of potential undefined results from links.find() operations when building invalidLinks arrays, because existing links are fetched from the database based on the input links, so matches are expected to always exist.
Applied to files:
apps/web/app/(ee)/api/events/route.tsapps/web/lib/analytics/get-events.ts
π Learning: in apps/web/ui/partners/rewards/rewards-logic.tsx, when setting the entity field in a reward conditi...
Learnt from: TWilson023
PR: dubinc/dub#2673
File: apps/web/ui/partners/rewards/rewards-logic.tsx:268-275
Timestamp: 2025-07-30T15:29:54.131Z
Learning: In apps/web/ui/partners/rewards/rewards-logic.tsx, when setting the entity field in a reward condition, dependent fields (attribute, operator, value) should be reset rather than preserved because different entities (customer vs sale) have different available attributes. Maintaining existing fields when the entity changes would create invalid state combinations and confusing UX.
Applied to files:
apps/web/ui/customers/customer-activity-list.tsxapps/web/lib/zod/schemas/analytics.tsapps/web/lib/analytics/query-parser.ts
π Learning: in apps/web/ui/partners/design/previews/lander-preview.tsx, the ellipsis wave animation delay calcul...
Learnt from: TWilson023
PR: dubinc/dub#2614
File: apps/web/ui/partners/design/previews/lander-preview.tsx:181-181
Timestamp: 2025-07-09T20:52:56.592Z
Learning: In apps/web/ui/partners/design/previews/lander-preview.tsx, the ellipsis wave animation delay calculation `3 - i * -0.15` is intentionally designed to create negative delays that offset each dot's animation cycle. This pattern works correctly for the intended ellipsis effect and should not be changed to positive incremental delays.
Applied to files:
apps/web/ui/analytics/events/metadata-viewer.tsx
π Learning: in the `useanalyticsfilteroption` function in `apps/web/ui/analytics/utils.ts`, the pattern `options...
Learnt from: TWilson023
PR: dubinc/dub#2519
File: apps/web/ui/analytics/utils.ts:35-37
Timestamp: 2025-06-16T19:21:23.506Z
Learning: In the `useAnalyticsFilterOption` function in `apps/web/ui/analytics/utils.ts`, the pattern `options?.context ?? useContext(AnalyticsContext)` is intentionally designed as a complete replacement strategy, not a merge. When `options.context` is provided, it should contain all required fields (`baseApiPath`, `queryString`, `selectedTab`, `requiresUpgrade`) and completely replace the React context, not be merged with it. This is used for dependency injection or testing scenarios.
Applied to files:
apps/web/tests/misc/analytics-query-parser.test.tsapps/web/lib/zod/schemas/analytics.tsapps/web/lib/analytics/get-events.tsapps/web/lib/analytics/get-analytics.tsapps/web/lib/analytics/query-parser.tsapps/web/lib/analytics/get-customer-events.ts
π Learning: in apps/web/ui/partners/rewards/add-edit-reward-sheet.tsx, the form schema uses partial condition ob...
Learnt from: TWilson023
PR: dubinc/dub#2673
File: apps/web/ui/partners/rewards/add-edit-reward-sheet.tsx:56-66
Timestamp: 2025-07-30T15:25:13.936Z
Learning: In apps/web/ui/partners/rewards/add-edit-reward-sheet.tsx, the form schema uses partial condition objects to allow users to add empty/unconfigured condition fields without type errors, while submission validation uses strict schemas to ensure data integrity. This two-stage validation pattern improves UX by allowing progressive completion of complex forms.
Applied to files:
apps/web/lib/zod/schemas/analytics.tsapps/web/lib/zod/schemas/leads.ts
π Learning: in the partnerprogramsummary email template (packages/email/src/templates/partner-program-summary.ts...
Learnt from: devkiran
PR: dubinc/dub#2448
File: packages/email/src/templates/partner-program-summary.tsx:0-0
Timestamp: 2025-05-29T04:45:18.504Z
Learning: In the PartnerProgramSummary email template (packages/email/src/templates/partner-program-summary.tsx), the stat titles are hardcoded constants ("Clicks", "Leads", "Sales", "Earnings") that will always match the ICONS object keys after toLowerCase() conversion, so icon lookup failures are not possible.
Applied to files:
apps/web/ui/analytics/events/use-column-visibility.ts
π Additional comments (26)
apps/web/app/(ee)/api/events/route.ts (1)
13-13: LGTM! Good documentation practice.Adding the comment to clearly identify the API endpoint improves code readability and documentation.
apps/web/ui/customers/customer-activity-list.tsx (1)
91-91: LGTM! Consistent metadata handling.The removal of JSON parsing logic aligns well with the schema changes that now treat metadata as a parsed object throughout the system. This simplification eliminates unnecessary parsing overhead and error handling while maintaining the same functionality.
Also applies to: 103-103
apps/web/lib/analytics/get-analytics.ts (1)
15-15: LGTM! Clean implementation of metadata filtering.The implementation correctly:
- Imports the query parser function
- Adds the
queryparameter to the function signature- Parses the query into structured filters
- Conditionally serializes and passes filters to the Tinybird pipe
The defensive approach of only serializing non-empty filters is appropriate.
Also applies to: 36-36, 105-105, 122-122
apps/web/lib/zod/schemas/analytics.ts (2)
235-243: LGTM! Clear and consistent query parameter definition.The field description and OpenAPI example are well-aligned, showing a query string format for metadata filtering. This addresses previous concerns about format consistency.
273-276: LGTM! Appropriate type constraint for filters.Using
z.string()for the filters field provides proper type safety while allowing the serialized filter data to be passed to the Tinybird pipe. This is more restrictive and safer than usingz.any().apps/web/ui/analytics/events/events-table.tsx (1)
45-45: LGTM! Well-implemented metadata column.The new metadata column is properly implemented with:
- Appropriate fixed width for table layout
- Defensive rendering logic that handles empty metadata gracefully
- Consistent fallback pattern (muted dash) matching other columns
- Compact preview using
previewItems={0}suitable for table cellsThe placement before the menu column maintains good table structure.
Also applies to: 477-492
apps/web/tests/misc/analytics-query-parser.test.ts (1)
1-78: LGTM! Comprehensive test coverage with one minor duplication.The test suite provides excellent coverage for the
parseFiltersFromQueryfunction, including edge cases and various query formats. The test structure is clean and well-organized.apps/web/lib/analytics/get-events.ts (4)
24-24: LGTM! Clean import addition.The import of
parseFiltersFromQueryis properly placed and follows the existing import structure.
43-43: LGTM! Proper parameter addition.The
queryparameter is correctly added to the destructured parameters with appropriate typing.
79-92: LGTM! Robust filter parsing and serialization.The implementation correctly:
- Parses filters from the query string
- Conditionally serializes filters only when they exist
- Passes the filters parameter to the Tinybird pipe
The conditional serialization prevents unnecessary empty filter parameters from being sent.
138-138: LGTM! Safe metadata parsing.The conditional parsing of metadata is implemented safely with proper null checking and JSON parsing. This aligns with the schema changes that added the metadata field.
apps/web/ui/analytics/events/metadata-viewer.tsx (4)
2-2: LGTM! Appropriate utility import.The addition of
pluralizeutility is well-placed and will be used for dynamic button text generation.
9-12: LGTM! Good API design with sensible default.The
previewItemsparameter makes the component more flexible while maintaining backward compatibility with a default value of 3. The optional parameter is properly typed.
39-42: LGTM! Consistent parameter usage.The logic correctly uses the
previewItemsparameter instead of hardcoded values, making the component behavior configurable.
98-101: LGTM! Improved button text with proper pluralization.The dynamic button text generation using
pluralizeutility provides better UX by showing accurate counts and proper grammar for both singular and plural cases.apps/web/lib/zod/schemas/leads.ts (2)
122-122: LGTM! Appropriate schema addition for Tinybird endpoint.The
metadatafield is correctly typed asz.string().nullish()for the Tinybird endpoint schema, which aligns with how JSON metadata is stored as strings in the database.
133-133: LGTM! Proper response schema typing.The
metadatafield in the response schema is correctly typed asz.any().nullish(), which is appropriate for parsed JSON objects that will be consumed by the frontend. The nullish typing maintains backward compatibility.apps/web/ui/analytics/events/use-column-visibility.ts (3)
8-21: LGTM! Improved column ordering for clicks.Moving the "timestamp" column to the beginning of the
allarray improves the logical ordering, as timestamp is a primary field for event identification and sorting.
26-41: LGTM! Consistent updates for leads with metadata support.The changes correctly:
- Move "timestamp" to the beginning for consistency with clicks
- Add "metadata" column to support the new metadata filtering feature
- Maintain the existing
defaultVisibleconfiguration for backward compatibility
46-63: LGTM! Comprehensive updates for sales events.The column configuration properly:
- Positions "timestamp" and "saleAmount" at the beginning as primary fields
- Includes the new "metadata" column for filtering support
- Preserves existing default visibility settings
The ordering prioritizes the most important fields (timestamp, amount) while making metadata available for advanced users.
packages/tinybird/pipes/v2_events.pipe (1)
197-212: LGTM! Improved formatting for UTM parameter checks.The multi-line formatting of UTM parameter checks improves readability while maintaining the same functionality.
apps/web/lib/analytics/get-customer-events.ts (1)
11-12: LGTM! Simplified schema usage and metadata handling.The removal of conditional
includeMetadatalogic and consistent use of base schemas withomit({ customer: true })simplifies the code while maintaining functionality.Also applies to: 79-81
apps/web/lib/analytics/query-parser.ts (1)
71-72: LGTM! Well-structured regex pattern for condition parsing.The unified regex pattern effectively handles both regular fields and complex metadata bracket notation while capturing the necessary groups for parsing.
apps/web/lib/zod/schemas/sales.ts (3)
136-136: LGTM! Added metadata support to Tinybird endpoint schema.The addition of the optional
metadatafield asz.string().nullish()aligns with the JSON string storage format used in the database.
144-156: LGTM! Improved schema organization and metadata support.The reordering of fields to group core event fields before nested objects improves readability. The addition of the
metadatafield asz.any().nullish()provides flexibility for JSON content while maintaining type safety.
157-171: LGTM! Clear separation of deprecated fields.Moving deprecated fields to the end of the schema and maintaining their descriptive metadata helps with API documentation and migration planning.
| {% if defined(filters) %} | ||
| {% for item in JSON(filters, '[]') %} | ||
| {% if item.get('operand', '').startswith('metadata.') %} | ||
| {% set metadataKey = item.get('operand', '').split('.')[1] %} | ||
| {% set operator = item.get('operator', 'equals') %} | ||
| {% set value = item.get('value', '') %} | ||
|
|
||
| {% if operator == 'equals' %} | ||
| AND JSONExtractString(metadata, {{ metadataKey }}) = {{ value }} | ||
| {% elif operator == 'notEquals' %} | ||
| AND JSONExtractString(metadata, {{ metadataKey }}) != {{ value }} | ||
| {% elif operator == 'greaterThan' %} | ||
| AND JSONExtractString(metadata, {{ metadataKey }}) > {{ value }} | ||
| {% elif operator == 'lessThan' %} | ||
| AND JSONExtractString(metadata, {{ metadataKey }}) < {{ value }} | ||
| {% elif operator == 'greaterThanOrEqual' %} | ||
| AND JSONExtractString(metadata, {{ metadataKey }}) >= {{ value }} | ||
| {% elif operator == 'lessThanOrEqual' %} | ||
| AND JSONExtractString(metadata, {{ metadataKey }}) <= {{ value }} | ||
| {% end %} | ||
| {% end %} | ||
| {% end %} | ||
| {% end %} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
π οΈ Refactor suggestion
Code duplication: Identical filter logic in both nodes.
The metadata filtering logic is duplicated between lead_events and sale_events nodes. Consider extracting this into a shared macro to improve maintainability.
Create a shared macro at the top of the file:
+{% macro apply_metadata_filters() %}
+ {% if defined(filters) %}
+ {% for item in JSON(filters, '[]') %}
+ {% if item.get('operand', '').startswith('metadata.') %}
+ {% set metadataKey = item.get('operand', '').split('.')[1] %}
+ {% set operator = item.get('operator', 'equals') %}
+ {% set value = item.get('value', '') %}
+
+ {% if operator == 'equals' %}
+ AND JSONExtractString(metadata, {{ metadataKey }}) = {{ value }}
+ {% elif operator == 'notEquals' %}
+ AND JSONExtractString(metadata, {{ metadataKey }}) != {{ value }}
+ {% elif operator == 'greaterThan' %}
+ AND JSONExtractString(metadata, {{ metadataKey }}) > {{ value }}
+ {% elif operator == 'lessThan' %}
+ AND JSONExtractString(metadata, {{ metadataKey }}) < {{ value }}
+ {% elif operator == 'greaterThanOrEqual' %}
+ AND JSONExtractString(metadata, {{ metadataKey }}) >= {{ value }}
+ {% elif operator == 'lessThanOrEqual' %}
+ AND JSONExtractString(metadata, {{ metadataKey }}) <= {{ value }}
+ {% end %}
+ {% end %}
+ {% end %}
+ {% end %}
+{% endmacro %}Then replace both filter blocks with {{ apply_metadata_filters() }}.
Also applies to: 215-237
π€ Prompt for AI Agents
In packages/tinybird/pipes/v2_events.pipe around lines 138 to 160 and also 215
to 237, the metadata filtering logic is duplicated in both lead_events and
sale_events nodes. To fix this, extract the repeated filter logic into a shared
macro defined at the top of the file, then replace the duplicated filter blocks
in both nodes with a call to this macro using {{ apply_metadata_filters() }}.
This will improve maintainability by centralizing the filter logic.
Security risk: Potential SQL injection in JSONExtractString parameter.
The metadataKey variable extracted from the filter is directly interpolated into the JSONExtractString function without validation or sanitization. This could lead to SQL injection if malicious input is provided.
Apply input validation to the metadataKey:
{% if item.get('operand', '').startswith('metadata.') %}
{% set metadataKey = item.get('operand', '').split('.')[1] %}
+ {% if not metadataKey.match('^[a-zA-Z0-9_]+$') %}
+ {% continue %}
+ {% end %}Committable suggestion skipped: line range outside the PR's diff.
π€ Prompt for AI Agents
In packages/tinybird/pipes/v2_events.pipe around lines 138 to 160, the
metadataKey variable is directly interpolated into the JSONExtractString
function without validation, posing a SQL injection risk. To fix this, validate
or sanitize metadataKey before using it in the query, ensuring it only contains
safe characters (e.g., alphanumeric and underscores) or matches expected keys.
Reject or escape any unexpected or malicious input to prevent injection
vulnerabilities.
| AND JSONExtractString(metadata, {{ metadataKey }}) = {{ value }} | ||
| {% elif operator == 'notEquals' %} | ||
| AND JSONExtractString(metadata, {{ metadataKey }}) != {{ value }} | ||
| {% elif operator == 'greaterThan' %} | ||
| AND JSONExtractString(metadata, {{ metadataKey }}) > {{ value }} | ||
| {% elif operator == 'lessThan' %} | ||
| AND JSONExtractString(metadata, {{ metadataKey }}) < {{ value }} | ||
| {% elif operator == 'greaterThanOrEqual' %} | ||
| AND JSONExtractString(metadata, {{ metadataKey }}) >= {{ value }} | ||
| {% elif operator == 'lessThanOrEqual' %} | ||
| AND JSONExtractString(metadata, {{ metadataKey }}) <= {{ value }} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Type mismatch: JSONExtractString returns strings for all comparison operators.
Using JSONExtractString for numeric comparisons (greaterThan, lessThan, etc.) will perform string comparison instead of numeric comparison, leading to incorrect results (e.g., "9" > "10" would be true in string comparison).
Consider using appropriate JSON extraction functions based on the expected data type:
{% if operator == 'equals' %}
AND JSONExtractString(metadata, {{ metadataKey }}) = {{ value }}
{% elif operator == 'notEquals' %}
AND JSONExtractString(metadata, {{ metadataKey }}) != {{ value }}
{% elif operator == 'greaterThan' %}
- AND JSONExtractString(metadata, {{ metadataKey }}) > {{ value }}
+ AND JSONExtractFloat(metadata, {{ metadataKey }}) > {{ Float64(value) }}
{% elif operator == 'lessThan' %}
- AND JSONExtractString(metadata, {{ metadataKey }}) < {{ value }}
+ AND JSONExtractFloat(metadata, {{ metadataKey }}) < {{ Float64(value) }}
{% elif operator == 'greaterThanOrEqual' %}
- AND JSONExtractString(metadata, {{ metadataKey }}) >= {{ value }}
+ AND JSONExtractFloat(metadata, {{ metadataKey }}) >= {{ Float64(value) }}
{% elif operator == 'lessThanOrEqual' %}
- AND JSONExtractString(metadata, {{ metadataKey }}) <= {{ value }}
+ AND JSONExtractFloat(metadata, {{ metadataKey }}) <= {{ Float64(value) }}Committable suggestion skipped: line range outside the PR's diff.
π€ Prompt for AI Agents
In packages/tinybird/pipes/v2_events.pipe around lines 146 to 156, the code uses
JSONExtractString for all comparison operators, which causes incorrect results
for numeric comparisons because it compares strings instead of numbers. To fix
this, replace JSONExtractString with JSONExtractInt or JSONExtractFloat for
numeric comparisons (greaterThan, lessThan, greaterThanOrEqual, lessThanOrEqual)
based on the expected data type, while keeping JSONExtractString for equality
and inequality checks on strings.
|
@CodeRabbit full review please |
|
@steven-tey I'll conduct a full review of the PR changes for filtering events & analytics by metadata. β Actions performedFull review triggered. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
π§Ή Nitpick comments (2)
apps/web/tests/analytics/index.test.ts (1)
43-60: LGTM! Well-structured integration test for metadata filtering.The test effectively validates the new metadata filtering functionality by:
- Using the correct query syntax with bracket notation
- Verifying both the HTTP status and the actual filtering results
- Following the established test patterns in the file
Consider adding a test case that verifies behavior when no events match the filter criteria to ensure edge cases are handled gracefully.
apps/web/tests/analytics/query-parser.test.ts (1)
45-50: Consider removing redundant test case.This test case duplicates the functionality already covered in the "should parse simple nested property" test (lines 5-10). The equals operator is already validated there.
- it("should parse equals operator (:) for nested property", () => { - const result = parseFiltersFromQuery("metadata['key']:value"); - expect(result).toEqual([ - { operand: "metadata.key", operator: "equals", value: "value" }, - ]); - });
π Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
π Files selected for processing (3)
apps/web/lib/analytics/get-analytics.ts(5 hunks)apps/web/tests/analytics/index.test.ts(1 hunks)apps/web/tests/analytics/query-parser.test.ts(1 hunks)
π§ Files skipped from review as they are similar to previous changes (1)
- apps/web/lib/analytics/get-analytics.ts
π§° Additional context used
π§ Learnings (2)
π Learning: in the `useanalyticsfilteroption` function in `apps/web/ui/analytics/utils.ts`, the pattern `options...
Learnt from: TWilson023
PR: dubinc/dub#2519
File: apps/web/ui/analytics/utils.ts:35-37
Timestamp: 2025-06-16T19:21:23.506Z
Learning: In the `useAnalyticsFilterOption` function in `apps/web/ui/analytics/utils.ts`, the pattern `options?.context ?? useContext(AnalyticsContext)` is intentionally designed as a complete replacement strategy, not a merge. When `options.context` is provided, it should contain all required fields (`baseApiPath`, `queryString`, `selectedTab`, `requiresUpgrade`) and completely replace the React context, not be merged with it. This is used for dependency injection or testing scenarios.
Applied to files:
apps/web/tests/analytics/index.test.tsapps/web/tests/analytics/query-parser.test.ts
π Learning: in the singular integration (apps/web/app/(ee)/api/singular/webhook/route.ts), the event names in th...
Learnt from: devkiran
PR: dubinc/dub#2637
File: apps/web/app/(ee)/api/singular/webhook/route.ts:0-0
Timestamp: 2025-07-17T06:41:45.620Z
Learning: In the Singular integration (apps/web/app/(ee)/api/singular/webhook/route.ts), the event names in the singularToDubEvent object have intentionally different casing: "Copy GAID" and "copy IDFA". This casing difference is valid and should not be changed, as these are the correct event names expected from Singular.
Applied to files:
apps/web/tests/analytics/index.test.ts
β° Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build
π Additional comments (4)
apps/web/tests/analytics/query-parser.test.ts (4)
5-17: LGTM! Comprehensive coverage of basic nested property parsing.The tests properly validate:
- Conversion from bracket notation to dot notation
- Support for both single and double quotes
- Correct parsing of quoted values
- Proper filter object structure
19-43: LGTM! Excellent coverage of deeply nested property parsing.The tests validate complex nested structures effectively:
- Multi-level nesting conversion (3+ levels deep)
- Real-world property naming patterns
- Consistent bracket-to-dot notation transformation
52-57: LGTM! Important coverage for the not equals operator.This test validates the
!=operator functionality, which is essential for comprehensive filtering capabilities.
59-77: LGTM! Excellent edge case coverage for defensive programming.The edge case tests properly validate that the parser:
- Returns
undefinedfor all invalid inputs- Handles runtime type issues gracefully with appropriate casting
- Covers all common falsy/invalid input scenarios
- Treats whitespace-only strings as invalid
This ensures the parser won't crash on malformed or missing input.
|
@CodeRabbit you did not leave any review β full review please π |
π§© Analysis chain@steven-tey I'll conduct a comprehensive review of the metadata filtering implementation. Let me analyze the changes across all the modified files. π Script executed: Length of output: 19809 π Script executed: Length of output: 26025 π Script executed: Length of output: 17671 @steven-tey I've completed my comprehensive review of the metadata filtering implementation. This is a well-architected feature that adds powerful filtering capabilities to the analytics system. Here are my findings: π― Overall Assessment: Excellent ImplementationThis PR successfully implements metadata filtering across the entire analytics stack with proper separation of concerns, comprehensive testing, and thoughtful UI integration. β Strengths1. Robust Query Parser Implementation
2. Comprehensive Schema Updates
3. Solid Tinybird Integration
4. Excellent Test Coverage
5. Thoughtful UI Implementation
π Minor Observations1. Query Parser Edge CaseThe regex pattern could potentially match invalid syntax like // In parseCondition function, after regex match:
if (keyPath === '[]' || keyPath.includes('[]')) {
return null;
}2. Error Handling EnhancementConsider adding more specific error messages for malformed queries: // In parseFiltersFromQuery
if (filters.length === 0 && conditions.length > 0) {
// Log warning about unparseable conditions
console.warn('Some query conditions could not be parsed:', conditions);
}3. Performance ConsiderationFor high-volume workspaces, consider adding query complexity limits: const MAX_FILTERS = 10;
if (filters.length > MAX_FILTERS) {
throw new Error(`Too many filters. Maximum allowed: ${MAX_FILTERS}`);
}π‘οΈ Security Assessmentβ
SQL Injection Prevention: Proper parameterization in Tinybird pipes π Performance Impactβ
Database Efficiency: Filters applied at query level using indexes π Documentation NeedsConsider adding documentation for:
π ConclusionThis is an exemplary implementation that demonstrates:
The implementation is production-ready and follows best practices throughout. The comprehensive test coverage and thoughtful error handling make this a robust addition to the analytics system. Recommendation: β APPROVE β Actions performedFull review triggered. |
Summary by CodeRabbit
New Features
Improvements
Bug Fixes
Tests