Skip to content
Merged
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
14 changes: 8 additions & 6 deletions libs/shared/ui-core-shared/src/query-soql-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,27 +117,29 @@ export async function fetchMetadataFromSoql(

for (const childRelationship in parsableFields.subqueries) {
const foundRelationship = rootSobjectDescribe.childRelationships.find(
(currChildRelationship) => currChildRelationship.relationshipName === childRelationship,
(currChildRelationship) => currChildRelationship.relationshipName?.toLowerCase() === childRelationship.toLowerCase(),
);
if (foundRelationship) {
if (foundRelationship && foundRelationship.relationshipName) {
// Use the canonical relationship name from Salesforce metadata for consistent downstream lookups
const canonicalRelationshipName = foundRelationship.relationshipName;
const rootSobjectChildDescribe = await describeSObjectWithLocalCache(org, foundRelationship.childSObject, isTooling, describeCache);
output.childMetadata[childRelationship] = {
output.childMetadata[canonicalRelationshipName] = {
objectMetadata: rootSobjectChildDescribe,
metadataTree: await fetchAllMetadata(
org,
isTooling,
rootSobjectChildDescribe,
parsableFields.subqueries[childRelationship],
describeCache,
childRelationship,
canonicalRelationshipName,
),
lowercaseFieldMap: getLowercaseFieldMap(rootSobjectChildDescribe.fields),
};

// add entries to lowercaseFieldMap for all related objects
getLowercaseFieldMapWithFullPath(
output.childMetadata[childRelationship].metadataTree,
output.childMetadata[childRelationship].lowercaseFieldMap,
output.childMetadata[canonicalRelationshipName].metadataTree,
output.childMetadata[canonicalRelationshipName].lowercaseFieldMap,
);
}
}
Expand Down
15 changes: 12 additions & 3 deletions libs/shared/ui-core/src/query/RestoreQuery/query-restore-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,8 @@ function processFields(data: SoqlFetchMetadataOutput, stateItems: Partial<QueryR
setSelectedFields(baseKey, data.selectedSobjectMetadata.sobject.fields, queryFields, data.metadata, stateItems);

// process subqueries
const childMetadataKeys = new Set(Object.keys(data.childMetadata).map((key) => key.toLowerCase()));

Object.keys(data.childMetadata).forEach((relationshipName) => {
const { objectMetadata, metadataTree } = data.childMetadata[relationshipName];
const childBaseKey = getSubqueryFieldBaseKey(objectMetadata.name, relationshipName);
Expand All @@ -233,11 +235,18 @@ function processFields(data: SoqlFetchMetadataOutput, stateItems: Partial<QueryR
stateItems,
relationshipName,
);
} else {
// ERROR - this should not happen (confirm if it is possible or not and remove this path if so)
// otherwise handle error
}
});

// Track subquery relationships from the query that were not found in metadata
queryFields
.filter((field): field is FieldSubquery => field.type === 'FieldSubquery')
.forEach((field) => {
if (!childMetadataKeys.has(field.subquery.relationshipName.toLowerCase())) {
stateItems.missingMisc = stateItems.missingMisc || [];
stateItems.missingMisc.push(`Child relationship '${field.subquery.relationshipName}' was not found`);
}
});
}

/**
Expand Down
2 changes: 1 addition & 1 deletion libs/ui/src/lib/data-table/DataTableSubqueryRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ export const SubqueryRenderer = ({ column, row, onRowChange }: RenderCellProps<R
google_clientId,
} = props;

const columns = columnDefinitions?.[column.key];
const columns = columnDefinitions?.[column.key.toLowerCase()];

if (!columns) {
return null;
Expand Down
78 changes: 47 additions & 31 deletions libs/ui/src/lib/data-table/data-table-utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -135,28 +135,37 @@ export function getColumnDefinitions(
subqueryColumns: {},
};

// Build a set of subquery relationship names for reliable lookup
// (positional index is unreliable because getFlattenedFields uses flatMap, e.g. TYPEOF expands to multiple entries)
const subqueryRelationshipNames = new Set(
results.parsedQuery?.fields?.filter(isFieldSubquery).map((f) => f.subquery.relationshipName.toLowerCase()) || [],
);

// map each field to the returned metadata from SFDC
let queryColumnsByPath: Record<string, QueryResultsColumn> = {};
if (results.columns?.columns) {
queryColumnsByPath = results.columns.columns.reduce((out, curr, i) => {
out[curr.columnFullPath.toLowerCase()] = curr;
// some subqueries (e.x. TYPEOF) is not returned from the salesforce "column.childColumnPaths"
// in this case, we need to mock the response structure
// https://github.com/paustint/jetstream/issues/3#issuecomment-728028624
if (!Array.isArray(curr.childColumnPaths) && results.parsedQuery?.fields?.[i] && isFieldSubquery(results.parsedQuery.fields[i])) {
curr.childColumnPaths = [];
}
queryColumnsByPath = results.columns.columns.reduce(
(out, curr) => {
out[curr.columnFullPath.toLowerCase()] = curr;
// some subqueries (e.x. TYPEOF) is not returned from the salesforce "column.childColumnPaths"
// in this case, we need to mock the response structure
// https://github.com/paustint/jetstream/issues/3#issuecomment-728028624
if (!Array.isArray(curr.childColumnPaths) && subqueryRelationshipNames.has(curr.columnFullPath.toLowerCase())) {
curr.childColumnPaths = [];
}

if (Array.isArray(curr.childColumnPaths)) {
curr.childColumnPaths.forEach((subqueryField) => {
out[subqueryField.columnFullPath.toLowerCase()] = {
...subqueryField,
columnFullPath: subqueryField.columnFullPath.split('.').slice(1).join('.'), // remove child relationship name
} as QueryResultsColumn;
});
}
return out;
}, {});
if (Array.isArray(curr.childColumnPaths)) {
curr.childColumnPaths.forEach((subqueryField) => {
out[subqueryField.columnFullPath.toLowerCase()] = {
...subqueryField,
columnFullPath: subqueryField.columnFullPath.split('.').slice(1).join('.'), // remove child relationship name
} as QueryResultsColumn;
});
}
return out;
},
{} as Record<string, QueryResultsColumn>,
);
}
// If there is a FIELDS('') clause in the query, then we know the data will not be shown
// in this case, fall back to Salesforce column data instead of the query results
Expand All @@ -168,8 +177,13 @@ export function getColumnDefinitions(
}

// Base fields
const parentColumns: ColumnWithFilter<RowWithKey>[] = getFlattenedFields(results.parsedQuery || {}).map((field, i) =>
getQueryResultColumn({ field, queryColumnsByPath, isSubquery: isFieldSubquery(results.parsedQuery?.[i]), fieldMetadata }),
const parentColumns: ColumnWithFilter<RowWithKey>[] = getFlattenedFields(results.parsedQuery || {}).map((field) =>
getQueryResultColumn({
field,
queryColumnsByPath,
isSubquery: subqueryRelationshipNames.has(field.toLowerCase()),
fieldMetadata,
}),
);

// set checkbox as first column
Expand Down Expand Up @@ -200,15 +214,16 @@ export function getColumnDefinitions(
results.parsedQuery?.fields
?.filter((field) => isFieldSubquery(field))
.forEach((parentField: FieldSubquery) => {
output.subqueryColumns[parentField.subquery.relationshipName] = getFlattenedFields(parentField.subquery || {}).map((field) =>
getQueryResultColumn({
field,
subqueryRelationshipName: parentField.subquery.relationshipName,
queryColumnsByPath,
isSubquery: false,
allowEdit: false,
fieldMetadata: fieldMetadataSubquery?.[field],
}),
output.subqueryColumns[parentField.subquery.relationshipName.toLowerCase()] = getFlattenedFields(parentField.subquery || {}).map(
(field) =>
getQueryResultColumn({
field,
subqueryRelationshipName: parentField.subquery.relationshipName,
queryColumnsByPath,
isSubquery: false,
allowEdit: false,
fieldMetadata: fieldMetadataSubquery?.[parentField.subquery.relationshipName.toLowerCase()],
}),
);
});

Expand Down Expand Up @@ -672,7 +687,8 @@ export function getSfdcRetUrl(record: any, id?: string, skipFrontdoorLoginOverri
try {
id = id || getIdFromRecordUrl(record?.attributes?.url || record?._record?.attributes?.url);
const baseRecordType = record?.attributes?.type || record?._record?.attributes?.type;
const relatedRecordType = RECORD_PREFIX_MAP[(id || '').substring(0, 3)] || null;
const recordPrefix = (id || '').substring(0, 3) as keyof typeof RECORD_PREFIX_MAP;
const relatedRecordType = RECORD_PREFIX_MAP[recordPrefix] || null;

if (baseRecordType === 'Group') {
return {
Expand Down Expand Up @@ -708,7 +724,7 @@ export function getSearchTextByRow<T>(rows: T[], columns: ColumnWithFilter<T>[],
if (key) {
columns.forEach((column) => {
if (column.key) {
let value = row[column.key];
let value = (row as Record<string, unknown>)[column.key];
if (column.getValue) {
value = column.getValue({ row, column });
}
Expand Down
Loading