I have http request batch class with the endpoint url is set by using Named Credential in apex code
Named Credential:
- Given as My Domain URL
- Identity type as Named Principal
- Authentication Protocol as OAuth
- Authentication Provider as Salesforce
-
I have checked Generate Authorization Header checkbox
req.setEndpoint('callout:Createddateconversionrate/services/data/v43.0/sobjects/DatedConversionRate');
Batch class
global class CreateDatedConversionRate implements Database.Batchable<sObject>,Database.Stateful,Database.AllowsCallouts {
private String sessionId;
public string querystring{get;set;}
global CreateDatedConversionRate(){
this.sessionId = querystring;
}
global Database.QueryLocator start(Database.BatchableContext BC) {
String query = '';
List<AggregateResult> startDateList = [SELECT StartDate FROM DatedConversionRate GROUP BY STARTDATE ORDER BY STARTDATE DESC LIMIT 1];
System.debug(LoggingLevel.INFO,'Last exchangeDate:'+startDateList.get(0));
String startDate = DateTime.newInstance(((Date)startDateList.get(0).get('StartDate')), Time.newInstance(0,0,0,0)).format('yyyy-MM-dd');
System.debug(LoggingLevel.INFO,'query string:'+startDate);
query = 'SELECT IsoCode, ConversionRate FROM DatedConversionRate WHERE STARTDATE = '+ startDate +' ORDER BY IsoCode';
System.debug(LoggingLevel.INFO,'Executed query:'+query);
return Database.getQueryLocator(query);
}
global void execute(Database.BatchableContext BC, List<DatedConversionRate> conversionList) {
System.debug(LoggingLevel.INFO,'conversionList size:'+conversionList.size());
String todayDate = DateTime.newInstance(System.Date.today(), Time.newInstance(0, 0, 0, 0)).format('yyyy-MM-dd');
if(conversionList != null && conversionList.size()>0){
for(DatedConversionRate conversionRate : conversionList){
String str = '' ;
str +='{ "IsoCode" : "';
str += conversionRate.IsoCode +'", "ConversionRate" : ';
str += conversionRate.ConversionRate + ', "StartDate" : "';
str += todayDate + '"';
str += '}';
/*REST API CALL TO INSERT RECORDS.*/
Http h = new Http();
HttpRequest req = new HttpRequest();
req.setBody(str);
req.setEndpoint('callout:Createddateconversionrate/services/data/v43.0/sobjects/DatedConversionRate');
req.setMethod('POST');
System.debug(LoggingLevel.INFO,'request body:'+req.getBody());
if(!Test.isRunningTest()){
HttpResponse res = h.send(req);
System.debug(LoggingLevel.INFO,'res'+res.getBody());
System.debug(LoggingLevel.INFO,'res'+res.getStatus());
}
}
}
}
global void finish(Database.BatchableContext BC) {
}
}
When i am trying to execute the Batch class.I am getting following error
{"message":"This session is not valid for use with the REST
API","errorCode":"INVALID_SESSION_ID"}]
How to rectify this error?
For GET Method is working,only for POST Method is not working?
please guide me for answer
Best Answer
Issue 1:
I suspect what you've run into is covered by the idea Batch Jobs - Multi User - Named Credential Usage. There is also the corresponding SFSE question Using Named Credentials in Batch Job (multi user). Basically, the Named Credential isn't working in a batch context if they are per user.
It appears that a Named Credential doing oAuth with a refresh token may be more successful. See Using Named Credentials to get Salesforce sessionId.
Issue 2:
When I first checked this I didn't have "Advanced Currency Management" enabled in the org. As such, the metadata I could see for DatedConversionRate via
/services/data/v43.0/sobjects/DatedConversionRate
indicated is neither createable (the typical reason to do a POST call) nor updateable (which would be the PATCH verb).After enabling "Advanced Currency Management" the DatedConversionRate records became createable and updateable.
Test POST from Workbench to create a new DatedConversionRate for a known currency ISOCode on a specific StartDate:
POST /services/data/v43.0/sobjects/DatedConversionRate
201 Response
Next steps in debugging...:
Do the same POST from Anonymous Apex with the local Session ID
Run with anonymous Apex:
Response
Do the same POST from within a batch with an established Session ID (login API)
I used a variation of the above code, but passed the Session ID into the constructor. This works for a quick test, but isn't recommended for actual batches.
Then start it with:
From the SerialBatchApexRangeChunkHandler debug log:
Do the same POST from Anonymous Apex with a Named Credential
I created a Named Credential, Connected App, and Auth Provider using the instructions in Using Named Credentials with the Apex Wrapper Salesforce Metadata API (apex-mdapi), they are pretty comprehensive on what is required.
Then I modified DateConversionRateCreate to handle the Session ID being null and to switch to the named credential in that scenario.
The logs showed this worked and used the Named Credential for authentication.
Do the same POST from within a batch with a Named Credential
Test with:
Result:
So that is all working. The Named Credential (using OAuth and a refresh token) is providing a valid Session ID to use in a batch context. The callout is hitting the REST API and creating the new DateConversionRate.