Force.com OAuth 2.0 Scribe Java Example

OAuthServiceConfig and OAuthServiceProvider Bean in ApplicationContext

<bean id="salesforceServiceConfig" class="com.example.oauth.OAuthServiceConfig">
		<constructor-arg value="xxx" />
		<constructor-arg value="xxxx"/>
		<constructor-arg value="https://www.example.com/oauth/salesforce"/>
		<constructor-arg value="com.example.oauth.SalesforceOauthApi"/>
	</bean>
	<bean id="salesforceServiceProvider" class="com.example.oauth.OAuthServiceProvider">
		<constructor-arg name="config" ref="salesforceServiceConfig" />
	</bean>

SalesforceOauthApi extends DefaultApi20

package com.example.oauth;

import org.scribe.builder.api.DefaultApi20;
import org.scribe.extractors.AccessTokenExtractor;
import org.scribe.extractors.JsonTokenExtractor;
import org.scribe.model.OAuthConfig;
import org.scribe.model.Verb;

public class SalesforceOauthApi extends DefaultApi20{

	private static final String ACCESSTOKEN = "https://login.salesforce.com/services/oauth2/token?grant_type=authorization_code";
	
	@Override
	public String getAccessTokenEndpoint() {
		return ACCESSTOKEN;
	}

	@Override
	public String getAuthorizationUrl(OAuthConfig config) {
		return String.format("https://login.salesforce.com/services/oauth2/authorize?client_id=%s&response_type=code&redirect_uri=%s&display=popup&scope=%s", config.getApiKey(), config.getCallback(), "full refresh_token");
	}
	
	 @Override
	 public Verb getAccessTokenVerb(){
	       return Verb.POST;
	 }
	
	@Override
	 public AccessTokenExtractor getAccessTokenExtractor() {
	    return new JsonTokenExtractor();
	 }
}

Spring MVC SalesforceController for requesting access token

package com.example.oauth.controller;

import static org.springframework.web.context.request.RequestAttributes.SCOPE_SESSION;

import java.util.Map;

import javax.servlet.http.HttpSession;

import org.scribe.model.Token;
import org.scribe.model.Verifier;
import org.scribe.oauth.OAuthService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.context.request.WebRequest;

import com.example.oauth.OAuthServiceProvider;

@Controller
@RequestMapping("/oauth/salesforce")
public class SalesforceController {

	@Autowired
	@Qualifier("salesforceServiceProvider")
	private OAuthServiceProvider salesforceServiceProvider;
	
	private static final Token EMPTY_TOKEN = null;

	@RequestMapping(value = "/login-salesforce", method = RequestMethod.GET)
	public String loginToSalesforce(Map<String, Object> map, WebRequest request) {
		OAuthService service = salesforceServiceProvider.getService();

		String authUrl = service.getAuthorizationUrl(EMPTY_TOKEN);
		System.out.println("RequestToken: " + authUrl);
		return "redirect:" + authUrl;
	}

	@RequestMapping(value = { "" }, method = RequestMethod.GET)
	public String callback(
			@RequestParam(value = "oauth_token", required = false) String oauthToken,
			@RequestParam(value = "code", required = false) String oauthVerifier,
			WebRequest request, Map<String, Object> map) {

		OAuthService service = salesforceServiceProvider.getService();

		// getting access token
		Verifier verifier = new Verifier(oauthVerifier);
		Token accessToken = service.getAccessToken(EMPTY_TOKEN, verifier);

		// store access token as a session attribute
		request.setAttribute("oauthAccessToken", accessToken, SCOPE_SESSION);

		ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder
				.currentRequestAttributes();
		HttpSession session = attr.getRequest().getSession(false); // create a
																	// new
																	// session
		session.setAttribute("accessToken", accessToken);

		return "settings";
	}
}

Force.com Tooling API IDE

Cloudsole Developer is an easy way to develop Salesforce apex classes, triggers, components and visualforce pages. It uses the salesforce API to access your code in a safe and secure way.

