Wednesday, 7 October 2015

Dynamic CustomField ID in URL hacking through Tooling API (Getting custom field id from Tooling API dynamically)

As we know that when want to autopopulate a custom field on standard insertion page from a custom button we do url hacking with custom field id. But custom field id is different from one org to other org. So when we deploy the custom button then again we have to replace the custom field id from the destination org. That means we have to change custom field ids whenever we deploy custom button from one org to another org manually.
If we do the same thing dynamically we don't need to worry about replace of custom field id after deployment.

For getting custom field id dynamically from a specificic custom field on an object   we can use "Tooling  API". We have an object "CustomField" to get the custom  field information from "Tooling API".We can get 18 characters id of custom field. But for populating customfield salesforce expects only 15 characters so after getting the id of custom field from tooling api we have to make it from 18 characters to 15 characters.

Tooling Api supports Rest API. So we can get the custom field id by using http callouts as shown below.

HttpRequest req = new HttpRequest();
req.setHeader('Authorization','Bearer ' + UserInfo.getSessionID());
 req.setHeader('Content-Type','application/json');
 req.setEndpoint(toolingapiurl);
  req.setMethod('GET');  
  // Send the request, and return a response
  HttpResponse res = h.send(req);     

For example i have "languages__c" custom field on contact object. I want to populate it with a value "English" from custom button "Create Contact" on Account record detail page as shown below.

Account Detail Page










Custom button code as shown below:

{!REQUIRESCRIPT("/soap/ajax/29.0/connection.js")}
{!REQUIRESCRIPT("/soap/ajax/29.0/apex.js")}
var result = sforce.apex.execute("RetrieveCustomFieldIdCls","getCustomFieldID",{customFieldName:"Languages__c",objectName:"Contact"});
window.open('/003/e?retURL=%2F{!Account.Id}&accid={!Account.Id}&'+result+'=English','_self');

in the above code "result" variable gets the custom field id from a method "getCustomFieldId" referenced in the "sforce.apex.execute" method.
here we are passing two parameters "Custom Field Name","Object Name".
I am passing " Languages__c"," Contact".When we click on custom button "Create Contact" it retrieves the "Languages__c" custom field id and then preparing url in the next line as shown below.

window.open('/003/e?retURL=%2F{!Account.Id}&accid={!Account.Id}&'+result+'=English','_self');
Here "result" variable contains custom field id.
Now we don't need to replaces the custom field id after deployment.Becase we are dynamically getting the customfield id from Tooling API.

Class  for custom button to retrieve custom field Id:

global class RetrieveCustomFieldIdCls {
    webservice static string getCustomFieldID(String customFieldName,String objectName){
        String customFieldId='';
        String customField=customFieldName.removeEndIgnoreCase('__c');     
        Http h = new Http();
       
         // Instantiate a new HTTP request, specify the method (GET) as well as the endpoint
        HttpRequest req = new HttpRequest();
        req.setHeader('Authorization','Bearer ' + UserInfo.getSessionID());
        req.setHeader('Content-Type','application/json');
        String fullFileURL = URL.getSalesforceBaseUrl().toExternalForm();
        String url1=fullFileURL+'/services/data/v28.0/tooling/query/?q=Select+id,+DeveloperName+from+customField+Where+DeveloperName=\'' + customField + '\'+and+TableEnumOrId=\''+objectName+'\'';
        req.setEndpoint(url1);
        req.setMethod('GET');  
        // Send the request, and return a response
        HttpResponse res = h.send(req);   
        RetrieveFieldParsercls retrieveObj=(RetrieveFieldParsercls) System.JSON.deserialize(res.getBody(), RetrieveFieldParsercls.class);
        System.debug('##:'+retrieveObj);
        for(RetrieveFieldParsercls.retrieveFieldDetails retrieveFieldDetailObj:retrieveObj.records){
                customFieldId=String.valueOf(retrieveFieldDetailObj.Id).substring(0,15);
        }  
        return customFieldId;  
    }
}

JSON parser class:
public class RetrieveFieldParsercls{
public Integer size; //1
public Integer totalSize; //1
public boolean done;
public cls_queryLocator queryLocator;
public String entityTypeName; //CustomFieldDefinition
public retrieveFieldDetails[] records;
public class cls_queryLocator {
}
public class retrieveFieldDetails {
public cls_attributes attributes;
public String Id; //00N280000025vdlEAA
public String DeveloperName; //Active
}
public class cls_attributes {
public String type; //CustomField
public String url; ///services/data/v28.0/tooling/sobjects/CustomField/00N280000025vdlEAA
}

}

Note:-we are doing http callout so we have to set endpoint url in Remote Site Settings accordingly.

Contact Screen when we click on "Create Contact" button on account record detail page









Tuesday, 28 July 2015

Test class for Webservice Callout

