Skip to content

Commit

Permalink
we now tell if a field is used for assignment or reading;new video up…
Browse files Browse the repository at this point in the history
…loaded to readme and login page (#36)
  • Loading branch information
pgonzaleznetwork authored Oct 1, 2020
1 parent bcddb3d commit 5f0d87e
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 2 deletions.
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,15 @@ No **development knowledge** required :x:

Just [log in](https://sfdc-happy-soup.herokuapp.com/) and start sipping the soup! :stew: :clap: :white_check_mark:

[Watch full demo](https://www.youtube.com/watch?v=2asljhebqlY&t=6s)

<p align="center">
<img src="./github-images/happysoupmini.gif" >
</p>

[Watch full demo](https://www.youtube.com/watch?v=NH4LGbdYaaE)




## Contents

Expand Down Expand Up @@ -191,6 +194,8 @@ Salesforce Happy Soup is built on top of the `MetadataComponentDependency` tooli

* Lookup filters are returned with cryptic names depending on whether they belong to a custom object or a standard one.

* The app will tell you if a field is used in an apex class in read or write mode. For example, if a field is used in an assignment expression, then you know the class is assigning values to that field. The app will show you this with a visual indicator; something that the raw API cannot do.

As said above, Salesforce Happy Soup has **fixed all** this issues so that you can focus on learning about your dependencies rather than fighting the API! :facepunch:

[Back to top](#salesforce-happy-soup)
Expand Down
20 changes: 20 additions & 0 deletions public/css/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -455,4 +455,24 @@ select {

.metadata-info .prop{
font-weight: bold;
}


.class-info{
border: 1px solid white;
border-radius: 50px;
padding-left: 5px;
padding-right: 5px;
margin-left: 5px;
padding-top: 0px;
padding-bottom: 1px;
font-size: 11px;
}

.write{
background-color: #d63031;
}

.read{
background-color: #49b677;
}
2 changes: 1 addition & 1 deletion public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ <h1>Happy Soup</h1>
</div>

<div class="video">
<iframe width="160" height="115" src="https://www.youtube.com/embed/NH4LGbdYaaE"
<iframe width="160" height="115" src="https://www.youtube.com/embed/2asljhebqlY"
frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen></iframe>
</div>
Expand Down
11 changes: 11 additions & 0 deletions public/js/tree.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ function createTreeNodes(refs,parentNode){
if(member.namespace){
memberNodeName.appendChild(createWarningIcon());
}

if(member.fieldMode){
memberNodeName.appendChild(createPill(member.fieldMode));
}

memberNames.appendChild(memberNodeName);
metadataTypeNode.appendChild(memberNames);
Expand All @@ -102,6 +106,13 @@ function createTreeNodes(refs,parentNode){
return warningIcon;
}

function createPill(text){
let pill = document.createElement('span');
pill.classList.add(...['class-info',text]);
pill.innerText = text;
return pill;
}

export const treeApi = {
createDependencyTree,createUsageTree
};
60 changes: 60 additions & 0 deletions sfdc_apis/usage.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ function usageApi(connection,entryPoint,cache){
let customFields = [];
let layouts = [];
let lookupFilters = [];
let apexClasses = [];
let otherMetadata = [];

metadataArray.forEach(metadata => {
Expand All @@ -84,6 +85,9 @@ function usageApi(connection,entryPoint,cache){
else if(type == 'LOOKUPFILTER'){
lookupFilters.push(metadata);
}
else if(type == 'APEXCLASS' && entryPoint.type == 'CustomField'){
apexClasses.push(metadata);
}
else{
otherMetadata.push(metadata);
}
Expand All @@ -101,16 +105,72 @@ function usageApi(connection,entryPoint,cache){
if(lookupFilters.length){
lookupFilters = await getLookupFilterDetails(lookupFilters);
}
if(apexClasses.length){
apexClasses = await getFieldUsageMode(apexClasses);
}

otherMetadata.push(...customFields);
otherMetadata.push(...validationRules);
otherMetadata.push(...layouts);
otherMetadata.push(...lookupFilters);
otherMetadata.push(...apexClasses);

return otherMetadata;

}

/**
* We know that the field is referenced in this class, but is it
* used for reading or assignment? We determine that here by checking
* if the field is used in an assignment expression in the body of the class
*/
async function getFieldUsageMode(apexClasses){

//i.e field_name__c without the object prefix
let refCustomField = entryPoint.name.split('.')[1];

/**
* This matches on custom_field__c = but it does NOT match
* on custom_field__c == because the latter is a boolean exp
* and we are searching for assignment expressions
* gi means global and case insensitive search
*/
let assignmentExp = new RegExp(`${refCustomField}=(?!=)`,'gi');

let ids = apexClasses.map(ac => ac.id);
ids = utils.filterableId(ids);

let query = `SELECT Id,Name,Body FROM ApexClass WHERE Id IN ('${ids}')`;
let soqlQuery = {query,filterById:true};

let results = await toolingApi.query(soqlQuery);

let classBodyById = new Map();

results.records.forEach(rec => {
classBodyById.set(rec.Id,rec.Body);
});


apexClasses.forEach(ac => {

//by default we assume that the mode is read only
ac.fieldMode = 'read';

let body = classBodyById.get(ac.id);
if(body){
//remove all white space/new lines
body = body.replace(/\s/g,'');

if(body.match(assignmentExp)){
ac.fieldMode = 'write';
}
}
});

return apexClasses;
}

async function getLookupFilterDetails(lookupFilters){

let metadataRecordToEntityMap = new Map();
Expand Down

0 comments on commit 5f0d87e

Please sign in to comment.