I'm trying to create drafts in Gmail using the .Net Client Library. I can successfully login and retrieve a list of drafts so the authentication and api are working. Now I need to create an instance of the Draft class and send it to the API. But what does the draft message need to look like? It doesn't matter what I fill in the API explorer on https://developers.google.com/gmail/api/v1/reference/users/drafts/create, my draft is always empty.
Also when doing it from my C# code I need to set the draft.Message.Raw field to something else I get an error:
Missing draft message [400]
Using the client library you can set the base64 encoded email to the raw property of a Message then use that Message as the message property of the Draft.
More generally:
The Draft Message consists of an id and a Message Resource
https://developers.google.com/gmail/api/v1/reference/users/drafts
{
"id": string,
"message": users.messages Resource
}
The Message Resource should have its "raw" field set to a base64 encoded RCF 2822 formatted string.
Eg:
from: me#email.com
to: you#email.com
subject: test email
email body
As a base64 encoded string is:
ZnJvbTogbWVAZW1haWwuY29tDQp0bzogeW91QGVtYWlsLmNvbQ0Kc3ViamVjdDogdGVzdCBlbWFpbA0KDQplbWFpbCBib2R5
So the request body of a draft.create should look something like:
{
"message": {
"raw": "ZnJvbTogbWVAZW1haWwuY29tDQp0bzogeW91QGVtYWlsLmNvbQ0Kc3ViamVjdDogdGVzdCBlbWFpbA0KDQplbWFpbCBib2R5"
}
}
I was struggling with this until today.
What I did was adapting the solution on this link for drafts.
http://jason.pettys.name/2014/10/27/sending-email-with-the-gmail-api-in-net-c/
Jason uses a nuget called AE.Net.Mail to serialize an mail object to RFC 2822.
what I did was I installed both nugets
Install-Package Google.Apis.Gmail.v1
Install-Package AE.Net.Mail
And after that I created two methods
static GmailService Service;
public static void CriaService(string emailaConectar)
{
var certificate = new X509Certificate2(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ClientCredentials.CertificatePath), ClientCredentials.ClientSecret, X509KeyStorageFlags.Exportable);
var credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(ClientCredentials.ServiceAccountEmail)
{
Scopes = new[] { GmailService.Scope.GmailCompose },
User = emailaConectar
}.FromCertificate(certificate)) { };
Service = new GmailService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = ClientCredentials.ApplicationName,
});
}
private static string Base64UrlEncode(string input)
{
var inputBytes = System.Text.Encoding.ASCII.GetBytes(input);
// Special "url-safe" base64 encode.
return Convert.ToBase64String(inputBytes)
.Replace('+', '-')
.Replace('/', '_')
.Replace("=", "");
}
And on my Main method I designed like this
CriaService("xpto#gmail.com");
var msg = new AE.Net.Mail.MailMessage
{
Subject = "Your Subject",
Body = "Hello, World, from Gmail API!",
From = new MailAddress("xpto#gmail.com")
};
msg.To.Add(new MailAddress("someone#gmail.com"));
msg.ReplyTo.Add(msg.From);
var msgStr = new StringWriter();
msg.Save(msgStr);
Message m = new Message();
m.Raw = Base64UrlEncode(msgStr.ToString());
var draft = new Draft();
draft.Message = m;
try
{
Service.Users.Drafts.Create(draft, "xpto#opportunity.com.br").Execute();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
message > raw is expected to be the full SMTP message.
{
"message": {
"raw": "From: me#example.com\nTo:you#example.com\nSubject:Ignore\n\nTest message\n"
}
Alternatively, you can also set the appropriate fields in message > payload:
{
"message": {
"payload": {
"headers": {
{"name": "From", "value": "me#example.com},
{"name": "To", "value": "you#example.com"},
{"name": "Subject", "value":"Ignore"}
},
"body": {
"data": "Test message"
}
}
}
}
Related
I've been trying to call Microsoft Graph API for creating events, however I've not been able to do it.
Context: I have a Web MVC application (C#) already in production, with the "common" authentication method, reading a database of users. Recently the customer asked me the possibility to create Microsoft Teams Meetings from the application and also those created meetings have to be scheduled in the Microsoft Teams Calendar with the "Join" button to enter the meeting.
I already configured the API permissions, client secret and used the other properties like tenant, user id, etc from the Azure Portal, I'm sharing a screenshot of my configuration. I'm doing the "Get access on behalf of a user" process.
API Permissions:
Permissions image
Taking the example of the authorize endpoint from the docs, of course I'm replacing the values with my own info
https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize?
client_id=11111111-1111-1111-1111-111111111111
&response_type=code
&redirect_uri=http%3A%2F%2Flocalhost%2Fmyapp%2F
&response_mode=query
&scope=offline_access%20user.read%20mail.read
&state=12345
Here is my code to Receive the code once the user authorizes the permissions, I'm just storing the value in a static class for testing
public ActionResult ReceiveCode(string code)
{
AuthenticationConfig.Code = code;
//this.Code = code;
return RedirectToAction("Index");
}
Once I got the Auth code, I'm using it to create the event with the generated token, also I already verified that the token contains the permissions given in the Azure Portal.
This is the input for the /events endpoint
var json = JsonConvert.SerializeObject(new
{
subject = "Let's go for lunch",
body = new
{
contentType = "HTML",
content = "Does noon work for you?"
},
start = new
{
dateTime = "2017-04-15T12:00:00",
timeZone = "Pacific Standard Time",
},
end = new
{
dateTime = "2017-04-15T14:00:00",
timeZone = "Pacific Standard Time"
},
location = new
{
displayName = "Harry's Bar",
},
attendees = new List<Attendee>()
{
new Attendee
{
EmailAddress = new EmailAddress
{
Address = "mymail#whatever.com",
Name = "Foo Bar"
},
Type = AttendeeType.Required
}
},
allowNewTimeProposals = true,
isOnlineMeeting = true,
onlineMeetingProvider = "teamsForBusiness",
});
This is the complete method, for the json value, please see the json above. I also tried with the "me" url but it does not work either.
public async Task<ActionResult> OnlineMeeting()
{
try
{
var httpClient = new HttpClient();
var paramsDictionary = new Dictionary<string, string>();
paramsDictionary.Add("client_id",AuthenticationConfig.ClientId);
paramsDictionary.Add("scope", "Calendars.ReadWrite");
paramsDictionary.Add("code", AuthenticationConfig.Code);
paramsDictionary.Add("redirect_uri", "https://localhost:44379/Meeting/Reunion/ReceiveCode");
paramsDictionary.Add("grant_type", "authorization_code");
paramsDictionary.Add("client_secret", AuthenticationConfig.ClientSecret);
var url = string.Format("https://login.microsoftonline.com/{0}/oauth2/v2.0/token", "tenant");
var response = await httpClient.PostAsync(url, new FormUrlEncodedContent(paramsDictionary));
if (response.IsSuccessStatusCode)
{
var jsonResponse = await response.Content.ReadAsStringAsync();
var jsonResult = JsonConvert.DeserializeObject(jsonResponse) as JObject;
var accessToken = jsonResult.GetValue("access_token").ToString();
httpClient = new HttpClient();
var json = JsonConvert.SerializeObject(new { });
var defaultRequestHeaders = httpClient.DefaultRequestHeaders;
if (defaultRequestHeaders.Accept == null || !defaultRequestHeaders.Accept.Any(m => m.MediaType == "application/json"))
{
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
defaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
var data = new StringContent(json);
response = await httpClient.PostAsync("https://graph.microsoft.com/v1.0/users/{user id}/events", data);
if (response.IsSuccessStatusCode)
{
// Nice
}
else
{
Console.WriteLine($"Failed to call the web API: {response.StatusCode}");
string content = await response.Content.ReadAsStringAsync();
}
}
else
{
Console.WriteLine($"Failed to call the web API: {response.StatusCode}");
string content = await response.Content.ReadAsStringAsync();
}
}
catch (Exception ex)
{
}
return View();
}
I'm able to the get the token, but when trying to create the event returns the next response.
{
"error": {
"code": "ResourceNotFound",
"message": "Resource could not be discovered.",
"innerError": {
"date": "2021-08-31T22:58:18",
"request-id": "c5c07afa-fa89-4948-a9f8-f80ca4cbafc3",
"client-request-id": "c5c07afa-fa89-4948-a9f8-f80ca4cbafc3"
}
}
}
Am I missing something? Maybe the wrong endpoint?
Please, help.
Thanks in advance.
I'm trying to embed a docusign signature page into a website. but I get this error
Message=Error calling CreateRecipientView: {
"errorCode": "UNKNOWN_ENVELOPE_RECIPIENT",
"message": "The recipient you have identified is not a valid recipient of
the specified envelope."
}
I'm using the .Net nuget client and here's the code I'm using (note I've changed guids and emails)
Authenticate
string userId = "0570b3da-652e-4040-842e-65a0e6fc8133"; // use your userId (guid), not email address
string integratorKey = "cf73e7bb-e05d-4ce9-9cea-ac065dc894ac";
string host = "https://demo.docusign.net/restapi";
ApiClient apiClient = new ApiClient(host);
string username = "my#email.com";
string password = "[password]";
// initialize client for desired environment (for production change to www)
Configuration.Default.ApiClient = apiClient;
// configure 'X-DocuSign-Authentication' header
string authHeader = "{\"Username\":\"" + username + "\", \"Password\":\"" + password + "\", \"IntegratorKey\":\"" + integratorKey + "\"}";
Configuration.Default.AddDefaultHeader("X-DocuSign-Authentication", authHeader);
Find account id
this was part of the example code
// we will retrieve this from the login API call
string accountId = null;
AuthenticationApi authApi = new AuthenticationApi(apiClient.Configuration);
LoginInformation loginInfo = authApi.Login();
//find the default account for this user
foreach (LoginAccount loginAcct in loginInfo.LoginAccounts)
{
if (loginAcct.IsDefault == "true")
{
accountId = loginAcct.AccountId;
string[] separatingStrings = { "/v2" };
// Update ApiClient with the new base url from login call
apiClient = new ApiClient(loginAcct.BaseUrl.Split(separatingStrings, StringSplitOptions.RemoveEmptyEntries)[0]);
break;
}
}
Create Envelope from a template
EnvelopeDefinition envDef = new EnvelopeDefinition();
TemplateRole tRole = new TemplateRole();
tRole.Email = "recipient#email.com";
tRole.RoleName = "Leaseholder";
tRole.ClientUserId = tRole.Email;
List<TemplateRole> rolesList = new List<TemplateRole> { tRole };
envDef.TemplateRoles = rolesList;
envDef.TemplateId = "2504e3f0-f4d9-4eca-9fd3-3b26cfd6c086";
RecipientViewRequest viewOptions = new RecipientViewRequest()
{
ReturnUrl = "http://localhost:64202/home/winning",
ClientUserId = tRole.Email, // must match clientUserId of the embedded recipient
AuthenticationMethod = "email",
UserName = tRole.Email,
Email = tRole.Email
};
EnvelopesApi envelopesApi = new EnvelopesApi();
var summary = envelopesApi.CreateEnvelope(accountId, envDef);
var receipients = envelopesApi.ListRecipients(accountId, summary.EnvelopeId);
ViewUrl viewUrl = envelopesApi.CreateRecipientView(accountId, summary.EnvelopeId, viewOptions);
return Content($"<h2>hmm</h2><iframe width=\"100%\" height=\"100%\" src=\"{viewUrl.Url}\"/>");
Recipients on the template
Recipients
{
"agents": [],
"carbonCopies": [],
"certifiedDeliveries": [],
"editors": [],
"inPersonSigners": [],
"intermediaries": [],
"recipientCount": "1",
"seals": [],
"signers": [
{
"clientUserId": "recipient#email.com",
"creationReason": "sender",
"deliveryMethod": "email",
"email": "recipient#email.com",
"isBulkRecipient": "false",
"name": "",
"note": "",
"recipientId": "1",
"recipientIdGuid": "52abdeea-2bd6-4108-97b9-170ca27d573a",
"requireIdLookup": "false",
"roleName": "Leaseholder",
"routingOrder": "1",
"status": "created",
"userId": "0570b3da-652e-4040-842e-65a0e6fc8133"
}
]
}
The UNKNOWN_ENVELOPE_RECIPIENT error usually means that one of the following three property values specified for the Recipient within the Envelope doesn't exactly match the corresponding value that's been specified in the Get Recipient View request:
Name
Email
ClientUserId
In your case (based upon the code you posted), I suspect that the UNKNOWN_ENVELOPE_RECIPIENT error is being caused by the fact that the info you've specified for the recipient does not include a value for the Name property -- where as the info you've specified when creating the RecipientViewRequest object does include a value for the UserName property (as it must).
To fix this error, I'd suggest that you try adding this line to the portion of your code when you're specifying information for the TemplateRole object (where "RECIPIENT_NAME" is the first and last name of the recipient).
tRole.Name = "RECIPIENT_NAME;
And then specify the same value for the UserName property of the RecipientViewRequest object:
UserName = "RECIPIENT_NAME",
(The value you specify as RECIPIENT_NAME will be the name that the recipient signs in the doc(s), so you should specify first name / last name of the person, not an email address.)
UPDATE
Re the subsequent RECIPIENT_NOT_IN_SEQUENCE error that you've mentioned in your comment, this error occurs when you call Get Recipient View for a recipient either before the Envelope has been sent or before it's "their turn" in the routing order to receive the Envelope. In your case, I suspect this is occurring because you're not setting the status of the Envelope to sent -- the recipient can't receive/access the Envelope until it's been sent. To resolve this error, set the status of of the Envelope when you're composing the EnvelopeDefinition object:
envDef.Status = "sent";
I want to send a POST request in c# and i need the following sent through
"jsonrpc": "2.0",
"id": "12345",
"method": "my method",
"params": {
"api_key": "my api key",
"preset_id": "my preset id"
}
I tried using
using (WebClient client = new WebClient ())
{
byte [] response =
client.UploadValues ("my url", new NameValueCollection ()
{
{ "jsonrpc", "2.0" },
{ "id", "12345"},
{ "method", "my method"},
{ "params", ""}
});
string result = System.Text.Encoding.UTF8.GetString (response);
}
But i couldnt make the params an array, Please help, Thank you
It appears that you are asking for the parameters to be in an array, but they are actually shown as a "subclass". If the values were in an array, they should have square brackets around them.
However, both results are easy to achieve using anonymous (or real) classes (which I much prefer over embedding the property names in quoted text (makes future modifications much easier to implement).
var parameters = new
{
api_key = "my api key",
preset_id = "my preset id"
};
var json = new
{
jsonrpc = "2.0",
id = "12345",
method = "my method",
#params = parameters
};
string sResult = (new System.Web.Script.Serialization.JavaScriptSerializer()).Serialize(json);
The above code will result in the same output that you have shown. If you want an actual array instead, you can change the parameters definition to:
var parameters = new NameValueCollection();
parameters.Add("api_key", "my api key");
parameters.Add("preset_id", "my preset id");
Note that I used the .Net framework json serializer (from System.Web.Extensions), but you can use the serializer of your choice (we generally use NewtonSoft's JsonConvert).
I'm trying to send an email with the Office 365 C# client library. I've successfully performed other operations with it, i.e. getting a folder and getting messages, but am unable to send an email.
I'm using a MailHelper class provided in a Microsoft sample. This is the method:
internal async Task<String> ComposeAndSendMailAsync(string subject,
string bodyContent,
string recipients)
{
// The identifier of the composed and sent message.
string newMessageId = string.Empty;
// Prepare the recipient list
var toRecipients = new List<Recipient>();
string[] splitter = { ";" };
var splitRecipientsString = recipients.Split(splitter, StringSplitOptions.RemoveEmptyEntries);
foreach (string recipient in splitRecipientsString)
{
toRecipients.Add(new Recipient
{
EmailAddress = new EmailAddress
{
Address = recipient.Trim(),
Name = recipient.Trim(),
},
});
}
// Prepare the draft message.
var draft = new Message
{
Subject = subject,
Body = new ItemBody
{
ContentType = BodyType.HTML,
Content = bodyContent
},
ToRecipients = toRecipients
};
try
{
// Make sure we have a reference to the Outlook Services client.
var outlookClient = await AuthenticationHelper.GetOutlookClientAsync("Mail");
//Send the mail.
await outlookClient.Me.SendMailAsync(draft, true);
return draft.Id;
}
//Catch any exceptions related to invalid OData.
catch (Microsoft.OData.Core.ODataException ode)
{
throw new Exception("We could not send the message: " + ode.Message);
}
catch (Exception e)
{
throw new Exception("We could not send the message: " + e.Message);
}
}
My arguments are not null and seem to be correct. The error I'm getting is: "Cannot read the request body.".
I've made sure my application is registered with the right permissions so I'm at a loss. Does anyone know what's going on with my code?
Try capturing a Fiddler trace of the request and check the Content-Type header.
I'm trying to use the C# Google Groups Migration API and not having much luck.
I have the following code:
var body =
#"Date: 16 Jul 07 10:12 GMT
From: samplesender#example.com
To: samplegroup#googlegroups.com
This is the body of the migrated email message.
";
var bytes = ASCIIEncoding.ASCII.GetBytes(body);
var messageStream = new MemoryStream(bytes);
UserCredential credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
new ClientSecrets { ClientId = "<insert client id here>", ClientSecret = "<insert client secret here>" },
new[] { "https://www.googleapis.com/auth/apps.groups.migration" },
"user",
CancellationToken.None,
new FileDataStore("GroupsMigration.Auth.Store")).Result;
var service = new GroupsMigrationService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "group migration application"
});
var request = service.Archive.Insert("<insert valid group email here>", messageStream, "message/rfc822");
IUploadProgress uploadStatus = request.Upload();
if (uploadStatus.Exception != null)
{
Console.WriteLine(uploadStatus.Exception.ToString());
}
I keep getting the following exception :
The service groupsmigration has thrown an exception: Google.GoogleApiException: Google.Apis.Requests.RequestError
Unable to parse the raw message [400]
Errors [
Message[Unable to parse the raw message] Location[ - ] Reason[invalid] Domain[global]
]
According to the Groups Migration API documentation (https://developers.google.com/admin-sdk/groups-migration/v1/reference/archive/insert see the responseCode section towards the bottom of the page), it indicates that the message I am trying to migrate is rejected as malformed. I tried many different messages and I always get the same error -> Unable to parse the raw message [400].
Did anybody find a message that the Google Groups Migration accepts and would like to share ? Anything else I am doing wrong ?
Any help gratefully appreciated!
Found the solution :
var body =
#"Date: 16 Jul 07 10:12 GMT
From: samplesender#example.com
To: samplegroup#googlegroups.com
Message-Id: <12345#acme.com>
This is the body of the migrated email message.
";
var bytes = ASCIIEncoding.ASCII.GetBytes(body);
var messageStream = new MemoryStream(bytes);
UserCredential credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
new ClientSecrets { ClientId = "<insert client id here>", ClientSecret = "<insert client secret here>" },
new[] { "https://www.googleapis.com/auth/apps.groups.migration" },
"user",
CancellationToken.None,
new FileDataStore("GroupsMigration.Auth.Store")).Result;
var service = new GroupsMigrationService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "group migration application"
});
var request = service.Archive.Insert("<insert valid group email here>", messageStream, "message/rfc822");
IUploadProgress uploadStatus = request.Upload();
if (uploadStatus.Exception != null)
{
Console.WriteLine(uploadStatus.Exception.ToString());
}
Bottom line, if you want Google to accept your message you must put a Message-id header with the following format <NNNN#mail.samplegroup.com>
Try adding a subject and message-id:
var body =
#"Date: 16 Jul 07 10:12 GMT
From: samplesender#example.com
To: samplegroup#googlegroups.com
Subject: test message
Message-Id: 1234#acme.com
This is the body of the migrated email message.
...