I am looking to create an app for AppExchange in Salesforce
The goal is to login to my PBX and enable click_and_dial
Actually I have this code:
<apex:page standardController="Contact" extensions="ClickAndDial" action="{!clickAndDial}">
<apex:form >
<apex:inputHidden value="{!contact.Id}"/>
<apex:inputHidden value="{!contact.Phone}"/>
</apex:form>
</apex:page>`
public class ClickAndDial {
public String Phone {get; set;}
public Id Id { get; set; }
public Contact contact { get; set; }
public ClickAndDial(ApexPages.StandardController controller) {
contact = (Contact) controller.getRecord();
Id = contact.Id;
System.debug('The case record: ' + contact);
Phone = contact.Phone;
}
....
public PageReference clickAndDial() {
PageReference pageRef = new PageReference('/' + Id);
HTTPRequest loginRequest = new HTTPRequest();
loginRequest.setEndpoint('callout:SalesforceNC/services/data/v32.0');
System.debug('Case Owner: ' + Phone);
System.debug('Case Id: ' + Id);
Http http = new Http();
HttpRequest request = new HttpRequest();
request.setMethod('POST');`
request.setEndpoint('https://somendpoint');
request.setHeader('Content-Type', 'application/x-www-form-urlencoded');
String payload = 'token=' + EncodingUtil.urlEncode('pbx_login_token','UTF-8') + '&data='+EncodingUtil.urlEncode('my_data','UTF-8');
request.setBody(payload);
HttpResponse response = http.send(request);
return pageRef;
}
}
I need user login token to be able to call API
How can I store user's PBX credentials and then retrieve them from clickAndDial method in order to perform login on PBX?
Related
I have created an invocable method to run through Process builder. Although the class works when triggered with some changes, I get an error when running it through the process builder. From what I have determined, it is happening because the call out isn't run asynchronously.
That being said, I have tried to separate the classes and make one a future call out but I cannot figure out how to pass the three strings I have from the invocable class to my call out class.
Any help would be great!
public class SendText {
public class DataWrapper {
#InvocableVariable(label='Correspondence Name' required=true)
public String CorrespondenceName;
#InvocableVariable(label='Phone Number' required=true)
public String PhoneNumber;
#InvocableVariable(label='Text Message' required=true)
public String textMessage;
}
#InvocableMethod(label='Send Text Message')
public static void callSendTextMessage (List<DataWrapper> passedData) {
for (DataWrapper dw: passedData) {
//Basic Info needed to send request
Http http = new Http();
HttpRequest request = new HttpRequest();
request.setEndpoint('https://api.podium.com/api/v2/conversations');
request.setMethod('POST');
request.setHeader('Content-Type', 'application/json');
request.setHeader('Accept', 'application/json');
request.setHeader('Authorization', 'API Key');
//Create the 4 required fields for Podium
Map<String, String> message = new Map<String, String>{
'customerPhoneNumber' => dw.PhoneNumber,
'message' => dw.textMessage,
'locationId' => '49257',
'customerName' => dw.CorrespondenceName
};
String messageJson = JSON.serialize(message);
System.debug(messageJson);
request.setBody(messageJson);
HttpResponse response = http.send(request);
// Parse the JSON response
if (response.getStatusCode() != 201) {
System.debug('The status code returned was not expected: ' +
response.getStatusCode() + ' ' + response.getStatus());
} else {
System.debug(response.getBody());
}
}
}
}
Not sure if you tried Queueable Apex but this is how I do it, give this a shot:
public class SendText {
public class DataWrapper {
#InvocableVariable(label='Correspondence Name' required=true)
public String CorrespondenceName;
#InvocableVariable(label='Phone Number' required=true)
public String PhoneNumber;
#InvocableVariable(label='Text Message' required=true)
public String textMessage;
}
#InvocableMethod(label='Send Text Message')
public static void callSendTextMessage (List<DataWrapper> passedData) {
Id jobId = System.enqueueJob(new TextMessaeQueueable(passedData));
System.debug('Text messages job Id => ' + jobId);
}
private class TextMessaeQueueable implements Queueable, Database.AllowsCallouts {
private List<DataWrapper> wrappedData;
public TextMessaeQueueable(List<DataWrapper> passedData) {
this.wrappedData = passedData
}
public void execute(QueueableContext context) {
for (DataWrapper dw: this.wrappedData) {
//Basic Info needed to send request
Http http = new Http();
HttpRequest request = new HttpRequest();
request.setEndpoint('https://api.podium.com/api/v2/conversations');
request.setMethod('POST');
request.setHeader('Content-Type', 'application/json');
request.setHeader('Accept', 'application/json');
request.setHeader('Authorization', 'API Key');
//Create the 4 required fields for Podium
Map<String, String> message = new Map<String, String>{
'customerPhoneNumber' => dw.PhoneNumber,
'message' => dw.textMessage,
'locationId' => '49257',
'customerName' => dw.CorrespondenceName
};
String messageJson = JSON.serialize(message);
System.debug(messageJson);
request.setBody(messageJson);
HttpResponse response = http.send(request);
// Parse the JSON response
if (response.getStatusCode() != 201) {
System.debug('The status code returned was not expected: ' +
response.getStatusCode() + ' ' + response.getStatus());
} else {
System.debug(response.getBody());
}
}
}
}
}
I have a class where in I am trying to call a HTTP request. I have created a Mock Test and a Test class.
My Test class is successful with 28% code coverage but it fails in recognizing the call out methods I have used in my class Below is code
My Class -
public class PD_WelcomeMaroPost {
#future(callout=true)
public static void sendEmailThroughMaro(string myInpEmail) {
string successContacts = '';
string failureContacts = '';
List<Stripe_Subscripton__c> subsToUpdate = new List<Stripe_Subscripton__c>();
//List<Case> newCase = new List<Case>();
// SQL to fetch FBO who Joined Today
list<Account> conts = new list<Account> ([SELECT Id, name, Email_FLP_com__c,
(SELECT Id FROM Stripe_Subscriptons__r WHERE Start_Date__c= TODAY
AND Status__c='active'
AND Welcome_Email__C = false LIMIT 1)from account
where ID IN (select Distributor__c from Stripe_Subscripton__c
where Start_Date__c= TODAY AND Status__c='active'
AND Welcome_Email__C = false)
AND Email_FLP_com__c != NULL LIMIT 100]);
system.debug('>>>>>>>>>>' + conts);
overallEmail myEmail = new overallEmail();
for(Account c : conts){
string resultBodyGet = '';
myEmail.email.campaign_id = 172;
myEmail.email.contact.Email = c.Email_FLP_com__c;
myEmail.email.contact.first_name = c.name;
/**MAp<String, String> tags = new Map<String, String>();
tags.put('firstName', c.name);
myEmail.email.tags = tags;**/
system.debug('#### Input JSON: ' + JSON.serialize(myEmail));
try{
String endpoint = 'http://api.maropost.com/accounts/1173/emails/deliver.json?auth_token=j-V4sx8ueUT7eKM8us_Cz5JqXBzoRrNS3p1lEZyPUPGcwWNoVNZpKQ';
HttpRequest req = new HttpRequest();
req.setEndpoint(endpoint);
req.setMethod('POST');
req.setHeader('Content-type', 'application/json');
req.setbody(JSON.serialize(myEmail));
Http http = new Http();
system.debug('Sending email');
HTTPResponse response = http.send(req);
system.debug('sent email');
resultBodyGet = response.getBody();
system.debug('Output response:' + resultBodyGet);
maroResponse myMaroResponse = new maroResponse();
myMaroResponse = (maroResponse) JSON.deserialize(resultBodyGet, maroResponse.class);
system.debug('#### myMaroResponse: ' + myMaroResponse);
if(myMaroResponse.message == 'Email was sent successfully')
successContacts = successContacts + ';' + c.Email_FLP_com__c;
else
failureContacts = failureContacts + ';' + c.Email_FLP_com__c;
}
catch (exception e) {
failureContacts = failureContacts + ';' + c.Email_FLP_com__c;
system.debug('#### Exception caught: ' + e.getMessage());
}
c.Stripe_Subscriptons__r[0].Welcome_Email__c = true;
c.Stripe_Subscriptons__r[0].Welcome_Email_Sent_Date__c = system.today();
subsToUpdate.add(c.Stripe_Subscriptons__r[0]);
}
Update subsToUpdate;
}
public class maroResponse {
public string message {get;set;}
}
public class overallEmail {
public emailJson email = new emailJson();
}
public class emailJson {
public Integer campaign_id;
public contactJson contact = new contactJson();
//Public Map<String, String> tags;
}
public class contactJson {
public string email;
public string first_name;
}
}
My MockTest Class- I have used this Mockclass to generate Mock response. The documentation does not have a test method thus used the same format
#isTest
Global class PD_WelcomeMaroPostMock implements HttpCalloutMock {
Global HttpResponse respond(HttpRequest req) {
// Create a fake response
//
//System.assertEquals(JSON.serialize(myEmail),req.getbody());
HttpResponse res = new HttpResponse();
res.setHeader('Content-Type', 'application/json');
res.setBody('{"status":"success"}');
res.setStatusCode(200);
return res;
}
}
This is my Test class - This is the class I have used for the response. I have a successful insert job but my HTTP responses are failing.
#IsTest
private class PD_WelcomeMaroPost_test {
public class overallEmail {
public emailJson email = new emailJson();
}
public class emailJson {
public Integer campaign_id;
public contactJson contact = new contactJson();
}
public class contactJson {
public string email;
public string first_name;
}
#IsTest
private static void testemail() {
overallEmail myEmail = new overallEmail();
Account a = new Account();
a.Name ='Test' ;
a.Email_FLP_com__c = 'test#nextsphere.com';
insert a ;
Stripe_Subscripton__c s = new Stripe_Subscripton__c();
// insert subscription --
s.Distributor__c = a.Id;
S.Welcome_Email__c = TRUE;
S.Welcome_Email_Sent_Date__c = system.today();
s.Subscription_Id__c = 'sub_9H0LLYFZkekdMA' ;
INSERT S;
Test.setMock(HttpCalloutMock.class, new PD_WelcomeMaroPostMock());
String endpoint = 'http://api.maropost.com/accounts/1173/emails/deliver.json?auth_token=j-V4sx8ueUT7eKM8us_Cz5JqXBzoRrNS3p1lEZyPUPGcwWNoVNZpKQ';
HttpRequest req = new HttpRequest();
req.setEndpoint(endpoint);
req.setMethod('POST');
req.setHeader('Content-type', 'application/json');
req.setbody(JSON.serialize(myEmail));
Test.startTest();
PD_WelcomeMaroPost.sendEmailThroughMaro('test#nextsphere.com');
Test.stopTest();
}
}
When declaring a Mock myself, I declare it inside the test start transaction:
Test.startTest();
Test.setMock(WebServiceMock.class, new WebServiceMockImpl());
// Rest of test code here
Test.stopTest();
Also inside your test class you seem to only build the HTTP request and not send it try adding the below:
HttpResponse res = http.send(req);
if (res.getStatusCode() == 200) { } else { }
I am working on Web API with AngularJS. I had implemented Web API token mechanism few days ago and able to login the application using the access token. I have used external DB table instead of ASP.NET identity table to authorize user.
I want to store user information in class so that it can be accessed easily from different controllers after User logged in. Currently I am using ClaimsIdentity in Controller Class to get the user information.
UserIdentityViewModel.cs
public class UserIdentityViewModel
{
public string UserName { get; set; }
public Guid UserId { get; set; }
}
Startup.cs
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
var myProvider = new AuthorizationServerProvider();
OAuthAuthorizationServerOptions options = new OAuthAuthorizationServerOptions
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/Token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = myProvider
};
app.UseOAuthAuthorizationServer(options);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}
}
AuthorizationServerProvider.cs
public class AuthorizationServerProvider : OAuthAuthorizationServerProvider
{
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated(); //
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
string userId = context.UserName;
string password = context.Password;
EmployeeAccessBLL chkEmpAccessBLL = new EmployeeAccessBLL();
EmployeeAccessViewModel vmEmployeeAccess = chkEmpAccessBLL.CheckEmployeeAccess(Convert.ToInt32(userId), password);
if(vmEmployeeAccess != null)
{
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim("username", vmEmployeeAccess.EmpName));
identity.AddClaim(new Claim("userid", Convert.ToString(vmEmployeeAccess.EmployeeId)));
UserIdentityViewModel vmUser = new UserIdentityViewModel();
vmUser.UserId = vmEmployeeAccess.EmployeeId;
vmUser.UserName = vmEmployeeAccess.EmpName;
context.Validated(identity);
}
else
{
context.SetError("invalid_grant", "Provided username and password is incorrect");
return;
}
}
}
EventController.cs
public class StreamEventController : ApiController
{
[Authorize]
[Route("api/addevent")]
[HttpPost]
public List<string> AddEvent(StreamEventViewModel vmEvent)
{
//Able to get User Information from Identity.Claims
var identity = (ClaimsIdentity)User.Identity;
string userId = identity.Claims
.Where(c => c.Type == "userid")
.Select(c => c.Value).FirstOrDefault();
//Not able to get User Information from following as new object instance gets created
UserIdentityViewModel vmUser = new UserIdentityViewModel();
vmEvent.CreatedBy = vmUser.UserId;
vmEvent.ModifiedBy = vmUser.UserId;
}
}
Instead of writing "Identity.Claims" in each method of every controller I want to use simple get/set approach or any other methodology to get User Information . The use of Static class is also bad in my opinion as it will store one information of user and multiple user login information gets missed.
Please help me and share with me the best approach that has been used in other Web API projects for login.
You can add a private variable which will be set in the constructor of the controller, like this:
// Should only be used in protected methods.
private ClaimsIdentity ThisUser = null;
public MyController()
{
if (User.Identity.IsAuthenticated)
ThisUser = (ClaimsIdentity)User.Identity;
}
[Authorize]
[Route("api/addevent")]
[HttpPost]
public List<string> AddEvent(StreamEventViewModel vmEvent)
{
string userId = ThisUser.FindFirstValue("userid");
}
Or create a User class where you load all properties:
private UserClass ThisUser = null;
public MyController()
{
if (User.Identity.IsAuthenticated)
ThisUser = new UserClass(User);
}
[Authorize]
[Route("api/addevent")]
[HttpPost]
public List<string> AddEvent(StreamEventViewModel vmEvent)
{
string userId = ThisUser.UserId;
}
Where UserClass is something like:
public class UserClass
{
public string UserId { get; private set; }
public UserClass(IPrincipal user)
{
UserId = user.FindFirstValue("userid");
}
}
But this is just overhead for the same thing.
You can consider to move things to an extension. In that case you get something like:
public static class RequestExtensions
{
public static UserClass GetUser(this HttpRequestMessage request)
{
return new UserClass(request.GetOwinContext().Authentication.User);
}
public static ClaimsIdentiy GetUser2(this HttpRequestMessage request)
{
return new (ClaimsIdentity)request.GetOwinContext().Authentication.User;
}
}
Which you can call:
[Authorize]
[Route("api/addevent")]
[HttpPost]
public List<string> AddEvent(StreamEventViewModel vmEvent)
{
string userId = Request.GetUser.UserId;
string userId2 = Request.GetUser2.FindFirstValue("userid");
}
I think I would go for Request.GetUser2.FindFirstValue("userid");
The code is meant to give you an idea. I didn't test the code but I think it should work.
I'm using Identity Server 4 and I've customised my ASP.NET Identity user as follows:
public class ApplicationUser : IdentityUser
{
[MaxLength(100)]
public virtual string FirstName { get; set; }
[MaxLength(100)]
public virtual string LastName { get; set; }
}
I can't see where I would configure Identity Server 4 to include these 2 properties in the claims collection. I've had a look through some of the Identity Server 4 samples but can't see any examples.
I'd ideally like to map these 2 user properties to the given_name and family_name claims.
I'm currently hooking up to the notifications and querying the userinfo endpoint (hybrid flow?). So I'm not sure if this is configuration of Identity Server or customization of the userinfo endpoint?
In order to include your custom claims, you need to implement your own GetProfileDataAsync() method using the IProfileService. This method is being called everytime a user claim is requested.
Here is my implementation of IProfileService.
public class CustomProfileService : IProfileService
{
private readonly UserManager<User> _userManager;
public CustomProfileService(UserManager<User> userManager)
{
_userManager = userManager;
}
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
{
var subjectId = context.Subject.GetSubjectId();
var user = await _userManager.FindByIdAsync(subjectId);
if (user == null) return;
var claims = new List<Claim>
{
new Claim("username", user.UserName),
new Claim("email", user.Email),
new Claim("firstname", user.FirstName),
new Claim("lastname", user.LastName)
};
var roles = await _userManager.GetRolesAsync(user);
foreach (var role in roles)
{
claims.Add(new Claim("role", role));
}
var userClaims = await _userManager.GetClaimsAsync(user);
foreach (var userClaim in userClaims)
{
claims.Add(new Claim(userClaim.Type, userClaim.Value));
}
context.IssuedClaims = claims;
}
public async Task IsActiveAsync(IsActiveContext context)
{
var user = await _userManager.FindByIdAsync(context.Subject.GetSubjectId());
context.IsActive = user.IsActive;
}
}
Then you will have to add this following line to Startup.ConfigureServices()
services.AddScoped<IProfileService, CustomProfileService>();
I was wondering why there is no documentation on this. It lead me to realise that I'm probably doing it wrong.
I'd not seen the table AspNetUserClaims created as part of ASP.NET Identity. I added my claim data into here and the claim data pulls through as part of the profile.
In the POST method for AccountController.Register I added:
var givenNameClaim = new IdentityUserClaim<string>()
{
ClaimType = "given_name",
ClaimValue = model.FirstName
};
var familyNameClaim = new IdentityUserClaim<string>()
{
ClaimType = "family_name",
ClaimValue = model.LastName
};
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
user.Claims.Add(givenNameClaim);
user.Claims.Add(familyNameClaim);
Here's what I've done: In AccountController.ExternalLoginCallback I added this:
//Add claim even if client didn't ask for it
additionalClaims.Add(new Claim(JwtClaimTypes.Email, "email#email.com"));
then I added the claim to the access_token by dependency injecting my ProfileService class and adding the claims in the MyProfileService.GetProfileDataAsync like this:
public Task GetProfileDataAsync(ProfileDataRequestContext context)
{
var claims = new List<Claim>();
Claim emailClaim = context.Subject.Claims.Where<Claim>(claim => claim.Type.Equals(JwtClaimTypes.Email)).FirstOrDefault();
if (emailClaim != null)
{
claims.Add(emailClaim);
}
context.IssuedClaims = claims;
return Task.FromResult(0);
}
I am using Docusign API Integration to Salesforce. I have a requirement to update the data field tag in a document by calling the Docusign API from Salesforce after it has been sent for signature.
Let's say the routing order of signers are from signer 1 to signer 4. Initially the value of that data field tag will be null. After signer 3 "completed" his signature, I have to trigger the API call from Salesforce and update the tag with the value which is retrieved from custom field value of an object in Salesforce. Then Signer 4 will be able to see the value in the data field tag before he signs the document.
I used Merge fields for docusign to import the Salesforce data into that tag. But as I said before, initial value will be null (because the salesforce data of that merge field will also be null at that time) and I have to "halt" the signing process, update the tag and then continue to remaining signer.
Is there any way that I could "refresh" the envelope so that the value in the data field tag gets updated?
Will docusign allow to update the tag values in document with Salesforce data through API callouts after the envelope is sent?
Note that I have read the Modify tabs (tags) for Recipient article in Docusign RestAPI guide Version2. Is that for modifying tag types or tag values?
It is possible with Salesforce and DS but not the way you are going at it. I answered your question about background correct on the other question at Send (Load) a URL to web browser and run it in background in APEX
Merge fields are part of the question, but they are only shown as the value when "SENT" and update Salesforce only when the envelope is COMPLETED successfully, so really this is only a half solution for your scenario.
So here is what I have done for another DocuSign Client as a sample of how to do this, but please reach out to your DocuSign Account Manager as I think if you are doing these types of advanced workflows you could benifit from DocuSign's Profession Services Group which I am apart of.
1. Components added:
DocuSign Connect Object - dseRecipientConnectUpdate
custom Object - dseRecipientConnectUpdate__c
Trigger - dseRCU_AfterUpdate
Class - desController (in Sandbox and code below as well)
Class - CheckRecursive (in sandbox and code below as well)
DS Template Example -Agreement with ContractID 960BD14E-6A09-4A9E-89E6-77B1D8444B72
2. What you need yet to do
Replace Send on Behalf user with Sender of envelope via code (hard Coded as david.grigsby#docusign.com in code) lookup using envelopeID in DocuSign Status and get sender, then lookup in user that sender's email
Classify any stringified body's you want
Error Condition handling
Test Classes
Testing, Testing, testing
How I tested:
0. Turn on debugging for API user and Myself
a. Sent Envelope from Template
b. Signed the first three recipients
c. Code updated the dseRecipientConnectUpdate__c record (a36) that was the autoresponse blocking user aka just changed record by editing but no real change, then save.
d. It would then fire trigger again (as mentioned you will need to change the send on behalf of user to automatically for final code to be sending user, but you can make it your email you send envelope by) and it will read recipients, get the contract id recipient and tab, add new recipient and tab with value, delete old reciepents (tag recipient and blocking)
Salesforce Custom Object:
<?xml version="1.0" encoding="UTF-8"?>
<CustomObject xmlns="http://soap.sforce.com/2006/04/metadata">
<!--actionoverrides removed for sake of SO answer size/>
<compactLayoutAssignment>SYSTEM</compactLayoutAssignment>
<deploymentStatus>Deployed</deploymentStatus>
<description>DocuSign Recipient Connect Update</description>
<enableActivities>true</enableActivities>
<enableFeeds>false</enableFeeds>
<enableHistory>true</enableHistory>
<enableReports>true</enableReports>
<fields>
<fullName>RecipStatus__c</fullName>
<externalId>false</externalId>
<label>RecipStatus</label>
<length>50</length>
<required>false</required>
<trackHistory>false</trackHistory>
<trackTrending>false</trackTrending>
<type>Text</type>
<unique>false</unique>
</fields>
<fields>
<fullName>RecipientEmail__c</fullName>
<description>Recipient Email</description>
<externalId>false</externalId>
<label>RecipientEmail</label>
<required>false</required>
<trackHistory>false</trackHistory>
<trackTrending>false</trackTrending>
<type>Email</type>
<unique>false</unique>
</fields>
<fields>
<fullName>RecipientID__c</fullName>
<externalId>false</externalId>
<label>RecipientID</label>
<length>50</length>
<required>false</required>
<trackHistory>false</trackHistory>
<trackTrending>false</trackTrending>
<type>Text</type>
<unique>false</unique>
</fields>
<fields>
<fullName>dsEnvelopeID__c</fullName>
<description>dsfs__DocuSign_Envelope_ID__c</description>
<externalId>false</externalId>
<label>dsEnvelopeID</label>
<length>56</length>
<required>false</required>
<trackHistory>false</trackHistory>
<trackTrending>false</trackTrending>
<type>Text</type>
<unique>false</unique>
</fields>
<label>dseRecipientConnectUpdate</label>
<listViews>
<fullName>All</fullName>
<filterScope>Everything</filterScope>
<label>All</label>
</listViews>
<nameField>
<displayFormat>dseRCU-{0000000000}</displayFormat>
<label>dseRecipientConnectUpdate Name</label>
<trackHistory>false</trackHistory>
<type>AutoNumber</type>
</nameField>
<pluralLabel>dseRecipientConnectUpdates</pluralLabel>
<recordTypeTrackHistory>false</recordTypeTrackHistory>
<recordTypes>
<fullName>dseRecipientConnectUpdate</fullName>
<active>true</active>
<description>dseRecipientConnectUpdate</description>
<label>dseRecipientConnectUpdate</label>
</recordTypes>
<searchLayouts/>
<sharingModel>ReadWrite</sharingModel>
</CustomObject>
Trigger
==========
trigger dseRCU_AfterUpdate on dseRecipientConnectUpdate__c (after update) {
try
{
if (CheckRecursive.runOnce())
{
List<dseRecipientConnectUpdate__c> myConnectUpdates = [Select d.dsEnvelopeID__c, d.RecipientID__c, d.RecipientEmail__c, d.RecipStatus__c, d.Id From dseRecipientConnectUpdate__c d WHERE Id IN:Trigger.newMap.keySet()];
for(dseRecipientConnectUpdate__c myConnectCompleted :myConnectUpdates)
{
system.debug(myConnectCompleted.Id);
if(myConnectCompleted.RecipStatus__c.indexOf('AutoResponded') != -1)
{
system.debug(myConnectCompleted.RecipStatus__c);
//Looking for bounce back user via status AutoResponded and #accelrys.com emails/recipients
if(myConnectCompleted.RecipientEmail__c.indexOf('invalidemail#baddomain.com') != -1)
{
//do modification to envelope here
dseController.updateEnvelope(myConnectCompleted.dsEnvelopeID__c, myConnectCompleted.RecipientID__c);
}
}
}
}
}
catch(Exception ex)
{
system.debug(ex);
}
finally
{
}
}
Class
==========
public with sharing class dseController {
public dseController()
{
}
public dseController(ApexPages.StandardController controller)
{
}
#future (callout=true)
public static void updateEnvelope( string envelopeID, string recipientID )
{
RecipientResponse RecipientResponseDeserialized = new RecipientResponse();
RecipientTabResponse RecipientTabResponseDeserialized = new RecipientTabResponse();
string rResponse = '{}';
string recipientGuidwithContractTab;
string recipientGuidForBlockingRecipient;
string rTabResponse = '{}';
string rSetRecipientResponse = '{}';
string rSetTabForRecipientResponse = '{}';
string rTabRecipientDeleteResponse = '{}';
string rBlockingRecipientDeleteResponse = '{}';
try
{
//Call to get envelope recipients
rResponse = getEnvelopeRecipients(envelopeID);
system.debug(rResponse);
RecipientResponseDeserialized = parseRecipentResponse(rResponse);
system.debug(RecipientResponseDeserialized);
recipientGuidwithContractTab = getRecipientwithContractTab(RecipientResponseDeserialized);
system.debug(recipientGuidwithContractTab);
//Call to get recipient tab
rTabResponse = getRecipientTab(envelopeID, recipientGuidwithContractTab);
system.debug(rTabResponse);
RecipientTabResponseDeserialized = parseRecipientTabResponse(rTabResponse);
system.debug(RecipientTabResponseDeserialized);
//Call to add recipient with new id
rSetRecipientResponse = setRecipientForNewTab(envelopeID,RecipientResponseDeserialized);
system.debug(rSetRecipientResponse);
//Call to add tab to new recipient with new id
rSetTabForRecipientResponse = setNewTabforNewRecipient(envelopeID,RecipientTabResponseDeserialized);
system.debug(rSetTabForRecipientResponse);
//Call to delete old clone recipient
rTabRecipientDeleteResponse = deleteRecipientTab(envelopeID, recipientGuidwithContractTab);
system.debug(rTabRecipientDeleteResponse);
//Call to delete blocking user
recipientGuidForBlockingRecipient = getBlockingRecipient(RecipientResponseDeserialized);
rBlockingRecipientDeleteResponse = deleteBlockingRecipient(envelopeID, recipientGuidForBlockingRecipient);
system.debug(rBlockingRecipientDeleteResponse);
}
catch(Exception ex)
{
system.debug(ex);
}
finally
{
}
}
public static string getRecipientwithContractTab(RecipientResponse rResponse)
{
string rContractTabID = 'Not Found';
try{
List<Signer> mySigners = rResponse.signers;
for(Signer mySigner : mySigners)
{
if(mySigner.roleName == 'ContractIDApprover')
{
rContractTabID = mySigner.recipientIdGuid;
}
}
}
catch(Exception ex)
{
system.debug(ex);
}
finally
{
}
return rContractTabID;
}
public static string getBlockingRecipient(RecipientResponse rResponse)
{
string rContractTabID = 'Not Found';
try{
List<Signer> mySigners = rResponse.signers;
for(Signer mySigner : mySigners)
{
if(mySigner.roleName == 'BlockingUser')
{
rContractTabID = mySigner.recipientIdGuid;
}
}
}
catch(Exception ex)
{
system.debug(ex);
}
finally
{
}
return rContractTabID;
}
public static string getEnvelopeRecipients(string envelopeID)
{
string response = '{}';
string DSEndpoint = 'https://demo.docusign.net/restapi/v2/';
string DSUserId = 'yourdsUserid';
string DSPassword = 'yourdspassword';
string DSAccountID = 'yourdsaccountID';
string DSIntegratorKey = 'yourdsintegratorkey';
try
{
//FORCES that the DocuSign member has to be in the DocuSign account DSFS is configured for
List<dsfs__DocuSignAccountConfiguration__c> dsAccountConfig = [Select d.dsfs__UseSendOnBehalfOf__c, d.dsfs__DocuSignBaseURL__c, d.dsfs__DSProSFUsername__c, d.dsfs__DSProSFPassword__c, d.dsfs__AccountId__c From dsfs__DocuSignAccountConfiguration__c d limit 1];
for(dsfs__DocuSignAccountConfiguration__c myConfig : dsAccountConfig)
{
DSEndpoint = myConfig.dsfs__DocuSignBaseURL__c + 'restapi/v2/';
DSUserId = myConfig.dsfs__DSProSFUsername__c;
DSPassword = myConfig.dsfs__DSProSFPassword__c;
}
HttpRequest request = new HttpRequest();
request.setEndpoint(DSEndpoint + 'accounts/'+DSAccountID+'/envelopes/'+envelopeID+'/recipients');
request.setMethod('GET');
request.setHeader('Content-Type', 'application/json');
request.setHeader('X-DocuSign-Authentication', '<DocuSignCredentials><Username>'+DSUserId+'</Username><Password>'+DSPassword+'</Password><IntegratorKey>'+DSIntegratorKey+'</IntegratorKey></DocuSignCredentials>');
request.setHeader('Accept', 'application/json');
request.setTimeout(120000);
system.debug(request.getHeader('X-DocuSign-Authentication'));
HttpResponse myResponse = (new Http()).send(request);
system.debug(myResponse.getBody());
if(myResponse.getStatusCode().format()=='200')
{
response = myResponse.getBody();
system.debug(response);
}
}
catch(Exception ex)
{
system.debug(ex);
}
finally
{
}
return response;
}
public static string getRecipientTab(string envelopeID, string recipientGuid)
{
string response = '{}';
string DSEndpoint = 'https://demo.docusign.net/restapi/v2/';
string DSUserId = 'yourdsUserid';
string DSPassword = 'yourdspassword';
string DSAccountID = 'yourdsaccountID';
string DSIntegratorKey = 'yourdsintegratorkey';
try
{
//FORCES that the DocuSign member has to be in the DocuSign account DSFS is configured for
List<dsfs__DocuSignAccountConfiguration__c> dsAccountConfig = [Select d.dsfs__UseSendOnBehalfOf__c, d.dsfs__DocuSignBaseURL__c, d.dsfs__DSProSFUsername__c, d.dsfs__DSProSFPassword__c, d.dsfs__AccountId__c From dsfs__DocuSignAccountConfiguration__c d limit 1];
for(dsfs__DocuSignAccountConfiguration__c myConfig : dsAccountConfig)
{
DSEndpoint = myConfig.dsfs__DocuSignBaseURL__c + 'restapi/v2/';
DSUserId = myConfig.dsfs__DSProSFUsername__c;
DSPassword = myConfig.dsfs__DSProSFPassword__c;
}
HttpRequest request = new HttpRequest();
request.setEndpoint(DSEndpoint + 'accounts/'+DSAccountID+'/envelopes/'+envelopeID+'/recipients/'+recipientGuid+'/tabs/');
request.setMethod('GET');
request.setHeader('Content-Type', 'application/json');
request.setHeader('X-DocuSign-Authentication', '<DocuSignCredentials><Username>'+DSUserId+'</Username><Password>'+DSPassword+'</Password><SendOnBehalfOf>david.grigsby#docusign.com</SendOnBehalfOf><IntegratorKey>'+DSIntegratorKey+'</IntegratorKey></DocuSignCredentials>');
request.setHeader('Accept', 'application/json');
request.setTimeout(120000);
system.debug(request.getHeader('X-DocuSign-Authentication'));
HttpResponse myResponse = (new Http()).send(request);
system.debug(myResponse.getBody());
if(myResponse.getStatusCode().format()=='200')
{
response = myResponse.getBody();
system.debug(response);
}
}
catch(Exception ex)
{
system.debug(ex);
}
finally
{
}
return response;
}
public static string setRecipientForNewTab(string envelopeID, RecipientResponse rResponse)
{
string response = '{}';
string DSEndpoint = 'https://demo.docusign.net/restapi/v2/';
string DSUserId = 'yourdsUserid';
string DSPassword = 'yourdspassword';
string DSAccountID = 'yourdsaccountID';
string DSIntegratorKey = 'yourdsintegratorkey';
try
{
//FORCES that the DocuSign member has to be in the DocuSign account DSFS is configured for
List<dsfs__DocuSignAccountConfiguration__c> dsAccountConfig = [Select d.dsfs__UseSendOnBehalfOf__c, d.dsfs__DocuSignBaseURL__c, d.dsfs__DSProSFUsername__c, d.dsfs__DSProSFPassword__c, d.dsfs__AccountId__c From dsfs__DocuSignAccountConfiguration__c d limit 1];
for(dsfs__DocuSignAccountConfiguration__c myConfig : dsAccountConfig)
{
DSEndpoint = myConfig.dsfs__DocuSignBaseURL__c + 'restapi/v2/';
DSUserId = myConfig.dsfs__DSProSFUsername__c;
DSPassword = myConfig.dsfs__DSProSFPassword__c;
}
Signer mySignerToAdd = new Signer();
List<Signer> mySigners = rResponse.signers;
for(Signer mySigner : mySigners)
{
if(mySigner.roleName == 'ContractIDApprover')
{
mySignerToAdd = mySigner;
}
}
String myBody;
myBody = '{"signers": [{"signInEachLocation": "false","name": "'+mySignerToAdd.name +'Added 1","email": "'+mySignerToAdd.email+'","recipientId": "7","requireIdLookup": "false","routingOrder": "19","roleName": "'+mySignerToAdd.roleName+'1"}]}';
HttpRequest request = new HttpRequest();
request.setEndpoint(DSEndpoint + 'accounts/'+DSAccountID+'/envelopes/'+envelopeID+'/recipients/');
request.setMethod('POST');
request.setHeader('Content-Type', 'application/json');
request.setHeader('X-DocuSign-Authentication', '<DocuSignCredentials><Username>'+DSUserId+'</Username><Password>'+DSPassword+'</Password><SendOnBehalfOf>david.grigsby#docusign.com</SendOnBehalfOf><IntegratorKey>'+DSIntegratorKey+'</IntegratorKey></DocuSignCredentials>');
request.setHeader('Accept', 'application/json');
request.setTimeout(120000);
request.setBody(myBody);
system.debug(request.getHeader('X-DocuSign-Authentication'));
HttpResponse myResponse = (new Http()).send(request);
system.debug(myResponse.getBody());
if(myResponse.getStatusCode().format()=='201')
{
response = myResponse.getBody();
system.debug(response);
}
}
catch(Exception ex)
{
system.debug(ex);
}
finally
{
}
return response;
}
public static string setNewTabforNewRecipient(string envelopeID, RecipientTabResponse rTabResponse)
{
string response = '{}';
string DSEndpoint = 'https://demo.docusign.net/restapi/v2/';
string DSUserId = 'yourdsUserid';
string DSPassword = 'yourdspassword';
string DSAccountID = 'yourdsaccountID';
string DSIntegratorKey = 'yourdsintegratorkey';
try
{
//FORCES that the DocuSign member has to be in the DocuSign account DSFS is configured for
List<dsfs__DocuSignAccountConfiguration__c> dsAccountConfig = [Select d.dsfs__UseSendOnBehalfOf__c, d.dsfs__DocuSignBaseURL__c, d.dsfs__DSProSFUsername__c, d.dsfs__DSProSFPassword__c, d.dsfs__AccountId__c From dsfs__DocuSignAccountConfiguration__c d limit 1];
for(dsfs__DocuSignAccountConfiguration__c myConfig : dsAccountConfig)
{
DSEndpoint = myConfig.dsfs__DocuSignBaseURL__c + 'restapi/v2/';
DSUserId = myConfig.dsfs__DSProSFUsername__c;
DSPassword = myConfig.dsfs__DSProSFPassword__c;
}
TextTabs myTextTabToAdd = new TextTabs();
List<TextTabs> myTextTabs = rTabResponse.textTabs;
for(TextTabs myTextTab : myTextTabs)
{
if(myTextTab.tabLabel == 'ContractID')
{
myTextTabToAdd = myTextTab;
}
}
String myBody;
myBody = '{"textTabs": [{"height": '+myTextTabToAdd.height+',"shared": "'+myTextTabToAdd.shared+'","requireInitialOnSharedChange": "'+myTextTabToAdd.requireInitialOnSharedChange+'","name": "'+myTextTabToAdd.name+'1","value": "ContractID12345","width": '+myTextTabToAdd.width+',"required": "'+myTextTabToAdd.required+'","locked": "'+myTextTabToAdd.locked+'","concealValueOnDocument": "'+myTextTabToAdd.concealValueOnDocument+'","disableAutoSize": "'+myTextTabToAdd.disableAutoSize+'","tabLabel": "'+myTextTabToAdd.tabLabel+'","documentId": "'+myTextTabToAdd.documentId+'","recipientId": "7","pageNumber": "'+myTextTabToAdd.pageNumber+'","xPosition": "'+myTextTabToAdd.xPosition+'","yPosition": "'+myTextTabToAdd.yPosition+'"}]}';
HttpRequest request = new HttpRequest();
request.setEndpoint(DSEndpoint + 'accounts/'+DSAccountID+'/envelopes/'+envelopeID+'/recipients/7/tabs');
request.setMethod('POST');
request.setHeader('Content-Type', 'application/json');
request.setHeader('X-DocuSign-Authentication', '<DocuSignCredentials><Username>'+DSUserId+'</Username><Password>'+DSPassword+'</Password><SendOnBehalfOf>david.grigsby#docusign.com</SendOnBehalfOf><IntegratorKey>'+DSIntegratorKey+'</IntegratorKey></DocuSignCredentials>');
request.setHeader('Accept', 'application/json');
request.setTimeout(120000);
request.setBody(myBody);
system.debug(request.getHeader('X-DocuSign-Authentication'));
HttpResponse myResponse = (new Http()).send(request);
system.debug(myResponse.getBody());
if(myResponse.getStatusCode().format()=='201')
{
response = myResponse.getBody();
system.debug(response);
}
}
catch(Exception ex)
{
system.debug(ex);
}
finally
{
}
return response;
}
public static string deleteRecipientTab(string envelopeID, string recipientGuid)
{
string response = '{}';
string DSEndpoint = 'https://demo.docusign.net/restapi/v2/';
string DSUserId = 'yourdsUserid';
string DSPassword = 'yourdspassword';
string DSAccountID = 'yourdsaccountID';
string DSIntegratorKey = 'yourdsintegratorkey';
try
{
//FORCES that the DocuSign member has to be in the DocuSign account DSFS is configured for
List<dsfs__DocuSignAccountConfiguration__c> dsAccountConfig = [Select d.dsfs__UseSendOnBehalfOf__c, d.dsfs__DocuSignBaseURL__c, d.dsfs__DSProSFUsername__c, d.dsfs__DSProSFPassword__c, d.dsfs__AccountId__c From dsfs__DocuSignAccountConfiguration__c d limit 1];
for(dsfs__DocuSignAccountConfiguration__c myConfig : dsAccountConfig)
{
DSEndpoint = myConfig.dsfs__DocuSignBaseURL__c + 'restapi/v2/';
DSUserId = myConfig.dsfs__DSProSFUsername__c;
DSPassword = myConfig.dsfs__DSProSFPassword__c;
}
HttpRequest request = new HttpRequest();
request.setEndpoint(DSEndpoint + 'accounts/'+DSAccountID+'/envelopes/'+envelopeID+'/recipients/'+recipientGuid);
request.setMethod('DELETE');
request.setHeader('Content-Type', 'application/json');
request.setHeader('X-DocuSign-Authentication', '<DocuSignCredentials><Username>'+DSUserId+'</Username><Password>'+DSPassword+'</Password><SendOnBehalfOf>youremail#yourdomain.com</SendOnBehalfOf><IntegratorKey>'+DSIntegratorKey+'</IntegratorKey></DocuSignCredentials>');
request.setHeader('Accept', 'application/json');
request.setTimeout(120000);
system.debug(request.getHeader('X-DocuSign-Authentication'));
HttpResponse myResponse = (new Http()).send(request);
system.debug(myResponse.getBody());
if(myResponse.getStatusCode().format()=='200')
{
response = myResponse.getBody();
system.debug(response);
}
}
catch(Exception ex)
{
system.debug(ex);
}
finally
{
}
return response;
}
public static string deleteBlockingRecipient(string envelopeID, string recipientGuid)
{
string response = '{}';
string DSEndpoint = 'https://demo.docusign.net/restapi/v2/';
string DSUserId = 'yourdsUserid';
string DSPassword = 'yourdspassword';
string DSAccountID = 'yourdsaccountID';
string DSIntegratorKey = 'yourdsintegratorkey';
try
{
//FORCES that the DocuSign member has to be in the DocuSign account DSFS is configured for
List<dsfs__DocuSignAccountConfiguration__c> dsAccountConfig = [Select d.dsfs__UseSendOnBehalfOf__c, d.dsfs__DocuSignBaseURL__c, d.dsfs__DSProSFUsername__c, d.dsfs__DSProSFPassword__c, d.dsfs__AccountId__c From dsfs__DocuSignAccountConfiguration__c d limit 1];
for(dsfs__DocuSignAccountConfiguration__c myConfig : dsAccountConfig)
{
DSEndpoint = myConfig.dsfs__DocuSignBaseURL__c + 'restapi/v2/';
DSUserId = myConfig.dsfs__DSProSFUsername__c;
DSPassword = myConfig.dsfs__DSProSFPassword__c;
}
HttpRequest request = new HttpRequest();
request.setEndpoint(DSEndpoint + 'accounts/'+DSAccountID+'/envelopes/'+envelopeID+'/recipients/'+recipientGuid);
request.setMethod('DELETE');
request.setHeader('Content-Type', 'application/json');
request.setHeader('X-DocuSign-Authentication', '<DocuSignCredentials><Username>'+DSUserId+'</Username><Password>'+DSPassword+'</Password><SendOnBehalfOf>youremail#yourdomain.com</SendOnBehalfOf><IntegratorKey>'+DSIntegratorKey+'</IntegratorKey></DocuSignCredentials>');
request.setHeader('Accept', 'application/json');
request.setTimeout(120000);
system.debug(request.getHeader('X-DocuSign-Authentication'));
HttpResponse myResponse = (new Http()).send(request);
system.debug(myResponse.getBody());
if(myResponse.getStatusCode().format()=='200')
{
response = myResponse.getBody();
system.debug(response);
}
}
catch(Exception ex)
{
system.debug(ex);
}
finally
{
}
return response;
}
public static RecipientResponse parseRecipentResponse(String json) {
return (RecipientResponse) System.JSON.deserialize(json, RecipientResponse.class);
}
public static RecipientTabResponse parseRecipientTabResponse(String json) {
return (RecipientTabResponse) System.JSON.deserialize(json, RecipientTabResponse.class);
}
public class Signer
{
public string name;
public string email;
public string recipientId;
public string recipientIdGuid;
public string requireIdLookup;
public string userId;
public string routingOrder;
public string roleName;
public string status;
public string signedDateTime;
public string deliveredDateTime;
public string templateLocked;
public string templateRequired;
}
public class RecipientResponse
{
public List<Signer> signers;
public List<Signer> agents;
public List<Signer> editors;
public List<Signer> intermediaries;
public List<Signer> carbonCopies;
public List<Signer> certifiedDeliveries;
public List<Signer> inPersonSigners;
public String recipientCount;
public String currentRoutingOrder;
}
public class TextTabs {
public Integer height;
public String validationPattern;
public String validationMessage;
public String shared;
public String requireInitialOnSharedChange;
public String name;
public String value;
public Integer width;
public String required;
public String locked;
public String concealValueOnDocument;
public String disableAutoSize;
public String tabLabel;
public String documentId;
public String recipientId;
public String pageNumber;
public String xPosition;
public String yPosition;
public String tabId;
}
public class RecipientTabResponse
{
public List<TextTabs> textTabs;
}
}