Some of the functionality of the IDE:

  • Create new Apex Triggers Classes, Visualforce Pages & Components
  • Select from a list of predefined templates to create class stubs
  • Code debugger
  • Run Anonymous Apex Code
  • Run SOQL queries
  • Run Batch queries
  • Metadata viewer and download
  • Code Metrics Dashboard

Force.com API Used:

  • Force.com OAuth
  • Force.com Tooling API
  • Force.com REST API
  • Force.com Batch API
  • Force.com Metadata API

Create new Triggers, Apex Classes, Visualforce Pages and Components:

CloudSoleApexClasses

Code Editor:

CloudSoleCodeEditor

Run Anonymous Apex Code:

CloudSoleAnonApex

Code Dashboard:

CloudSoleDashboard

Run SOQL Queries:

CloudSoleDataManagement

Create Force.com Batch Jobs:

CloudSoleBatchJob

Force.com HTTPCallout Test Class

Below is the test class to test an HTTP Callout from apex:

Main Test Class

@isTest
global class HttpCalloutToFeedTest {
	@isTest static void testCallOutToFeed() {
           Test.setMock(HttpCalloutMock.class, new HttpCalloutToFeedMock());
           HttpResponse res = HttpCalloutToFeed.sentMessageToFeed();

           System.assertEquals(201, res.getStatusCode());
	}
}

HTTP Callout Mock

@isTest
global class HttpCalloutToFeedMock implements HttpCalloutMock {
    global HTTPResponse respond(HTTPRequest httpreq) {
      System.assertEquals('http://cloudsole-feed.herokuapp.com/sfdc/public/feed', httpreq.getEndpoint());
      System.assertEquals('POST', httpreq.getMethod());
      String message = '{"name": "Test Class for CloudSole Feed", "message": "This test passed!!!"}';
      
      httpreq.setHeader('Accept', 'application/json');
      httpreq.setHeader('Content-type', 'application/json');
      httpreq.setBody(message);
      httpreq.setMethod('POST');

      HttpResponse res = new HttpResponse();
      res.setStatusCode(201);
      return res;
  }
}

As this is a POST there is no allot of response but the statusCode. 201 means created so the feed was successfully created.

Salesforce custom LinkedIn button

An easy way to search for users in Salesforce is to add a ‘View in LinkedIn’ button

On Contact:

http://www.linkedin.com/commonSearch?type=people&keywords=!Contact.FirstName+!Contact.FirstName&pplSearchOrigin=GLHD&pageKey=nmp-home&search=Search

On Account (create custom formula fields to Contact FirstName and LastName):

http://www.linkedin.com/commonSearch?type=people&keywords=!Account.First_Name__c+!Account.Last__c&pplSearchOrigin=GLHD&pageKey=nmp-home&search=Search

Apigee as Force.com REST provider

Apigee has an extensive Force.com REST interface to interact with Salesforce. Follow this tutorial to create an app in apigee, create a remote connection in Salesforce, add a user to the app to get your smart-key.

Complete the tutorial below to get the information needed to run this class:

http://developer.apigee.com/salesforce_tutorial.html

You will need the following to run this class:
1. Smart-key
2. Application Name

import com.apigee.sdk.oauth.api.exception.RestCallException;
import com.apigee.sdk.oauth.api.rest.RestCallResult;
import com.apigee.sdk.oauth.api.rest.RestCall;
import com.apigee.sdk.oauth.impl.model.Request;

public class ForceApigee {

	   //Configure your SMARTKEY and APPNAME
	   final static String SMARTKEY = "YOURSMARTKEY";
	   final static String APPNAME = "YOURAPPLICATIONNAME";
	   final static String PROVIDER = "salesforce";
	   
	   final static String VERSIONENDPOINT = "/services/data";
	   final static String RESOURCESENDPOINT = "/services/data/v27.0";
	   final static String SOBJECTENDPOINT = "/services/data/v27.0/sobjects";
	   final static String SOBJECTBASICENDPOINT = "/services/data/v24.0/sobjects/Account";
	   final static String SOBJECTDESCRIBEENDPOINT = "/services/data/v27.0/sobjects/Account/describe";
	   final static String SOQLQUERYENDPOINT = "/services/data/v24.0/query?q=SELECT name from Account";
	   final static String SOQLSEARCHENDPOINT = "/services/data/v24.0/search?q=FIND {test} IN ALL FIELDS";
	
	   public static String callToSalesforce(Request.HttpVerb verb, String Endpoint)
	   {
		   try 
		   {
	           RestCallResult result = new RestCall(verb, Endpoint)
	             .withSmartKey(SMARTKEY)
	             .toProvider(PROVIDER)
	             .usingApp(APPNAME)
	             .invoke();
				 return result.getResponsePayload().toString();
	       } catch (RestCallException e) {
			   return e.getMessage();
	       }
	   } 
	   
	   public static void main(String[] args)
	   {
	   		System.out.println("***VERSIONENDPOINT****: \n" + callToSalesforce(Request.HttpVerb.GET, VERSIONENDPOINT));
	   		System.out.println("***RESOURCESENDPOINT***: \n" + callToSalesforce(Request.HttpVerb.GET, RESOURCESENDPOINT));
	   		System.out.println("***SOBJECTENDPOINT***: \n" + callToSalesforce(Request.HttpVerb.GET, SOBJECTENDPOINT));
	   		System.out.println("***SOBJECTBASICENDPOINT***: \n" + callToSalesforce(Request.HttpVerb.GET, SOBJECTBASICENDPOINT));
	   		System.out.println("***SOBJECTDESCRIBEENDPOINT***: \n" + callToSalesforce(Request.HttpVerb.GET, SOBJECTDESCRIBEENDPOINT));
	   		System.out.println("***SOQLQUERYENDPOINT***: \n" + callToSalesforce(Request.HttpVerb.GET, SOQLQUERYENDPOINT));
	   		System.out.println("***SOQLSEARCHENDPOINT***: \n" + callToSalesforce(Request.HttpVerb.GET, SOQLSEARCHENDPOINT));
	   }
}

Force.com FourSquare Visualforce Page

