I've successfully posted a message to a Teams channel using the Micorosoft.Graph.GraphServiceClient (version 3.14). However I would like to include an image attachment (via ChatMessageAttachment). According to the documentation in order to do this I should set the ChatMessageAttachment.ContentType to "image/jpeg" and set the ContentUrl to a base64 string representation of the image file. Here is my code:
var chatMessage = new ChatMessage
{
Subject = subject,
Body = new ItemBody
{
ContentType = BodyType.Html,
Content = message // Including <attachment id="<imageId>"></attachment>
}
};
chatMessage.Attachments = new List<ChatMessageAttachment>
{
new ChatMessageAttachment
{
Id = imageId,
ContentType = "image/jpeg",
ContentUrl = base64String,
Name = "HunnySuckle.jpg"
}
};
var res = await graphClient.Teams[teamId].Channels[channelId].Messages
.Request()
.AddAsync(chatMessage);
However when I post the request the response is 500 with the follwing body:
{
"error": {
"code": "InternalServerError",
"message": "Failed to process request.",
"innerError": {
"date": "2020-09-18T20:39:46",
"request-id": "44143c44-1c48-4a90-9bb5-20a3ba1b9905",
"client-request-id": "44143c44-1c48-4a90-9bb5-20a3ba1b9905"
}
}
}
This is the extract from the docs (https://learn.microsoft.com/en-us/graph/api/resources/chatmessageattachment?view=graph-rest-1.0)
contentType . . image/: Image type with the type of the image
specified ex: image/png, image/jpeg, image/gif. Populate the
contentUrl field with the base64 encoding of the file in data: format.
However I'm not sure what "in data: format" means - maybe this could be the key to why it's not working.
The only other reference this error that I've found by searching online is on this page: https://github.com/microsoftgraph/microsoft-graph-docs/issues/7211 which is the same problem as mine I think (but sadly no answer).
Related
Email Not been recieved with attachments when I try to use Uploadsession using Graph API. can someone help me uderstand why this is happening. I have not recieved any error.
Message draft = await graphServiceClient.Users["UserID"].Messages.Request().AddAsync(email);
//Message draft = graphServiceClient.Users["UserID"].Mailfolders.Drafts.Messages.Request().AddAsync(email);
var stream = System.IO.File.Open(#"C:\attach\DYN28_6579332_33242556.csv", System.IO.FileMode.Open, FileAccess.Read, FileShare.None);
var attachmentItem = new AttachmentItem
{
AttachmentType = AttachmentType.File,
Name = "DYN28_6579332_33242556.csv",
Size = stream.Length
};
var uploadSession = await graphServiceClient.Users["Userid"].Messages[draft.Id]
.Attachments
.CreateUploadSession(attachmentItem)
.Request()
.PostAsync();
var maxSlicesize = 320 * 1024;
var largeFileUploadTask = new LargeFileUploadTask<FileAttachment>(uploadSession, stream, maxSlicesize);
IProgress<long> progress = new Progress<long>(prog => {
Console.WriteLine($"Uploaded {prog} bytes of {stream.Length} bytes");
});
// Upload the file
var uploadResult = await largeFileUploadTask.UploadAsync(progress);
if (uploadResult.UploadSucceeded)
{
// The ItemResponse object in the result represents the
// created item.
//Console.WriteLine($"Upload complete, item ID: {uploadResult.ItemResponse.Id}");
Console.WriteLine("upload completed");
}
Finally sending email with
await graphServiceClient.Users["userid"].Messages[draft.Id]
.Send()
.Request()
.PostAsync();
There is a limit of 4MB on a single request in the Graph API. To send larger attachments, you need to first create an upload session against the email message/calendar event, and upload your attachment in a number of requests as part of this session. AFAIK each of the smaller POST requests would also need to stay below the 4MB limit.
You can find more detailed documentation and a sample walkthrough here.
POST https://graph.microsoft.com/v1.0/me/messages/AAMkADI5MAAIT3drCAAA=/attachments/createUploadSession
Content-type: application/json
{
"AttachmentItem": {
"attachmentType": "file",
"name": "flower",
"size": 3483322
}
}
We are using the ms graph api to post messages to a teams channel from a internal desktop application. The main purpose is to attach images to the message. We upload the image files into the one-drive folder of the channel as shown below.
var uploadProps = new DriveItemUploadableProperties
{
ODataType = null,
AdditionalData = new Dictionary<string, object>
{
{ "#microsoft.graph.conflictBehavior", "replace" }
}
};
var session = await graphClient.Drives[driveId]
.Items[parentId].ItemWithPath(fileName).CreateUploadSession(uploadProps).Request().PostAsync(token);
int maxSliceSize = 320 * 1024;
var fileUploadTask =
new LargeFileUploadTask<DriveItem>(session, fileStream, maxSliceSize);
// Create a callback that is invoked after each slice is uploaded
IProgress<long> progress = new Progress<long>(reportAsync);
// Upload the file
var uploadResult = await fileUploadTask.UploadAsync(progress);
if (uploadResult.UploadSucceeded)
{
return uploadResult.ItemResponse;
}
We then send a message to the channel and attach the images uploaded previously as reference attachments.
var chatMsg = new ChatMessage();
chatMsg.Body = new ItemBody();
chatMsg.Body.ContentType = BodyType.Html;
chatMsg.Body.Content = msg + " " + string.Join(" ", attachments.Select(d => $"<attachment id=\"{parseEtag(d.ETag)}\"></attachment>"));
chatMsg.Attachments = attachments.Select(d => new ChatMessageAttachment()
{
Id = parseEtag(d.ETag),
ContentType = "reference",
ContentUrl = d.WebUrl,
Name = d.Name
});
return await this.graphClient.Teams[teamId].Channels[channelId].Messages
.Request()
.AddAsync(chatMsg, token);
The problem is that the message only shows the names of the attachments with no preview as seen in the message at the bottom. We want to have a preview as seen (top message) when attaching a file within the teams application.
We've tried to set the thumbnailurl property of the attachment to the thumbnail url fetched from the ms-graph api with no success.
We've uploaded a file using the teams application (with preview) and then created an identical message with the same file (same driveitem id) in our application (show's no preview). Then we fetched both messages using the graph api and could not discern any differences between the two besides the message id's ofc.
We've scoured these forums, the ms documentations and even suggestion pages and found nothing.
We have been able to show previews separately in the body of the message referencing the thumbnail urls and in messagecards but ideally we want the preview directly in the attachments.
EDIT
The thumbnail urls seem to expire after 24 hours and are therefor not a great solution.
We managed to solve exactly this problem using the Simple Upload Api, with the added ?$expand=thumbnails query parameter. I haven't tried but the query param ought to work for the endpoint you're using as well.
Pick a size from the ThumbnailSet in the upload response and add it to the body of your message as an image tag. See below:
// channel, file, extractIdFromEtag, message omitted for brevity.
// PUT /groups/{group-id}/drive/items/{parent-id}:/{filename}:/content
const uploadUrl = `https://graph.microsoft.com/beta/groups/${channel.teamId}/drive/items/root:/${channel.displayName}/${file.name}:/content?$expand=thumbnails`;
const res = await this.http.put(uploadUrl, file).toPromise(); // FYI Using Angular http service
const attachment = {
id: extractIdFromEtag(res.eTag),
contentType: 'reference',
contentUrl: res.webUrl,
name: res.name,
thumbnailUrl: res.webUrl
};
const postBody = {
subject: null,
body: {
contentType: 'html',
content: message
},
};
// This is what makes the image show in the message as if posted from teams
postBody.body.content += `<br><br><img src="${res.thumbnails[0].large.url}" alt="${res.name}"/>`;
const messageUrl = `https://graph.microsoft.com/beta/teams/${channel.teamId}/channels/${channel.id}/messages`;
const result = await this.http.post(messageUrl, postBody).toPromise();
// Done
You can also keep adding the attachment as you already do, if you want the original image attached as a file, as well as showing the image preview in the message.
I am wondering if the /v1.0/me/sendMail has the ability to delay sending an email. In the Outlook client, you can specify that you want your email sent at a later date and time. I've snooped around to see if there is a property that can be set on the message object to indicate this.
Did anyone find a way to get this working? Of course, I could implement something in my software to handle the delayed sending, but why re-create something if it is already there.
You can achieve delayed sending of emails using extended properties. These can be set on the Graph API request payload using the "singleValueExtendedProperties" attribute.
The property to use is PidTagDeferredSendTime which has the ID 0x3FEF and type SystemTime.
The id attribute of "singleValueExtendedProperties" takes different formats depending on the property you are setting.
For the deferred send time you would use SystemTime 0x3FEF.
Example using a HTTP JSON POST Payload:
{
"message": {
"subject": "Meet for lunch?",
"body": {
"contentType": "Text",
"content": "The new cafeteria is open."
},
"toRecipients": [
{
"emailAddress": {
"address": "bob#contoso.com"
}
}
],
"singleValueExtendedProperties":
[
{
"id":"SystemTime 0x3FEF",
"value":"2019-01-29T20:00:00"
}
]
}
}
Example using the Microsoft Graph API client library:
var client = /* Create and configure GraphServiceClient */;
var msg = new Message();
msg.ToRecipients = List<Recipient>();
msg.ToRecipients.Add(new Recipient() {
EmailAddress = new EmailAddress() { Address ="bob#contoso.com" }
};
msg.Subject = "Meet for lunch?";
msg.Body = new ItemBody()
{
Content = "The new cafeteria is open.",
ContentType = BodyType.Text,
};
msg.SingleValueExtendedProperties = new MessageSingleValueExtendedPropertiesCollectionPage();
msg.SingleValueExtendedProperties.Add(new SingleValueLegacyExtendedProperty()
{
Id = "SystemTime 0x3FEF",
Value = DateTime.UtcNow.AddMinutes(5).ToString("o")
});
await client.Me.SendMail(msg, true).Request().PostAsync();
https://gallery.technet.microsoft.com/office/Send-Emails-until-a-9cee20cf
You set the deferred send time extended prop when creating the item.
Graph API Send Email document states that I can include a file attachment in the same sendMail action call. Would someone be able to provide an example code of how this can be achieved using me/sendmail with attachment from the file generated which content is stored in the memorystream?
One option is to use the Microsoft Graph .NET SDK and I found this sample code in another Stack Overflow answer.
// Create the message with attachment.
byte[] contentBytes = System.IO.File.ReadAllBytes(#"C:\test\test.png");
string contentType = "image/png";
MessageAttachmentsCollectionPage attachments = new MessageAttachmentsCollectionPage();
attachments.Add(new FileAttachment
{
ODataType = "#microsoft.graph.fileAttachment",
ContentBytes = contentBytes,
ContentType = contentType,
ContentId = "testing",
Name = "testing.png"
});
Message email = new Message
{
Body = new ItemBody
{
Content = Resource.Prop_Body + guid,
ContentType = BodyType.Text,
},
Subject = Resource.Prop_Subject + guid.Substring(0, 8),
ToRecipients = recipients,
Attachments = attachments
};
// Send the message.
await graphClient.Me.SendMail(email, true).Request().PostAsync();
Trying to run just a basic https://graph.microsoft.com/v1.0/me?$expand=extensions
But either get this error
Expected ',' instead of '{'
with
{"#odata.context":"https://graph.microsoft.com/v1.0/$metadata#users/$entity"{
"error": {
"code": "BadRequest",
"message": "The entity instance value of type 'microsoft.graph.user' doesn't have a value for property 'id'. To compute an entity's metadata, its key and concurrency-token property values must be provided.",
"innerError": {
"request-id": "39759fbe-06ed-4176-8cc3-efe167a532cb",
"date": "2017-05-17T22:47:35"
}
}
}
I am trying to find the id of my openExtensions my account so I can delete some to make room because I must of accidentally added too many but I can't even get a list of the extensions and I can't even filter it by id. All works fine with other accounts I think i must have just bricked my account while playing around with it. Any ideas?
EDIT
Trying the query https://graph.microsoft.com/v1.0/me?$select=id,displayName&$expand=extensions
results in
{"#odata.context":"https://graph.microsoft.com/v1.0/$metadata#users(id,displayName,extensions)/$entity","id":"MY ID","displayName":"MY NAME","extensions#odata.context":"https://graph.microsoft.com/v1.0/$metadata#users('MY ID')/extensions","extensions":[{"#odata.type":"#microsoft.graph.openTypeExtension"{
"error": {
"code": "InternalServerError",
"message": "Unsupported extension property type.",
"innerError": {
"request-id": "9fe3c7aa-f3d8-48be-90e4-b440516f9010",
"date": "2017-05-17T23:14:46"
}
}
}
I think I just ran into a similar problem.
I created my extension like this
var ext = new OpenTypeExtension();
ext.ExtensionName = "[Unique Name]";
ext.AdditionalData = new Dictionary<string, object>();
ext.AdditionalData.Add("[settingName]", "[settingValue]");
await graph.Me.Extensions.Request().AddAsync(ext);
But now I can no longer expand the extensions property for my account.
var profile = await graph.Me
.Request()
.Expand("extensions")
.GetAsync();
This throws a Microsoft.Graph.ServiceException:
Code: generalException
Message: Unexpected exception returned from the service.
When I try to make the request in the graph explorer, I get the following response,
{
"#odata.context":"https://graph.microsoft.com/v1.0/$metadata#users/$entity"
{
"error": {
"code": "BadRequest",
"message": "The entity instance value of type 'microsoft.graph.user' doesn't have a value for property 'id'. To compute an entity's metadata, its key and concurrency-token property values must be provided.",
"innerError": {
"request-id": "5e3887db-1687-461a-8d5c-da0f34eea83b",
"date": "2018-06-01T01:41:12"
}
}
}
}
So one of our devs took a look at the data and it looks like you were calling the API incorrectly - which was valid JSON, but not what our service can handle - especially on read back.
It looks like when you were creating an open extension, you were pushing the following in the payload:
{ "openTypeExtension": {
"#odata.type": "#Microsoft.Graph.Extensibility.openTypeExtension",
"extensionName": "roaming.settings",
"randvalue": 1
} }
But the openTypeExtension bit is not required and throws us off on a read back. What you should be sending is:
{
"#odata.type": "#Microsoft.Graph.Extensibility.openTypeExtension",
"extensionName": "roaming.settings",
"randvalue": 1
}
We've implemented a fix to prevent this in the future, which will roll out next week.
We have the IDs for your extensions, so you could delete them, but we're not sure how to communicate these to you securely. If you are OK with us providing them on this thread, please let us know.
Hope this helps,
I got the same error. I use Graph API lib.
Finally I was able to retrieve the extension other way - using Users.Extensions property (I guess it can be done against current user analogically - Me.Extensions).
I added the extension this way:
var additionalData = new Dictionary<string, object> { { "DataName", value } };
await graph.Users["userId"]
.Extensions
.Request()
.AddAsync(new OpenTypeExtension { ExtensionName = "uniqueExtensionName", AdditionalData = additionalData });
..and retrieving:
var extensionObject = ( await graph.Users["userId"].Extensions.Request().GetAsync() )
.CurrentPage[0];