Create Master Category Via Microsoft Graph API - c#

Im Trying to create a new category via an event for outlook. Below is what I have so far.
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", AdviserBearerToken);
client.DefaultRequestHeaders.Accep.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var categoryName = new List<string>();
categoryName.Add("New Event");
var startTime = new Time();
var endTime = new Time();
startTime.DateTime = "2016-07-15T15:00:00.0000000";
startTime.TimeZone = "UTC";
endTime.DateTime = "2016-07-15T15:30:00.0000000";
endTime.TimeZone = "UTC";
var eventModel = new EventModelForGraph
{
categories = categoryName,
subject = "This is an event",
Start = startTime,
End = endTime
};
var serializedObject = JsonConvert.SerializeObject(eventModel);
var createBody = new StringContent(serializedObject, System.Text.Encoding.UTF8, "application/json");
var response = await client.PostAsync("https://graph.microsoft.com/v1.0/me/calendar/events", createBody);
var responseString = await response.Content.ReadAsStringAsync();
}
The event shows up in the calendar and the category as the header but it is not listed under the categorize tab which leads me to my question.
Is it possible to create such a category using the API?

I know this is an older question, but I was looking into the same thing and figured I'd post an update. This is now possible with the current version of the Graph API. You can see the documentation here from MSDN. You can create categories by sending a POST API request:
POST https://graph.microsoft.com/beta/me/outlook/masterCategories
Content-type: application/json
Content-Length: 70
{
"displayName":"Project expenses",
"color":"preset9"
}
After the category is created, you can assign it when you create your event by adding the category's displayName property to the categories collection of the item.
You can find more details about when these API endpoints were added here and more details about categories here.

No, you cannot add categories to the master category list via the REST API. You cannot add them directly via any API.
However, you CAN modify the list if you're willing to manipulate the XML directly. The gory details are documented in MS-OXOCFG. You can use EWS for example to access the config item.
This would be a great feature to add to the REST API. You should suggest it on UserVoice.

Related

How to use XRM in C#?

