My Question is exactly same as this one – https://developer.salesforce.com/forums/?id=9060G0000005MLWQA2
But i will give more details.
We just turned on Lightning Knowledge on in our environment, and goal is to write a script that can update recordtype on all Draft and published articles.
For published articles, i need to do this –
Step 1 - Make a rest call to create a draft version
Step 2 - Make a rest call to update draft version (change its recordtype)
Step 3 - Make a rest call to publish the above draft version
For Articles that are in draft, i can reuse the code for Step 2 (even though updating draft can be done by simple database changes via apex, but that causes issue in conjunction with Step 3 if everything is done in one transaction .. so i am sticking to rest call for step 2).
Since there are over 4000 articles, and each could require upto 3 rest calls, i am writing a batch apex, and intend to run it with a batch size of 1.
For reference, i am using this to make rest calls – https://www.jitendrazaa.com/blog/salesforce/call-salesforce-rest-api-from-apex/
I wrote code for step 2, and tried testing it, but getting the error {"errorCode" : "INVALID_SESSION_ID", "message" : "Session expired or invalid"}
Instead of batch class, if i call my utility method directly from workbench or anonymous window, it works (for a single article).
here are my codes
batch class
global class pf_knowledgeMigrator implements Database.Batchable<sObject>, Database.AllowsCallouts{
global Database.QueryLocator start(Database.BatchableContext BC){
String query = 'Select Id, PublishStatus from Knowledge__kav where PublishStatus=\'Draft\' and IsLatestVersion= True and RecordTypeId=null';
return Database.getQueryLocator(query);
}
global void execute(Database.BatchableContext BC, List<Knowledge__kav> articles){
for(Knowledge__kav currentArticle : articles){
knowledgeMigratorHelper.processDraftArticle(currentArticle);
}
}
global void finish(Database.BatchableContext BC) {
}
}
My helper class
public without sharing class knowledgeMigratorHelper{
private static Id howToRecTypeId = [Select Id from RecordType where DeveloperName = 'How_To' limit 1].Id;
public static void processDraftArticle(Knowledge__kav article){
String articleId = String.ValueOf(article.Id);
String howToRecordTypeIdString = String.ValueOf(howToRecTypeId);
HttpResponse httpResponse = changeRecordType(articleId,howToRecordTypeIdString);
if(httpResponse.getStatusCode() != 204){
String response = JSON.serializePretty( JSON.deserializeUntyped(httpResponse.getBody()) );
System.debug(response);
}
}
/*
* Utility Method to change recordType of a draft version of an article
*/
public static HttpResponse changeRecordType(String articleId, String recTypeId){
String sfdcURL = URL.getSalesforceBaseUrl().toExternalForm();
String restAPIURL = sfdcURL + '/services/data/v40.0/sobjects/Knowledge__kav/'+articleId+'?_HttpMethod=PATCH';
HttpRequest httpRequest = new HttpRequest();
httpRequest.setMethod('POST');
httpRequest.setHeader('Authorization', 'OAuth ' + UserInfo.getSessionId());
httpRequest.setHeader('Authorization', 'Bearer ' + UserInfo.getSessionID());
httpRequest.setHeader('Content-Type', 'application/json');
httpRequest.setEndpoint(restAPIURL);
httpRequest.setBody('{ "RecordTypeId": "' + recTypeId + '"}');
Http http = new Http();
HttpResponse httpResponse = http.send(httpRequest);
return httpResponse;
}
}
When i run the command Database.executeBatch(new pf_knowledgeMigrator(),1), i get that error
However, if i directly use console anonymous window or workbench and test my utility method like following, it works –
String articleId = 'ka1c00000000YYvAAM';
String recTypeId = '012c0000000AWxvAAG';
HttpResponse httpResponse = knowledgeMigratorHelper.changeRecordType(articleId,recTypeId);
System.debug('@@@@ response code'+httpResponse.getStatusCode());
Doing above will udpate the recordtype of article with Id ka1c00000000YYvAAM
So something is different when REST callout is made via batch. Not sure how i can make this work
Best Answer
The session id that you get back from
UserInfo.getSessionId()
is not valid from batch code. This is actually due to be changed in the Winter 19 release:https://releasenotes.docs.salesforce.com/en-us/winter19/release-notes/rn_apex_streamline_api_calls.htm
Edit
In comments, I suggested that the questioner tries making the batch class stateful, then gets the session id before launching the batch. They report that this works. Probably not 100% bulletproof as the session may expire in a long batch, but seems to be a stop-gap solution until Winter 19.