Pages

Friday, August 24, 2018

Way to get list of Scheduled reports and Jobs from Salesforce

This is very common ask from business admins or end users to get list of schedule reports or schedule jobs (batch jobs, scheduled jobs etc) from Salesforce.

There is no standard view provided by Salesforce to view list of scheduled reports or reports. Users have to navigate to Monitor section under SetUp.


This section display list of jobs either schedule or completed jobs but does not provide complete list of jobs.

By using apex, you get the list of schedule reports or jobs in csv format via email. 

Please find below the apex code for that:

public class SK_ScheduleJobUtility{
public static void findScheduledJobDetails(string jobType){
string jobTypeNumber=jobTypeMap.get(jobType);
if(jobTypeNumber!=null && jobTypeNumber!=''){
List<CronTrigger> cjList=new List<CronTrigger>();
Set<string> userIdSet = new Set<string>();
for(CronTrigger cj :[select id, CronJobDetailId,CronJobDetail.Name,CronJobDetail.JobType, ownerId from CronTrigger where CronJobDetail.JobType=:jobTypeNumber]){
cjList.add(cj);
userIdSet.add(cj.ownerId);
}
//fetch user information as we cannot access user details using child to parent query using dot notation
Map<string,User> userMap= new Map<string,User>();
for(User userRecord:[select id,username,email,IsActive from User where Id IN:userIdSet]){
userMap.put(userRecord.Id,userRecord);
}
string header = 'CronJobDetailId , CronJobDetail Name, CronJobDetail JobType,Submittedby Username,SubmittedBy Userid, User IsActive, User Email \n';
string finalstr = header ;
for(CronTrigger jb: cjList){
string recordString='';
if(userMap.get(jb.ownerId)!=null){
recordString = jb.CronJobDetailId+','+jb.CronJobDetail.Name+','+jobType + ','+userMap.get(jb.ownerId).username + ','+jb.ownerId + ','+userMap.get(jb.ownerId).IsActive + ',' +userMap.get(jb.ownerId).email+'\n';
}else{
recordString = jb.CronJobDetailId+','+jb.CronJobDetail.Name+','+jobType + ', ,'+jb.ownerId + ', ,\n';
}
finalstr = finalstr +recordString;
}
Messaging.EmailFileAttachment csvAttc = new Messaging.EmailFileAttachment();
blob csvBlob = Blob.valueOf(finalstr);
string csvname= jobType+ ':Schedule Job List'+system.now()+'.csv';
csvAttc.setFileName(csvname);
csvAttc.setBody(csvBlob);
Messaging.SingleEmailMessage email =new Messaging.SingleEmailMessage();
String[] toAddresses = new list<string> {UserInfo.getUserEmail()};
String subject =jobType+': Scheduled jobs report';
email.setSubject(subject);
email.setToAddresses( toAddresses );
email.setPlainTextBody(subject);
email.setFileAttachments(new Messaging.EmailFileAttachment[]{csvAttc});
Messaging.SendEmailResult [] r = Messaging.sendEmail(new Messaging.SingleEmailMessage[] {email});
}
}
public static Map<string,string> jobTypeMap = new Map<string, string>{
'Data Export' => '0',
'Weekly Export' => '1',
'Test' => '2',
'Dashboard Refresh' => '3',
'Reporting Snapshot' => '4',
'System' => '5',
'Scheduled Apex'=>'7',
'Report Run' => '8',
'Batch Job' => '9'
};
//run below script to get job list in email
//SK_ScheduleJobUtility.findScheduledJobDetails('Report Run');
//SK_ScheduleJobUtility.findScheduledJobDetails('Batch Job');
//SK_ScheduleJobUtility.findScheduledJobDetails('Scheduled Apex');
}

Now if you have to get list of scheduled reports in your org, then just execute below script in developer console:

  • For getting list of scheduled reports:         SK_ScheduleJobUtility.findScheduledJobDetails('Report Run');

  • For getting list of scheduled batch jobs:         SK_ScheduleJobUtility.findScheduledJobDetails('Batch Job');

  • For getting list of scheduled apex jobs:        SK_ScheduleJobUtility.findScheduledJobDetails('Scheduled Apex');


