11

I'm trying to deploy a managed package that will create some new fields on the Opportunity object on install using the Metadata API.

  1. The entire code in the callout method below works in my dev org (i.e. it makes the HTTP call to my test endpoint and also creates the field).
  2. The HTTP call works fine by itself when I install the package in a different org (because I've added my test endpoint URL to Remote Site Settings).
  3. When I add in the Metadata API part, the package successfully installs, I get no error notifications, but it doesn't call my HTTP endpoint or create the field.

I'm using https://github.com/financialforcedev/apex-mdapi.

global class ReadMetadata implements InstallHandler {
    
    global void onInstall(InstallContext context) {
        createField(UserInfo.getSessionId());
    }
    
    @future(callout=true)
    public static void createField(String sessionId){
        // For logging debug statements.
        Http http = new Http();
        HttpRequest request = new HttpRequest();
        request.setEndpoint('<TEST_ENDPOINT>');
        request.setMethod('POST');
        request.setHeader('Content-Type', 'application/json');
                        
        MetadataService.MetadataPort service = new MetadataService.MetadataPort();
        service.SessionHeader = new MetadataService.SessionHeader_element();
        service.SessionHeader.sessionId = sessionId;
        
        MetadataService.Metadata[] newFields = new MetadataService.Metadata[]{};
        MetadataService.SaveResult[] results;
        
        MetadataService.CustomField customField = new MetadataService.CustomField();
        customField.fullName = 'Opportunity.foo__c';
        customField.label = 'Foo';
        customField.type_x = 'DateTime';
        newFields.add(customField);
        results = service.createMetadata(newFields);
        
        request.setBody('{\n"result":"' + String.valueOf(results) + '"\n}');
        HttpResponse response = http.send(request);
        System.debug(response.getStatusCode());

    }
}

Any ideas on what the issue might be?

4
  • 4
    Can you explain why you don't simply include the custom fields as part of your managed package?
    – Phil W
    Commented Nov 21, 2020 at 16:15
  • The install handler runs under a dedicated profile which AFAIK is not well documented: perhaps that profile blocks this operation or there is no way to get the remote site settings set up in advance to allow the HTTP call. We use a Setup page for anything that has to be dynamic (i.e. not included all the time) and documented instructions for that page to be used after an install or upgrade.
    – Keith C
    Commented Nov 21, 2020 at 20:58
  • @PhilW I'm trying to generate new fields based on picklist values from an existing field, but I won't know in advance what the picklist values are. Commented Nov 22, 2020 at 17:48
  • @KeithC I think the setup page is the way I'll go. Thanks! Commented Nov 22, 2020 at 17:48

1 Answer 1

11

The postinstall context for a managed package is fairly weird. It comes with a laundry list of restrictions:

  • It can initiate batch, scheduled, and future jobs.
  • It can’t access Session IDs.
  • It can only perform callouts using an async operation. The callout occurs after the script is run and the install is complete and committed.
  • It can’t call another Apex class in the package if that Apex class uses the with sharing keyword. This keyword can prevent the package from successfully installing.

It's the lack of a Session Id or API-enabled Session Id (I'm not entirely clear on which is the case) that's getting you here. You'll have to defer calls to the Metadata API to a point when your packaged code can execute in a real user context (and one which has appropriate permissions to call the Metadata API).

Separately, I'd suggest that doing this kind of a complex and risky operation in a postinstall script isn't a good idea anyway, because postinstall scripts are notoriously tricky to troubleshoot and can be failure-prone due to the aforementioned little-understood restrictions.

Further, there's an expectation that installing a managed package should be an inert operation with respect to the subscriber org's unmanaged metadata. Automating MDAPI calls to mutate subscriber-owned metadata without the full and informed consent of the admin is a risk both to the stability of the application and the trust of the user.

3
  • 1
    Those are good points. I'm going to change my approach and do this in a setup page after installation is complete. Thanks for your help. Commented Nov 22, 2020 at 17:50
  • 2
    Worth bringing out the "ghost user" issues here too IMHO. Avoiding setting up schedulables is the installer is a good idea, since these can misbehave when running after the fact due to weird limitations on this not-quite-a-user.
    – Phil W
    Commented Nov 22, 2020 at 18:26
  • 4
    @David Reed this is one of the best StackExchange answers I've ever seen. Short, direct, and packed full of incredibly valuable, hard-earned knowledge. Only now, years into understanding managed packages in practice can I fully appreciate just how damn good this answer is. Thank you! Commented Dec 23, 2021 at 13:16

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .