Skip to content

Commit 91518a0

Browse files
authored
relatedLogEntries LWC Improvements (#956)
* Refactored RelatedLogEntriesController to use SOSL to search for the related record ID in all fields (instead of only checking LogEntry__c.RecordId__c) * Also updated the controller so picklist labels are returned instead of the raw value (e.g., LoggingLevel__c's labels include emojis that are now displayed) * Updated relatedLogEntries LWC to visually better align with the appearance of standard related lists * Scope creep: cleaned up a few unrelated bits in a few Apex classes * Scope creep: Added Apex class access for CallableLogger to the LoggerLogCreator permission set to ensure the running user can use it in contexts like OmniStudio
1 parent d375791 commit 91518a0

20 files changed

Lines changed: 154 additions & 253 deletions

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@
55

66
The most robust observability solution for Salesforce experts. Built 100% natively on the platform, and designed to work seamlessly with Apex, Lightning Components, Flow, OmniStudio, and integrations.
77

8-
## Unlocked Package - v4.17.2
8+
## Unlocked Package - v4.17.3
99

10-
[![Install Unlocked Package in a Sandbox](./images/btn-install-unlocked-package-sandbox.png)](https://test.salesforce.com/packaging/installPackage.apexp?p0=04tg700000015gnAAA)
11-
[![Install Unlocked Package in Production](./images/btn-install-unlocked-package-production.png)](https://login.salesforce.com/packaging/installPackage.apexp?p0=04tg700000015gnAAA)
10+
[![Install Unlocked Package in a Sandbox](./images/btn-install-unlocked-package-sandbox.png)](https://test.salesforce.com/packaging/installPackage.apexp?p0=04tg70000001IMHAA2)
11+
[![Install Unlocked Package in Production](./images/btn-install-unlocked-package-production.png)](https://login.salesforce.com/packaging/installPackage.apexp?p0=04tg70000001IMHAA2)
1212
[![View Documentation](./images/btn-view-documentation.png)](https://github.com/jongpie/NebulaLogger/wiki)
1313

14-
`sf package install --wait 20 --security-type AdminsOnly --package 04tg700000015gnAAA`
14+
`sf package install --wait 20 --security-type AdminsOnly --package 04tg70000001IMHAA2`
1515

1616
---
1717

docs/apex/Log-Management/LogManagementDataSelector.md

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -457,29 +457,6 @@ List<Schema.Group>
457457

458458
The list of matching `Schema.Group` records
459459

460-
#### `getRecordLogEntries(Id recordId, String fieldsClause, String orderByClause, Integer rowLimit)``List<LogEntry__c>`
461-
462-
Returns the list of `LogEntry__c` records related to the specified record ID
463-
464-
##### Parameters
465-
466-
| Param | Description |
467-
| --------------- | ---------------------------------------------------------------------------------- |
468-
| `recordId` | The `ID` to use for filtering `LogEntry__c` records |
469-
| `fieldsClause` | A comma-separated `String` of field API names to include in the query |
470-
| `orderByClause` | A comma-separated `String` of field API names to use for sorting the query results |
471-
| `rowLimit` | The maximum number of records to return |
472-
473-
##### Return
474-
475-
**Type**
476-
477-
List&lt;LogEntry\_\_c&gt;
478-
479-
**Description**
480-
481-
The list of matching `LogEntry__c` records
482-
483460
#### `getTagsByName(Set<String> tagNames)``List<LoggerTag__c>`
484461

485462
Returns a list of `LoggerTag__c` records with one of the specified tag names

docs/apex/Log-Management/RelatedLogEntriesController.md

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Controller class for the lightning web component `related-log-entries`
1010

1111
### Methods
1212

13-
#### `getQueryResult(Id recordId,String fieldSetName,Integer rowLimit,Integer rowOffset,String sortByFieldName,String sortDirection,String search)``LogEntryQueryResult`
13+
#### `getQueryResult(Id recordId,String fieldSetName,Integer rowLimit,String sortByFieldName,String sortDirection,String search)``LogEntryQueryResult`
1414

1515
Used by the component relatedLogEntries to get log entries for a particular record (based on record ID)
1616

@@ -21,7 +21,6 @@ Used by the component relatedLogEntries to get log entries for a particular reco
2121
| `recordId` | Used to filter LogEntry**c records where RecordId**c == recordId |
2222
| `fieldSetName` | The API/developer name of the field set |
2323
| `rowLimit` | The max number of rows to query |
24-
| `rowOffset` | Reserved for future use |
2524
| `sortByFieldName` | The field to sort by |
2625
| `sortDirection` | The direction to sort by (asc or desc)) |
2726
| `search` | An optional search term to filter by |
@@ -128,8 +127,4 @@ Contains the plural label of the log entry sObject, fetched using a describe cal
128127

129128
contains the log entry results from the query.
130129

131-
###### `totalLogEntriesCount``Integer`
132-
133-
Contains the number of records returned via the log entries query.
134-
135130
---

docs/apex/Logger-Engine/LogEntryEventBuilder.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ Used by `Logger` to instantiate a new instance of `LogEntryEventBuilder`
3636

3737
#### `aggregateQueriesMax``Integer`
3838

39+
#### `apexCursorFetchCallsMax``Integer`
40+
41+
#### `apexCursorRowsMax``Integer`
42+
3943
#### `asyncCallsMax``Integer`
4044

4145
#### `calloutsMax``Integer`

nebula-logger/core/main/log-management/classes/LogEntryFieldSetPicklist.cls

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public without sharing class LogEntryFieldSetPicklist extends VisualEditor.Dynam
3838
}
3939

4040
private VisualEditor.DataRow createDataRow(Schema.FieldSet fieldSet) {
41-
String namespacePrefix = String.isBlank(fieldSet.getNameSpace()) ? '' : fieldSet.getNameSpace() + '__';
41+
String namespacePrefix = String.isBlank(fieldSet.getNamespace()) ? '' : fieldSet.getNamespace() + '__';
4242
return new VisualEditor.DataRow(fieldSet.getLabel(), namespacePrefix + fieldSet.getName());
4343
}
4444
}

nebula-logger/core/main/log-management/classes/LogManagementDataSelector.cls

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -364,29 +364,6 @@ public without sharing virtual class LogManagementDataSelector {
364364
return [SELECT Id, DeveloperName FROM Group WHERE Type = 'Queue' AND DeveloperName IN :queueDeveloperNames];
365365
}
366366

367-
/**
368-
* @description Returns the list of `LogEntry__c` records related to the specified record ID
369-
* @param recordId The `ID` to use for filtering `LogEntry__c` records
370-
* @param fieldsClause A comma-separated `String` of field API names to include in the query
371-
* @param orderByClause A comma-separated `String` of field API names to use for sorting the query results
372-
* @param rowLimit The maximum number of records to return
373-
* @return The list of matching `LogEntry__c` records
374-
*/
375-
@SuppressWarnings('PMD.ExcessiveParameterList')
376-
public virtual List<LogEntry__c> getRecordLogEntries(Id recordId, String fieldsClause, String orderByClause, Integer rowLimit) {
377-
List<Object> queryTextReplacements = new List<Object>{
378-
fieldsClause,
379-
Schema.LogEntry__c.SObjectType.toString(),
380-
Schema.LogEntry__c.RecordId__c.toString(),
381-
orderByClause,
382-
rowLimit
383-
};
384-
String logEntryQuery = 'SELECT {0} FROM {1} WHERE {2} = :recordId ORDER BY {3} LIMIT {4}';
385-
logEntryQuery = String.format(logEntryQuery, queryTextReplacements);
386-
387-
return (List<LogEntry__c>) System.Database.query(String.escapeSingleQuotes(logEntryQuery));
388-
}
389-
390367
/**
391368
* @description Returns a list of `LoggerTag__c` records with one of the specified tag names
392369
* @param tagNames The set of `String` tag names to query

nebula-logger/core/main/log-management/classes/RelatedLogEntriesController.cls

Lines changed: 24 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public with sharing class RelatedLogEntriesController {
1919
* @param recordId Used to filter LogEntry__c records where RecordId__c == recordId
2020
* @param fieldSetName The API/developer name of the field set
2121
* @param rowLimit The max number of rows to query
22-
* @param rowOffset Reserved for future use
22+
2323
* @param sortByFieldName The field to sort by
2424
* @param sortDirection The direction to sort by (asc or desc))
2525
* @param search An optional search term to filter by
@@ -31,57 +31,52 @@ public with sharing class RelatedLogEntriesController {
3131
Id recordId,
3232
String fieldSetName,
3333
Integer rowLimit,
34-
Integer rowOffset,
3534
String sortByFieldName,
3635
String sortDirection,
3736
String search
3837
) {
39-
// TODO use rowOffset to implement infinite-loading
4038
FieldSetMetadata fieldSetMetdata = new FieldSetMetadata(LOG_ENTRY_SOBJECT_TYPE, fieldSetName);
4139

4240
String fieldsClause = getFieldsClause(fieldSetMetdata.fields);
4341
String orderByClause = getOrderByClause(sortByFieldName, sortDirection);
4442

45-
List<LogEntry__c> records;
46-
if (String.isNotBlank(search) && search.length() >= 3) {
47-
records = search(recordId, search, fieldsClause, orderByClause, rowLimit);
48-
} else {
49-
records = LogManagementDataSelector.getInstance().getRecordLogEntries(recordId, fieldsClause, orderByClause, rowLimit);
50-
}
51-
52-
// Somewhat redundant security check for FLS (but extra security > less security)
53-
System.SObjectAccessDecision securityDecision = System.Security.stripInaccessible(System.AccessType.READABLE, records);
54-
records = securityDecision.getRecords();
55-
56-
Integer totalLogEntriesCount = LogManagementDataSelector.getInstance().getCountOfRelatedRecordLogEntries(recordId);
57-
58-
return new LogEntryQueryResult(fieldSetMetdata, records, totalLogEntriesCount);
43+
List<LogEntry__c> records = search(recordId, search, fieldsClause, orderByClause, rowLimit);
44+
return new LogEntryQueryResult(fieldSetMetdata, records);
5945
}
6046

61-
// Private static helper methods
62-
6347
@SuppressWarnings('PMD.ExcessiveParameterList')
6448
private static List<LogEntry__c> search(Id recordId, String searchTerm, String fieldsClause, String orderByClause, Integer rowLimit) {
65-
searchTerm = '\'*' + String.escapeSingleQuotes(searchTerm) + '*\'';
49+
// SOSL can search for a 15-character ID, but for some reason, using the 18-character version returns no results (╯‵□′)╯︵┻━┻
50+
String fullSearchExpression = '*' + String.escapeSingleQuotes(String.valueOf(recordId).left(15)) + '*';
51+
if (String.isNotBlank(searchTerm)) {
52+
fullSearchExpression += ' AND *' + String.escapeSingleQuotes(searchTerm) + '*';
53+
}
54+
fullSearchExpression = '\'' + fullSearchExpression + '\'';
6655

6756
List<Object> searchTextReplacements = new List<Object>{
68-
searchTerm,
57+
fullSearchExpression,
6958
String.valueOf(Schema.LogEntry__c.SObjectType),
7059
fieldsClause,
71-
String.valueOf(Schema.LogEntry__c.RecordId__c),
7260
orderByClause,
7361
rowLimit
7462
};
75-
String logEntrySearch = 'FIND {0} IN ALL FIELDS RETURNING {1}({2} WHERE {3} = :recordId ORDER BY {4} LIMIT {5})';
76-
logEntrySearch = String.format(logEntrySearch, searchTextReplacements);
63+
String logEntrySearch = String.format('FIND {0} IN ALL FIELDS RETURNING {1} ({2} ORDER BY {3} LIMIT {4})', searchTextReplacements);
7764

78-
return (List<LogEntry__c>) System.Search.query(logEntrySearch).get(0);
65+
List<LogEntry__c> matches = (List<LogEntry__c>) System.Search.query(logEntrySearch).get(0);
66+
67+
// Somewhat redundant security check for FLS (but extra security > less security)
68+
return System.Security.stripInaccessible(System.AccessType.READABLE, matches).getRecords();
7969
}
8070

8171
private static String getFieldsClause(List<FieldMetadata> fields) {
82-
List<String> fieldNames = new List<String>();
72+
// Make sure these fields are always included, even if they're not in the fieldset, in case the LWC needs them
73+
Set<String> fieldNames = new Set<String>{
74+
LogEntry__c.DatabaseResultJson__c.toString(),
75+
LogEntry__c.RecordId__c.toString(),
76+
LogEntry__c.RecordJson__c.toString()
77+
};
8378
for (FieldMetadata fieldMetadata : fields) {
84-
fieldNames.add(fieldMetadata.fieldName);
79+
fieldNames.add(fieldMetadata.type == 'picklist' ? ('toLabel(' + fieldMetadata.fieldName + ')') : fieldMetadata.fieldName);
8580

8681
// For lookups, also include the display name of parent object
8782
if (fieldMetadata.lookupDisplayFieldName != null) {
@@ -186,19 +181,12 @@ public with sharing class RelatedLogEntriesController {
186181
@AuraEnabled
187182
public List<LogEntry__c> records { get; set; }
188183

189-
/**
190-
* @description Contains the number of records returned via the log entries query.
191-
*/
192-
@AuraEnabled
193-
public Integer totalLogEntriesCount { get; set; }
194-
195-
private LogEntryQueryResult(FieldSetMetadata fieldSetMetadata, List<LogEntry__c> records, Integer totalLogEntriesCount) {
184+
private LogEntryQueryResult(FieldSetMetadata fieldSetMetadata, List<LogEntry__c> records) {
196185
this.fieldSet = fieldSetMetadata;
197186
this.isAccessible = LOG_ENTRY_SOBJECT_TYPE.getDescribe().isAccessible();
198187
this.label = LOG_ENTRY_SOBJECT_TYPE.getDescribe().getLabel();
199188
this.labelPlural = LOG_ENTRY_SOBJECT_TYPE.getDescribe().getLabelPlural();
200189
this.records = records;
201-
this.totalLogEntriesCount = totalLogEntriesCount;
202190
}
203191
}
204192

@@ -235,7 +223,7 @@ public with sharing class RelatedLogEntriesController {
235223
}
236224
}
237225

238-
String namespacePrefix = String.isBlank(fieldSet.getNameSpace()) ? '' : fieldSet.getNameSpace() + '__';
226+
String namespacePrefix = String.isBlank(fieldSet.getNamespace()) ? '' : fieldSet.getNamespace() + '__';
239227

240228
this.label = fieldSet.getLabel();
241229
this.name = namespacePrefix + fieldSet.getName();

nebula-logger/core/main/log-management/lwc/relatedLogEntries/__tests__/data/getQueryResult.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
{
2-
"totalLogEntriesCount": 4,
32
"records": [
43
{
54
"attributes": { "type": "LogEntry__c", "url": "/services/data/v53.0/sobjects/LogEntry__c/a0054000004OazXAAS" },

nebula-logger/core/main/log-management/lwc/relatedLogEntries/relatedLogEntries.html

Lines changed: 39 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,32 +5,47 @@
55

66
<template>
77
<template if:true={showComponent}>
8-
<lightning-card title={title} icon-name="custom:custom83" class="log-entry-card">
9-
<div class="slds-grid" slot="actions">
10-
<lightning-input
11-
class="slds-m-right_xx-small"
12-
data-id="enter-search"
13-
placeholder="Search"
14-
type="search"
15-
value={search}
16-
onchange={handleSearchChange}
17-
variant="label-hidden"
18-
></lightning-input>
19-
<lightning-button-icon icon-name="utility:refresh" onclick={refresh}></lightning-button-icon>
8+
<article class="slds-card slds-card_boundary">
9+
<div class="slds-card__header slds-grid">
10+
<header class="slds-media slds-media_center slds-has-flexi-truncate">
11+
<div class="slds-media__figure">
12+
<lightning-icon icon-name="custom:custom83" size="small"></lightning-icon>
13+
</div>
14+
<div class="slds-media__body slds-m-bottom_small">
15+
<h2 class="slds-card__header-title">
16+
<span class="slds-truncate">{title}</span>
17+
</h2>
18+
</div>
19+
<div class="slds-no-flex">
20+
<lightning-input
21+
class="slds-m-right_xx-small"
22+
data-id="enter-search"
23+
placeholder="Search"
24+
type="search"
25+
value={search}
26+
onchange={handleSearchChange}
27+
style="display: inline-block"
28+
variant="label-hidden"
29+
></lightning-input>
30+
<lightning-button-icon icon-name="utility:refresh" onclick={refresh}></lightning-button-icon>
31+
</div>
32+
</header>
2033
</div>
21-
<div class="slds-is-relative">
22-
<lightning-datatable
23-
key-field="id"
24-
show-row-number-column="true"
25-
data={queryResult.records}
26-
columns={queryResult.fieldSet.fields}
27-
onsort={handleSort}
28-
sorted-by={sortBy}
29-
sorted-direction={sortDirection}
30-
default-sort-direction={sortDirection}
31-
></lightning-datatable>
34+
<div class="slds-card__body">
3235
<lightning-spinner if:true={isLoading} size="small"></lightning-spinner>
36+
<div class="slds-is-relative" if:true={queryResult}>
37+
<lightning-datatable
38+
key-field="id"
39+
show-row-number-column="true"
40+
data={queryResult.records}
41+
columns={queryResult.fieldSet.fields}
42+
onsort={handleSort}
43+
sorted-by={sortBy}
44+
sorted-direction={sortDirection}
45+
default-sort-direction={sortDirection}
46+
></lightning-datatable>
47+
</div>
3348
</div>
34-
</lightning-card>
49+
</article>
3550
</template>
3651
</template>

nebula-logger/core/main/log-management/lwc/relatedLogEntries/relatedLogEntries.js

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,20 @@ export default class RelatedLogEntries extends LightningElement {
1717
@api search = '';
1818
@api queryResult;
1919

20-
@track showComponent = false;
21-
@track title;
2220
@track wiredResult;
2321
@track isLoading = true;
2422

23+
get title() {
24+
return this.queryResult ? this.queryResult.labelPlural + ' (' + this.queryResult.records.length + ')' : '';
25+
}
26+
27+
get showComponent() {
28+
return this.queryResult?.isAccessible === true;
29+
}
30+
2531
@api
2632
handleSort(event) {
33+
this.isLoading = true;
2734
this.sortBy = event.detail.fieldName;
2835
this.sortDirection = event.detail.sortDirection;
2936
}
@@ -43,15 +50,11 @@ export default class RelatedLogEntries extends LightningElement {
4350
search: '$search'
4451
})
4552
wiredLogEntries(result) {
53+
this.isLoading = true;
4654
this.wiredResult = result;
4755
if (result.data) {
48-
let queryResult = this.processResult(result.data);
49-
this.queryResult = queryResult;
50-
this.showComponent = queryResult.isAccessible;
51-
this.title = queryResult.labelPlural + ' (' + queryResult.totalLogEntriesCount + ' Total)';
56+
this.queryResult = this.processResult(result.data);
5257
} else if (result.error) {
53-
this.logEntryData = undefined;
54-
this.logEntryColumns = undefined;
5558
/* eslint-disable-next-line no-console */
5659
console.log(result.error);
5760
}

0 commit comments

Comments
 (0)