9

Question

Is it possible to create or update a NamedCredential from within Apex?

Investigation

I looked into available options, and it seems the SOAP API only supports the describeSObjects(), query(), and retrieve() operations. The Tooling API doesn't seem to support the object at all. But the Metadata API does!

Let's play around with some scripts in Execute Anonymous and see what we can do with the Apex Wrapper Salesforce Metadata API.

Working with the Metadata API

Helper Methods

Here are some bare-bones helper methods pulled out of the MetadataServiceExamples class:

static MetadataService.MetadataPort createService()
{
    MetadataService.MetadataPort service = new MetadataService.MetadataPort();
    service.SessionHeader = new MetadataService.SessionHeader_element();
    service.SessionHeader.sessionId = UserInfo.getSessionId();
    return service;
}
static List<MetadataService.SaveResult> createMetadata(MetadataService.Metadata input)
{
    return createService().createMetadata(new List<MetadataService.Metadata> { input });
}
static List<MetadataService.SaveResult> updateMetadata(MetadataService.Metadata input)
{
    return createService().updateMetadata(new List<MetadataService.Metadata> { input });
}
static List<MetadataService.DeleteResult> deleteMetadata(String type, String fullName)
{
    return createService().deleteMetadata(type, new List<String> { fullName });
}

NamedCredential

I tried to run a NamedCredential through these methods and was not able to get createMetadata or updateMetadata to work. The errors are included below. To my surprise, however, deleteMetadata runs successfully if the other methods are left out. I don't see any mention in the documentation of which operations are supported...

Script

MetadataService.NamedCredential credential = new MetadataService.NamedCredential();
credential.fullName = 'Demo_Credential';
credential.label = 'Demo Credential';
credential.endpoint = 'https://www.DEMO2.com';

system.debug(createMetadata(credential));
system.debug(updateMetadata(credential));
system.debug(deleteMetadata('NamedCredential', credential.fullName));

Error on createMetadata

Line: 11534, Column: 1
System.CalloutException: Web service callout failed: WebService returned a SOAP Fault: '' is not a valid value for the enum 'ExternalPrincipalType' faultcode=soapenv:Client faultactor=

Error on updateMetadata

Line: 11364, Column: 1
System.CalloutException: Web service callout failed: WebService returned a SOAP Fault: '' is not a valid value for the enum 'ExternalPrincipalType' faultcode=soapenv:Client faultactor=

ApexPage

Just to make sure my installation works correctly, I slightly tweaked some examples from the MetadataServiceExamples class. The createMetadata(), updateMetadata(), and deleteMetadata() calls each work fine for the ApexPage type.

Script

MetadataService.ApexPage apexPage = new MetadataService.ApexPage();
apexPage.apiVersion = 25;
apexPage.fullName = 'test';
apexPage.label = 'Test Page';
apexPage.content = EncodingUtil.base64Encode(Blob.valueOf('<apex:page/>'));

system.debug(createMetadata(apexPage));
system.debug(updateMetadata(apexPage));
system.debug(deleteMetadata('ApexPage', apexPage.fullName));

Abbreviated Logs

(SaveResult:[errors=null, fullName=test, success=true])
(SaveResult:[errors=null, fullName=test, success=true])
(DeleteResult:[errors=null, fullName=test, success=true])

  • 1
    On the docs developer.salesforce.com/docs/atlas.en-us.api_meta.meta/… there's a ExternalPrincipalType enum. Maybe you need to put a value while setting up the named credential? – Fernando Gavinho Mar 22 '17 at 17:52
  • You'll probably need a protocol as well – Fernando Gavinho Mar 22 '17 at 17:53
  • 1
    @FernandoGavinho Interesting, I searched for it in the documentation but didn't see it for some reason. – Adrian Larson Mar 22 '17 at 17:53
  • @FernandoGavinho Right on both counts. If you can whip up an answer that verifies as much I will accept it. Not sure if you have the package installed anywhere... If it's too much work I can also answer it myself. – Adrian Larson Mar 22 '17 at 18:05
5

You need to add two required "fields" to your request: principalType and protocol (you can see the entire list of fields here)

Script

MetadataService.NamedCredential credential = new MetadataService.NamedCredential();
credential.fullName = 'Demo_Credential';
credential.label = 'Demo Credential';
credential.endpoint = 'https://www.DEMO2.com';
credential.principalType = 'NamedUser';
credential.protocol = 'NoAuthentication';

system.debug(createMetadata(credential));
system.debug(updateMetadata(credential));

Abbreviated logs

(SaveResult:[errors=null, fullName=Demo_Credential, success=true)
(SaveResult:[errors=null, fullName=Demo_Credential, success=true)

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.