<apex:page showHeader="false" showChat="false" sidebar="false">
<html>
<head>
  <title>foursquare :: Explore Sample</title>
  <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js" type="text/javascript" id="jquery"></script>
  <link href="{!URLFOR($Resource.ForceSquare, 'ForceSquare/leaflet.css')}" type="text/css" rel="stylesheet" />
  <script src="{!URLFOR($Resource.ForceSquare, 'ForceSquare/apisamples.js')}" type="text/javascript"></script>
  <script type="text/javascript" src="https://ss0.4sqi.net/scripts/third_party/jquery.ba-bbq-eddd4adf74d0c1310a401475178c57df.js"></script>
  <script src="{!URLFOR($Resource.ForceSquare, 'ForceSquare/leaflet.js')}" type="text/javascript"></script>
  <script src="{!URLFOR($Resource.ForceSquare, 'ForceSquare/wax.leaf.min.js')}" type="text/javascript"></script>
 
  <style type="text/css">
    html { height: 100%; }
    body { height: 100%; margin: 0; padding: 0; }
    /* Give our markers a background image */
    .leaflet-marker-icon {
      background: url(https://foursquare.com/img/pin-blue-transparent.png);
      padding: 6px;
      padding-bottom: 17px;
      top: -6px;
      left: -6px;
      }
  </style>
 
  <script type="text/javascript">
  
  //<![CDATA[
  var client_id = '<YOURCLIENTID>';
  var callback_url = 'https://cs17.salesforce.com/apex/FourSquarePage';

  /* Attempt to retrieve access token from URL. */
  if ($.bbq.getState('access_token')) {
    var token = $.bbq.getState('access_token');
    $.bbq.pushState({}, 2)
  } else if ($.bbq.getState('error')) {
  } else {
    /* Redirect for foursquare authentication. */
    window.location.href = 'https://foursquare.com/oauth2/authenticate?client_id=' + client_id
    + '&response_type=token&redirect_uri=' + callback_url;
  }  

  /* HTML 5 geolocation. */
  navigator.geolocation.getCurrentPosition(function(data) {
    var lat = data['coords']['latitude'];
    var lng = data['coords']['longitude'];
    /* Create map. */
    var map = new L.Map('map_canvas')
      .setView(new L.LatLng(lat, lng), 15);
    /**
     * This is a sample map url that you need to change.
     * Sign up at http://mapbox.com/foursquare for a custom map url.
     */
    var mapboxUrl = 'http://a.tiles.mapbox.com/v3/foursquare.map-b7qq4a62.jsonp';
    wax.tilejson(mapboxUrl, function(tilejson) {
      map.addLayer(new wax.leaf.connector(tilejson));
    });

    /* Query foursquare API for venue recommendations near the current location. */
    $.getJSON('https://api.foursquare.com/v2/venues/explore?ll=' + lat + ',' + lng + '&oauth_token=' + token, {}, function(data) {
      venues = data['response']['groups'][0]['items'];
      /* Place marker for each venue. */
      for (var i = 0; i < venues.length; i++) {
        /* Get marker's location */
        var latLng = new L.LatLng(
          venues[i]['venue']['location']['lat'], 
          venues[i]['venue']['location']['lng']
        );
        /* Build icon for each icon */
        var leafletIcon = L.Icon.extend({
          iconUrl: venues[i]['venue']['categories'][0]['icon'],
          shadowUrl: null,
          iconSize: new L.Point(32,32),
          iconAnchor: new L.Point(16, 41),
          popupAnchor: new L.Point(0, -51)
        });
        var icon = new leafletIcon();
        var marker = new L.Marker(latLng, {icon: icon})
          .bindPopup(venues[i]['venue']['name'], { closeButton: false })
          .on('mouseover', function(e) { this.openPopup(); })
          .on('mouseout', function(e) { this.closePopup(); });
        map.addLayer(marker);
      }       
    })
  })
  //]]>
  </script>
 
  
</head>
<body>
  <div style="width: 100%; height: 100%;" id="map_canvas"></div>
</body>
</html>
</apex:page>

Set in visualforce page:

var client_id = ‘YOURCLIENTAPPID’;
var callback_url = https://.salesforce.com/apex/FourSquarePage

Set in FourSquare App Setup Page:

Redirect URL https://.salesforce.com/apex/FourSquarePage

Force.com REST Swagger Spring MVC

Swagger is a specification and complete framework implementation for describing, producing, consuming, and visualizing RESTful web services. The overarching goal of Swagger is to enable client and documentation systems to update at the same pace as the server. The documentation of methods, parameters, and models are tightly integrated into the server code, allowing APIs to always stay in sync. With Swagger, deploying managing, and using powerful APIs has never been easier.

Spring Swagger Controller code

package com.thysmichels.swagger4forcedotcom.controllers;

import com.knappsack.swagger4springweb.controller.ApiDocumentationController;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

/**
 * This is an example of how you might extend the ApiDocumentationController in order to set your own RequestMapping
 * (instead of the default "/api") among other possibilities.  Going this route, you do not necessarily have to define
 * the controller in your servlet context.
 */
@Controller
@RequestMapping(value = "/api")
public class ExampleDocumentationController extends ApiDocumentationController {

    public ExampleDocumentationController() {
        setBaseControllerPackage("com.thysmichels.swagger4forcedotcom.controllers.api");
        setBaseModelPackage("com.thysmichels.swagger4forcedotcom.models");
        setApiVersion("v1");
    }

    @RequestMapping(value = "/", method = RequestMethod.GET)
    public String documentation() {
        return "api";
    }
}

Force.com REST Implementation Code

package com.thysmichels.swagger4forcedotcom.controllers.api;

import java.util.List;

import javax.annotation.PostConstruct;
import javax.inject.Inject;
import javax.inject.Named;
import javax.ws.rs.core.Response;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;


import com.force.api.ForceApi;
import com.force.api.QueryResult;
import com.thysmichels.swagger4forcedotcom.models.Account;
import com.thysmichels.swagger4forcedotcom.services.SalesforceLogin;
import com.wordnik.swagger.annotations.Api;
import com.wordnik.swagger.annotations.ApiError;
import com.wordnik.swagger.annotations.ApiErrors;
import com.wordnik.swagger.annotations.ApiOperation;
import com.wordnik.swagger.annotations.ApiParam;

@Controller
@RequestMapping(value = "/api/v1/account")
@Api(value = "Account operations", listingClass = "AccountController", basePath = "/api/v1/account", description = "All operations for accounts")
public class AccountController {
	
	@Inject
	@Named("SalesforceLogin")
	private SalesforceLogin sfdcLogin;
	private ForceApi api;
	
	@PostConstruct
	public void init()
	{
		api = sfdcLogin.loginToSalesforce();
	}
	
	 @ApiOperation(value = "Find all accounts", notes = "Get all account currently available", httpMethod = "GET", responseClass = "Account", multiValueResponse = true)
	 @ApiError(code = 500, reason = "Process error")
	 @RequestMapping(value = "/", method = RequestMethod.GET, produces = "application/json")
	 public @ResponseBody List<Account> showAllAccounts() {
	    	QueryResult<Account> res = api.query("SELECT Name FROM Account", Account.class);
	        return res.getRecords();
	 }
	
	@ApiOperation(value = "Find account by Id", notes = "Get account by specifying Id", httpMethod = "GET", responseClass = "Account", multiValueResponse = true)
	@ApiErrors(value = { @ApiError(code = 400, reason = "Invalid ID supplied"), @ApiError(code = 404, reason = "Account not found") })
	@RequestMapping(value = "/{accountId}", method = RequestMethod.GET, produces = "application/json")
    public @ResponseBody Account[] findAccountById(@ApiParam(internalDescription = "java.lang.string", name = "accountId", required = true, value = "string") @PathVariable String accountId) {
    	Account account = api.getSObject("account", accountId).as(Account.class);
        return new Account[]{account};
    }

	
	@ApiOperation(value = "Delete a account", notes = "Delete a specific account with the given ID", httpMethod = "DELETE")
    @ApiError(code = 500, reason = "Process error")
    @RequestMapping(value = "/{accountId}", method = RequestMethod.DELETE, produces = "application/json")
    public @ResponseBody String deleteAccount(@ApiParam(internalDescription = "java.lang.string", name = "accountId", required = true, value = "string") @PathVariable String accountId) {
    	api.deleteSObject("account", accountId);
    	return "{status: success}";
    }
	
	@ApiOperation(value = "Create a account using Param", notes = "Creates a new account in salesforce using Param", httpMethod = "POST")
    @ApiError(code = 500, reason = "Process error")
    @RequestMapping(method = RequestMethod.POST, produces = "application/json")
    public @ResponseBody String createAccountFromParamName(@ApiParam(internalDescription = "java.lang.string", value="string", name = "Name", required = false) @RequestParam(required = true) String Name) 
    {	
		Account newAccount = new Account();
		newAccount.setName(Name);
    	String id = api.createSObject("Account", newAccount);
    	return "{id:" + id +"}";
    }
	
	@ApiOperation(value = "Create a account", notes = "Creates a new account in salesforce", httpMethod = "POST")
    @ApiError(code = 500, reason = "Process error")
    @RequestMapping(method = RequestMethod.POST, consumes = "application/json", produces = "application/json")
    public @ResponseBody String createAccountFromJSON(@RequestBody Account newAccount) 
    {	
    	String id = api.createSObject("Account", newAccount);
    	return "{id:" + id +"}";
    }
	
	@ApiOperation(value = "Update Account", notes = "Update a existing Account", httpMethod = "POST") 
	@RequestMapping(value = "/{accountId}", method = RequestMethod.POST, consumes = "application/json")
	 public @ResponseBody String updateAccount(@PathVariable String accountId, @RequestBody Account account) {
		 api.updateSObject("Account", accountId, account);
		 return "{status: success}";
	 }
}

Screenshots

Salesforce Spring MVC Swagger

Salesforce Spring MVC Swagger1

Force.com Unit Testing Best Practice

Test classes should use the @isTest annotation

Test methods setup() creates all data needed for a method and does not rely on current data in Org
public static List<Asset> setup(Decimal assetAssetInstanceNumber, String productName)
    {
        Product2 productId = new Product2(Name=productName);
        insert productId;
        Account account = new Account(Name = '[TEST] Account ', SubscriptionStatus__c='Trial');
        insert account;

        Contact contact = new Contact(FirstName = '[TEST] FirstName ', LastName = '[TEST] Last Name ', Email='test@org.com', Phone = '12345566777',Incorporation_Type__c='Incorp Doc' , LastCompIncorpState__c='CA', AccountId =account.Id);
        insert contact;

        List<Asset> createTestAssets = new List<Asset>();
        createTestAssets.add(new Asset(Name='[TEST]', ContactId=contact.Id, AccountId=account.Id, AssetInstanceID__c=assetAssetInstanceNumber, Product2Id=productId.Id));

        Test.startTest();
            insert createTestAssets;
        Test.stopTest();
        return createTestAssets;
    }
 @isTest(SeeAllData=true) static void testContactGetsAddedToControlGroup() {
        List<Asset> getAssets = setup(1237.0,'Employee Warning Letter');

        List<CampaignMember> controlCampaignMemeber = [Select id from CampaignMember where Campaign.Name='SMB Trial Nurture Control Group' and ContactId=:getAssets[0].ContactId];
        System.assert(controlCampaignMemeber.size() == 1);
    }

Use System.assert to prove that code behaves as expected.


     Account testAccount = [select id,name from Account where Owner.Name = 'Salesforce Administrator' limit 1];
     testAccount.billingState='CA';
     Test.startTest();
     update testAccount; 
     Test.stopTest();  
     List updatedAccount = [SELECT billingState FROM Account WHERE Id = :testAccount.Id];
     System.assertEquals('CA', updatedAccount[0].billingState);
     System.assertNotEquals('NY', updatedAccount[0].billingState);
     System.assert(updatedAccount.size() == 1);

Write test methods that both pass and fail for certain conditions and test for boundary conditions.


 public static Account setupAccount()
    {
        Account testAccount = new Account(Name='[Test Account]', billingState='NY');
        insert testAccount;
        return testAccount;
    }
    @isTest static void testAccountBillingStateHasChangedToCA()
    {
        Account updatedAccount = setupAccount();
        updatedAccount.billingState='CA';
        Test.startTest();
         update updatedAccount;
        Test.stopTest();

        System.assertEquals('CA', updatedAccount.billingState);
    }
    @isTest static void testAccountBillingStateHasNotChangedToCA()
    {
        Account updatedAccount = setupAccount();
        System.assertNotEquals('CA', updatedAccount.billingState);
    }

Test triggers to process 200 records – make sure your code is “bulkified” for 200 records.

Tip: Create a variable that specified amounts of records to be created. 
After you tested no limit where reached changed amount to 1. This will speed up testing in Prod.

private static final Integer amountOfRecordsToCreate = 200;
public static List setupAccount()
    {
        List accountsToInsert = new List();
        for (int k = 0; k < amountOfRecordsToCreate;k++)
              accountsToInsert.add(new Account(Name='[Test Account]' + k, billingState='NY'));
        insert accountsToInsert;
        return accountsToInsert;
    }


If you still get the “Too many SOQL queries: 21″ exception" review
where you are making SOQL queries inside a for loop

When testing for governor limits, use Test.startTest and Test.stopTest and the Limit class instead of hard-coding governor limits.


    System.debug('Total Number of SOQL Queries allowed in this apex code context: ' +  Limits.getLimitQueries());
    System.debug('Total Number of records that can be queried  in this apex code context: ' +  Limits.getLimitDmlRows());
    System.debug('Total Number of DML statements allowed in this apex code context: ' +  Limits.getLimitDmlStatements() );
    System.debug('Total Number of script statements allowed in this apex code context: ' +  Limits.getLimitScriptStatements());
    
   //This code inefficiently queries the Opportunity object in two seperate queries
    List opptys = [select id, description, name, accountid,  closedate, stagename from Opportunity where accountId IN :Trigger.newMap.keySet()];
   
    System.debug('1.Number of Queries used in this apex code so far: ' + Limits.getQueries());
    System.debug('2.Number of rows queried in this apex code so far: ' + Limits.getDmlRows());
    System.debug('3. Number of script statements used so far : ' +  Limits.getDmlStatements());
    
    System.debug('4.Number of Queries used in this apex code so far: ' + Limits.getQueries());
    System.debug('5.Number of rows queried in this apex code so far: ' + Limits.getDmlRows());

Use System.runAs() to execute code as a specific user to test for sharing rules (but not CRUD or FLS permissions)


public class TestRunAs {
   public static testMethod void testRunAs() {
      // Setup test data
      // This code runs as the system user

         Profile p = [select id from profile where name='Standard User']; 
         User u = new User(alias = 'standt', email='standarduser@testorg.com', 
            emailencodingkey='UTF-8', lastname='Testing', languagelocalekey='en_US', 
            localesidkey='en_US', profileid = p.Id, 
            timezonesidkey='America/Los_Angeles', username='standarduser@testorg.com');


         System.runAs(u) {
           // The following code runs as user 'u' 
           System.debug('Current User: ' + UserInfo.getUserName());
           System.debug('Current Profile: ' + UserInfo.getProfileId()); }
           // Run some code that checks record sharing
        }
}

Use Salesforce SmartFactory to help setup Test Records.

https://github.com/thysmichels/SmartFactory-for-Force.com


Account account = (Account)SmartFactory.createSObject('Account');
Contact contact = (Contact)SmartFactory.createSObject('Contact', true);
Custom_Object__c customObject = (Custom_Object__c)SmartFactory.createSObject('Custom_Object__c');

Java Code: Export Salesforce Attachements to Dropbox

I wrote a class the exports Salesforce Attachements to be exported to your local Machine. I put the exported attachement files in a dropbox folder which is automatically the shared with others in the organization.

Note: make sure the export directory is shared on Dropbox.

package com.thys.michels.sfdc.doc;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URISyntaxException;

import com.sforce.soap.enterprise.Connector;
import com.sforce.soap.enterprise.EnterpriseConnection;
import com.sforce.soap.enterprise.QueryResult;
import com.sforce.soap.enterprise.sobject.Attachment;
import com.sforce.ws.ConnectionException;
import com.sforce.ws.ConnectorConfig;

public class SFDCDoc {

	/**
	 * @param args
	 * @throws NumberFormatException 
	 * @throws IOException 
	 * @throws URISyntaxException 
	 * @throws DOMException 
	 */
	public static void main(String[] args) throws NumberFormatException, IOException, URISyntaxException {
		// TODO Auto-generated method stub
		

	   //Create a new connectionconfig to your Salesforce Org
	    ConnectorConfig sfconfig = new ConnectorConfig();
	    //Use your salesforce username = email
	    sfconfig.setUsername("yourusername");
	    //Use your saleforce password with your security token look like: passwordjeIzBAQKkR6FBW8bw5HbVkkkk
	    sfconfig.setPassword("passwordwithsecuritytoken");
	 
	    EnterpriseConnection partnercon = null;
	    
	    try {
	    	 
	        // create a salesforce connection object with the credentials supplied in your connectionconfig
	        partnercon = Connector.newConnection(sfconfig);
	        QueryResult describeGlobalResult = partnercon.query("select Id, Name, Body from Attachment where ContentType='application/pdf'");
	        System.out.println(describeGlobalResult.getRecords().length);
	        boolean done = false;
	     
	        while(!done)
	        {
	        	for (int k = 0; k < describeGlobalResult.getRecords().length; k++)
	        	{
	        		
	        	    Attachment a = (Attachment)describeGlobalResult.getRecords()[k];
	        	    File path = new File("//Users//tmichels//Dropbox//SFDCAttachments");
	        	    String mySubFolder = a.getId();
	        	    File newDir = new File(path + File.separator + mySubFolder);
	        	    System.out.println(newDir);
	        	    boolean success = newDir.mkdirs();
	        	    FileOutputStream fos = new FileOutputStream(newDir + File.separator+ a.getName());
	        	    fos.write(a.getBody());
	        	    fos.close();
	        	}
	        	if (describeGlobalResult.isDone()) {
	                done = true;
	             } else {
	            	 describeGlobalResult = partnercon.queryMore(describeGlobalResult.getQueryLocator());
	             }
	        }
	        
	        
	    } catch (ConnectionException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
	    }
	}
}