1

How can I call a Metadata API from a LWC controller. I have a class which used Metadata API to fetch Entitlement process details which works fine in anonymous block but not with LWC

My code is below :

public with sharing class FetchEntitlementMetadata {

@AuraEnabled
public static string fetchData(){
// Instantiate the Metadata API service
MetadataService.MetadataPort metadataPort = new MetadataService.MetadataPort();
metadataPort.SessionHeader = new MetadataService.SessionHeader_element();
metadataPort.SessionHeader.sessionId = UserInfo.getSessionId();
// Specify the type (Entitlement Process)
String metadataType = 'EntitlementProcess';

// Fetch all Entitlement Process names from the org
MetadataService.ListMetadataQuery query = new MetadataService.ListMetadataQuery();
query.type_x = metadataType; // Use "type_x" to specify the metadata type
System.debug(UserInfo.getSessionId());
MetadataService.FileProperties[] fileProperties = metadataPort.listMetadata(new List<MetadataService.ListMetadataQuery> { query }, 50);

Set<String> entitlementProcessNames = new Set<String>();

for (MetadataService.FileProperties fileProperty : fileProperties) {
    // Decode the name to make it proper
    String decodedName = EncodingUtil.urlDecode(fileProperty.fullName, 'UTF-8');
    entitlementProcessNames.add(decodedName);

    System.debug('******' + decodedName);
}
List<String> fullNames = new List<String>{'Test'};

// Map to store process name as key and its values as category names and time in minutes
Map<String, Map<String, Integer>> processMap = new Map<String, Map<String, Integer>>();
// Read metadata for the specified Entitlement Process
MetadataService.IReadResult readResult = metadataPort.readMetadata(metadataType, fullNames);
MetadataService.Metadata[] mdInfo = readResult.getRecords();
// Process the metadata
for (MetadataService.Metadata md : mdInfo) {
    if (md instanceof MetadataService.EntitlementProcess) {
        MetadataService.EntitlementProcess entitlementProcess = (MetadataService.EntitlementProcess) md;
        String processName = entitlementProcess.fullName;

        for (MetadataService.EntitlementProcessMilestoneItem milestone : entitlementProcess.milestones) {
            // Filter milestones that have milestoneCriteriaFilterItems with 'Case.Category__c'
            List<MetadataService.FilterItem> criteriaFilterItems = milestone.milestoneCriteriaFilterItems;

            if (criteriaFilterItems != null) {
                for (MetadataService.FilterItem filterItem : criteriaFilterItems) {
                    if ('Case.Category__c'.equals(filterItem.field)) {
                        String milestoneName = milestone.milestoneName;
                        Integer minutesToComplete = milestone.minutesToComplete;
                        String values = filterItem.value;

                        // Split the comma-separated values and create entries in the map
                        List<String> individualValues = values.split(',');
                        for (String individualValue : individualValues) {
                            if (!processMap.containsKey(processName)) {
                                processMap.put(processName, new Map<String, Integer>());
                            }
                            processMap.get(processName).put(individualValue, minutesToComplete);
                        }
                    }
                }
            }
        }
    }
}
// Print the process map
System.debug('Process Map: ' + JSON.serializePretty(processMap));
return JSON.serialize(processMap);

}

}

When i run this in developer console using anonymous block it works fine, but when I use it in LWC it returns an error as below :

error{"status":500,"body":{"exceptionType":"System.CalloutException","isUserDefinedException":false,"message":"Web service callout failed: WebService returned a SOAP Fault: INVALID_SESSION_ID: This session is not valid for use with the API faultcode=sf:INVALID_SESSION_ID faultactor=","stackTrace":"Class.MetadataService.MetadataPort.listMetadata: line 13519, column 1\nClass.FetchEntitlementMetadata.fetchData: line 21, column 1"},"headers":{},"ok":false,"statusText":"Server Error","errorType":"fetchResponse"}

How can this be used in LWC? this is my LWC JS code

import { LightningElement } from 'lwc'; import fetchData from '@salesforce/apex/FetchEntitlementMetadata.fetchData'; export default class Metadatafetch extends LightningElement {

connectedCallback(){
    this.fetchData();
}

fetchData() { fetchData().then(result => { console.log('££££'+JSON.stringify(result)) }).catch(error => { console.log('error'+JSON.stringify(error)); }) }

}

Sfdc
  • 29
  • 4

1 Answers1

2

You will need a more powerful session to access Salesforce Metadata API.

When you invoke Apex from LWC it uses session Id from lightning domain which does give access to the Salesforce metadata due to security issues.

Here are couple of approved approaches

1. Using VF Page for a proxy session

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

Apex Utility

   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);
      }
    }

Then you will need something below

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;
 }

2. Using a Named Credential approach like this article talks about.

Mohith Shrivastava
  • 91,131
  • 18
  • 158
  • 209