5

I am trying to create Approval process using Metadata API but unable to figure out what i am missing.

One doubt is XML don't have any symbol why which me or Salesforce could identify on which object i am creating the approval process.

I have tried by using wsdl and REST API but no success.

Receiving error:

soapenv:ClientMust specify a {http://www.w3.org/2001/XMLSchema-instance}type attribute value for the {http://soap.sforce.com/2006/04/metadata}metadata element

Using WSDL:

        MetadataService_Chris.Metadata_Chris service = new MetadataService_Chris.Metadata_Chris();
        service.SessionHeader = new MetadataService_Chris.SessionHeader_element();
        service.SessionHeader.sessionId = UserInfo.getSessionId();

        MetadataService_Chris.ApprovalProcess process = new MetadataService_Chris.ApprovalProcess();
        process.fullName = 'TestApproval';
        process.active = true;
        process.allowRecall = true;

        MetadataService_Chris.ApprovalSubmitter submitter = new MetadataService_Chris.ApprovalSubmitter();
        submitter.type_x = 'user';
        submitter.submitter = UserInfo.getUserId();

        List<MetadataService_Chris.ApprovalSubmitter> submitters = new List<MetadataService_Chris.ApprovalSubmitter>();
        submitters.add(submitter);

        process.allowedSubmitters = submitters;
        process.label = 'Test Approval';
        process.showApprovalHistory = true;

        //MetadataService_Chris.Metadata mdata = new MetadataService_Chris.Metadata();
        //mdata.fullName = 'Test Metadata';

        MetadataService_Chris.AsyncResult[] results = service.create(new List<MetadataService_Chris.Metadata> { process });
        System.debug('----------------------------------  '+results);

Using REST:

        HTTP h = new HTTP();
        HTTPRequest req = new HTTPRequest();
        req.setMethod('POST');
        req.setHeader('Content-Type', 'text/xml');
        req.setHeader('SOAPAction', 'create');

        String xml = /*'<?xml version="1.0" encoding="UTF-8"?>'+
        '<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">'+
           '<env:Header>'+
              '<SessionHeader xmlns="http://soap.sforce.com/2006/04/metadata">'+
                 '<sessionId>'+userinfo.getSessionId()+'</sessionId>'+
              '</SessionHeader>'+
           '</env:Header>'+
           '<env:Body>'+
              '<create xmlns="http://soap.sforce.com/2006/04/metadata">'+
                 '<metadata xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">'+ // xsi:type="Account"
                        '<active>false</active>'+
                        '<allowRecall>true</allowRecall>'+
                        '<allowedSubmitters>'+
                           '<submitter>005i00000033nPoAAI</submitter>'+
                           '<type />'+
                        '</allowedSubmitters>'+
                        '<label>Test Approval</label>'+
                        '<recordEditability />'+
                        '<showApprovalHistory>true</showApprovalHistory>'+
                 '</metadata>'+
              '</create>'+
           '</env:Body>'+
        '</env:Envelope>';

        req.setBody(xml);
        req.setCompressed(false);
        req.setEndpoint('https://na15.salesforce.com/services/Soap/m/29.0');
        HTTPResponse resp = h.send(req);
        System.debug(resp.getBody());

I have tried with several combinations but no success. In back-end XML formed as:

<?xml version="1.0" encoding="UTF-8"?>
    <env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
       <env:Header>
          <SessionHeader xmlns="http://soap.sforce.com/2006/04/metadata">
             <sessionId>00Di0000000k2kv!AR4AQBKoHVPE4sLzQbOuu.CAU0o1qsMldVzPRDGFStoboKik1lt_WUNe.5YiHR2MQOlrKExKf05TnYB_7SO7kPdwQGfjwNis</sessionId>
          </SessionHeader>
       </env:Header>
       <env:Body>
          <create xmlns="http://soap.sforce.com/2006/04/metadata">
             <metadata>
                <active>true</active>
                <allowRecall>true</allowRecall>
                <allowedSubmitters>
                   <submitter>005i00000033nPoAAI</submitter>
                   <type />
                </allowedSubmitters>
                <label>Test Approval</label>
                <recordEditability />
                <showApprovalHistory>true</showApprovalHistory>
             </metadata>
          </create>
       </env:Body>
    </env:Envelope>

The problem is "xsi:type="ApprovalProcess" which throws exception.

  • 2
    Your correct the issue is the xsi:type attribute this is critical to the Metadata API know what type of metadata your sending. By default the generated classes don't support this, i have worked up a library that addresses this here github.com/financialforcedev/apex-mdapi I'll work up and example and post an answer within the next hour or so for you. – Andrew Fawcett Mar 19 '14 at 9:07
  • 1
    @AndrewFawcett Thanks for the response. I have tried your mdapi source code with some modification but no luck. Extending with "Metadata" and adding "String type = 'ApprovalProcess'" didn't work for me. I hope you will get succeed. – Ashwani Mar 19 '14 at 10:16
  • I've got it working for you, check out my answer below. – Andrew Fawcett Mar 19 '14 at 11:57
4

I've just uploaded a new MetadataService.cls with support for this, you can also see in the MetadataSeviceExamples.cls class an example as follows below on how to do this. I'm not 100% what your issue was with your copy of the generated class, but at least you could compare with the one above if your interested.

    MetadataService.MetadataPort service = createService();
    MetadataService.ApprovalProcess approvalProcess = new MetadataService.ApprovalProcess();
    approvalProcess.fullName = 'Test__c.TestApproval';
    approvalProcess.label = 'Test Approval';
    approvalProcess.active = false;
    approvalProcess.allowRecall = false;
    approvalProcess.showApprovalHistory = true;
    approvalProcess.recordEditability = 'AdminOnly';
    approvalProcess.finalApprovalRecordLock = false;
    approvalProcess.finalRejectionRecordLock = false;
    approvalProcess.showApprovalHistory = false;
    MetadataService.ApprovalSubmitter submitter = new MetadataService.ApprovalSubmitter();
    submitter.type_x = 'user';
    submitter.submitter = UserInfo.getUserName();
    approvalProcess.allowedSubmitters = new List<MetadataService.ApprovalSubmitter> { submitter };
    MetadataService.AsyncResult[] results = service.create(new List<MetadataService.Metadata> { approvalProcess });

Finally, keep in mind (at least for now) there is only an Async option for the create operation, which means you need to check back using the checkAsync method the final status. For more information about doing this you can read this blog.

Once Spring'14 is released (later in April) i will update the library to support the new immediate create operation which does not require you to poll for the result.

  • Can we perform the Rest call on Metadata services? If so, could you give an example of creating a CustomObject salesforce.com/us/developer/docs/api_meta/Content/… ? Hopefully at this endpoint: /services/Soap/m/29.0 – sanchit Mar 19 '14 at 13:45
  • 1
    Sure here is an example of creating a Custom Object, github.com/financialforcedev/apex-mdapi/blob/master/apex-mdapi/…. When you say REST i think you actually mean sending the XML data directly without using the Apex classes, just checking? If so, you can do this if you want but not sure why you would do this when the classes are available? Anyway, to get the raw SOAP XML to send via HTTP methods, just run the example above and copy paste from the debug log the XML sent. – Andrew Fawcett Mar 19 '14 at 13:52
  • Can we still do it in the raw form, like @regal done in one of his example? pastebin.com/M3DgT2hn Actually my need is to retrieve some information like Field-Level-Security for a specific field using an external application like Java. Please suggest me as I'm not able to get Field-Level-Security using ListMetadataQuery class. – sanchit Mar 19 '14 at 14:00
  • 1
    Yes as i suggested above, you can use the HTTP classes in Apex to send raw XML, i don't have an example of that, other than my suggestion above, to use the Apex class approach from MetadataServiceExamples.cls and copy paste the XML from the debug log. As regards Java and Metadata API search Google for Salesforce WSC (Web Service Connector). – Andrew Fawcett Mar 19 '14 at 15:36
  • 1
    Regarding FLS security, you can query this information via SOQL (either directly in Apex or via Salesforce REST/SOAP API) the object called FieldPermissions, see ERD bottom of this, salesforce.com/us/developer/docs/api/Content/… – Andrew Fawcett Mar 19 '14 at 15:37

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.