Webservice class:
-----------------
globalclass RecordCreationservicecls {
       webservicestatic String createRecord(String jsonString){
              //Process to create a record
              return'';
       }
}



Generated class for the above web service class “RecordCreationservicecls”

//Generated by wsdl2apex
publicclass GeneratedRecordCreationService {
publicclass LogInfo {
public String category;
public String level;
private String[] category_type_info = new String[]{'category','http://soap.sforce.com/schemas/class/RecordCreationservicecls',null,'1','1','false'};
private String[] level_type_info = new String[]{'level','http://soap.sforce.com/schemas/class/RecordCreationservicecls',null,'1','1','false'};
private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/schemas/class/RecordCreationservicecls','true','false'};
private String[] field_order_type_info = new String[]{'category','level'};
    }
publicclass AllowFieldTruncationHeader_element {
public Boolean allowFieldTruncation;
private String[] allowFieldTruncation_type_info = new String[]{'allowFieldTruncation','http://soap.sforce.com/schemas/class/RecordCreationservicecls',null,'1','1','false'};
private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/schemas/class/RecordCreationservicecls','true','false'};
private String[] field_order_type_info = new String[]{'allowFieldTruncation'};
    }
publicclass DebuggingHeader_element {
public GeneratedRecordCreationService.LogInfo[] categories;
public String debugLevel;
private String[] categories_type_info = new String[]{'categories','http://soap.sforce.com/schemas/class/RecordCreationservicecls',null,'0','-1','false'};
private String[] debugLevel_type_info = new String[]{'debugLevel','http://soap.sforce.com/schemas/class/RecordCreationservicecls',null,'1','1','false'};
private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/schemas/class/RecordCreationservicecls','true','false'};
private String[] field_order_type_info = new String[]{'categories','debugLevel'};
    }
publicclass CallOptions_element {
public String client;
private String[] client_type_info = new String[]{'client','http://soap.sforce.com/schemas/class/RecordCreationservicecls',null,'1','1','false'};
private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/schemas/class/RecordCreationservicecls','true','false'};
private String[] field_order_type_info = new String[]{'client'};
    }
publicclass location {
public Double latitude;
public Double longitude;
private String[] latitude_type_info = new String[]{'latitude','http://soap.sforce.com/schemas/class/RecordCreationservicecls',null,'1','1','false'};
private String[] longitude_type_info = new String[]{'longitude','http://soap.sforce.com/schemas/class/RecordCreationservicecls',null,'1','1','false'};
private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/schemas/class/RecordCreationservicecls','true','false'};
private String[] field_order_type_info = new String[]{'latitude','longitude'};
    }
publicclass address {
public String city;
public String country;
public String countryCode;
public String postalCode;
public String state;
public String stateCode;
public String street;
private String[] city_type_info = new String[]{'city','http://soap.sforce.com/schemas/class/RecordCreationservicecls',null,'1','1','false'};
private String[] country_type_info = new String[]{'country','http://soap.sforce.com/schemas/class/RecordCreationservicecls',null,'1','1','false'};
private String[] countryCode_type_info = new String[]{'countryCode','http://soap.sforce.com/schemas/class/RecordCreationservicecls',null,'1','1','false'};
private String[] postalCode_type_info = new String[]{'postalCode','http://soap.sforce.com/schemas/class/RecordCreationservicecls',null,'1','1','false'};
private String[] state_type_info = new String[]{'state','http://soap.sforce.com/schemas/class/RecordCreationservicecls',null,'1','1','false'};
private String[] stateCode_type_info = new String[]{'stateCode','http://soap.sforce.com/schemas/class/RecordCreationservicecls',null,'1','1','false'};
private String[] street_type_info = new String[]{'street','http://soap.sforce.com/schemas/class/RecordCreationservicecls',null,'1','1','false'};
private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/schemas/class/RecordCreationservicecls','true','false'};
private String[] field_order_type_info = new String[]{'city','country','countryCode','postalCode','state','stateCode','street'};
    }
publicclass createRecordResponse_element {
public String result;
private String[] result_type_info = new String[]{'result','http://soap.sforce.com/schemas/class/RecordCreationservicecls',null,'1','1','true'};
private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/schemas/class/RecordCreationservicecls','true','false'};
private String[] field_order_type_info = new String[]{'result'};
    }
publicclass createRecord_element {
public String jsonString;
private String[] jsonString_type_info = new String[]{'jsonString','http://soap.sforce.com/schemas/class/RecordCreationservicecls',null,'1','1','true'};
private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/schemas/class/RecordCreationservicecls','true','false'};
private String[] field_order_type_info = new String[]{'jsonString'};
    }
publicclass SessionHeader_element {
public String sessionId;
private String[] sessionId_type_info = new String[]{'sessionId','http://soap.sforce.com/schemas/class/RecordCreationservicecls',null,'1','1','false'};
private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/schemas/class/RecordCreationservicecls','true','false'};
private String[] field_order_type_info = new String[]{'sessionId'};
    }
publicclass DebuggingInfo_element {
public String debugLog;
private String[] debugLog_type_info = new String[]{'debugLog','http://soap.sforce.com/schemas/class/RecordCreationservicecls',null,'1','1','false'};
private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/schemas/class/RecordCreationservicecls','true','false'};
private String[] field_order_type_info = new String[]{'debugLog'};
    }
publicclass RecordCreationservicecls {
public String endpoint_x = 'https://ap2.salesforce.com/services/Soap/class/RecordCreationservicecls';
public Map<String,String> inputHttpHeaders_x;
public Map<String,String> outputHttpHeaders_x;
public String clientCertName_x;
public String clientCert_x;
public String clientCertPasswd_x;
public Integer timeout_x;
public GeneratedRecordCreationService.AllowFieldTruncationHeader_element AllowFieldTruncationHeader;
public GeneratedRecordCreationService.CallOptions_element CallOptions;
public GeneratedRecordCreationService.SessionHeader_element SessionHeader;
public GeneratedRecordCreationService.DebuggingInfo_element DebuggingInfo;
public GeneratedRecordCreationService.DebuggingHeader_element DebuggingHeader;
private String AllowFieldTruncationHeader_hns = 'AllowFieldTruncationHeader=http://soap.sforce.com/schemas/class/RecordCreationservicecls';
private String CallOptions_hns = 'CallOptions=http://soap.sforce.com/schemas/class/RecordCreationservicecls';
private String SessionHeader_hns = 'SessionHeader=http://soap.sforce.com/schemas/class/RecordCreationservicecls';
private String DebuggingInfo_hns = 'DebuggingInfo=http://soap.sforce.com/schemas/class/RecordCreationservicecls';
private String DebuggingHeader_hns = 'DebuggingHeader=http://soap.sforce.com/schemas/class/RecordCreationservicecls';
private String[] ns_map_type_info = new String[]{'http://soap.sforce.com/schemas/class/RecordCreationservicecls', 'GeneratedRecordCreationService'};
public String createRecord(String jsonString) {
            GeneratedRecordCreationService.createRecord_element request_x = new GeneratedRecordCreationService.createRecord_element();
            request_x.jsonString = jsonString;
            GeneratedRecordCreationService.createRecordResponse_element response_x;
            Map<String, GeneratedRecordCreationService.createRecordResponse_element> response_map_x = new Map<String, GeneratedRecordCreationService.createRecordResponse_element>();
            response_map_x.put('response_x', response_x);
            WebServiceCallout.invoke(
this,
              request_x,
              response_map_x,
new String[]{endpoint_x,
'',
'http://soap.sforce.com/schemas/class/RecordCreationservicecls',
'createRecord',
'http://soap.sforce.com/schemas/class/RecordCreationservicecls',
'createRecordResponse',
'GeneratedRecordCreationService.createRecordResponse_element'}
            );
            response_x = response_map_x.get('response_x');
return response_x.result;
        }
    }
}