Important Use case

Sometime reports are scheduled by user which becomes inactive, then all system admin start getting email saying:

"The user account that runs the report (the running user) is inactive."

This utility will send email along with user details which will help in identifying all inactive user for whom reports are scheduled as running user.


Hope this will help!!

Looking forward for everyone's comments and feedback.

Monday, August 20, 2018

Creating Lightning Components Dynamically

Through this blog, I will share the approach to create lightning components dynamically by sharing an example in which we will display account summary information by creating summary component dynamically.

I have already posted one blog through which we can create ui:button dynamically and can destroy it. Please refer below URL if you want to refer that before continuing with this blog:

creating or destroying buttons dynamically

Below is snapshot of app which will display recent accounts on left side and on right side, it will display opportunities and case summary for account. You can click on "show Info" link before any account record and its related information will appear on right side dynamically.


Code to create dynamic component

You can use $A.createComponent method which accepts 3 parameters:
  1. ComponentName (String) :Type of component to create like "c:newCmp" etc.
  2. attributes : JSON specify component attributes values
  3. callback function: Once the component is created, this callback function will be invoked.This function can be used to append newly created component to target component. You can place the newly created component inside some div to display.
Below is sample code along with comments to explain steps:

 //find the div markup where you want to display dynamic component
//here "dynamicCmpSection" is aura:id of div
var targetCmp = component.find("dynamicCmpSection");
//create JSON specify all attributes needed for new component
var cmpAttributes = {
                "attribute1": "attribute Value",
"attribute1": "attribute Value"
            }
$A.createComponent(
"c:Demo_DynamicViewCmp", 
cmpAttributes,
function(tempbody) {
//Check if component is valid
if (component.isValid()) {
var targetCmp = component.find("dynamicCmpSection");
//first remove all content inside the div by making its body as null
targetCmp.set("v.body",[]);
var body = targetCmp.get("v.body");
//add newly created component in div
body.push(tempbody);
targetCmp.set("v.body", body);
console.log('***component loaded successfully');
}
}
);

Below is complete code related to snapshot shown in blog.

