Skip to content

Commit ab0700d

Browse files
committed
Update version to 2.1.0 and add feature to show lookup names in record queries. Enhance SOQL query handling to include relationship fields with name fields, and update CHANGES.md to reflect new feature [feature #1130](#1130).
1 parent cbc5124 commit ab0700d

3 files changed

Lines changed: 98 additions & 10 deletions

File tree

CHANGES.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Release Notes
22

3+
## Version 2.1
4+
5+
- `Show All Data` See Lookup Names on lookup title [feature #1130](https://github.com/tprouvot/Salesforce-Inspector-reloaded/issues/1130)
6+
37
## Version 2.0
48

59
- `Data Export` Fix unrecognized Salesforce Ids [issue #984](https://github.com/tprouvot/Salesforce-Inspector-reloaded/issues/984)

addon/inspect.js

Lines changed: 92 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* global React ReactDOM */
22
import {sfConn, apiVersion} from "./inspector.js";
3-
import {copyToClipboard, downloadCsvFile} from "./utils.js";
3+
import {copyToClipboard, downloadCsvFile, getStandardObjectNameField} from "./utils.js";
44
/* global initButton */
55
import {getObjectSetupLinks, getFieldSetupLinks} from "./setup-links.js";
66
import {PageHeader} from "./components/PageHeader.js";
@@ -10,6 +10,66 @@ import AgentforceModal from "./components/AgentforceModal.js";
1010
// Constants
1111
const GET_FIELD_USAGE_LABEL = "Get field usage";
1212

13+
/**
14+
* Builds a SOQL query to fetch a record with lookup name fields.
15+
* Uses STANDARD_OBJECT_NAME_FIELDS to only add relationship fields when the referenced object has a name field.
16+
* @param {Object} sobjectDescribe - Object describe from REST API
17+
* @param {string} recordId - Record Id to fetch
18+
* @returns {{query: string, lookupFieldMap: Array<{fieldName: string, relationshipPath: string, nameField: string}>}}
19+
*/
20+
function buildRecordQueryWithLookupNames(sobjectDescribe, recordId) {
21+
let selectFields = [];
22+
const lookupFieldMap = [];
23+
24+
for (const field of sobjectDescribe.fields) {
25+
if (!field.name || field.name === "attributes") {
26+
continue;
27+
}
28+
selectFields.push(field.name);
29+
30+
if (field.type === "reference" && field.relationshipName && field.referenceTo && field.referenceTo.length === 1) {
31+
const referencedObject = field.referenceTo[0];
32+
const nameField = getStandardObjectNameField(referencedObject);
33+
if (nameField !== null) {
34+
const relationshipField = nameField === "N/A" ? "Name" : nameField;
35+
selectFields.push(field.relationshipName + "." + relationshipField);
36+
lookupFieldMap.push({fieldName: field.name, relationshipPath: field.relationshipName, nameField: relationshipField});
37+
}
38+
}
39+
}
40+
41+
const query = "SELECT " + selectFields.join(", ") + " FROM " + sobjectDescribe.name + " WHERE Id = '" + recordId + "'";
42+
return {query, lookupFieldMap};
43+
}
44+
45+
/**
46+
* Flattens a SOQL record result and extracts lookup names into lookupNames map.
47+
* @param {Object} record - Single record from SOQL query (records[0])
48+
* @param {Array} lookupFieldMap - From buildRecordQueryWithLookupNames
49+
* @returns {{flatRecord: Object, lookupNames: Object}}
50+
*/
51+
function flattenSoqlRecordWithLookupNames(record, lookupFieldMap) {
52+
const flatRecord = {};
53+
const lookupNames = {};
54+
55+
for (const key in record) {
56+
if (key === "attributes") {
57+
continue;
58+
}
59+
const value = record[key];
60+
if (value && typeof value === "object" && value.attributes) {
61+
const lookupInfo = lookupFieldMap.find(l => l.relationshipPath === key);
62+
if (lookupInfo && value[lookupInfo.nameField] != null) {
63+
lookupNames[lookupInfo.fieldName] = value[lookupInfo.nameField];
64+
}
65+
} else {
66+
flatRecord[key] = value;
67+
}
68+
}
69+
70+
return {flatRecord, lookupNames};
71+
}
72+
1373
class Model {
1474
constructor(sfHost) {
1575
this.reactCallback = null;
@@ -49,6 +109,7 @@ class Model {
49109
this.popupTmpReactElement = undefined;
50110
this.popupReactElement = undefined;
51111
this.recordName;
112+
this.lookupNames = {}; // Maps lookup field name -> display name (e.g. AccountId -> "Acme Corp")
52113
let trialExpDate = localStorage.getItem(sfHost + "_trialExpirationDate");
53114
if (localStorage.getItem(sfHost + "_isSandbox") != "true" && (!trialExpDate || trialExpDate === "null")) {
54115
//change background color for production
@@ -407,19 +468,26 @@ Structure your response clearly with appropriate headings.`;
407468
}
408469
setRecordData(recordDataPromise) {
409470
this.spinFor("retrieving record", recordDataPromise.then(res => {
410-
for (let name in res) {
471+
let recordData = res;
472+
if (res.recordData && res.lookupNames) {
473+
this.lookupNames = res.lookupNames;
474+
recordData = res.recordData;
475+
} else {
476+
this.lookupNames = {};
477+
}
478+
for (let name in recordData) {
411479
if (name != "attributes") {
412-
this.fieldRows.getRow(name).dataTypedValue = res[name];
480+
this.fieldRows.getRow(name).dataTypedValue = recordData[name];
413481
}
414482
}
415483
this.fieldRows.resortRows();
416-
this.recordData = res;
484+
this.recordData = recordData;
417485
this.fieldRows.showHideColumn(true, "value");
418486
this.spinFor(
419487
"describing layout",
420488
this.sobjectDescribePromise.then(sobjectDescribe => {
421489
if (sobjectDescribe.urls.layouts) {
422-
return sfConn.rest(sobjectDescribe.urls.layouts + "/" + (res.RecordTypeId || "012000000000000AAA"));
490+
return sfConn.rest(sobjectDescribe.urls.layouts + "/" + (recordData.RecordTypeId || "012000000000000AAA"));
423491
}
424492
return undefined;
425493
}).then(layoutDescribe => {
@@ -475,6 +543,7 @@ Structure your response clearly with appropriate headings.`;
475543
}
476544
this.recordData = null;
477545
this.layoutInfo = null;
546+
this.lookupNames = {};
478547
}
479548
startLoading() {
480549

@@ -498,9 +567,20 @@ Structure your response clearly with appropriate headings.`;
498567
this.childRows.resortRows();
499568
}));
500569

501-
// Fetch record data using record retrieve call
570+
// Fetch record data using SOQL query (includes lookup names when referenced object has a name field)
502571
if (this.recordId) {
503-
this.setRecordData(sfConn.rest("/services/data/v" + apiVersion + "/" + (this.useToolingApi ? "tooling/" : "") + "sobjects/" + this.sobjectName + "/" + this.recordId));
572+
const recordPromise = this.sobjectDescribePromise.then(sobjectDescribe => {
573+
const {query, lookupFieldMap} = buildRecordQueryWithLookupNames(sobjectDescribe, this.recordId);
574+
const queryUrl = "/services/data/v" + apiVersion + "/" + (this.useToolingApi ? "tooling/" : "") + "query/?q=" + encodeURIComponent(query);
575+
return sfConn.rest(queryUrl).then(res => {
576+
if (!res.records || res.records.length === 0) {
577+
throw new Error("Record not found");
578+
}
579+
const {flatRecord, lookupNames} = flattenSoqlRecordWithLookupNames(res.records[0], lookupFieldMap);
580+
return {recordData: flatRecord, lookupNames};
581+
});
582+
});
583+
this.setRecordData(recordPromise);
504584
}
505585

506586
// Fetch fields using a Tooling API call, which returns fields not readable by the current user, but fails if the user does not have access to the Tooling API.
@@ -1387,6 +1467,9 @@ class FieldRow extends TableRow {
13871467
}
13881468
return false;
13891469
}
1470+
lookupDisplayValue() {
1471+
return this.rowList.model.lookupNames?.[this.fieldName] ?? null;
1472+
}
13901473
idLink() {
13911474
return "https://" + this.rowList.model.sfHost + "/" + this.dataTypedValue;
13921475
}
@@ -2281,9 +2364,10 @@ class FieldValueCell extends React.Component {
22812364
)
22822365
);
22832366
} else if (row.isId()) {
2367+
const lookupTitle = row.lookupDisplayValue() ? row.lookupDisplayValue() : null;
22842368
return h("td", {className: col.className, onDoubleClick: this.onTryEdit},
22852369
h("div", {className: "pop-menu-container"},
2286-
h("div", {className: "sfir-inspect-table-text quick-select"}, h("a", {href: row.idLink() /*used to show visited color*/, onClick: this.onRecordIdClick}, row.dataStringValue())),
2370+
h("div", {className: "sfir-inspect-table-text quick-select"}, h("a", {href: row.idLink() /*used to show visited color*/, onClick: this.onRecordIdClick, title: lookupTitle}, row.dataStringValue())),
22872371
row.recordIdPop == null ? null : h("div", {className: "slds-dropdown slds-dropdown_left slds-dropdown_actions pop-menu"},
22882372
h("ul", {className: "slds-dropdown__list"},
22892373
row.recordIdPop.map(link =>

addon/manifest.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
22
"name": "Salesforce Inspector Reloaded",
33
"description": "Productivity tools for Salesforce administrators and developers to inspect data and metadata directly from the Salesforce UI.",
4-
"version": "2.0.0",
5-
"version_name": "2.0",
4+
"version": "2.1.0",
5+
"version_name": "2.1",
66
"icons": {
77
"128": "icon128.png"
88
},

0 commit comments

Comments
 (0)