18

Background

I'm using Apex Wrapper Salesforce Metadata API to update some picklist values on the fly. (i.e. Every time a record is inserted/updated/deleted in Table A, I'm adding/subtracting picklist values into a MultiselectePicklist in table B).

Since,

An Apex trigger can execute a callout when the callout is invoked within a method defined as asynchronous: that is, defined with the @future keyword.

I implemented a method with the future annotation, and

For Apex code that is executed asynchronously, such as @future methods, Batch Apex jobs, or scheduled Apex jobs, getSessionId returns null.

so, I needed to pass the session Id as a parameter in the method, I implemented something like:

@future (callout=true)
public static void addContactPicklistPreference(List<String> preferences, String sessionId){
    Schema.DescribeSObjectResult sObjectDescribe = Contact.sObjectType.getDescribe();
    String picklistFieldName = 'Preference__c';              

    Picklist_MDT.addPicklistValues(sObjectDescribe, picklistFieldName, artists, sessionId);
}

public class Picklist_MDT { 
    public static void addPicklistValues(Schema.DescribeSObjectResult sObjectDescribe, String picklistFieldName, List<String> picklistValueNames, String sessionId) {
        MetadataService.MetadataPort service = createService(sessionId);
        ...    
    }

     public static MetadataService.MetadataPort createService(String sessionId) {
        MetadataService.MetadataPort service = new MetadataService.MetadataPort();
        service.SessionHeader = new MetadataService.SessionHeader_element();
        service.SessionHeader.sessionId = sessionId;
        return service;
    }
}

And called the addContactPicklistPreference method from trigger context passing it the UserInfo.getSessionId() value.

Problem

This worked just fine in salesforce classic but not in lightning, when it triggers in lightning I'm getting System.CalloutException: Web service callout failed: WebService returned a SOAP Fault: INVALID_SESSION_ID.... Funny thing is that if I put a System.debug(UserInfo.getSessionId()); before calling the addContactPicklistPreference method it prints a valid session Id in both classic and lightning.

I would really appreciate any input about this.

23

So, as it turns outs there is no way of getting a valid API-capable Session Id from lightning component, here is a quote from the partner discussion forums:

Three things:

  1. There's no $Api global variable in Lightning Components today. So, {! $Api.whatever } will always fail.

  2. This is intentional at this time. There's no official/supported way to get an API-capable session ID in Lightning Components. Again, this is by design.

  3. With only a little creativity and cleverness you can create a bare-bones Visualforce page with {! $Api.SessionID }, and then call getContent(thatPage) from Apex to get a page that contains an API-capable session ID, which you should be able to parse out easily enough.

From there, it's not hard to pass that session ID along to Lightning Components via an @AuraEnabled method.

But let's be clear: this is a hack, the performance will be poor, and there's good reasons to keep an API-capable session ID out of your Lightning Components code.

So, I ended-up building the not recommended hack as a workaround for this to work in Lightning.

Just create a simple Visualforce Page as:

<apex:page>
    Start_Of_Session_Id{!$Api.Session_ID}End_Of_Session_Id
</apex:page>

And then get the Session Id with the visualforce_Page.getContent() method, something like:

public static MetadataService.MetadataPort createService()
{
    MetadataService.MetadataPort service = new MetadataService.MetadataPort();
    service.SessionHeader = new MetadataService.SessionHeader_element();
    //service.SessionHeader.sessionId = UserInfo.getSessionId();
    service.SessionHeader.sessionId = Utils.getSessionIdFromVFPage(Page.SessionId);
    return service;
}

Where

global class Utils {
    global static String getSessionIdFromVFPage(PageReference visualforcePage){
        String content = visualforcePage.getContent().toString();
        Integer s = content.indexOf('Start_Of_Session_Id') + 'Start_Of_Session_Id'.length(),
                e = content.indexOf('End_Of_Session_Id');
        return content.substring(s, e);
    }
}

And then you got a valid session id to work with.

  • 4
    interesting...amounts to the same, but I created a page like this, makes parsing a bit easier <apex:page contentType="application/json" > { "sessionId": "{!$Api.Session_ID}" } </apex:page> – Phil Hawthorn Jan 9 '17 at 20:08
  • I have same requirement and I am using your trick but I am getting some error Page session id Doesn't exist . cloud you please help me . – Nagaraja SV Jan 19 '17 at 14:48
  • @NagarajaSV I think your org is having Lightning locker service enabled. This approach will not work with locker service enabled. – Jai Chaturvedi Mar 6 '17 at 1:09
  • One way I think might work, but haven't tried. Try firing up the event with session Id as param from vf page and then catch that in lightning component and assign it to an attribute. – Jai Chaturvedi Mar 6 '17 at 1:10
  • Hi @NagarajaSV, do you have 'My Domain' (under Setup - Domain Management - My Domain) enable? This will prevent you from making a callout to the same domain so the MetadataService won't work. – Jose Mar 16 '17 at 21:45
3

According to the known issue Summer'17- Generating a Session Id from Lightning Domain Provides Invalid Session Id (W-3981698) it should be possible to pass UserInfo.getSessionId() out to lightning component via an @AuraEnabled an use it for API calls.

This should be possible from Winter '18 onward.

  • I say should here as I've had someone tell me this still isn't working for them. – Daniel Ballinger Mar 20 '18 at 1:44
  • 7
    UserInfo.getSessionId() doesn't work in Summer 18 – Matthew Mitchener Jun 8 '18 at 16:06
  • Can anyone please confirm if this is actually fixed. I am on ApiVersion 45. From lightning component, I am calling controller, which in turn calls the MetadataService class to deploy trigger metadata. In metadata service I am using "UserInfo.getSessionId()" but still getting the same error as mentioned here. Am I missing something? – MandyKool Aug 30 at 21:18

protected by Community Apr 3 '18 at 12:58

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

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