The Salesforce wsdl2apex tool does not support a number of WSDL features. See Supported WSDL Features
That section does say:
Apex also supports the following schema constructs:
- xsd:attribute, in Apex code saved using API version 15.0 and later
However, I've found in practice you usually get the Unsupported Schema element found
error message that you encountered.
I've been working with an intern on an alternative WSDL to Apex converter to try and overcome a number of the limitations. You can get it for free from WSDL Parser and Apex Generator. Ensure you get v1.6.0 or later for WSDLs with attributes. It currently only supports Windows. (Full disclosure: I currently work for the company that creates this tool).
In this case the unsupported <xsd:attribute>
elements will be skipped from the WSDL and warnings added to the resulting apex. You will need to manually enforce the 3 character limit on the hexBinary
values.
Note that the Salesforce wsdl2apex tool doesn't support <xs:extension>
either. We should be able to handle this by replicating the Apex members from the base class and extending the parameters passed to WebService.invoke.
If you do have further issues, can you share your WSDL via some other means (DropBox, Google Docs, etc...). It will be easier to check it as a whole.
Essentially you're trying to implement a two-phase commit transaction with Salesforce yet that feature as of this writing is not supported.
Your next best option is a formal integration pattern that can capture errors (e.g. in a custom object) and report / notify people as-needed when the integration fails at any point so data inconsistencies can be fixed. So you'll really be making trade-offs on which system needs to have the change occur first and rely on that response on next actions.
Salesforce Integration Design Patterns
Please see Chapter 2: Remote Process Invocation - Request and Reply of the Salesforce Integration Patterns and Practices. This talks through similar process as you've described where a Visualforce page's controller will make Http Callout to external system and receive response.
Http Callouts and Uncommitted Work
Since Http Callouts cannot be done if any DML has occurred prior to the call, you will either need to:
- make the http callout before you do Salesforce DML, or
- do the Salesforce DML then perform http callout asynchronously such as Queueable job or @Future method
See this help article with more details on the use case and example code.
Recommendations
In case of update to any contact what could be the best approach. Update operation can be performed from Visualforce or standard UI. Please note, we need to make a single synchronous call to external system.
Option 1: I would make the http callout, if successful, then continue with DML operation in Salesforce, else display error to user. This prevents Salesforce data from being created unless external system is successful.
Option 2: Save data to Salesforce immediately. Use near real-time integration (batchable or middleware) to query for un-synced records and post to external system keeping log of any integration errors. If errors, escalate to proper teams to investigate. Salesforce user-experience will be faster, non-blocking, and external system will eventually catch up.
In case of deleting a contact what could be the best approach. Delete operation will be perform from salesforce standard UI.
Option 1: If deletion occurs from standard delete API or standard delete button then you will only know this is happening in an apex trigger. At this point you have no opportunity to make a real-time http callout due to uncommitted work. You would have to use asynchronous apex to make http callout. The record in Salesforce would be deleted and external system may or may not be successful. You can use the undelete API method in Salesforce to restore the data if necessary, but I would worry more on why external system failed to delete and keep pushing forward, not undo it.
Option 2: Override the standard Delete action with a custom Visualforce page and Apex class. Make the http callout first, if successful then perform delete DML in apex, else display error to user. This prevents Salesforce data from being deleted unless external system is successful.
Option 3: If external system is the master in your Master Data Management scenario, then deletes in Salesforce should only ever be supported as a request to delete and not allow people to physically delete in Salesforce outside of an integration user doing the DML delete operation triggered by external system.
Personal Preference if Salesforce is "Source of Truth"
I prefer to use near real-time integrations where the external system is eventually consistent with data mastered in Salesforce.
To achieve this, I would use Batchable Apex or Middleware ETL to query for inserted/updated/deleted records using below methods then make necessary Http Callouts. Ideally, the end system could receive bulk records and perform changes all at once so as to reduce the chattiness of the integration. You would need to store in perhaps a custom setting the last timestamp when you ran these methods so that you can use it as the next start time in the next calls.
Database.getUpdated( sobjectType, startDateTime, endDateTime )
and
Database.getDeleted( sobjectType, startDateTime, endDateTime )
Apex Documentation.
SOAP API Documentation.
Also be thinking about if an error occurs and a record fails to sync then it may be left out of the next window when you call getUpdated / getDeleted. Error records may need to be re-processed in a separate batch scenario, which means you'd need a flag on the record or in some tracking object to know which records are failing and still need to be resolved.
Best Answer
If you have defined your Apex class as global and your method as a webservice, then in Salesforce, you should be able to generate the WSDL.
Find the class and click on the WSDL link to the left in the list of Apex classes. https://login.salesforce.com/01p