<aura:component controller="Demo_CreateDynamicCmpController">
<aura:attribute name="ltngAccList" type="List"/>
<aura:attribute name="menu" type="List" default="View,Edit"/>
<aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
<div class="slds-grid slds-wrap">
<div class="slds-col slds-size_1-of-1 slds-small-size_1-of-2 slds-medium-size_2-of-4">
<lightning:card footer="@sunil02kumar" title="Recient Accounts">
<aura:set attribute="actions">
<!-- Add action if required-->
</aura:set>
<p class="slds-p-horizontal_small">
<!--display all accounts-->
<table class="slds-table slds-table_fixed-layout slds-table_bordered slds-table_cell-buffer">
<thead>
<tr class="slds-text-title--caps">
<th scope="col">Actions</th>
<th scope="col">Name</th>
<th scope="col">Industry</th>
<th scope="col">Type</th>
</tr>
</thead>
<tbody>
<aura:iteration items="{!v.ltngAccList}" var="item">
<tr class="slds-hint-parent">
<td scope="row" class="slds-cell-wrap">
<a href=""> <ui:outputText value="show Info"
click="{!c.showSummerizedinfo}" class="{!item.Id}" /></a>
</td>
<td scope="row" class="slds-cell-wrap"> {!item.Name}</td>
<td scope="row" class="slds-cell-wrap"> {!item.Industry}</td>
<td scope="row" class="slds-cell-wrap"> {!item.Type}</td>
</tr>
</aura:iteration>
</tbody>
</table>
</p>
</lightning:card>
</div>
<div aura:id="dynamicCmpSection" class="slds-col slds-size_1-of-1 slds-small-size_1-of-2 slds-medium-size_2-of-4">
<!--section to display summerized information-->
</div>
</div>
</aura:component>
<aura:application extends="force:slds">
<c:Demo_CreateDynamicCmp/>
</aura:application>
public class Demo_CreateDynamicCmpController {
@AuraEnabled
public static List<Account> findAccounts(){
List<Account> AccList=new List<Account>();
AccList=[select id, Name, Industry, Type from Account order by Lastmodifieddate DESC Limit 10];
return AccList;
}
@AuraEnabled
public static List<summerizedInfowrapper> findSummerizedInfo(string accid){
List<summerizedInfowrapper> returList= new List<summerizedInfowrapper>();
//summerize opportunity data
summerizedInfowrapper opportunitywrapper = new summerizedInfowrapper();
List<string> columns= new List<string>{'Stage','Amount'};
opportunitywrapper.colLabels=columns;
opportunitywrapper.title='Opportunities Grouped by Stage';
string queryStringOppy ='SELECT StageName, SUM(Amount) FROM Opportunity where AccountId=:accid ';
queryStringOppy = queryStringOppy + ' GROUP BY StageName';
AggregateResult[] groupedResults = database.query(queryStringOppy);
List<dataWrapper> dataList= new List<dataWrapper>();
integer count =0;
for (AggregateResult ar : groupedResults)
{
dataWrapper data1=new dataWrapper();
string categorylabel = string.valueof(ar.get('StageName'));
data1.columnValue = categorylabel;
decimal amount = (decimal)ar.get('expr0');
data1.dataValue = string.valueof(amount);
dataList.add(data1);
}
opportunitywrapper.summerizedData=dataList;
returList.add(opportunitywrapper);
system.debug('**** opportunity returList:'+returList);
//summerized case data
summerizedInfowrapper caseWrapper = new summerizedInfowrapper();
List<string> caseColumns= new List<string>{'Case status','No.of Cases'};
caseWrapper.colLabels=caseColumns;
caseWrapper.title='Cases Grouped by Status';
string CaseQueryString = 'SELECT COUNT(Id), Status FROM Case where accountId = :accid';
CaseQueryString = CaseQueryString + ' GROUP BY Status';
AggregateResult[] groupedResults1 = database.query(CaseQueryString);
List<dataWrapper> caseDataList= new List<dataWrapper>();
for (AggregateResult ar : groupedResults1)
{
dataWrapper caseData=new dataWrapper();
caseData.columnValue = string.valueof(ar.get('Status'));
caseData.datavalue = string.valueof(ar.get('expr0'));
caseDataList.add(caseData);
}
caseWrapper.summerizedData=caseDataList;
returList.add(caseWrapper);
system.debug('****case returList:'+returList);
return returList;
}
public class summerizedInfowrapper{
@AuraEnabled
public string title;
@AuraEnabled
public list<dataWrapper> summerizedData;
@AuraEnabled
public List<string> colLabels;
public summerizedInfowrapper(){
summerizedData= new list<dataWrapper>();
colLabels=new List<string>();
}
}
public class dataWrapper{
@AuraEnabled
public string columnValue;
@AuraEnabled
public string dataValue;
}
}
({
doInit : function(component, event, helper) {
console.log('doinit gets called in Demo_CreateDynamicCmp');
var params = {};
helper.callToServer(
component,
"c.findAccounts",
function(response)
{
console.log('apex response :'+JSON.stringify(response));
component.set("v.ltngAccList",response);
//code to create dynamic component using helper function
var selectedId = response[0].id;
var cmpAttributes = {
ltngAccId: selectedId
}
//creating component dynamically using helper function
helper.createDynamicCmp(
component,
"dynamicCmpSection",
"c:Demo_DynamicViewCmp",
cmpAttributes
);
},
params
);
},
showSummerizedinfo : function(component, event, helper) {
var selectedId = event.getSource().get('v.class');
if (selectedId) {
var cmpAttributes = {
ltngAccId: selectedId
}
var targetCmp = component.find("dynamicCmpSection");
console.log('****calling createDynamicCmp in Demo_CreateDynamicCmp helper');
$A.createComponent(
"c:Demo_DynamicViewCmp",
cmpAttributes,
function(tempbody) {
//Add the dynamic cmp to div
if (component.isValid()) {
var targetCmp = component.find("dynamicCmpSection");
targetCmp.set("v.body",[]);
var body = targetCmp.get("v.body");
body.push(tempbody);
targetCmp.set("v.body", body);
console.log('***component loaded successfully');
}
}
);
}
}
})
({
callToServer : function(component, method, callback, params) {
console.log('Calling helper callToServer function to get data');
var action = component.get(method);
if(params){
action.setParams(params);
}
console.log('****param to controller:'+JSON.stringify(params));
action.setCallback(this, function(response) {
var state = response.getState();
if (state === "SUCCESS") {
callback.call(this,response.getReturnValue());
}else if(state === "ERROR"){
var errors = response.getError();
console.error(errors);
alert('Problem with connection. Please try after sometime or contact your system administrator.');
}
});
$A.enqueueAction(action);
},
createDynamicCmp : function(component, divSection, componentName, params){
var targetCmp = component.find(divSection);
console.log('****calling createDynamicCmp in Demo_CreateDynamicCmp helper');
$A.createComponent(
componentName,
params,
function(dynSecDiv) {
//Add the dynamic cmp to div
if (component.isValid()) {
var targetCmp = component.find(divSection);
targetCmp.set("v.body",[]);
var body = targetCmp.get("v.body");
body.push(dynSecDiv);
targetCmp.set("v.body", body);
console.log('****'+componentName+' component loaded successfully');
}
}
);
}
})
<aura:component controller="Demo_CreateDynamicCmpController">
<aura:attribute name="ltngSummerizedInfo" type="Object"/>
<aura:attribute name="ltngAccId" type="string"/>
<aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
<div class="slds-text-heading_large">Displaying Account related information for Opportunity and Cases by creating below component dynamically</div>
<aura:iteration items="{!v.ltngSummerizedInfo}" var="item">
<div class="slds-card slds-grid slds-wrap" style="padding:20px;">
<div class="slds-col slds-size_1-of-1" >
{!item.title}
<table class="slds-table slds-table_bordered slds-table_cell-buffer slds-table_striped">
<thead>
<tr class="slds-text-title_caps">
<aura:iteration items="{!item.colLabels}" var="labels">
<th scope="col">
<div class="slds-truncate" title="{!labels}">
{!labels}
</div>
</th>
</aura:iteration>
</tr>
</thead>
<tbody>
<aura:iteration items="{!item.summerizedData}" var="rec">
<tr>
<th scope="row" >
<div class="slds-truncate" title="{!rec.columnValue}" >
{!rec.columnValue}
</div>
</th>
<td data-label="{!rec.dataValue}">
{!rec.dataValue}
</td>
</tr>
</aura:iteration>
</tbody>
</table>
</div>
</div>
</aura:iteration>
</aura:component>
({
doInit : function(component, event, helper) {
console.log('***ltngAccId:'+ component.get("v.ltngAccId"));
var params ={"accid":component.get("v.ltngAccId")};
helper.callToServer(
component,
"c.findSummerizedInfo",
function(response)
{
console.log('apex response :'+JSON.stringify(response));
component.set("v.ltngSummerizedInfo",response);
},
params
);
}
})
({
callToServer : function(component, method, callback, params) {
console.log('Calling helper callToServer function to get data');
var action = component.get(method);
if(params){
action.setParams(params);
}
console.log('****param to controller:'+JSON.stringify(params));
action.setCallback(this, function(response) {
var state = response.getState();
if (state === "SUCCESS") {
callback.call(this,response.getReturnValue());
}else if(state === "ERROR"){
var errors = response.getError();
console.error(errors);
alert('Problem with connection. Please try after sometime or contact your system administrator.');
}
});
$A.enqueueAction(action);
}
})
Hope this Helps!!

