Recipes by Category

App Distribution (2) Bundle logic, interface and services for distribution. App Logic (37) The Apex programming language, workflow and formulas for logic. Collaboration (6) The Salesforce Chatter collaboration platform. Database (29) Data persistence, reporting and analytics. Integration (33) Web Service APIs and toolkits for integration. Security (9) Platform, application and data security. Tools (4) Force.com tooling User Interface (36) Visualforce MVC and metadata-drive user interfaces. Web Sites (12) Public web sites and apps with optional user registration and login.
Beta Feedback
Cookbook Home » Calling Salesforce Web Services Using Apex

Calling Salesforce Web Services Using Apex

Post by LukeJFreeland  (2012-01-23)

Status: Unverified
Level: advanced

Problem

Most, if not all, of the Web service examples and tutorials use an external programming language to call/invoke the salesforce Web services. However, this requires external hardware and software to run and is not native to the salesforce platform. With a few tweaks to the salesforce Web services' wsdl and Apex generated classes, it's possible to invoke them through native Apex.

Solution

To begin, you always need to have either an Apex Partner or Enterprise Web service class so that you can login and get a session id, which will be used in any future Web service calls.

Let's generate an Apex Partner Web service class first.

  1. Go to Setup | Develop | API -> Click on "Generate Partner WSDL".
  2. Save the file and name it "Partner.wsdl".
  3. Go to Setup | Develop | Apex Classes -> Click on "Generate from WSDL".
  4. Select the "Partner.wsdl" file and click the "Parse" button.
  5. Accept the default class names and click the "Generate Apex code" button.
  6. Now, you'll see the following message "The following generated class(es) have compilation errors: Error: partnerSoapSforceCom Error: unexpected token: 'delete' at 670:51". This results from some of the partner web methods being the same as Apex DML keywords. To resolve this, simply change the name of the Apex method names.
  7. Copy all the partnerSoapSforceCom Apex code in the textbox.
  8. Go to Setup | Develop | Apex Classes -> New.
  9. Paste the partnetSoapSforceCom Apex code.
  10. Change
    public partnerSoapSforceCom.DeleteResult[] delete(String[] ids)
    to
    public partnerSoapSforceCom.DeleteResult[] deleteSObjects(String[] ids) 
  11. Change
    public partnerSoapSforceCom.UpsertResult[] upsert
    to
    public partnerSoapSforceCom.UpsertResult[] upsertSObjects
  12. Change
    public partnerSoapSforceCom.MergeResult[] merge
    to
    public partnerSoapSforceCom.MergeResult[] mergeSObjects
  13. Change
    public partnerSoapSforceCom.SaveResult[] update
    to
    public partnerSoapSforceCom.SaveResult[] updateSObjects
  14. Change
    public partnerSoapSforceCom.UndeleteResult[] undelete
    to
    public partnerSoapSforceCom.UndeleteResult[] undeleteSObjects
Now you have an Apex Partner class.

Next, let's generate an Apex class for the Apex Web service.

  1. Go to Setup | Develop | API -> click the "Generate Apex WSDL" button.
  2. Save the file as "Apex.wsdl".
  3. Go to Setup | Develop | Apex Classes -> Click on "Generate from WSDL".
  4. Select the "Apex.wsdl" file and click the "Parse" button.
  5. Keep the default "soapSforceCom200608Apex" name and click the "Generate Apex code" button.
Now, you should see the following message "The following generated class(es) compiled successfully with no errors: soapSforceCom200608Apex", which indicates that you now have an Apex class for the Apex Web service.

Before we can use some code to demonstrate that this works, we have to configure our org so that we're allowed to call the Web services. Not a biggie.

  1. Go to Setup | Security Controls | Remote Site Settings
  2. Click "New Remote Site".
  3. Enter "SalesforceLogin" for the Site Name.
  4. Enter either "https://login.salesforce.com" or "https://test.salesforce.com" as the Remote Site URL depending on your org.
  5. Click Save. Now, you have granted your org the ability to login, but you still have to grant it access to the correct salesforce web node to allow the Apex Web service to be called, which we'll do next.
  6. Go to Setup | Security Controls | Remote Site Settings
  7. Click "New Remote Site".
  8. Enter "SalesforceApex" for the Site Name.
  9. Enter "https://<Prefix>-api.salesforce.com" where <Prefix> is the salesforce sub-domain you're using, which can be found by looking at the URL in your browser. Since I'm on "https://na12-api.salesforce.com", I would enter "https://na12-api.salesforce.com".
  10. Click "Save"
Now, we're ready to run a quick example that will run all the unit tests in our org in a synchronous fashion.

partnerSoapSforceCom.Soap sp = new partnerSoapSforceCom.Soap();

/* For demonstration purposes only, enter your credentials on the following
   lines, but if you're going to use this a lot or in production, encrypt your credentials and store them somewhere and then decrypt them here.
*/
String username = '<Your username here>';
String password = '<Your password here>';

partnerSoapSforceCom.LoginResult loginResult = sp.login(username, password);

system.debug('   loginResult ' + loginResult);

soapSforceCom200608Apex.Apex apexWebSvc = new soapSforceCom200608Apex.Apex();
soapSforceCom200608Apex.SessionHeader_element sessionHeader = new soapSforceCom200608Apex.SessionHeader_element();
sessionHeader.sessionId = loginResult.sessionId; 

// The Web services have a maximum timeout of 2 minutes. The timeout value
// is in milliseconds.
apexWebSvc.timeout_x = 120000;
apexWebSvc.SessionHeader = sessionHeader;