Hello I need to convert a JS Xrm web api function into C#
var opp = Xrm.Page.data.entity.getId();
Xrm.WebApi.retrieveMultipleRecords(
"dei_opportunityline",
"?$select=_dei_vendor_value,dei_multiplier,dei_iscommission,dei_opportunitylineid&$filter=_dei_opportunity_value eq " +
opp
).then(
function success(result) {
for (var i = 0; i < result.entities.length; i++) {
var newMulti = null;
if (result.entities[i].dei_iscommission) {
if (
result.entities[i]["_dei_vendor_value"] ==
"5fc8d03b-c41c-eb11-a813-000d3a31ed8d"
) {
if (jcimulti != null) newMulti = jcimulti;
} else {
if (commulti != null) newMulti = commulti;
}
For methods like .retrieveMultipleRecords, result.entities[i].dei_iscomission
is there a way to write this in C# plugin ?!
You cannot use Xrm in C#, this is useful inside Dynamics client scripting (JS) context only. ie web form context.
For Server side (C#), you have to use HttpClient. Sample code
HttpClient httpClient = null;
httpClient = new HttpClient();
//Default Request Headers needed to be added in the HttpClient Object
httpClient.DefaultRequestHeaders.Add("OData-MaxVersion", "4.0");
httpClient.DefaultRequestHeaders.Add("OData-Version", "4.0");
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
//Set the Authorization header with the Access Token received specifying the Credentials
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authToken);
httpClient.BaseAddress = new Uri("https://yourorg.api.crmx.dynamics.com/api/data/v9.0/");
var response = httpClient.GetAsync("dei_opportunityline?$select=_dei_vendor_value,dei_multiplier,dei_iscommission,dei_opportunitylineid&$filter=_dei_opportunity_value eq <guid>").Result;
if (response.IsSuccessStatusCode)
{
var records = response.Content.ReadAsStringAsync().Result;
//you can get dei_iscomission from above records
}
This is how we use in console application, but in plugin or workflow code - you can use Org Service to get data using Query Expression or fetchxml. Methods like service.RetrieveMultiple will suffice your need. Read more
Simply include the crmsdk nuget package in C#. Then you can connect like you want.
See docs
This packages gives you access to the official c# SDK for XRM. the methods are much easier to use then directly calling the ODATA Web API. For an example to fetch multiple records, see https://community.dynamics.com/crm/f/microsoft-dynamics-crm-forum/246665/how-to-retrieve-multiple-records-from-an-entity-in-crm-using-c
For a quick start, see https://learn.microsoft.com/en-us/powerapps/developer/data-platform/webapi/get-started-dynamics-365-web-api-csharp

Simple PUT request to API endpoint

I'm trying to PUT JSON to the ElasticSearch api in an effort to index/insert a document in an existing index but I can't find any code examples that will actually work. I've looked at httpClient, httpWebRequests and the elastic .net plugin - NEST.
Everything works fine in something like Postman. How would the below translate to C#?
Here is an example of what I'm trying to PUT in ARC(Advanced Rest Client):
Something like this?
public void PutAPI(string basicAuth, string json)
{
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add("authorization", $"Basic {basicAuth}");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = client.PutAsync("https://mydankapi.com/v1/put", new StringContent(json, Encoding.UTF8, "application/json")).Result;
if (!response.IsSuccessStatusCode)
throw new Exception(response.ReasonPhrase);
}
}
Why didn't NEST meet your requirements? According to their Getting Started - Indexing, you can index a document like this:
var tweet = new Tweet
{
Id = 1,
User = "kimchy",
PostDate = new DateTime(2009, 11, 15),
Message = "Trying out NEST, so far so good?"
};
var response = client.Index(tweet, idx => idx.Index("mytweetindex")); //or specify index via settings.DefaultIndex("mytweetindex");
All you have to do is to create a POCO class for your document type (DataMetadata?) and set the index name to data_metadata I believe.

How to send a iCal invite with Mailgun Rest API (C#)

I am attempting to add a calendar invite in iCal format to an email sent via the MailGun API. This is what i have so far:
var request = new RestRequest();
request.AddParameter("domain", this.domain, ParameterType.UrlSegment);
request.Resource = "{domain}/messages";
request.AddParameter("from", contactDetails.SenderAddress);
request.AddParameter("to", contactDetails.RecipientAddress);
request.AddParameter("subject", message.Subject);
request.AddParameter("text", message.TextBody);
request.AddParameter("html", message.HtmlBody);
if (!string.IsNullOrWhiteSpace(message.IcalAttachment))
{
request.AddFileBytes("attachment",
Encoding.UTF8.GetBytes(message.IcalAttachment),
"invite.ics",
"text/calendar");
}
request.Method = Method.POST;
return request;
This results in the calendar being included in the email as an attachment, not an alternative view of the email. The attachment works fine in gmail however in Outlook it appears as an attachment file that you must first click on, then agree to adding the calendar to the Outlook calendar. Is there another way to use the REST api so that the calendar invites are sent correctly, as alternative email views?
To be clear, this is how I would send a calendar invite using .Net SmtpClient:
var contentType = new ContentType("text/calendar");
if (contentType.Parameters != null)
{
contentType.Parameters.Add("method", "REQUEST");
contentType.CharSet = "UTF-8";
}
// this is the same way you add a html view to the message
request.AlternateViews.Add(
AlternateView.CreateAlternateViewFromString(
message.IcalAttachment,
contentType));
Special thanks to Mailgun support for pointing me in the right direction. The relevant part or their response was:
You can use the /message.mime endpoint to construct the MIME for the calendar invite:
https://documentation.mailgun.com/api-sending.html#sending
Creating a mime message isnt as easy as simply using their /message endpoint but there are several .net libraries available to do this. I used MimeKit in this example.
var request = new RestRequest();
request.AddParameter("domain", this.domain, ParameterType.UrlSegment);
request.Resource = "{domain}/messages.mime";
request.AddParameter("to", contactDetails.RecipientAddress);
request.AddFile(
"message",
Encoding.UTF8.GetBytes(BuildMimeContent(message)),
"message.mime");
request.Method = Method.POST;
return request;
The mime content that I want to create will contain a multipart/mixed body, which will in turn contain a multipart/alternative as well as every attachment. The calendar invite will actually be attached twice, as a alternative view and as an attachment. This is to aid in compatibilitiy across different email clients.
The implementation of BuildMimeContent(message) looks like the following:
// create the alternative views
var textBody = new TextPart("plain") { Text = message.TextBody };
var htmlBody = new TextPart("html") { Text = message.HtmlBody };
// add views to the multipart/alternative
var alternative = new Multipart("alternative");
alternative.Add(textBody);
alternative.Add(htmlBody);
if (!string.IsNullOrWhiteSpace(message.CalendarInvite))
{
// also add the calendar as an alternative view
// encoded as base64, but 7bit will also work
var calendarBody = new TextPart("calendar")
{
Text = message.CalendarInvite,
ContentTransferEncoding = ContentEncoding.Base64
};
// most clients wont recognise the alternative view without the
// method=REQUEST header
calendarBody.ContentType.Parameters.Add("method", "REQUEST");
alternative.Add(calendarBody);
}
// create the multipart/mixed that will contain the multipart/alternative
// and all attachments
var multiPart = new Multipart("mixed") { alternative };
if (!string.IsNullOrWhiteSpace(message.CalendarInvite))
{
// add the calendar as an attachment
var calAttachment = new MimePart("application", "ics")
{
ContentDisposition = new ContentDisposition(ContentDisposition.Attachment),
ContentTransferEncoding = ContentEncoding.Base64,
FileName = "invite.ics",
ContentObject = new ContentObject(GenerateStreamFromString(message.CalendarInvite))
};
multiPart.Add(calAttachment);
}
// TODO: Add any other attachements to 'multipart' here.
// build final mime message
var mimeMessage = new MimeMessage();
mimeMessage.From.Add(GetMimeAddress(message.MessageInfo.SenderName, message.MessageInfo.SenderAddress));
mimeMessage.To.Add(GetMimeAddress(message.MessageInfo.RecipientName, message.MessageInfo.RecipientAddress));
mimeMessage.Subject = message.Subject;
mimeMessage.Body = multiPart;
// parse and return mime message
return mimeMessage.ToString();
Warning for people testing with Office 365
Office365 is extremely picky when it comes to validating calendar invites. In order to not get a message like the one below, you will need to ensure that the vCal's organizer email address matches the email's from address. This is not possible if you are using mailgun's sandbox test environment.

Assign a manager using Microsoft Graph .NET Client Library

Can someone give me a hint how to set the User.Manager field using the Microsoft Graph .NET Client Library? I know how to do this via a direct REST call but want to avoid those calls (bypassing the wrapper) as much as possible. With the code below I was able to clear the manager property.
client = new GraphClient(...);
var builder = new DirectoryObjectWithReferenceRequestBuilder(
client.Users["<userid>"].Manager.Request().RequestUrl,client
);
builder.Request().DeleteAsync().Wait()
However, I still can't figure out which class allows me to build a PUT request for the member reference (as described here). I tried to the following code:
var mgr = new DirectoryObject();
mgr.Id = "<Id of the user that should be set as manager>";
var usrPatch = new User();
usrPatch.Manager = mgr;
client.Users["<Id of the user to be updated>"].Request().UpdateAsync(usrPatch).Wait();
This code doesn't throw an exception but it also doesn't update the manager. The request is wrong. The above code sends a PATCH instead of a PUT to the "base" object.
Request generated by the code above:
PATCH https://graph.microsoft.com/v1.0/users/[Id of the user to be updated] HTTP/1.1
SdkVersion: graph-dotnet-1.0.1
Content-Type: application/json
Host: graph.microsoft.com
Content-Length: 45 Expect: 100-continue
{"id":"[Id of the user that should be set as manager]"}
The response is a 204.
I see the following to remove a manager:
graphClient.Users[newUser.Id].Manager.Reference.Request().DeleteAsync();
But we should have something like the following to assign a manager:
graphClient.Users[newUser.Id].Manager.Reference.Request().AddAsync(manager);
I'll file a bug for this and update when fixed.
Workaround till updating the "manager" is fully supported:
var authToken = "<get your token here>";
var client = new GraphClient(...);
var usrId = "<id of the user to update>"
var mgrId = "<id of the manager>"
var url = client.Users[usrId].Manager.Reference.Request().RequestUrl;
var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", authToken);
var content = new StringContent(
client.HttpProvider.Serializer.SerializeObject(
new ReferenceRequestBody
{
ODataId =
$"{client.BaseUrl}/directoryObjects/{mgrId}"
}),
Encoding.Default,
"application/json");
var resp = httpClient.PutAsync(url, content).Result;
if (!resp.IsSuccessStatusCode)
{
// throw exception/log etc
}
Two years later and this is still not working.
I'm getting the following exception when using await graphClient.Users[user.Id].Manager.Request().UpdateAsync(usrPatch);
Microsoft.Graph.ServiceException: 'Code: BadRequest
Message: Write requests are only supported on contained entities
I had to use stefboe's workaround logic to update the manager.
Microsoft.Graph DLL Version is 1.12.
Use PutAsync() to set the manager using the Graph Library:
graphClient.Users[user.Id].Manager.Reference.Request().PutAsync(manager.Id);

Client OData Patch without retrieving object first?

I am using a C# OData 4 client as described here:
http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/odata-v4/create-an-odata-v4-client-app
I have a product class and it has an Id, Name, Price and Category. I'd like to do something like:
var product = new ProductService.Models.Product {
Id = 2,
Price = 4
};
container.AttachTo("Products", product);
container.UpdateObject(product);
So that I can update only the price property and ignore all the rest of them. I can see that this won't work because Name and Category are Created as null when Product object is created so they will be sent in the resulting request as null.
Is there a way of doing this without first retrieving the object that I want to update? (I'm guessing that I need to go down the HttpClient route).
One workaround is to use HttpClient directly:
HttpRequestMessage request = new HttpRequestMessage(new HttpMethod("PATCH"), requestUri);
request.Content = new StringContent(#"{{""#odata.type"":""#ProductService.Models.Product"",""Price"":3000}}");
request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");
HttpResponseMessage response = new HttpClient.SendAsync(request).Result;

Categories