Sunday, August 19, 2018

Application Events : Way to Communicate Between Different Lightning Components

Application events are used to communicate between different lightning components. These are different from component events as these are not dependent on containment hierarchy.

Application events behaves similar to standard events. There is no need to register for application event by any component. Any component can fire application event where as in component events, components have to register first in order to fire that event.

If you want to learn more about component events, then refer below URL:
Component Events: Way to Communicate Between Components in Containment Hierarchy

Through this blog, I will explain different aspect of using application events:

I have created a lightning app which contains 2 separate components. User is entering some information and sending it to second component by clicking on button.



Below are different steps you need to follow in order to implement application events:

  • Create lightning event "SK_MessagerEvent" of type Application
       <aura:event type="APPLICATION" description="Event template" >    
            <aura:attribute name="msg" type="String" access="GLOBAL"/>                           
       </aura:event>

  • Use below syntax to fire event from lightning component:
        var appEvent = $A.get("e.c:SK_MessengerEvent");
        appEvent.setParams({"msg":component.get("v.ltngUserInput")}); 
        appEvent.fire();

  • Create handler in component which want to consume or catch the information passed using application events.
       <aura:handler action="{!c.handleNotification}" event="c:SK_MessengerEvent" />
  • Create function in controller.js which will get invoked whenever this application event will be fired.
        var sentMessage= event.getParam("msg"); 
        console.log('******sentMessage by app event:'+sentMessage);
        component.set("v.recMsg",sentMessage);

