0

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

3

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:

API calls sometimes require a session ID and a URL. You can obtain that session ID using the System.UserInfo.getSessionId() method. This method previously returned null in asynchronous Apex, but it now returns a value whether it’s run synchronously or asynchronously.

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.

6
  • That's actually good news. It's been a thorn in a lot of sides for a long time now. – sfdcfox Aug 31 '18 at 8:32
  • That is some good info. But i am still getting the same error even after using System.UserInfo.getSessionId() – user2957592 Aug 31 '18 at 9:35
  • @user2957592: Winter'19 is not yet released and until then you will keep receiving null for getSessionId() on Batch Apex. – Jeferson Chaves Aug 31 '18 at 9:39
  • @JefersonChaves So until Winter'19 release happens, there is no way of accomplishing this? Rest calls via batch apex is not possible at all? – user2957592 Aug 31 '18 at 9:42
  • What @JefersonChaves said, basically. I haven't tried it, but you could maybe try making your batch class stateful, and getting the session id before you start the batch. I guess the session id may expire, or maybe just not work at all. But the whole thing will be much cleaner in Winter 19 – Aidan Aug 31 '18 at 9:44
0

Second post by Aidan gave the solution -

you could maybe try making your batch class stateful, and getting the session id before you start the batch. I guess the session id may expire, or maybe just not work at all. But the whole thing will be much cleaner in Winter 19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.