Tuesday, November 22, 2022

#939 OIC -> Working with the AdobeSign Adapter Part II

Carrying on from the previous post - 

Multiple Signers


first area I want to cover is multiple signers.
I try this out in Postman to check the proper input payload format - 

{
   "documentCreationInfo": {
       "signatureType""ESIGN",
       "recipientSetInfos": [
           {
               "recipientSetMemberInfos": [
                   {
                       "email""signer1@gmail.com"
                   },
                    {
                       "email""signer2@gmail.com"
                   }
               ],
               "recipientSetRole""SIGNER"
           }
        ],
       "signatureFlow""SENDER_SIGNATURE_NOT_REQUIRED",
       "message""Please Sign this from us!",
       "fileInfos": [
           {
               "transientDocumentId""3AAABLblqZhAyspx613JK3tJq6cUmD-dKzXZm95pfWOY_wkXEVnuy4aXg3JLnPnPCOGjkMnQfnvl7jObWlKB81fS0_vwZiYgq1J-zMa9KYxuE0THz7JZpVflhnKlV9lcZmtZnNwMYELmg_dW4MkjTD8E04Y-bu_mQolAB_Pbjbe0c7gJouPEOKYr6MVinoC0PTl7IyqjXor45xtXDqt3iO6VIezm_bt0ZtQBCTzfz530wv-innjXk8ItuAJY_EXqVAqh-a_xMROmmo3r4r3nsDoYSUvYuL5XE8OlA8Zr4VpQ3hIAxmJ7Yy7LlMnKPG1ZJFeqRonntSlP-kyYIlts-8J__AI2HQtI3"
           }
       ],
       "name""agreementNewEmpContract"
   },
   "options": {
        "noChrome"true,
        "authoringRequested"false,
        "autoLoginUser"true
}
}

So now I know what I need to re-create in OIC -

My trigger request is now as follows - 




 








The mapping for SendAgreement is as follows -





Checking on Agreement Status

So the document is now out for signing. You want to take the next steps, once all signers have done the necessary. First step is to check agreement status - here is the basic OIC scheduled integration flow for such - 



The AdobeSign Invoke is configured as follows - 


























As you can see, I am retrieving a list of agreements and then checking the status of each. I could, of course, use the Get Agreement Status operation, if I was only interested in a specific agreement. 

Get Agreement List for User allows me to add a query parameter to the request.


The query can be set as follows - 










I could also set this to the title of the document etc.

I run the integration and see 3 agreements have been returned -



I validate in AdobeSign -











Currently, the OIC AdobeSign adapter cannot be dropped as a trigger, so polling is the way to go here. 
However, AdobeSign supports webhooks - 


 

We can design an OIC integration that implements the required methods. check out the adobe docs for details.









Monday, November 21, 2022

#938 OIC -> Working with the Adobe Sign Adapter Part I



Use case here is e-Signing a pdf. Here I will leverage Adobe Sign, via OIC, of course.

Step 1 - Get Access to Adobe Sign 

I am using the 30 day free trial.

Step 2 - Get Adobe Client ID/Secret



As you can see, I require these values from Adobe. I login to Adobe Sign and go to Profile




API Applications -> Let's create one...







I enter the OIC Redirect URI.


The next section covers scopes - I select the following - 





Click on View to see Client ID and Secret - 



All I need now is the Subdomain value - in my case, secure.eu2.


I enter the following in the Scope field - user_login:account agreement_read:account agreement_write:account agreement_send:account library_read:account


I click Provide Consent and then am prompted to login to AdobeSign. This I do - 


I return to the OIC connection definition and click Test













Step 3 - Upload Document / Send for Signing

The AdobeSign api works as follows -

1. Invoke the doc upload api and receive back a transientDocumentId.
2. Invoke the send for signing api - passing the transientDocumentId and the email address of the signer.

Simple enough - here's my doc - 














I upload this to a folder on the OIC File Server.
I will download and read this in the OIC integration, then upload the doc to AbobeSign.

Here is that part of the integration - 


 






















Nothing wild or wonderful so far. The ReadFile is configured for the following content format -









Now to invoking AdobeSign - first invoke - upload document. The invoke is configured as follows - 





















The Map is configured as follows - 
MimeType set to "application/pdf".








Next step is to invoke the Send Agreement for Signature operation - 

























Map configured as follows - firstly, the payload from a test I did in Postman -
























Note authoringRequested = false. If this is set to true, an agreement will be created, but the signer will not receive an email. i.e. the agreement will be in Authoring status. I am pointing this out, because there may be use cases where this is what's wanted. Big thanks to my colleague Naveen N. for pointing this out to me.

Here's a screenshot of the mapping target - I don't go into great detail, but just fill in the fields shown in the JSON request screenshot.



Probably a good idea to use an OIC Lookup, instead of hardcoding the static values. 

The final step is to query the Agreement status and return this in the OIC integration response.

The key is the agreementId returned by Send Agreement for Signature.



Invoke GetAgreementStatus configuration - 



Map configuration - 












Ok, let's test - 



I check my email - 











Finally, I tested many times with Authoring set to true. No email was sent out to the signer and the agreements simply stay in Draft mode until they get "authored".




















Part II will cover checking document status in AdobeSign. 
This is generally what customers want - think of the following simple HCM scenario - 
 
An employment contract is uploaded to AdobeSign and the signer(s) are informed. HCM needs to be updated, once that document has been signed. 

So here we will have 2 OIC integrations - 1. upload and send for signing. 2. Do whatever one needs to do in HCM, once the employment contract has been signed by all signers.  















 


















Monday, November 14, 2022

#937 OIC Error Handling

Error handling is a big part of integration design. OIC allows one to catch faults at scope and global(integration) level. So when do we use what?

Let's look at a simple example - I have a synchronous integration with 2 scopes - 

























This is another ubiquitous order process, scope 1 checks the validity of the country, scope 2 checks the product. The logic is very simple - the country name Ireland is invalid, we only accept the Gaelic versions √Čire or √Čireann.

Regarding Product - we are no longer accepting orders for our iScooter - too risky - a 300kmh scooter is not acceptable on any civilised footpath.

The checkCountry scope is as follows -

























The scope also has a fault handler - it doesn't do much - just logs the fault.




















The Product scope is similar, however, it does not implement the Fault Handler -















Finally, we look at the Global Fault handler - as the name suggests, this is at the integration level. Again, simple logic here -









Now I test with an order for Ireland - 










The order is processed successfully - 


I check out the activity stream - 














As you can see, the fault was thrown within the scope and caught the scope fault handler. Processing continues to the next scope - checkProduct.

Takeaway 1 

You can catch errors at scope level and continue processing, if that is your business requirement.
As you see, the Global error handler did not fire.

Next test is with an invalid product, the crazy iScooter.


 
















Takeaway 2 

If you throw a fault in a scope without  fault handler, it automatically bubbles up to the Global Fault Handler.

Sometimes you might want both fault handlers to execute- the scope fault handler does some scope specific stuff, while the global fault handler may invoke a generic error handler.

This is easy - let's go back to the country scope and add a Re-throw fault action. Then test -  







Takeaway 3 

Rethrowing faults is a good idea if you have your generic fault/error processing defined in the Global Fault Handler.

Some Best Practices

To paraphrase the great George Orwell, all errors are equal but some are more equal than others. All errors should be thrown and then processed in a uniform manner. But what about the simple example of the invalidCountry we had? 

Here we could use the Scope Fault Handler to log this fault - e.g. order nr 1 had Ireland as destination country. We could also do the necessary repair e.g. assign Eire or Eireann to a variable and use this later in any potential mappings, for example, when creating the customer in Netsuite. Here we're classifying invalidCountry as NOT a blocking error. The Scope Fault Handler could also be used to create business relevant error messages - e.g. in case of Netsuite duplicate customer errors.

Capturing and Processing Errors  

I added a createNSCustomer scope to the integration. It invokes Netsuite to create a customer. Duplicates could be a potential issue - the error structure returned by Netsuite is as follows - 


Note that this response won't automatically trigger an error in the integration. check out the first <status isSuccess="true" /> This tells us, from a technical perspective, the Netsuite invoke worked. The second <status> is not set to success, Note the Netsuite specific error code - UNIQUE_CUST_ID_REQD. Finally, the message spells out what's wrong.

Error data is moved about via the OIC Fault Object; it's structure is as follows - 



My goal here is to inform the LOB owner, via her preferred channel,  about any errors in her area. The error message will need to be understandable from a business perspective.

This is where the Generic Error Handler integration comes in - as you can see, I have only implemented email notification, but this could be augmented with human intervention, via an invoke of an OIC Process to allow business users to view and possibly fix/resubmit errored integrations. 


 

So back to the original order processing integration - I need to do some work here, beginning with the createCustomer scope -
  • Check for Netsuite error
  • Businessify the error message
  • Set fields in fault structure
  • Bubble up the error to the global fault handler
In the global fault handler -
  • Invoke the Generic Error Handler integration passing the fault structure values.














































I run the integration again, with the duplicate Netsuite customer -


And check my email -