Skip to content

Commit

Permalink
[data-export] Add query plan to export data (#315)
Browse files Browse the repository at this point in the history
## Describe your changes
Add query plan button to get info for the current query, display
articles link in order to understand the results and how to improve
query perfs

<img width="1422" alt="image"
src="https://github.com/tprouvot/Salesforce-Inspector-reloaded/assets/35368290/583a4328-372d-4de6-92cd-fdd1de2fd2a9">


## Issue ticket number and link
Closing #314 

## Checklist before requesting a review
- [x] I have read and understand the [Contributions
section](https://github.com/tprouvot/Salesforce-Inspector-reloaded#contributions)
- [x] Target branch is releaseCandidate and not master
- [x] I have performed a self-review of my code
- [x] I ran the [unit
tests](https://github.com/tprouvot/Salesforce-Inspector-reloaded#unit-tests)
and my PR does not break any tests
- [x] I documented the changes I've made on the
[CHANGES.md](https://github.com/tprouvot/Salesforce-Inspector-reloaded/blob/master/CHANGES.md)
and followed actual conventions
- [ ] I added a new section on
[how-to.md](https://github.com/tprouvot/Salesforce-Inspector-reloaded/blob/master/docs/how-to.md)
(optional)
  • Loading branch information
tprouvot authored Feb 22, 2024
1 parent 04c88fa commit f1659a2
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 16 deletions.
2 changes: 1 addition & 1 deletion CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Version 1.23

- Add Query Plan to data export [feature 314](https://github.com/tprouvot/Salesforce-Inspector-reloaded/issues/314)
- Align show-all data 'Type' column with Salesforce's 'Data Type' field [issue 312](https://github.com/tprouvot/Salesforce-Inspector-reloaded/issues/312) by [efcdilascio](https://github.com/efcdilascio)
- Make data export suggestions scrollable [feature 301](https://github.com/tprouvot/Salesforce-Inspector-reloaded/issues/301) by [Vranisimo](https://github.com/vranisimo)
- Show the number of filtered records in data export [feature 300](https://github.com/tprouvot/Salesforce-Inspector-reloaded/issues/300) by [Vranisimo](https://github.com/vranisimo)
Expand Down Expand Up @@ -48,7 +49,6 @@
## Version 1.20.1

- Bugfix Delete Button not enabled when only one record is queried/filtered (contribution by [Oscar Gomez Balaguer](https://github.com/ogomezba))

- Bugfix User selection not displaying information (for orgs without community enabled) [issue 211](https://github.com/tprouvot/Salesforce-Inspector-reloaded/issues/211)

## Version 1.20
Expand Down
2 changes: 1 addition & 1 deletion addon/data-export-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ export async function dataExportTest(test) {
// Autocomplete object
setQuery("select Id from OpportunityLi", "", "");
assertEquals("Objects suggestions:", vm.autocompleteResults.title);
assertEquals(["OpportunityLineItem"], getValues(vm.autocompleteResults.results));
assertEquals(["OpportunityLineItem", "OpportunityLineItemChangeEvent"], getValues(vm.autocompleteResults.results));

// Autocomplete unknown object
setQuery("select Id from UnknownObj", "", "");
Expand Down
61 changes: 47 additions & 14 deletions addon/data-export.js
Original file line number Diff line number Diff line change
Expand Up @@ -360,19 +360,23 @@ class Model {
vm.autocompleteProgress.abort();
}

vm.autocompleteClick = ({value, suffix}) => {
vm.queryInput.focus();
//handle when selected field is the last one before "FROM" keyword, or if an existing comma is present after selection
let indexFrom = query.toLowerCase().indexOf("from");
if (suffix.trim() == "," && (query.substring(selEnd + 1, indexFrom).trim().length == 0 || query.substring(selEnd).trim().startsWith(",") || query.substring(selEnd).trim().toLowerCase().startsWith("from"))) {
suffix = "";
}
vm.queryInput.setRangeText(value + suffix, selStart, selEnd, "end");
//add query suffix if needed
if (value.startsWith("FIELDS") && !query.toLowerCase().includes("limit")) {
vm.queryInput.value += " LIMIT 200";
vm.autocompleteClick = ({value, suffix, link}) => {
if (link){
window.open(link, "_blank");
} else {
vm.queryInput.focus();
//handle when selected field is the last one before "FROM" keyword, or if an existing comma is present after selection
let indexFrom = query.toLowerCase().indexOf("from");
if (suffix.trim() == "," && (query.substring(selEnd + 1, indexFrom).trim().length == 0 || query.substring(selEnd).trim().startsWith(",") || query.substring(selEnd).trim().toLowerCase().startsWith("from"))) {
suffix = "";
}
vm.queryInput.setRangeText(value + suffix, selStart, selEnd, "end");
//add query suffix if needed
if (value.startsWith("FIELDS") && !query.toLowerCase().includes("limit")) {
vm.queryInput.value += " LIMIT 200";
}
vm.queryAutocompleteHandler();
}
vm.queryAutocompleteHandler();
};

// Find the token we want to autocomplete. This is the selected text, or the last word before the cursor.
Expand Down Expand Up @@ -905,6 +909,28 @@ class Model {
stopExport() {
this.exportProgress.abort();
}
doQueryPlan(){
let vm = this; // eslint-disable-line consistent-this
let exportedData = new RecordTable(vm);

vm.spinFor(sfConn.rest("/services/data/v" + apiVersion + "/query/?explain=" + encodeURIComponent(vm.queryInput.value)).then(res => {
exportedData.addToTable(res.plans);
vm.exportStatus = "";
vm.performancePoints = [];
vm.exportedData = exportedData;
vm.updatedExportedData();
vm.didUpdate();
}, () => {
vm.isWorking = false;
}));
vm.autocompleteResults = {
sobjectName: "",
title: "Query Plan Tool:",
results: [{value: "Developer Console Query Plan Tool FAQ", title: "Developer Console Query Plan Tool FAQ", rank: 1, autocompleteType: "fieldName", dataType: "", link: "https://help.salesforce.com/s/articleView?id=000386864&type=1"},
{value: "Get Feedback on Query Performance", title: "Get Feedback on Query Performance", suffix: " ", rank: 1, autocompleteType: "fieldName", dataType: "", link: "https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_query_explain.htm"},
]
};
}
}

function RecordTable(vm) {
Expand Down Expand Up @@ -1017,6 +1043,7 @@ class App extends React.Component {
this.onToggleSavedOptions = this.onToggleSavedOptions.bind(this);
this.onExport = this.onExport.bind(this);
this.onCopyQuery = this.onCopyQuery.bind(this);
this.onQueryPlan = this.onQueryPlan.bind(this);
this.onCopyAsExcel = this.onCopyAsExcel.bind(this);
this.onCopyAsCsv = this.onCopyAsCsv.bind(this);
this.onCopyAsJson = this.onCopyAsJson.bind(this);
Expand Down Expand Up @@ -1129,6 +1156,11 @@ class App extends React.Component {
navigator.clipboard.writeText(url.toString());
model.didUpdate();
}
onQueryPlan(){
let {model} = this.props;
model.doQueryPlan();
model.didUpdate();
}
onCopyAsExcel() {
let {model} = this.props;
model.copyAsExcel();
Expand Down Expand Up @@ -1313,8 +1345,9 @@ class App extends React.Component {
h("div", {className: "flex-right"},
h("button", {tabIndex: 1, disabled: model.isWorking, onClick: this.onExport, title: "Ctrl+Enter / F5", className: "highlighted"}, "Run Export"),
h("button", {tabIndex: 2, onClick: this.onCopyQuery, title: "Copy query url", className: "copy-id"}, "Export Query"),
h("a", {tabIndex: 3, className: "button", hidden: !model.autocompleteResults.sobjectName, href: model.showDescribeUrl(), target: "_blank", title: "Show field info for the " + model.autocompleteResults.sobjectName + " object"}, model.autocompleteResults.sobjectName + " Field Info"),
h("button", {tabIndex: 4, href: "#", className: model.expandAutocomplete ? "toggle contract" : "toggle expand", onClick: this.onToggleExpand, title: "Show all suggestions or only the first line"},
h("button", {tabIndex: 3, onClick: this.onQueryPlan, title: "Run Query Plan"}, "Query Plan"),
h("a", {tabIndex: 4, className: "button", hidden: !model.autocompleteResults.sobjectName, href: model.showDescribeUrl(), target: "_blank", title: "Show field info for the " + model.autocompleteResults.sobjectName + " object"}, model.autocompleteResults.sobjectName + " Field Info"),
h("button", {tabIndex: 5, href: "#", className: model.expandAutocomplete ? "toggle contract" : "toggle expand", onClick: this.onToggleExpand, title: "Show all suggestions or only the first line"},
h("div", {className: "button-icon"}),
h("div", {className: "button-toggle-icon"})
)
Expand Down

0 comments on commit f1659a2

Please sign in to comment.