Search Metadata In Lightning Component

SessionIdPage:

<apex:page >
Start_Of_Session_Id{!$Api.Session_ID}End_Of_Session_Id
</apex:page>
view raw SessionIdPage hosted with ❤ by GitHub

UtilsClass:

global class UtilsClass {
global static String getSessionIdFromVFPage(PageReference visualforcePage){
String content = visualforcePage.getContent().toString();
Integer s = content.indexOf('Start_Of_Session_Id') + 'Start_Of_Session_Id'.length(),
e = content.indexOf('End_Of_Session_Id');
system.debug('Session ID: ' + content.substring(s, e));
return content.substring(s, e);
}
}
view raw UtilsClass hosted with ❤ by GitHub

searchMetadataCntrl:

/* Name: searchMetadataCntrl
* Description: This Apex class makes callout to Salesforce Tooling Api and get the metadata information about the objects.
* Created Date: 15/06/2018
* LastModifiedDate: 15/06/2018
* Created By: Arun Kumar
*/
public class searchMetadataCntrl {
@AuraEnabled
public static string GetMetaData(string type){
HttpRequest req = new HttpRequest();
String session_id;
if(!test.isRunningTest()){
session_id = UtilsClass.getSessionIdFromVFPage(Page.SessionIdPage);
}
system.debug('Session ID: ' +session_id);
req.setHeader('Authorization', 'Bearer ' + session_id);
string domainUrl=URL.getSalesforceBaseUrl().toExternalForm();
string endpoint='';
if(type=='ApexClass & ApexTrigger'){
endpoint='select+id,ApexClassOrTrigger.Name,NumLinesCovered,NumLinesUncovered+from+ApexCodeCoverageAggregate';
}
else if(type=='ValidationRule'){
endpoint='Select+id,ErrorDisplayField,createdDate,ValidationName+from+ValidationRule';
}
else if(type=='WorkflowRule'){
endpoint='Select+id,Name+from+WorkflowRule';
}
else if(type=='VisualforcePages'){
endpoint='Select+id,Name,Description,ControllerType,ApiVersion+from+ApexPage';
}else if(type=='Static Resource'){
endpoint='select+id,Name,ContentType+from+StaticResource';
}else if(type=='Email Template'){
endpoint='select+id,Name,UIType+from+EmailTemplate';
}else if(type=='Documents'){
endpoint='select+id,Name,Type,Url+from+Document';
}else if(type=='Visualforce Component'){
endpoint='select+id,name+from+ApexComponent';
}
req.setEndpoint(domainUrl+'/services/data/v42.0/tooling/query/?q='+endpoint);
req.setMethod('GET');
Http h = new Http();
try{
HttpResponse response = h.send(req);
system.debug(response.getBody());
return response.getBody();
}
catch(Exception e){
string exceptionString=e.getMessage();
system.debug('Exception: '+exceptionString);
return exceptionString;
}
}
}
view raw searchMetadataCntrl hosted with ❤ by GitHub

SearchMetadataCmp:

<aura:component controller="searchMetadataCntrl" implements="force:appHostable,flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId,forceCommunity:availableForAllPageTypes,force:lightningQuickAction" access="global" >
<aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
<aura:attribute name="Result" type="Object"/>
<aura:attribute name="metadataType" type="string[]"/>
<aura:attribute name="selectedMetadata" type="string"/>
<aura:attribute name="searchKeyword" type="string"/>
<aura:attribute name="unFilteredResult" type="object"/>
<aura:handler event="aura:waiting" action="{!c.showSpinner}"/>
<aura:handler event="aura:doneWaiting" action="{!c.hideSpinner}"/>
<aura:attribute name="Spinner" type="boolean" default="false"/>
<aura:attribute name="isApexType" type="boolean" default="false"/>
<aura:attribute name="searchPlaceHolder" type="string"/>
<aura:attribute name="TotalRecReturned" type="Integer"/>
<aura:attribute type="String" name="headerBgColor" />
<aura:attribute type="String" name="bodyBgColor" />
<aura:attribute name="headerTextColor" type="string"/>
<aura:attribute name="bodyTextColor" type="string"/>
<aura:attribute name="showSettings" type="boolean" default="false"/>
<div class="slds">
<div class="slds-page-header slds-theme_shade slds-theme_alert-texture " style="{! 'background-color:' + v.headerBgColor }">
<span style="{! 'color:' + v.headerTextColor }">Metadata Search</span>
<!--<a href="" onclick="{!c.toggleSettings}" id="settingIcon">
<lightning:icon iconName="utility:settings" alternativeText="setting" size="xx-small"/>
</a>-->
</div>
<!-- Starting Page-->
<div>
</div>
<!--End-->
<div class="bodyPart" style="{! 'background-color:' + v.bodyBgColor }">
<div style="{! 'color:' + v.bodyTextColor }">
<lightning:select name="selectItem" label="Select an item" onchange="{!c.OnSelectChange}">
<aura:iteration items="{!v.metadataType}" var="m">
<option value="{!m}" label="{!m}" />
</aura:iteration>
</lightning:select>
<lightning:input label="Search" placeholder="{!v.searchPlaceHolder}" value="{!v.searchKeyword}" onkeyup="{!c.SearchMethod}"></lightning:input>
<br/>
<aura:if isTrue="{!v.Spinner}">
<div aura:id="spinnerId" class="slds-spinner_container">
<div class="slds-spinner--brand slds-spinner slds-spinner--large slds-is-relative" role="alert">
<span class="slds-assistive-text">Loading</span>
<div class="slds-spinner__dot-a"></div>
<div class="slds-spinner__dot-b"></div>
</div>
</div>
</aura:if>
<aura:if isTrue="{!v.Result !=null}">
<div class="slds-table--header-fixed_container hdr_container" style=" ">
<div class="searchResult">
<table class="slds-table slds-table--bordered slds-max-medium-table_stacked-horizontal slds-table--header-fixed" aura:id="myTable">
<thead>
<tr class="slds-text-title_caps">
<th class="" scope="col">
<div class="slds-truncate slds-cell-fixed thdr" title="Name">Name</div>
</th>
<th class="">
<div class="slds-truncate slds-cell-fixed thdr" title="ID">ID</div>
</th>
<aura:if isTrue="{!v.isApexType}">
<th class="slds-cell-shrink" scope="col">
<div class="slds-truncate slds-cell-fixed thdr" title="Type">Type</div>
</th>
<th class="" scope="col">
<div class="slds-truncate slds-cell-fixed thdr" title="NumLines Covered">Lines Cov.</div>
</th>
<th class="" scope="col">
<div class="slds-truncate slds-cell-fixed thdr" title="NumLines Uncovered">Lines Uncov.</div>
</th>
</aura:if>
</tr>
</thead>
<tbody>
<aura:iteration items="{!v.Result}" var="r">
<tr class="slds-hint-parent trow" >
<td class="slds-cell-shrink" data-label="Select Row"><a aura:id="trow" href="" id="{!r.Id}" onclick="{!c.navigate}">{!r.Name}</a></td>
<td class="slds-cell-shrink" data-label="Select Row">{!r.Id}</td>
<td class="slds-cell-shrink" data-label="Select Row">{!r.Type}</td>
<td class="slds-cell-shrink" data-label="Select Row">{!r.NumLinesCovered}</td>
<td class="slds-cell-shrink" data-label="Select Row">{!r.NumLinesUncovered}</td>
</tr>
</aura:iteration>
</tbody>
</table>
</div>
</div>
<br/>
<p> Total {!v.TotalRecReturned} Records Returned.</p>
</aura:if>
</div>
</div>
</div>
</aura:component>
view raw SearchMetadataCmp hosted with ❤ by GitHub

SearchMetadataCmpController:

({
doInit : function(component, event, helper) {
var type=['ApexClass & ApexTrigger','ValidationRule','WorkflowRule','VisualforcePages','Static Resource','Email Template','Documents','Visualforce Component'];
component.set("v.metadataType",type);
component.set("v.selectedMetadata",'ApexClass & ApexTrigger');
helper.GetSearchedData(component,event,helper);
},
OnSelectChange:function(component,event,helper){
var selected=event.getSource().get("v.value");
console.log('selectted: ' +selected);
component.set("v.searchPlaceHolder",'Search '+selected);
component.set("v.selectedMetadata",selected);
if(selected=='--None--'){
component.set("v.Result",null);
}else{
helper.GetSearchedData(component,event,helper);
}
},
showSpinner: function(component, event, helper) {
component.set("v.Spinner", true);
},
hideSpinner : function(component,event,helper){
component.set("v.Spinner", false);
},
navigate:function(component,event,helper){
var idx = event.target.id;
var urlEvent = $A.get("e.force:navigateToURL");
urlEvent.setParams({
"url": "/"+idx,
"target": "_blank"
});
urlEvent.fire();
},
SearchMethod:function(component,event,helper){
var searchKeyWord=event.getSource().get("v.value");
if(searchKeyWord!=''){
helper.SearchItems(component,event,searchKeyWord);
}else{
component.set("v.Result",component.get("v.unFilteredResult"));
}
}
})

SearchMetadataCmpHelper:

({
GetSearchedData : function(component,event,helper) {
var action=component.get("c.GetMetaData");
var selectedType=component.get("v.selectedMetadata");
action.setParams({
"type": component.get("v.selectedMetadata")
});
action.setCallback(this,function(response){
var state=response.getState();
if(state=='SUCCESS'){
var result=JSON.parse(response.getReturnValue());
var returnedString = JSON.stringify(response.getReturnValue());
if(returnedString.includes("Unauthorized endpoint, please check Setup")){
//alert('Unauthorized end point');
var sMsg ='Unauthorized endpoint, please goto Setup->Security->Remote site settings ';
sMsg+='and add a remote site setting (endpoint = https://your_domain-dev-ed.my.salesforce.com/)';
var toastEvent = $A.get("e.force:showToast");
toastEvent.setParams({
mode: 'sticky',
message: sMsg,
type : 'warning'
});
toastEvent.fire();
}
else{
console.log('result size: ' +result.size);
console.log('result: '+ JSON.stringify(result));
component.set("v.TotalRecReturned",result.size);
var arrayData=new Array();
if(selectedType=='ApexClass & ApexTrigger'){
component.set("v.isApexType",true);
for(var i=0; i<result.records.length;i++){
if(result.records[i].ApexClassOrTrigger !=null){
var str=result.records[i].ApexClassOrTrigger.attributes.url;
var rest = str.substring(0, str.lastIndexOf("/") );
var Ids = str.substring(str.lastIndexOf("/") + 1, str.length);
var mType = rest.substring(rest.lastIndexOf("/") + 1, rest.length);
var obj={"Id":Ids,
"Name":result.records[i].ApexClassOrTrigger.Name.toUpperCase(),
"Type":mType,
"NumLinesCovered":result.records[i].NumLinesCovered,
"NumLinesUncovered":result.records[i].NumLinesUncovered};
arrayData.push(obj);
component.set("v.Result",arrayData);
component.set("v.unFilteredResult",arrayData);
}
}
}
if(selectedType=='ValidationRule'){
component.set("v.isApexType",false);
for(var i=0; i<result.records.length;i++){
var obj={"Id":result.records[i].Id,
"Name":result.records[i].ValidationName,
"TableEnumOrId":result.records[i].ValidationName}
arrayData.push(obj);
}
component.set("v.Result",arrayData);
component.set("v.unFilteredResult",arrayData);
}
if(selectedType=='WorkflowRule'){
component.set("v.isApexType",false);
for(var i=0; i<result.records.length;i++){
var obj={"Id":result.records[i].Id,
"Name":result.records[i].Name,
"TableEnumOrId":result.records[i].TableEnumOrId}
arrayData.push(obj);
}
component.set("v.Result",arrayData);
component.set("v.unFilteredResult",arrayData);
}
if(selectedType=='VisualforcePages'){
component.set("v.isApexType",false);
for(var i=0; i<result.records.length;i++){
var obj={"Id":result.records[i].Id,
"Name":result.records[i].Name,
}
arrayData.push(obj);
}
component.set("v.Result",arrayData);
component.set("v.unFilteredResult",arrayData);
}
if(selectedType=='Static Resource'){
component.set("v.isApexType",false);
for(var i=0; i<result.records.length;i++){
var obj={"Id":result.records[i].Id,
"Name":result.records[i].Name.toUpperCase(),
}
arrayData.push(obj);
}
component.set("v.Result",arrayData);
component.set("v.unFilteredResult",arrayData);
}
if(selectedType=='Email Template'){
component.set("v.isApexType",false);
for(var i=0; i<result.records.length;i++){
var obj={"Id":result.records[i].Id,
"Name":result.records[i].Name.toUpperCase(),
}
arrayData.push(obj);
}
component.set("v.Result",arrayData);
component.set("v.unFilteredResult",arrayData);
}
if(selectedType=='Documents'){
component.set("v.isApexType",false);
for(var i=0; i<result.records.length;i++){
var obj={"Id":result.records[i].Id,
"Name":result.records[i].Name.toUpperCase(),
}
arrayData.push(obj);
}
component.set("v.Result",arrayData);
component.set("v.unFilteredResult",arrayData);
}
if(selectedType=='Visualforce Component'){
component.set("v.isApexType",false);
for(var i=0; i<result.records.length;i++){
var obj={"Id":result.records[i].Id,
"Name":result.records[i].Name.toUpperCase(),
}
arrayData.push(obj);
}
component.set("v.Result",arrayData);
component.set("v.unFilteredResult",arrayData);
}
}
}
});
$A.enqueueAction(action);
},
SearchItems:function(component,event,searchKey){
console.log('Search Key: ' +searchKey);
var arrayForSearch=component.get("v.Result");
var NameArray=new Array();
for(var i=0; i<arrayForSearch.length; i++){
var obj={"Id":arrayForSearch[i].Id,
"Name":arrayForSearch[i].Name.toUpperCase(),
"Type":arrayForSearch[i].Type,
"NumLinesCovered":arrayForSearch[i].NumLinesCovered,
"NumLinesUncovered":arrayForSearch[i].NumLinesUncovered};
NameArray.push(obj);
}
var resultArray=new Array();
for (var j=0; j<NameArray.length; j++) {
if(searchKey!=''){
if (NameArray[j].Name.toString().match (searchKey.toUpperCase()))
{
console.log('Matching: ');
resultArray.push(NameArray[j]);
component.set("v.Result",resultArray);
}
}
else{
}
}
}
})

SearchMetadataCmpCSS:

.THIS .changeMe{
display: "";
color:red;
background-color:yellow;
}
.THIS .ul_list{
padding:10px;
}
.THIS .slds-page-header {
border-radius: 0px;
}
.THIS .RemoveMe{
display: "none";
}
.THIS .bodyPart{
background-color:white;
padding:10px 10px 10px 10px;
}
.THIS .trow{
height:35px;
}
.THIS .thdr{
padding-top: 1.5%;
padding-left: 1%;
}
.THIS .searchResult{
height:200px;
overflow-y: scroll;
border-radius:3px;
}
.THIS .hdr_container {
padding-top: 5%;
}
.THIS #settingIcon{
float:right;
}
.THIS #settingDrpdwn{
position: relative;
float: right;
right: 22%;
top: -2px;
}
.THIS .menu{
width: 194px;
padding: 10px;
}
.THIS .slds-page-header{
border-radius:none;
}
view raw SearchMetadataCmpCSS hosted with ❤ by GitHub

Find the code on github here.

Happy Coding… 🙂

Thanks

Arun Kumar