I am developing an aplication that send and email with one or multiple attachments via Microsoft Graph, but when try to upload file send me an error: ": Invalid total bytes specified in the Content-Range header"
i asume that i must specifi Range Value in same where, but no idea.
This is my code:
private static async void SenMailUsingMicrosoftGraph(List<String>Destinations, List<String>Cc, string HidenCopy, string Body, string Title, List<FileInfo>Filess);
{
ClientSecretCredential credential = new ClientSecretCredential("MyTenantID", "MyClientId", "MyClientSecret");
List<Recipient> recipientsDestinatarios = new List<Recipient>();
List<Recipient> recipientsCopias = new List<Recipient>();
foreach (var c in Destinations)
{
recipientsDestinatarios.Add(
new Recipient
{
EmailAddress = new EmailAddress
{
Address = c
}
});
}
foreach (var mail in Cc)
{
recipientsCopias.Add(
new Recipient
{
EmailAddress = new EmailAddress
{
Address = mail
}
});
}
#endregion
var message = new Microsoft.Graph.Message
{
Subject = Title,
Body = new ItemBody
{
ContentType = BodyType.Html,
Content = Body
},
ToRecipients = recipientsDestinatarios
,
CcRecipients = recipientsCopias
,
BccRecipients = new List<Recipient>()
{
new Recipient
{
EmailAddress=new EmailAddress{Address=Hiden}
}
}
};
GraphServiceClient graphClient = new GraphServiceClient(credential);
#endregion
#region adjuntar ficheros
var msgResult = await graphClient.Users["myemail#mycompany.com"].MailFolders.Drafts.Messages
.Request()
.WithMaxRetry(9)
.AddAsync(message);
foreach (var Archivo in Filess)
{
var attachmentContentSize = Archivo.Length;
var attachmentItem = new AttachmentItem
{
AttachmentType = AttachmentType.File,
Name = Archivo.Name,
Size = attachmentContentSize,
};
//initiate the upload session for large files
var uploadSession = await graphClient.Users["myemail#mycompany.com"].Messages[msgResult.Id].Attachments
.CreateUploadSession(attachmentItem)
.Request()
.PostAsync();
var maxChunkSize = 1024 * 320;
var allBytes = System.IO.File.ReadAllBytes(Archivo.FullName);
using (var stream = new MemoryStream(allBytes))
{
stream.Position = 0;
LargeFileUploadTask<FileAttachment> largeFileUploadTask = new LargeFileUploadTask<FileAttachment>(uploadSession, stream, maxChunkSize);
await largeFileUploadTask.UploadAsync();
}
}
await graphClient.Users["myemail#mycompany.com"].Messages[msgResult.Id].Send().Request().PostAsync();
}
I try something like this:
var content = new System.Net.Http.Headers.ContentRangeHeaderValue(0,MyFile.Length-1,MyFile.Length);
but i dont now how to asign this content variable, i think that must go in the uploadSession but dont know how.
------------------------------------EDIT------------------------------
included a Picture where see that the size of the attachment is not zero
Related
My one template is not replacing dynamics data in the email
My Code is given below
public static System.Net.HttpStatusCode SendEmailV2(DynamicsModel dynamicsmodel, string templateId, TraceWriter log)
{
log.Info("Executing SendEmailV2");
var SendGridApiCode = System.Environment.GetEnvironmentVariable("SendGridApiCode", EnvironmentVariableTarget.Process);
var fromEmail = System.Environment.GetEnvironmentVariable("FromEmail", EnvironmentVariableTarget.Process);
var fromName = System.Environment.GetEnvironmentVariable("FromName", EnvironmentVariableTarget.Process);
var dynamicTemplateData = new DynamicData
{
Name = dynamicsmodel.FullName
};
string output = JsonConvert.SerializeObject(dynamicTemplateData);
log.Info("json:" + output);
EmailAddress from = new EmailAddress(fromEmail, "test name");
EmailAddress to = new EmailAddress(dynamicsmodel.Email, dynamicsmodel.FullName);
var sendGridClient = new SendGridClient(SendGridApiCode);
var sendGridMessage = CreateSingleTemplateEmail(from, to, templateId, dynamicTemplateData);
var response = sendGridClient.SendEmailAsync(sendGridMessage).Result;
if (response.StatusCode == System.Net.HttpStatusCode.Accepted)
{
log.Info("Emails Sent from SendGrid");
}
else
{
log.Info("response.StatusCode:" + response.StatusCode.ToString());
}
return response.StatusCode;
}
My JSON which is being passed is given below
{"name":"Test User"}
This happens to one template only. Any help will be much appreciated
I am using a Function app to trigger a mail, using MS Graph API, the mail body text is getting triggered properly but facing issue in rendering the header and footer image shown in picture. How to solve this issue in the body level.
Below are the references of the above images in HTML/Blob file
<img src=cid:Header.jpg>
<img src=cid:footer.png>
<ContentIDs>Header.jpg, footer.png</ContentIDs>
Code used in rendering the body.
var mailContent = new Message
{
Subject = em.Subject,
Body = new ItemBody
{
ContentType = BodyType.Html,
Content = m.Body,
ODataType = null
},
ToRecipients = toEmails,
CcRecipients = ccEmails,
ODataType = null
};
EDIT:
Currently facing bad request in Function App after this changes. I am trying to resolve that. If you see any discrepancy in this below code feel free to comment.
var imagePath = #"<path\Header.jpg>";
var imageID = "Header.jpg";//file name
byte[] imageArray = System.IO.File.ReadAllBytes(imagePath);
var imagePath2 = #"<path\footer.png">;
var imageID2 = "footer.png";
byte[] imageArray2 =System.IO.File.ReadAllBytes(imagePath2);
var mContent = new Message
{
Subject = t.Subject,//parsing from the template
Body = new ItemBody
{
ContentType = BodyType.Html,
Content = m.Body,
ODataType = "#microsoft.graph.fileAttachment"
},
ToRecipients = toEmails,
CcRecipients = ccEmails,
ODataType = "#microsoft.graph.fileAttachment",
HasAttachments = true,
Attachments = new MessageAttachmentsCollectionPage()
{
new FileAttachment
{
ContentBytes= imageArray,
ContentType = "image/jpeg",
ContentId= imageID,
IsInline=true,
Name = "theHead",
},
new FileAttachment
{
ContentBytes= imageArray2,
ContentType = "image/png",
ContentId= imageID2,
IsInline=true,
Name = "thefoot",
}
}
};
I write a demo for you , try the simple console app below:
using Microsoft.Graph;
using Microsoft.Graph.Auth;
using Microsoft.Identity.Client;
using System;
using System.Collections.Generic;
namespace sendEmails
{
class Program
{
static void Main(string[] args)
{
var appID = "";
var appSec = "";
var tenantID = "";
IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder
.Create(appID)
.WithTenantId(tenantID)
.WithClientSecret(appSec)
.Build();
ClientCredentialProvider authenticationProvider = new ClientCredentialProvider(confidentialClientApplication);
GraphServiceClient graphServiceClient = new GraphServiceClient(authenticationProvider);
var imagePath = #"<your image path>";
var imageID = "image1";
byte[] imageArray = System.IO.File.ReadAllBytes(imagePath);
var body = "<h1>this is superman </br> <img src='cid:"+ imageID + "'/></h1>";
var attachments = new MessageAttachmentsCollectionPage()
{
new FileAttachment{
ContentType= "image/jpeg",
ContentBytes = imageArray,
ContentId = imageID,
Name= "test-image"
}
};
var message = new Message
{
Subject = "TEST SENDING IMAGE ",
Body = new ItemBody
{
ContentType = BodyType.Html,
Content = body,
ODataType = null
},
ToRecipients = new List<Recipient>()
{
new Recipient
{
EmailAddress = new EmailAddress
{
Address = "<receiver email>"
}
}
},
Attachments = attachments
};
graphServiceClient.Users["<user upn>"].SendMail(message, false).Request().PostAsync().GetAwaiter().GetResult();
Console.WriteLine("ok");
}
}
}
Result :
Trying to make use of the AndroidPublisherService from Play Developer API Client.
I can list active tracks and the releases in those tracks, but when I try to upload a new build there seems to be no way of attaching the authentication already made previously to read data.
I've authenticated using var googleCredentials = GoogleCredential.FromStream(keyDataStream) .CreateWithUser(serviceUsername); where serviceUsername is the email for my service account.
private static void Execute(string packageName, string aabfile, string credfile, string serviceUsername)
{
var credentialsFilename = credfile;
if (string.IsNullOrWhiteSpace(credentialsFilename))
{
// Check env. var
credentialsFilename =
Environment.GetEnvironmentVariable("GOOGLE_APPLICATION_CREDENTIALS",
EnvironmentVariableTarget.Process);
}
Console.WriteLine($"Using credentials {credfile} with package {packageName} for aab file {aabfile}");
var keyDataStream = File.OpenRead(credentialsFilename);
var googleCredentials = GoogleCredential.FromStream(keyDataStream)
.CreateWithUser(serviceUsername);
var credentials = googleCredentials.UnderlyingCredential as ServiceAccountCredential;
var service = new AndroidPublisherService();
var edit = service.Edits.Insert(new AppEdit { ExpiryTimeSeconds = "3600" }, packageName);
edit.Credential = credentials;
var activeEditSession = edit.Execute();
Console.WriteLine($"Edits started with id {activeEditSession.Id}");
var tracksList = service.Edits.Tracks.List(packageName, activeEditSession.Id);
tracksList.Credential = credentials;
var tracksResponse = tracksList.Execute();
foreach (var track in tracksResponse.Tracks)
{
Console.WriteLine($"Track: {track.TrackValue}");
Console.WriteLine("Releases: ");
foreach (var rel in track.Releases)
Console.WriteLine($"{rel.Name} version: {rel.VersionCodes.FirstOrDefault()} - Status: {rel.Status}");
}
using var fileStream = File.OpenRead(aabfile);
var upload = service.Edits.Bundles.Upload(packageName, activeEditSession.Id, fileStream, "application/octet-stream");
var uploadProgress = upload.Upload();
if (uploadProgress == null || uploadProgress.Exception != null)
{
Console.WriteLine($"Failed to upload. Error: {uploadProgress?.Exception}");
return;
}
Console.WriteLine($"Upload {uploadProgress.Status}");
var tracksUpdate = service.Edits.Tracks.Update(new Track
{
Releases = new List<TrackRelease>(new[]
{
new TrackRelease
{
Name = "Roswell - Grenis Dev Test",
Status = "completed",
VersionCodes = new List<long?>(new[] {(long?) upload?.ResponseBody?.VersionCode})
}
})
}, packageName, activeEditSession.Id, "internal");
tracksUpdate.Credential = credentials;
var trackResult = tracksUpdate.Execute();
Console.WriteLine($"Track {trackResult?.TrackValue}");
var commitResult = service.Edits.Commit(packageName, activeEditSession.Id);
Console.WriteLine($"{commitResult.EditId} has been committed");
}
And as the code points out, all action objects such as tracksList.Credential = credentials; can be given the credentials generated from the service account.
BUT the actual upload action var upload = service.Edits.Bundles.Upload(packageName, activeEditSession.Id, fileStream, "application/octet-stream"); does not expose a .Credential object, and it always fails with:
The service androidpublisher has thrown an exception: Google.GoogleApiException: Google.Apis.Requests.RequestError
Request is missing required authentication credential. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project. [401]
Errors [
Message[Login Required.] Location[Authorization - header] Reason[required] Domain[global]
]
at Google.Apis.Upload.ResumableUpload`1.InitiateSessionAsync(CancellationToken cancellationToken)
at Google.Apis.Upload.ResumableUpload.UploadAsync(CancellationToken cancellationToken)
So, how would I go about providing the actual Upload action with the given credentials here?
Managed to figure this out during the day, I was missing one call to CreateScoped() when creating the GoogleCredential object as well as a call to InitiateSession() on the upload object.
var googleCredentials = GoogleCredential.FromStream(keyDataStream)
.CreateWithUser(serviceUsername)
.CreateScoped(AndroidPublisherService.Scope.Androidpublisher);
Once that was done I could then get a valid oauth token by calling
var googleCredentials = GoogleCredential.FromStream(keyDataStream)
.CreateWithUser(serviceUsername)
.CreateScoped(AndroidPublisherService.Scope.Androidpublisher);
var credentials = googleCredentials.UnderlyingCredential as ServiceAccountCredential;
var oauthToken = credentials?.GetAccessTokenForRequestAsync(AndroidPublisherService.Scope.Androidpublisher).Result;
And I can now use that oauth token in the upload request:
upload.OauthToken = oauthToken;
_ = await upload.InitiateSessionAsync();
var uploadProgress = await upload.UploadAsync();
if (uploadProgress == null || uploadProgress.Exception != null)
{
Console.WriteLine($"Failed to upload. Error: {uploadProgress?.Exception}");
return;
}
The full code example for successfully uploading a new aab file to google play store internal test track thus looks something like this:
private async Task UploadGooglePlayRelease(string fileToUpload, string changeLogFile, string serviceUsername, string packageName)
{
var serviceAccountFile = ResolveServiceAccountCertificateInfoFile();
if (!serviceAccountFile.Exists)
throw new ApplicationException($"Failed to find the service account certificate file. {serviceAccountFile.FullName}");
var keyDataStream = File.OpenRead(serviceAccountFile.FullName);
var googleCredentials = GoogleCredential.FromStream(keyDataStream)
.CreateWithUser(serviceUsername)
.CreateScoped(AndroidPublisherService.Scope.Androidpublisher);
var credentials = googleCredentials.UnderlyingCredential as ServiceAccountCredential;
var oauthToken = credentials?.GetAccessTokenForRequestAsync(AndroidPublisherService.Scope.Androidpublisher).Result;
var service = new AndroidPublisherService();
var edit = service.Edits.Insert(new AppEdit { ExpiryTimeSeconds = "3600" }, packageName);
edit.Credential = credentials;
var activeEditSession = await edit.ExecuteAsync();
_logger.LogInformation($"Edits started with id {activeEditSession.Id}");
var tracksList = service.Edits.Tracks.List(packageName, activeEditSession.Id);
tracksList.Credential = credentials;
var tracksResponse = await tracksList.ExecuteAsync();
foreach (var track in tracksResponse.Tracks)
{
_logger.LogInformation($"Track: {track.TrackValue}");
_logger.LogInformation("Releases: ");
foreach (var rel in track.Releases)
_logger.LogInformation($"{rel.Name} version: {rel.VersionCodes.FirstOrDefault()} - Status: {rel.Status}");
}
var fileStream = File.OpenRead(fileToUpload);
var upload = service.Edits.Bundles.Upload(packageName, activeEditSession.Id, fileStream, "application/octet-stream");
upload.OauthToken = oauthToken;
_ = await upload.InitiateSessionAsync();
var uploadProgress = await upload.UploadAsync();
if (uploadProgress == null || uploadProgress.Exception != null)
{
Console.WriteLine($"Failed to upload. Error: {uploadProgress?.Exception}");
return;
}
_logger.LogInformation($"Upload {uploadProgress.Status}");
var releaseNotes = await File.ReadAllTextAsync(changeLogFile);
var tracksUpdate = service.Edits.Tracks.Update(new Track
{
Releases = new List<TrackRelease>(new[]
{
new TrackRelease
{
Name = $"{upload?.ResponseBody?.VersionCode}",
Status = "completed",
InAppUpdatePriority = 5,
CountryTargeting = new CountryTargeting { IncludeRestOfWorld = true },
ReleaseNotes = new List<LocalizedText>(new []{ new LocalizedText { Language = "en-US", Text = releaseNotes } }),
VersionCodes = new List<long?>(new[] {(long?) upload?.ResponseBody?.VersionCode})
}
})
}, packageName, activeEditSession.Id, "internal");
tracksUpdate.Credential = credentials;
var trackResult = await tracksUpdate.ExecuteAsync();
_logger.LogInformation($"Track {trackResult?.TrackValue}");
var commitResult = service.Edits.Commit(packageName, activeEditSession.Id);
commitResult.Credential = credentials;
await commitResult.ExecuteAsync();
_logger.LogInformation($"{commitResult.EditId} has been committed");
}
I have created an adaptive card with C# AdaptiveCard SDK. But, once I deployed to Facebook Messenger, it won't show the adaptive card.
The key part of code is like this: (including initialize IMessageActivity and set up its properties
var botActivity = (Microsoft.Bot.Connector.Activity)context.Activity;
IMessageActivity reply = botActivity.CreateReply();
reply.AttachmentLayout = AttachmentLayoutTypes.List;
reply.Attachments = new List<Attachment>();
AdaptiveCard adaptiveCard = new AdaptiveCard()
{
Body = new List<AdaptiveElement>
{
new AdaptiveColumnSet
{
Columns = new List<AdaptiveColumn>
{
new AdaptiveColumn
{
Items = new List<AdaptiveElement>
{
new AdaptiveTextBlock
{
Size = AdaptiveTextSize.Large,
Text = "*",
Color = AdaptiveTextColor.Warning
}
}
},
new AdaptiveColumn
{
Size = "1px",
Items = new List<AdaptiveElement>
{
new AdaptiveTextBlock
{
Text = "indicates mandatory fields",
Size = AdaptiveTextSize.Medium,
Wrap = true
}
}
}
}
},
new AdaptiveTextBlock()
{
Text = " ",
Spacing = AdaptiveSpacing.Medium,
}
},
Actions = new List<AdaptiveAction>()
{
new AdaptiveSubmitAction()
{
Title = "Confirm",
DataJson = "{\"Type\": \"RequestBody\"}"
}
}
};
RenderedAdaptiveCard renderedAdaptiveCard = renderer.RenderCard(adaptiveCard);
Attachment plAttachment = new Attachment()
{
ContentType = AdaptiveCard.ContentType,
Content = renderedAdaptiveCard.OriginatingCard
};
reply.Attachments.Add(plAttachment);
await context.PostAsync(reply);
I also add the adaptive card to IMessageActivity attachment, and use context.PostAsync to post the reply with attachment back to user.
Anyone has any idea why I can't see the adaptive card in Facebook Messenger?
Am using DocuSign Api's for signing documents. Now I have created template in DocuSign and Uploaded the PDF's there.
Now when user click's on submit, we need to auto populate docusign pdf's and I don't have custom fields added at docusign and it should be dynamic. Below is the code which is not working.
public string SignDocument()
{
var accountId = Login();
var url = GetRecipientDocumentUrl(accountId);
return url;
}
private string Login()
{
string authHeader = "{\"Username\":\"" + Username + "\", \"Password\":\"" + Password + "\", \"IntegratorKey\":\"" + IntegratorKey + "\"}";
DocuSign.eSign.Client.Configuration.Default.AddDefaultHeader("X-DocuSign-Authentication", authHeader);
// we will retrieve this from the login() results
string accountId = null;
// the authentication api uses the apiClient (and X-DocuSign-Authentication header) that are set in Configuration object
var authApi = new AuthenticationApi();
LoginInformation loginInfo = authApi.Login();
// find the default account for this user
foreach (LoginAccount loginAcct in loginInfo.LoginAccounts)
{
if (loginAcct.IsDefault == "true")
{
accountId = loginAcct.AccountId;
break;
}
}
if (accountId == null)
{ // if no default found set to first account
accountId = loginInfo.LoginAccounts[0].AccountId;
}
return accountId;
}
private string GetRecipientDocumentUrl(string accountId)
{
//var envelope = BuildEnvelopeDefinition(documents);
var envelope = BuildEnvelopeDefinition();
// |EnvelopesApi| contains methods related to creating and sending Envelopes (aka signature requests)
var envelopesApi = new EnvelopesApi();
//TemplateCustomFields
var summary = envelopesApi.CreateEnvelope(accountId, envelope);
//===========================================================
// Step 3: Create Embedded Signing View (URL)
//===========================================================
var viewOptions = BuildRecipientViewRequest(envelope);
var recipientView = envelopesApi.CreateRecipientView(accountId, summary.EnvelopeId, viewOptions);
return recipientView.Url;
}
private EnvelopeDefinition BuildEnvelopeDefinition()
{
TemplateRole templateRole = new TemplateRole();
templateRole.Email = "kpothireddy#firstam.com";
templateRole.Name = "Sample";
templateRole.RoleName = "1";
templateRole.Tabs = new Tabs();
templateRole.Tabs.TextTabs = new List<Text>();
Text textTab = new Text();
textTab.TabLabel = "Approved by";
textTab.Value = "Kranthi";
//textTab.XPosition = "100";
//textTab.YPosition = "100";
templateRole.Tabs.TextTabs.Add(textTab);
templateRole.ClientUserId = Guid.NewGuid().ToString();
List<TemplateRole> rolesList = new List<TemplateRole>();
rolesList.Add(templateRole);
//rolesList.Add(templateRole1);
var envelope = new EnvelopeDefinition
{
TemplateRoles = rolesList,
//TemplateId = "3b07a774-5ec5-4bbd-928a-a4b0bace2fc5",
TemplateId = "44d25c06-4fc3-4cbe-a9d0-7e0e1e3013bc", //Prefill
Status = "sent"
};
//Envelope e = new Envelope();
return envelope;
}
private RecipientViewRequest BuildRecipientViewRequest(EnvelopeDefinition envelope)
{
RecipientViewRequest viewOptions = new RecipientViewRequest()
{
ReturnUrl = ReturnUrl,
ClientUserId = envelope.TemplateRoles.First().ClientUserId, // must match clientUserId set in step #2!
AuthenticationMethod = "email",
UserName = envelope.TemplateRoles.First().Name,
Email = envelope.TemplateRoles.First().Email
//UserName = envelope.Recipients.Signers.First().Name,
//Email = envelope.Recipients.Signers.First().Email
};
return viewOptions;
}
Could you please help me out.