Create Service Mockup:
Sample Mockup class:
Create Mockupclass:
------------------------
global class YourWebServiceMockImpl implements WebServiceMock {
   global void doInvoke(
           Object stub,
           Object request,
           Map<String, Object> response,
           String endpoint,
           String soapAction,
           String requestName,
           String responseNS,
           String responseName,
           String responseType) {

        // Create response element from the autogenerated class.
        // Populate response element.
        // Add response element to the response parameter, as follows:
        response.put('response_x', responseElement);
   }
}

Original Mockup class:
globalclass GeneratedRecordCreationServiceMockupcls implements WebServiceMock {
       globalvoid doInvoke(
           Object stub,
           Object request,
           Map<String, Object> response,
           String endpoint,
           String soapAction,
           String requestName,
           String responseNS,
           String responseName,
           String responseType) {

// Create response element from the autogenerated class.
        GeneratedRecordCreationService.createRecordResponse_element responseElement = new GeneratedRecordCreationService.createRecordResponse_element();
//Here createRecordResponse_element is an inner class in generated service class for preparing response
// Populate response element.
        responseElement.result = 'Welcome to Salesfoce';//here we have to set the value for the property result
// Add response element to the response parameter, as follows:
        response.put('response_x', responseElement);
   }
      
}


Test class:
@isTest
privateclass TestGeneratedRecordCreationService {

statictestMethodvoid myUnitTest() {
// TO DO: implement unit test
 Test.setMock(WebServiceMock.class, new GeneratedRecordCreationServiceMockupcls());

        GeneratedRecordCreationService.RecordCreationservicecls recordCreationObj = new  GeneratedRecordCreationService.RecordCreationservicecls();
       recordCreationObj.createRecord('Give input');
      //This method “createRecord” contains the webservice callout.
    }
}

So by using mockup class we can do test code coverage for the method which includes web service callout .

References: