Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion apps/api/src/services/SegmentService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -818,7 +818,10 @@ export class SegmentService {
value: unknown,
unit?: 'days' | 'hours' | 'minutes',
): Prisma.ContactWhereInput {
const path = jsonPath.split('.');
// Use the entire jsonPath as a single path element.
// Contact data is a flat JSON object, so field names like "prefix.key"
// must be treated as literal keys, not nested paths.
const path = [jsonPath];
Comment on lines +821 to +824
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing Prisma JSON path from jsonPath.split('.') to [jsonPath] removes support for querying nested JSON in contact.data (e.g. filtering on data.profile.tier previously produced path: ['profile','tier'], but will now look for a literal key profile.tier). Since contact.data is a Json column and other parts of the codebase/tests use nested objects, consider preserving backwards compatibility by generating filters that can match both interpretations when the field contains dots (e.g., OR between [jsonPath] and jsonPath.split('.')), or introducing an explicit escaping/syntax to disambiguate literal-dot keys vs nested paths.

Copilot uses AI. Check for mistakes.
Comment on lines +821 to +824
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are extensive SegmentService operator tests, but none seem to cover dotted custom field keys (e.g. contact.data { "app.plan": "premium" } with filter field data.app.plan). Adding a test case here would validate the intended fix and prevent regressions for both dotted-key and nested-path behavior.

Copilot uses AI. Check for mistakes.

switch (operator) {
case 'equals':
Expand Down
10 changes: 9 additions & 1 deletion apps/api/src/services/WorkflowExecutionService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1111,7 +1111,15 @@ export class WorkflowExecutionService {
normalizedField = field.substring(8); // Remove "contact." prefix, leaving "data.X"
}

const parts = normalizedField.split('.');
// Split only on the first dot to separate the top-level field (e.g., "data")
// from the custom field name. This preserves dots in custom field names
// (e.g., "data.prefix.key" → ["data", "prefix.key"]).
const firstDotIndex = normalizedField.indexOf('.');
const parts =
firstDotIndex === -1
? [normalizedField]
: [normalizedField.substring(0, firstDotIndex), normalizedField.substring(firstDotIndex + 1)];

Comment on lines +1114 to +1122
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resolveField now splits only on the first dot, which breaks existing nested field paths like data.profile.tier (it will look for a literal key profile.tier under data). There are tests relying on nested access (e.g. WorkflowConditions.operators.test.ts uses data.profile.tier). Consider switching to a recursive resolver that (1) tries direct key lookup for the full remaining path and (2) otherwise descends by splitting on the next dot, so both nested objects and dotted custom-field keys are supported.

Copilot uses AI. Check for mistakes.
Comment on lines +1114 to +1122
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change is intended to support custom field names containing dots, but there doesn’t appear to be test coverage exercising a dotted key (e.g. contact.data { "app.plan": "premium" } with condition field data.app.plan). Adding an integration test in the existing Workflow condition test suite would help prevent regressions and ensure both dotted-key and nested-path cases behave as expected.

Copilot uses AI. Check for mistakes.
let value: unknown = data;

for (const part of parts) {
Expand Down
21 changes: 16 additions & 5 deletions packages/shared/src/template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,24 @@ export function renderTemplate(template: string, variables: Record<string, unkno
const [mainKey, defaultValue] = key.split('??').map((s: string) => s.trim());

// Handle nested property access (e.g., data.firstName)
// Uses recursive first-dot splitting so that literal dots in custom field
// names (e.g., "prefix.key") are resolved correctly: direct key lookup is
// tried before descending into nested objects.
const getValue = (obj: Record<string, unknown>, path: string): unknown => {
return path.split('.').reduce((current: Record<string, unknown> | unknown, key) => {
if (current && typeof current === 'object' && !Array.isArray(current)) {
return (current as Record<string, unknown>)[key];
}
if (path in obj) {
return obj[path];
}
const firstDotIndex = path.indexOf('.');
if (firstDotIndex === -1) {
return undefined;
}, obj);
}
const firstKey = path.substring(0, firstDotIndex);
const rest = path.substring(firstDotIndex + 1);
const next = obj[firstKey];
if (next && typeof next === 'object' && !Array.isArray(next)) {
return getValue(next as Record<string, unknown>, rest);
}
return undefined;
};

// Try multiple lookup strategies
Expand Down