2

This is my first time using WSDL2Apex, or attempting a Callout to a 3rd party API, so please bare with me.

Implementation Goal: Contract is a child-object of Quote. When a new Contract record is created for a Quote, all other Contracts that are associated with that Quote and not Signed should be expired via Webservice Callout to Sertifi's API.


Sertifi's Web Service is accessible via: https://apps.sertifi.net/services/gateway.asmx

The Method I am attempting to use is SetDocumentParameters


I received a WSDL file from the 3rd party (Sertifi) and followed instructions on how to create Apex classes automatically by uploading the WSDL into Apex Classes. This resulted in 3 classes being created:

  1. appsSertifiNetServicesLinkparameter
  2. appsSertifiNetServices
  3. AsyncAppsSertifiNetServices

Within the appsSertifiNetServices class, I found the portion that I believe I need to send parameters to (I cut out a large portion of this WSDL class that doesn't matter, apologies if the code context is not 100% accurate):

public class appsSertifiNetServices {
public class SetDocumentParametersResponse_element {
    public String SetDocumentParametersResult;
    private String[] SetDocumentParametersResult_type_info = new String[]{'SetDocumentParametersResult','http://apps.sertifi.net/services/',null,'0','1','false'};
    private String[] apex_schema_type_info = new String[]{'http://apps.sertifi.net/services/','true','false'};
    private String[] field_order_type_info = new String[]{'SetDocumentParametersResult'};
}

public class SetDocumentParameters_element {
    public String p_ApiCode;
    public String p_FileID;
    public String p_DocumentID;
    public String p_XMLContent;
    private String[] p_ApiCode_type_info = new String[]{'p_ApiCode','http://apps.sertifi.net/services/',null,'0','1','false'};
    private String[] p_FileID_type_info = new String[]{'p_FileID','http://apps.sertifi.net/services/',null,'0','1','false'};
    private String[] p_DocumentID_type_info = new String[]{'p_DocumentID','http://apps.sertifi.net/services/',null,'0','1','false'};
    private String[] p_XMLContent_type_info = new String[]{'p_XMLContent','http://apps.sertifi.net/services/',null,'0','1','false'};
    private String[] apex_schema_type_info = new String[]{'http://apps.sertifi.net/services/','true','false'};
    private String[] field_order_type_info = new String[]{'p_ApiCode','p_FileID','p_DocumentID','p_XMLContent'};
}

public String SetDocumentParameters(String p_ApiCode,String p_FileID,String p_DocumentID,String p_XMLContent) {
        appsSertifiNetServices.SetDocumentParameters_element request_x = new appsSertifiNetServices.SetDocumentParameters_element();
        request_x.p_ApiCode = p_ApiCode;
        request_x.p_FileID = p_FileID;
        request_x.p_DocumentID = p_DocumentID;
        request_x.p_XMLContent = p_XMLContent;
        appsSertifiNetServices.SetDocumentParametersResponse_element response_x;
        Map<String, appsSertifiNetServices.SetDocumentParametersResponse_element> response_map_x = new Map<String, appsSertifiNetServices.SetDocumentParametersResponse_element>();
        response_map_x.put('response_x', response_x);
        WebServiceCallout.invoke(
          this,
          request_x,
          response_map_x,
          new String[]{endpoint_x,
          'http://apps.sertifi.net/services/SetDocumentParameters',
          'http://apps.sertifi.net/services/',
          'SetDocumentParameters',
          'http://apps.sertifi.net/services/',
          'SetDocumentParametersResponse',
          'appsSertifiNetServices.SetDocumentParametersResponse_element'}
        );
        response_x = response_map_x.get('response_x');
        return response_x.SetDocumentParametersResult;
}
}

However, in my code I am trying to do the following in a loop for each contract document:

appsSertifiNetServices.SetDocumentParameters(ApiCode,FileID,DocumentID,XMLContent);

and I receive the following error: Compile Error: Method does not exist or incorrect signature: appsSertifiNetServices.SetDocumentParameters(String, String, String, String) at line 229 column 11

I have never used WSDL2Apex before, and the class created seems slightly different than those that I write on my own so I'm sure I'm not passing this data correctly, but unsure of how I should proceed. What am I doing wrong?


Full Apex Class for those who may want to see it for Troubleshooting:

public without sharing class EContractTriggerHandler {

    public EContractTriggerHandler(){
    }

    public static void expireRelatedDocuments(List<Sertifi2_0__TestContract__c> newContracts){

        // Grab APICode from Sertifi Application Settings (Custom Setting)
        Sertifi2_0__SertifiSettings__c settings = Sertifi2_0__SertifiSettings__c.getValues('Settings');
        String ApiCode = settings.Sertifi2_0__APICode__c;
        System.debug('MM++++ ApiCode Echo: ' +ApiCode);


        // Create ID set for EContract IDs
        Set<Id> QuoteIds = new Set<Id>();
        Set<Id> EContractIds = New Set<Id>();
        Map<Id,Sertifi2_0__TestContract__c> contractMap = new Map<Id,Sertifi2_0__TestContract__c>();

        // Gather EContractID from related Sertifi eContract and add to the ID Set
        for(Sertifi2_0__TestContract__c contract : newContracts){
            QuoteIds.add(contract.Quote__c);

            contractMap.add(contract.Id,contract);
        }

        List<Sertifi2_0__EDocument__c> eContractList = [SELECT Id,Sertifi2_0__,Sertifi2_0__TestContract__r.Sertifi2_0__ContractID__c,Sertifi2_0__Name__c,Sertifi2_0__Sertifi_ID__c,Sertifi2_0__Status__c 
                                                        FROM Sertifi2_0__TestContract__c 
                                                        WHERE Sertifi2_0__TestContract__c IN :EContractIds
                                                        AND Sertifi2_0__Status__c != 'Signed'];


        if(eDocumentList.size() > 0){
            for(Sertifi2_0__EDocument__c edoc : eDocumentList){
                String FileID = edoc.Sertifi2_0__TestContract__r.Sertifi2_0__ContractID__c;
                String DocumentID = edoc.Sertifi2_0__Sertifi_ID__c;
                String XMLContent = '<?xml version="1.0" encoding="UTF-8"?><document><parameter name="changeExpiredStatus" value="expire" />document>';

                System.debug('MM++++ ApiCode:' +ApiCode);
                System.debug('MM++++ FileID:' +FileID);
                System.debug('MM++++ DocumentID:' +documentID);
                System.debug('MM++++ XMLContent:' +XMLContent);
                appsSertifiNetServices.SetDocumentParameters(ApiCode,FileID,documentID,XMLContent);
            }
        }
    }
}
  • The method SetDocumentParameters is not a static member of the class appsSertifiNetServices. You need to create object of the class appsSertifiNetServices and then invoke the method. – javanoob Apr 11 '16 at 22:22
2

There will be an inner class generated in appsSertifiNetServices that you need to create an instance of first to call the required method. When I tried the inner class in question was called GatewaySoap. It will be the class that has

public String endpoint_x = 'https://apps.sertifi.net/services/gateway.asmx';

So, create an instance of this inner class, and call the method on that.

appsSertifiNetServices.GatewaySoap webServiceInstance = new appsSertifiNetServices.GatewaySoap();
string result = webServiceInstance.SetDocumentParameters(ApiCode,FileID,documentID,XMLContent);

If needed, you might also like to adjust the timeout_x on the GatewaySoap.


I notice that that WSDL has a number of elements that usually cause problems for Wsdl2Apex. Namely, the <s:import> elements and `' Soap 1.2 bindings. You can solve these by manually updating the WSDL by hand as you have done. If you get tired of that, I've made a free tool that will automate the process. See the FuseIT SFDC Explorer.

  • This was exactly what I was missing, thank you! The class was called GatewaySoap as you suggested, and after creating a new instance of it I was able to pass my params to the SetDocumentParameters method. NOW I am receiving a result string of Error: Invalid XML but at least that tells me that I am reaching the API correctly and receiving a response. Not the response I'd hoped for, but at least a response :) I sent an email to Sertifi hoping they can help me clear up the syntax problem in the XML. Thank you Daniel! – Morgan Marchese Apr 12 '16 at 18:00
  • You might find it helpful to capture the CALLOUT_REQUEST and CALLOUT_RESPONSE from the debug log. This will show the raw SOAP XML request that is being sent to the web service. You could then compare this with the request generated by a tool like SOAP UI. – Daniel Ballinger Apr 12 '16 at 19:24
1

>> alternate approach" answer in case WSDL2Apex doesn't work out!

As @Daniel pointed out, there are components in your WSDL that may not play nicely with WSDL2Apex. WSDL rework may solve your problems, but if not... read on.

I've encountered several grumpy WSDLs where rework was not possible or not feasible in a timely manner. In these situations I found it best to ignore the WSDL2Apex machine altogether and instead approach the integration as a simple "DIY" exercise with custom parsing engine.

Here is an alternate route you could take if WSDL2Apex just isn't happening for whatever reasons:
1) SoapUI (3rd party tool, nothing to do w/ SFDC).
2) Load your WSDL there.
3) Be able to successfully connect to your service.
4) IMPORTANT: make sure you understand both the REQUEST PAYLOAD and the RESPONSE PAYLOAD. They are simple XML strings. Its on YOU to understand the service you are attempting to connect with.
5) Once you understand the service well enough via SoapUI, take your learnings back into Apex and build out your simple parser engine, and wrap the HttpRequest/ HttpResponse pattern into wrapper class. The parser is basically a bunch of methods accepting string(s), and returning strings and/or maps of strings.

Ultimate goal of the parser is 1) be able to generate string payload(s) you will send over in your HttpRequest.setBody() to the various endpoints of your service, and 2) be able to parse the XML string response that comes back and do actionable things with them e.g. feed your method the string xml payload from HttpResponse.getBody(), the method parses and returns a map of key-value pairs that you then can DML, or pass to other methods or subsequent callouts. Use string methods to find the exact elements you are interested in e.g.

<ns1:LocationId>some fancy location id</ns1:LocationId>.<br>

Here is how you could parse that:

String xmlResponse = '<some xml response>';//this is the XML response from your HttpResponse.getBody();
Integer locationID_1 = xmlResponse.indexOf('<ns1:LocationId>')+'<ns1:LocationId>'.length();
Integer locationID_2 = xmlResponse.indexOf('</ns1:LocationId>');

Then, you use those integers to bound the value of the element of interest, just reexamine the string XML response and grab what you need e.g.

String locationIDfromWebService = xmlResponse.substring(locationID_1, locationID_2)

It can get complicated if there are multiple elements with same names, but at the end of the day its just string parsing and logic games to get exactly what you need out of the XML response.

** If your endpoint is online and accessible (no firewalls or weirdness), and you actually understand a valid payload, you could have a POC up in hours, not days. I assure you of that. **

Reach out if you want to learn more about the DIY approach.

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.