Void an email previously sent using DocuSign C# API - c#

As the title says, I am using the DocuSign C# API and I am trying to void an envelope that has been sent, then send an updated one in its place.
So an example:
I send out a DocuSign envelope to a customer
They open it to see that I have made a mistake (or want items adding etc)
They ask for a new quote
I trigger a resend (via the API using my back end database using status flags)
It is now being resent, but the previous one is still "active" and doesn't automatically void.
So how would I go about voiding this using the C# API?
Thanks for the help, if you need any more information just ask and I will get back to you.
EDIT - here is the current code that I have currently
try
{
envelopeGenerator = new EnvelopeDefinitionGenerator(quoteID, account_id);
envDef = envelopeGenerator.GetDefinition();
if (item.EQuoteStatus == 4)
{
envelope = envelopesApi.GetEnvelope(account_id, envelopeId);
envelope.Status = "voided";
envelope.VoidedReason = "This envelope was voided";
envelope.PurgeState = null;
updateSummary = envelopesApi.Update(account_id, envelopeSummary.EnvelopeId, envelope);
envelopeInfo = JObject.FromObject(envDef);
jsonString = JsonConvert.SerializeObject(envelopeInfo);
//throw new Exception(); // DEBUG LINE, COMMENT OUT - TO TEST IF EXCEPTION CODE WORKS.
envelopesApi = new EnvelopesApi(apiClient.Configuration);
envelopeSummary = envelopesApi.CreateEnvelope(account_id, envDef);
Console.WriteLine($"Quote {envelopeCounter} of {quotes.Count}. Envelope Summary: {envelopeSummary.Status}");
Logger.Log.Info($"Quote {envelopeCounter} of {quotes.Count}. Envelope Summary: {envelopeSummary.Status}");
successFlag = true;
}

When using the UpdateEnvelope() method to void an envelope, don't use the previous envelope's object as the envelope definition parameter. Instead, create a new, empty Envelope object and use that.
It also looks like you're referencing envelope in the first part, and then getting the envelope ID from envelopeSummary.EnvelopeId. You'll want to confirm you have the correct ID for the envelope you're attempting to void.
Try this:
Envelope nullEnvelope = new Envelope();
nullEnvelope.Status = "voided";
nullEnvelope.VoidedReason = "This envelope was voided";
updateSummary = envelopesApi.Update(account_id, envelopeId, nullEnvelope);

If you have only one signer scenario and that signer has complained that something is wrong on the document, then I would suggest to use Update document flow that would save your envelope count and some saving. You can update Document(s) in an envelope if no recipient in the workflow has taken any action by following any of the below endpoint,
Update
Document
Update Document
List

Related

How to manage a series of PATCH calls to Azure DevOps REST API to avoid HTTP Response Conflict error 409

In a custom C# Winforms app, I'm using the Azure DevOps REST API Update Comments call to update work item comments using async/await
My call to UpdateComment_Async is in a tight loop designed to submit all update comment requests for a single work item and then process each comment update as it completes.
Following is a mix of p-code and C# for (almost) working code. In the case where there are 2 (or more) comments to update, the first update returns HttpResponseMessage.Status 200 (success), but the second update submitted returns HttpResponseMessage.Status 409 "Conflict". I assume that ADS has the work item locked for the first update, and so the 2nd update fails with the 409. I think I proved this by sleeping the thread for 5 seconds after the call to UpdateComment_Async. With the sleep in place, both updates work.
Is there a way to manage the series of calls to UpdateComment_Async so that subsequent calls aren't done until the previous one is complete?
CODE
// get comment(s) for one work item .......
foreach (comment in workItem)
{
newCommentText = "new comment text blah-blah-blah";
Task<Tuple<string, string, HttpResponseMessage>> updateCommentTask = UpdateComment_Async(projectUrl, workItem.Id.ToString(), comment.Id.ToString(), newCommentText);
// I put a thread.sleep(5000) right here
updateCommentTaskList.Add(updateCommentTask);
}
// Process update comments tasks as they complete
while (updateCommentTaskList.Count > 0)
{
Task<Tuple<string, string, HttpResponseMessage>> finishedUpdateCommentTask = await Task.WhenAny(updateCommentTaskList);
// Get Results
Tuple<string, string, HttpResponseMessage> updateCommentTaskResult = finishedUpdateCommentTask.Result;
// process updateCommentTaskResult
// etc
// etc
// etc
updateCommentTaskList.Remove(finishedUpdateCommentTask);
}
//*******************************
public async Task<Tuple<string, string, HttpResponseMessage>> UpdateComment_Async(string projectUrl, string workItemId, string commentId, string commentNumber, string newCommentText)
{
HttpResponseMessage responseResult = null;
#region MAKE JSON REQUEST BODY
IList<ClsUpdateComment> updateFieldJsonList = new List<ClsUpdateComment>();
updateFieldJsonList.Clear();
// Note: This code works but is not in compliance with MS docs on 2 counts.
//
// 1) The MS docs on comment update say that the body should look like fig 1.
// The only I could do this was to create a new ClsUpdateComment and then add it to
// List<ClsUpdateComment>. Adding the ClsUpdateComment object to a list causes the [ ]
// to be created when the list is serialized. To make this work, I had to serialize
// just the ClsUpdateComment object so that when serialzed, it ends up looking like Fig 2 (no brackets)
//
// 2) application/json-patch+json causes error 415 - unsupported media type to occur. application/json
// works.
/*
Fig 1
[
{
"text": "Moving to the right area path - Fabrikam-Git"
}
]
Fig 2
{
"text": "Moving to the right area path - Fabrikam-Git"
}
*/
ClsUpdateComment updateFieldJson = new ClsUpdateComment
{
Text = $"{newCommentText}"
};
updateFieldJsonList.Add(updateFieldJson);
#endregion MAKE JSON REQUEST BODY
#region SUBMIT UPDATE REQUEST
string request = $"{projectUrl}/_apis/wit/workitems/{workItemId}/comments/{commentId}?api-version=5.1-preview.3";
JsonSerializerSettings jsonSerializerSettings = new JsonSerializerSettings
{
StringEscapeHandling = StringEscapeHandling.EscapeHtml,
};
string updateFieldJsonSerialized = JsonConvert.SerializeObject(updateFieldJson, Formatting.None, jsonSerializerSettings);
using (HttpClient client = new HttpClient(new HttpClientHandler() { UseDefaultCredentials = true, ClientCertificateOptions = ClientCertificateOption.Manual }))
{
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
HttpRequestMessage httpRequestMessage = new HttpRequestMessage(new HttpMethod("PATCH"), request)
{
Content = new StringContent(updateFieldJsonSerialized, Encoding.UTF8, "application/json")
};
using (responseResult = await client.SendAsync(httpRequestMessage))
{
// don't need Content, just the HttpResponseMessage
// //string content = await responseResult.Content.ReadAsStringAsync();
}
}
#endregion SUBMIT UPDATE REQUEST
return Tuple.Create(workItemId, commentNumber, responseResult);
}
It's not an answer, but after trying to do mass updates of comments using the REST API in various ways, I've come to the conclusion that it simply doesn't support mass updates of comments

Passing parameters to a Twilio via CallResource.CreateAsync to programmatically add a bot to a conference call

I'm trying to create a conference call with a Moderator and several participants one of which is a bot.
The bot is controlled by the Moderator via a back channel so it can Say things etc to the conference call.
I setup the conference call when the Moderator calls in from a web client and then dial in the other participants and the bot using CallResource.CreateAsync with a callback url so I know which conference to add them to.
The bot needs to get a parameter so it knows which moderator to listen to for instructions.
However, I can't seem to pass any parameters to the bot (which is currently being triggered via another TwiML app) from the C# API using CallResource.CreateAsync.
Adding a participant to the call (callbackUrl adds the connected call to the conference) - this works fine:
var to = new PhoneNumber(callData.PhoneNumber);
var from = new PhoneNumber(_twilioSettings.PhoneNumber);
var callbackUrl = GetConnectConferenceUrl(callData.CallToken);
var url = new Uri(callbackUrl);
var participantCallResource = await CallResource.CreateAsync(to, from, url: url);
Adding the bot to call (Phone number is setup in Twilio as a TwiML app with a webhook back to my server) - how do I pass parameters to the TwiML app?
var toBot = new PhoneNumber(botNumber);
var fromBot = new PhoneNumber(_twilioSettings.PhoneNumber);
var botCallbackUrl = GetConnectConferenceUrl(callData.CallToken, isBot: true);
var botUrl = new Uri(botCallbackUrl);
var botCallResource = await CallResource.CreateAsync(toBot, fromBot, url: botUrl)
How do I pass parameters to a TwiML bin or Webhook or phonenumber from C#?
Do I need to add the bot in to the call a different way?
Parameters are passed either on the To or on the Url per https://www.twilio.com/docs/voice/how-share-information-between-your-applications
I believe your use case would put it on the Url. Assuming you have a moderatorId:
var botCallbackUrl = GetConnectConferenceUrl(callData.CallToken, isBot: true, moderatorId);
var botUrl = new Uri(botCallbackUrl);
where GetConnectConferenceUrl adds the parameter you need.
I've used it on the To line for a Client endpoint, so that looked like
var toBot = $"{new PhoneNumber(botNumber)}?moderatorId={moderatorId}";

Sendgrid API: The provided authorization grant is invalid, expired, or revoked

I've just created a sendgrid account. Then I went to settings=>API Keys
and clicked on "Create API Key" and gave any possible permission.
Then I've created a c# project, added nuget packages and put my write the hello world code from here
public async Task HelloEmail()
{
dynamic sg = new SendGrid.SendGridAPIClient("XXX-XXXXXXXXXXXXXXXXXX", "https://api.sendgrid.com");
Email from = new Email("MY#Email.com");
String subject = "Hello World from the SendGrid CSharp Library";
Email to = new Email("test#example.com");
Content content = new Content("text/plain", "Textual content");
Mail mail = new Mail(from, subject, to, content);
Email email = new Email("test2#example.com");
mail.Personalization[0].AddTo(email);
dynamic response = await sg.client.mail.send.post(requestBody: mail.Get());
var x=response.StatusCode;
var y = response.Body.ReadAsStringAsync().Result;
var z = response.Headers.ToString();
}
But I get
Unauthorized =>
"{\"errors\":[{\"message\":\"The provided authorization grant is invalid, expired, or revoked\",\"field\":null,\"help\":null}]}"
In the example, they got the API key from the EnvironmentVariableTarget.User is it related to that?
string apiKey = Environment.GetEnvironmentVariable("NAME_OF_THE_ENVIRONMENT_VARIABLE_FOR_YOUR_SENDGRID_KEY", EnvironmentVariableTarget.User);
dynamic sg = new SendGridAPIClient(apiKey);
*The problem is that no one reads messages when creating a key, also Microsoft chooses to show us "API Key ID" which is worst name ever
It is not a duplicate because although the reason was the same, no one would guess it since in c# we use a nuget library, not the api.
Something is wrong with your API key. Check this answer, generate a new key, and double check your permissions.
You also don't need to specify the URL in your SendGrid.SendGridAPIClient. I'd remove that line to reduce hardcoded values.
Put key directly , do not use System.getenv(KEY)
String key = "YOUR KEY";
SendGrid sg = new SendGrid(key);

Calling MailChimp API v3.0 with .Net

I'm trying to access our MailChimp account via the new 3.0 REST API. I've done the following:
using(var http = new HttpClient())
{
var creds = Convert.ToBase64String(Encoding.ASCII.GetBytes("username:mailchimpapikey-us1"));
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", creds);
string content = await http.GetStringAsync(#"https://us1.api.mailchimp.com/3.0/lists");
Console.WriteLine(content);
}
However, when I run this code, I get a 401 error with the following json details:
{"type":"http://kb.mailchimp.com/api/error-docs/401-api-key-invalid","title":"API Key Invalid","status":401,"detail":"Your API key may be invalid, or you've attempted to access the wrong datacenter.","instance":"a9fe4028-519e-41d6-9f77-d2caee4d4683"}
The datacenter I'm using in my URI (us1 in this example) matches the dc on my API key. My API key works if I use the MailChimp SDK so I know my key isn't invalid. Also, using Fiddler, I can see that the MailChimp SDK is calling the same dc as I'm doing in my URI.
Any Ideas as to why I am having trouble Authenticating?
EDIT
As noted in the question, I'm asking specifically about accessing the new 3.0 REST API. I'm trying to do this directly as opposed to using a third party wrapper.
The new API is composed of http calls so it should be pretty straight forward. I'm simply having trouble with the authentication piece.
So I was able to finally chat with a super tech support person at MailChimp.
The MailChimp docs state the following
The easiest way to authenticate is using HTTP Basic Auth. Enter any string
as the username and supply your API Key as the password.
Your HTTP library should have built-in support for basic authorization.
Their documentation is a bit misleading. Typically the Auth header for Basic Auth would look like what I was sending:
Authorization: Basic xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
where the row of x would represent the base64 encoded username:password.
However, talking with the support tech, the actual implementation they use is:
Authorization: username keyid
No base64 encoding, no Basic keyword. Username doesn't even have to be your username.
So, here is the working code:
using(var http = new HttpClient())
{
http.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Basic", mailchimpapikey-us1);
string content = await http.GetStringAsync(#"https://us1.api.mailchimp.com/3.0/lists");
Console.WriteLine(content);
}
EDIT
Note the comments. TooMuchPete was correct in that the normal HTTP Basic Auth headers do work. Apparently I was hitting some old code or something on the MailChimp side.
I'm leaving the post as a reference for anyone who is trying to call the new 3.0 API.
I wrote an article on a simple way up adding subscribers to a list using:
Dim mailchimp As New ZmailChimp
Dim ListId$ = "9b2e63f0b9" 'List Sage' List
Dim email$ = "samsmith20#anymail.com" '"sam19#postcodelite.com"
Dim fieldListOnAdd = "FNAME,Sam,LNAME,Smith,MTYPE,User,MID,631637"
Dim fieldListOnUpdate = "FNAME,Sam,LNAME,Smith,MID,631637" 'Don't change MTYPE
'Put on 'Sage One' and 'Sage 50' group
Dim groupList = "407da9f47d,05086211ba"
With mailchimp
.API$ = "46cMailChimpAPIKeyd1de-us14" 'MailChimp API key
.dataCenter$ = "us14" 'Last 4 letters of API key
.password$ = "Password!"
MsgBox(.addSubscriber(ListId$, email, fieldListOnAdd, fieldListOnUpdate, groupList))
End With
mailchimp = Nothing
see:http://www.codeproject.com/Tips/1140339/Mail-Chimp-Add-Update-e-mail-to-List-and-Subscribe
this may save someone some time
Mailchimp Ecommerce
var mcorder = new Standup.Ecomm.MailChimpManager(ConfigurationManager.AppSettings["MailChimpApiKey"]);
var orders = new MailOrder();
orders.CampaignId = ConfigurationManager.AppSettings["MailChimpCampaignId"];
orders.EmailId = ConfigurationManager.AppSettings["MailChimpEmailId"];
orders.Id = orderNumber;
orders.StoreId = "abcde";
orders.StoreName = "E-Commerce Store";
orders.Total = Convert.ToDouble(orderTotal);
orders.Tax = Convert.ToDouble(tax);
orders.Items = new List<MailOrderItem>();
foreach (var orderItem in orderItemList)
{
var item = new MailOrderItem();
item.ProductId = orderItem.OrderNumber;
item.ProductName = orderItem.Title;
item.SKU = orderItem.Sku;
item.CategoryId = 0;
item.CategoryName = " ";
item.Quantity = orderItem.Quantity;
item.Cost = Convert.ToDouble(orderItem.ProductCost);
orders.Items.Add(item);
}
mcorder.AddOrder(orders);

Error while deserializing Azure ServiceBus Queue message sent from node.js (azure sdk)

Here's my scenario:
I'm sending an Azure ServiceBus Queue message from Node.js using the node azure sdk like so:
var message = {
body: JSON.stringify({ foo: 'Bar' })
};
serviceBusService.sendQueueMessage('myQueue', message, function (error) {
if (!error) {
console.log('msessage sent');
}
});
I have a c# worker role that is listening to the Queue:
QueueClient Client = QueueClient.CreateFromConnectionString(connStr, QueueName);
Client.OnMessage((receivedMessage) =>
{
var body = receivedMessage.GetBody<string>();
});
When the GetBody method gets executed, i get the following error:
There was an error deserializing the object of type System.String. The input source is not correctly formatted
After some digging around, i found THIS article that helped me get a solution:
Client.OnMessage((receivedMessage) =>
{
var bodyJson = new StreamReader(receivedMessage.GetBody<Stream>(), Encoding.UTF8).ReadToEnd();
var myMessage = JsonConvert.DeserializeObject<MyMessage>(bodyJson);
});
If anyone has faced this issue and found a better solution, please let me know!
Thanks!
To anyone who found this question if they were getting this error from sending the message using Service Bus Explorer (like me).
Make sure you specify the correct message type in the drop down:
Thanks for the update, I was doing the reverse and this helped me. I thought I'd add to your solution for completeness. The DeserializeObject method needs the "MyMessage" class defining. In your original post, your JSON is:
{ foo: 'Bar' }
If we drop that into json2csharp (json2csharp.com) we now have the class required to complete your solution:
public class MyMessage
{
public string foo { get; set; }
}
Of course, the dependency is having Newtonsoft.Json package added to your Visual Studio solution:
Install-Package Newtonsoft.Json -Pre
Using the nuget package: Microsoft.Azure.ServiceBus
The following info is contained inside as as comment:
If a message is only being sent and received using this Microsoft.Azure.ServiceBus
client library, then the below extension methods are not relevant and should not be used.
If this client library will be used to receive messages that were sent using both WindowsAzure.Messaging client library and this (Microsoft.Azure.ServiceBus) library, then the Users need to add a User property Microsoft.Azure.ServiceBus.Message.UserProperties while sending the message. On receiving the message, this property can be examined to determine if the message was from WindowsAzure.Messaging client library and if so use the message.GetBody() extension method to get the actual body associated with the message.
---------------------------------------------- Scenarios to
use the GetBody Extension method: ----------------------------------------------
If message was constructed using the WindowsAzure.Messaging client library as
follows:
var message1 = new BrokeredMessage("contoso"); // Sending a plain string var
message2 = new BrokeredMessage(sampleObject); // Sending an actual customer object
var message3 = new BrokeredMessage(Encoding.UTF8.GetBytes("contoso")); // Sending
a UTF8 encoded byte array object await messageSender.SendAsync(message1); await
messageSender.SendAsync(message2); await messageSender.SendAsync(message3);
Then retrieve the original objects using this client library as follows: (By
default Microsoft.Azure.ServiceBus.InteropExtensions.DataContractBinarySerializer
will be used to deserialize and retrieve the body. If a serializer other than
that was used, pass in the serializer explicitly.)
var message1 = await messageReceiver.ReceiveAsync(); var returnedData1 = message1.GetBody();
var message2 = await messageReceiver.ReceiveAsync(); var returnedData2 = message1.GetBody();
var message3 = await messageReceiver.ReceiveAsync(); var returnedData3Bytes =
message1.GetBody(); Console.WriteLine($"Message3 String: {Encoding.UTF8.GetString(returnedData3Bytes)}");
------------------------------------------------- Scenarios to NOT use the GetBody
Extension method: ------------------------------------------------- If message
was sent using the WindowsAzure.Messaging client library as follows: var message4
= new BrokeredMessage(new MemoryStream(Encoding.UTF8.GetBytes("contoso"))); await
messageSender.SendAsync(message4); Then retrieve the original objects using this
client library as follows: var message4 = await messageReceiver.ReceiveAsync();
string returned = Encoding.UTF8.GetString(message4.Body); // Since message was
sent as Stream, no deserialization required here.
May it help you
With the latest Service Bus client libraries (.NET, JS, Java, Python), you can send message(s) using the JS library like this:
const serviceBusClient = new ServiceBusClient("<connectionstring>");
const sender = serviceBusClient.createSender("<queuename>");
await sender.sendMessages([{
body: {
title: "hello"
}
}]);
Note that .sendMessages takes a list as an input, even if you're just sending one message.
And get the body of the received message using .NET library like this:
await using var client = new ServiceBusClient("<connectionstring>");
ServiceBusReceiver receiver = client.CreateReceiver("<queuename>");
ServiceBusReceivedMessage receivedMessage = await receiver.ReceiveMessageAsync();
string body = receivedMessage.Body.ToString();
Console.WriteLine(body); //prints {"title":"hello"}

Categories