soapSforceCom200608Apex.RunTestsRequest testsRequest = new soapSforceCom200608Apex.RunTestsRequest();
testsRequest.allTests = true;
		
soapSforceCom200608Apex.RunTestsResult testResults = apexWebSvc.runTests(testsRequest);

/* Do something worthwhile with the test results here */

Discussion

This is just the tip of the iceberg, since we're only using the Apex Web service. With a little bit of effort, you can generate an Apex Metadata class for the Metadata Web service. This could "theoretically", for example, let you deploy code from one org to another using a "deployment" org without the need of the migration tool.

Another example would be creating an automated testing scheduled job, using the above code as a starting point, and emailing the results to your development team. Keep in mind that if you have a lot of tests, it may take longer than 120 seconds and this approach won't work. You'll have to switch to the asynchronous "ApexTestQueueItem" and "ApexTestResult" objects, but they don't currently provide nearly as much information as the synchronous "RunTestsResult" object.

From here, try out different things and please share your findings. I'm certain I'm not the only interested party.

I hope this helps and happy coding.

Luke

Share

Recipe Activity - Please Log in to write a comment

Nice artical!

I got a question that is probably to invoke the webservice, customer wants to interact Salesforce with external database like 
SQL server to get the real time data into the Salesforce when visual force action performed.

I'm welcoming to take any suggestios upon to accomplish this using webservice.

Thank you in advance!


by SFDC Force.com  (2014-04-04)

Nice artical!
I got a question that is it possible to invoke the web service provided by Salesforce using other web service client like SoapUI?
I've tried to invoke the Login method in enterprise.wsdl, and got successfully response, while how can I do this for other methods, for example the query method? Thank you.

by a093000000XaCHP  (2013-09-12)

Good article, but how could we unit test this?


I know the key is Test.setMock ... but I'm having trouble figuring out how to make the mock server(s)....

by Brian Kessler  (2013-08-21)

Using the APEXtoWSDL option is one way to do this, but creating the XML on your own can lead to a very small and simple set of code.  I wrote a blog post on this option here: http://www.sundoginteractive.com/sunblog/posts/getting-a-session-id-using-the-login-partner-api-call

by Terry Luschen  (2013-08-05)

Thanks for explaining in detail !

by Pradnya Sabnis  (2012-12-16)

Oops link got corrupted, https://github.com/financialforcedev/apex-mdapi

by andyinthecloud  (2012-12-07)

Steve, take a look at this, ttps://github.com/financialforcedev/apex-mdapi, I've taken the pain out of importing the Metadata WSDL and provided some sample code to help. That said if you want to do a describeGlobal that is already supported in native Apex. Take a look at the Apex develoeprs guide for this.

by andyinthecloud  (2012-12-07)

Id also like to call the enterprise wsdl from Rest within Salesforce, which Ive been able to do, but I've only been able to call describeglobal. Id also like to call to get each field of every object.

The xml Im using is

String body = '<?xml version="1.0" encoding="utf-8"?>' +
+'<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:enterprise.soap.sforce.com">' +
+'<soap:Header><urn:SessionHeader><urn:sessionId>' + UserInfo.getSessionId() +'</urn:sessionId></urn:SessionHeader></soap:Header>'
+'<soap:Body><urn:describeGlobal /></soap:Body></soap:Envelope>';

This is fine to get describeGlobal but Im not sure what to pass to get the fields?

by SteveJ Fouracre  (2012-11-24)

I'd like to consume the metadata api in Salesforce but Im also having issues doing this, not sure what parts of the wsdl I should change, can anyone help


by SteveJ Fouracre  (2012-11-24)

test

voted as verified by Rajasekhar Julakanti  (2012-10-12)

https://github.com/financialforcedev/apex-mdapi

by andyinthecloud  (2012-10-11)

I have been successful in getting parts of the Metadata API to work from Apex.

I've uploaded the code and a write up to Github, include pros and cons of this approach. 

    MetadataService.CustomObject customObject = new MetadataService.CustomObject();
    customObject.fullName = 'Test__c';
    customObject.label = 'Test';
    customObject.pluralLabel = 'Tests';
    customObject.nameField = new MetadataService.CustomField();
    customObject.nameField.type_x = 'Text';
    customObject.nameField.label = 'Test Record';
    customObject.deploymentStatus = 'Deployed';
    customObject.sharingModel = 'ReadWrite';
    MetadataService.AsyncResult[] results = service.create(new List<MetadataService.Metadata> { customObject });

Enjoy!

by andyinthecloud  (2012-10-11)

Hi Mohammed,
Did you manage to tweak the Metadata API and call it from Apex ?

by Jatin Jain  (2012-09-24)

It is nice.. i tried and it is working for me.

Thanks
Somes

by Someswara Rao Vudattula  (2012-07-31)

I'm totally impressed!

It's a very good article.

This has helped me in my project where I am now.
 
Thank you!

by jose luis almazan  (2012-07-26)
Page 1 of  2

X

Vote to Verify a Recipe

Verifying a recipe is a way to give feedback to others and broaden your own understanding of the capabilities on Force.com. When you verify a recipe, please make sure the code runs, and the functionality solves the articulated problem as expected.

Please make sure:
  • All the necessary pieces are mentioned
  • You have tested the recipe in practice
  • Have sent any suggestions for improvements to the author

Please Log in to verify a recipe

You have voted to verify this recipe.