Below is complete code for your reference related to snapshot shown above:

<aura:application extends="force:slds">
<c:SK_FirstComponent/>
<c:SK_SecondComponent/>
</aura:application>
<aura:component >
<aura:attribute type="String" name="ltngUserInput"/>
<div style="background-color:#E6E6FA;border-style: solid;margin:5%;padding:2%;">
<b>First Component</b>
<br/>
<lightning:input value="{!v.ltngUserInput}" label="Enter some value"
required="true" placeholder="Enter some value"/>
<lightning:button variant="brand" label="Send entered value to next component" onclick="{!c.handleClick }" />
</div>
</aura:component>
({
handleClick:function(component, event, helper) {
console.log("handle click get called.");
var appEvent = $A.get("e.c:SK_MessengerEvent");
appEvent.setParams({"msg":component.get("v.ltngUserInput")});
appEvent.fire();
}
})
<aura:event type="APPLICATION" description="Event template" >
<aura:attribute name="msg" type="String" access="GLOBAL"/>
</aura:event>
<aura:component >
<aura:attribute name="recMsg" type="String" />
<aura:handler action="{!c.handleNotification}" event="c:SK_MessengerEvent" />
<div style="background-color:grey;border-style: solid;margin:5%;padding:2%;">
<b>Second component</b>
<br />
Passed value from First componet--<b>{!v.recMsg}</b>
</div>
</aura:component>
({
handleNotification:function(component, event, helper) {
console.log("Event Handled");
var sentMessage= event.getParam("msg");
console.log('******sentMessage by app event:'+sentMessage);
component.set("v.recMsg",sentMessage);
}
})

Hope this help!!


More Blogs>>: 
COMPONENT EVENTS: Communicate Between Components in Containment Hierarchy    
INHERITANCE IN LIGHTNING    
FIRING EVENT FROM LIGHTNING COMPONENT AND PASSING IT TO VF PAGE    
CHANGES TO LIGHTNING DATA SERVICE IN SUMMER'17    
LIGHTNING DATA SERVICES    
PASSING LIGHTNING COMPONENT ATTRIBUTE VALUE FROM VF PAGE    
FIRE LIGHTNING EVENTS FROM VF PAGE    
DYNAMICALLY CREATING AND DESTROYING LIGHTNING COMPONENTS    
RAISING AND HANDLING CUSTOM EVENTS IN SALESFORCE LIGHTNING    
WHY TO USE DESIGN RESOURCE AND HOW TO ADD DYNAMIC OPTION TO DATASOURCE    
PASSING LIGHTNING COMPONENT ATTRIBUTE VALUE FROM VF PAGE    
PASSING INNER WRAPPER CLASS TO LIGHTNING COMPONENT    
LIGHTNING COMPONENT FOR RECORDTYPE SELECTION FOR ANY SOBJECT    
CUSTOM COMPONENT TO SHOW/HIDE SPINNER IMAGE    

Monday, August 13, 2018

Component Events: Way to Communicate Between Components in Containment Hierarchy

Components events can be used to pass information between lightning components which are in containment hierarchy. Component events can be handled by component which is firing it or by component container.

If you want to learn more about Application events, then refer below URL:
Application Events : Way to Communicate Between Different Lightning Components

Through this blog, I am going to explain how to utilize component events with sample code.


I have created a sample lightning app which contains "c:SK_MasterCmp".  "c:SK_MasterCmp" contains another component "c:SK_ChildCmp"

Below are steps which you need to follow while utilizing component events:

  • Create Lightning event of type "COMPONENT".
        <aura:event type="COMPONENT" description="Event template" >
              <aura:attribute name="passedValue" type="string"/>
        </aura:event>

  • Register the component event which is going to fire component event.
        <aura:registerEvent name="changeValueEvent" type="c:SK_LightningEventChannel"/>
  • Use below syntax to fire event and pass information from "c:ChildCmp".
        var newEvent = component.getEvent("changeValueEvent"); 
        newEvent.setParams({"passedValue":"twitter handle-@sunil02kumar"});                               
        newEvent.fire();
  • Create handler in component which needs to handle the events means want to consume that information. Note the name of handler should be same as that while registering event:
       <!--name should be equal to name while register event-->
       <aura:handler name="changeValueEvent" event="c:SK_LightningEventChannel" 
                  action="{!c.handleNotification}"/>
  • Create function in controller.js which will be called when "c:MasterCmp" will handle the the event.
       handleNotification : function(component, event, helper){
           console.log('****handling event');
           var sentMessage= event.getParam("passedValue");
           component.set("v.mastervalue", sentMessage);
      }


Below is complete code for reference:

<aura:component >
<aura:attribute name="childValue" type="string" default="Child"/>
<aura:registerEvent name="changeValueEvent" type="c:SK_LightningEventChannel"/>
<lightning:button name="ddff" label="Fire event" onclick="{!c.fireEvent}"/>
</aura:component>
view raw SK_ChildCmp.cmp hosted with ❤ by GitHub
({
fireEvent : function(component, event, helper) {
console.log('********firing event');
var newEvent = component.getEvent("changeValueEvent");
newEvent.setParams({"passedValue":"twitter handle-@sunil02kumar"});
newEvent.fire();
}
})
<aura:application extends="force:slds">
<c:SK_MasterCmp/>
</aura:application>
<aura:event type="COMPONENT" description="Event template" >
<aura:attribute name="passedValue" type="string"/>
</aura:event>
<aura:component >
<!--name should be equal to name while register event-->
<aura:handler name="changeValueEvent" event="c:SK_LightningEventChannel"
action="{!c.handleNotification}"/>
<aura:attribute name="mastervalue" type="string" default=""/>
<div style="background-color:#E6E6FA;border-style: solid;height:200px;margin:5%;padding:2%;">
<p>This is master Component- container for c:SK_ChildCmp</p>
<br/>
Display value passed from child component using component events---<b>{!v.mastervalue}</b>
<br/>
<div style="background-color:grey;border-style: solid;padding:2%;">
<p>This is child component present inside c:MasterCmp</p>
<c:SK_ChildCmp/>
</div>
</div>
</aura:component>
({
handleNotification : function(component, event, helper){
console.log('****handling event');
var sentMessage= event.getParam("passedValue");
component.set("v.mastervalue", sentMessage);
}
})


Hope this help!!

Thursday, August 9, 2018

How to get User Session Id in Future Method or Batch Class

Sometime it is required to perform Callout in future method and Batch apex class. If we use UserInfo.getsessionId() method inside the future method or batch apex, then it will return null.

Below are different option to get sessionId:

Option 1:

We can either pass sessionId as parameter to future method or to batch apex class.

Option 2:

Create a VF page and use {!$Api.Session_ID} in body.  In apex, get VF page content which will contain session Id.

Below is code snippet explaining the Option 2 by using future method. Same can be used in Batch Apex.
<apex:page showHeader="true" sidebar="true">
SessionIDStart{!$Api.Session_ID}SessionIDEnd
StartVFPageBaseURL{!LEFT($CurrentPage.URL,FIND('/',$CurrentPage.URL,9))}EndVFPageBaseURL
</apex:page>
//Author- sunil Kumar
//Twitter handle-@sunil02kumar
//purpose- finding user sessionid in asynchrounous apex call
public class SessionIdUtility{
@future(callout=true)
public static void checkIfUserSessionIdExistInFutureCall(){
HttpRequest req = new HttpRequest();
system.debug('********session Id using UserInfo.getSessionID():'+UserInfo.getSessionID());
string sessionId=findSessionIdUsingVFPage(Page.APICallUtilityVF);
system.debug('********session Id by generating from VF:'+sessionId);
}
public static String findSessionIdUsingVFPage(PageReference visualforcePage){
String content = visualforcePage.getContent().toString();
Integer startIndex = content.indexOf('SessionIDStart') + 'SessionIDStart'.length(),
endIndex = content.indexOf('SessionIDEnd');
return content.substring(startIndex, endIndex);
}
}

Run below code snippet in execute anonymous to check the output in debug logs:

SessionIdUtility.checkIfUserSessionIdExistInFutureCall();



Hope this help!!!

Remote Site Settings : Way to Create/Update Using Apex (Metadata API)

We can create or update Remote Site Settings in Apex using Metadata API.

You can either download Metadata API WSDL from Salesforce and generate apex class by clicking on Generate from WSDL button.


Or you can download the MetadataService class from below link:

MetadataService.cls

Below is sample to code to create remote site settings.

//Author- sunil Kumar
//Twitter handle-@sunil02kumar
//purpose- create remote site setting so that we can call rest api created in same org
public class MetadataAPIUtility{
public static boolean createRemoteSiteSettings(){
boolean isSucessfull=false;
Map<string,string> RemoteSiteMap = new Map<string,string>();
//specify the remote site name and url in RemoteSiteMap
RemoteSiteMap.put('FOR_REST_API_CALL',URL.getSalesforceBaseUrl().toExternalForm());
//String PageURL1 = URL.getCurrentRequestUrl().toExternalForm();
//RemoteSiteMap.put('FOR_REST_API_CALL',URL.getCurrentRequestUrl().toExternalForm());
List<MetadataService.Metadata> allMetadataList = new List<MetadataService.Metadata>();
for(string ss: RemoteSiteMap.keyset()){
MetadataService.RemoteSiteSetting remoteSiteDetails = new MetadataService.RemoteSiteSetting();
remoteSiteDetails.fullName = ss;
remoteSiteDetails.description = ss+'-created through apex';
remoteSiteDetails.disableProtocolSecurity = false;
remoteSiteDetails.isActive = true;
remoteSiteDetails.url=RemoteSiteMap.get(ss);
allMetadataList.add(remoteSiteDetails);
}
if(allMetadataList.size()>0){
system.debug('****RemoteSiteSettings allMetadataList:'+allMetadataList);
MetadataService.MetadataPort service = createService();
List<MetadataService.UpsertResult> results = service.upsertMetadata(allMetadataList);
for(MetadataService.UpsertResult result: results){
if(result.success){
system.debug('added remote site setting success='+result);
isSucessfull=true;
}else{
system.debug('****error while adding remote site settings:'+result.errors[0].message);
}
}
}
return isSucessfull;
}
private static MetadataService.MetadataPort createService(){
MetadataService.MetadataPort service = new MetadataService.MetadataPort();
service.SessionHeader = new MetadataService.SessionHeader_element();
service.SessionHeader.sessionId = UserInfo.getSessionId();
return service;
}
}

Execute this static method from execute anonymous and check if records created or not.

 MetadataAPIUtility.createRemoteSiteSettings();

Below is snapshot of remote site settings created


Note:
  • I have used upsertMetadata method. If you want to only insert new remote site setting, then use insertMetadata method
  • fullname property is considered in order to decide whether remote site setting needs to be created or updated.
Hope this will help!!!


Create Custom Metadata Types Using Metadata API
Create Update Custom Label by Using Metadata API