Office 365 Group Services

Office 365 Users

Office 365 Users and Groups services

This blog covers the services related to managing users and groups, which can be security or dynamic groups or the new Microsoft Office 365 Groups. The blog covers the most useful API entities, entity sets, actions, and navigation properties to handle these resources and services.

 

Users services

the main step for consuming some resources through the Microsoft Graph API in Microsoft .NET is to define the domain model types that can be used to serialize and deserialize the exchanged JSON messages. From a Users services perspective, the main type to define is the User class. In Listing, you can see an excerpt of the User type definition.

 

LISTING Code excerpt to define the User type for consuming the Users services

<summary>
Defines a single tenant user
</summary>
public class User : BaseModel {
<summary>
Defines whether the user's account is enabled or not
</summary>
public Boolean AccountEnabled;
<summary>
List of licenses assigned to the user
</summary>
public List<AssignedLicense> AssignedLicenses;
<summary>
List of Office 365 plans assigned to the user
</summary>
public List<AssignedPlan> AssignedPlans;
<summary>
List of user's business phones
</summary>
public List<String> BusinessPhones;
<summary>
City of the user
</summary> public String City;
<summary>
Company of the user
</summary>
public String CompanyName;
<summary>
Display Name of the user
</summary>
public String DisplayName;
<summary>
Mail of the user
</summary> public String Mail;
<summary>
UPN for the user
</summary>
public String UserPrincipalName;
// ... omissis ...
}

The list of the user’s attributes handled by the service is long, and for the sake of simplicity in Listing  you can see just a few of them. Some of the most important are UserPrincipalName, Mail, and AccountEnabled. In the following sections, you will see how to handle users instances like this.

 

Reading users

To browse the list of users registered in an Office 365 tenant, you can make an HTTP GET request for the users endpoint of the Microsoft Graph, as illustrated in Listing.

 

LISTING Code excerpt to enumerate the users registered in the current tenant

<summary>
This method retrieves the list of users registered in Azure AD
</summary>
<param name="numberOfItems">Defines the TOP number of items to retrieve</param>
<returns>The list of users in Azure AD</returns>
public static List<User> ListUsers(Int32 numberOfItems = 100) {
String jsonResponse = MicrosoftGraphHelper.MakeGetRequestForString( String.Format("{0}users?$top={1}",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri, numberOfItems));
var usersList = JsonConvert.DeserializeObject<UsersList>(jsonResponse); return (usersList.Users);
}

As you can see, the method internally leverages the helper method

 

Notice the $top query string parameter to retrieve just a subset of users. By default, if you don’t specify any value for the $top parameter, the query returns the top 100 items. The values allowed for the $top parameter are between 1 and 999. Thus, you cannot retrieve more than 999 users per query, but you can apply some partitioning rules using the $filter query string parameter to reduce the result set according to the filtering rules supported by the users collection.

Thus, if you would like to filter the result—for example, extracting only the users working in a specified department—you can use a code excerpt like the one illustrated in Listing.

 

LISTING Code excerpt to enumerate all the users working in a specified department

<summary>
This method retrieves the list of users working in a specific department
</summary>
<param name="department">The department to filter the users on</param>
<param name="numberOfItems">Defines the TOP number of items to retrieve</param>
<returns>The list of users in Azure AD</returns>
public static List<User> ListUsersByDepartment(String department, Int32 numberOfItems = 100) {
String jsonResponse = MicrosoftGraphHelper.MakeGetRequestForString( String.Format("{0}users?$filter=department%20eq%20'{1}'&$top={2}",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri,
department,
numberOfItems));
var usersList = JsonConvert.DeserializeObject<UsersList>(jsonResponse); return (usersList.Users);
}

 

In Listing, you can see the OData $filter query string parameter, which selects those user items that have the department field equal to a specific filter value.

Another interesting filtering option is based on the userType property, which assumes a value of Guest for the external users. Thus, if you would like to select all the external users registered in the current tenant, you can use a query like the one defined in Listing.

 

LISTING Code excerpt to enumerate all the external users for a target tenant

<summary>
This method retrieves the list of all the external users for a tenant
</summary>
<param name="numberOfItems">Defines the TOP number of items to retrieve</param>
<returns>The list of externa users in Azure AD</returns>
public static List<User> ListExternalUsers(Int32 numberOfItems = 100) {
String jsonResponse = MicrosoftGraphHelper.MakeGetRequestForString( String.Format("{0}users?$filter=userType%20eq%20'Guest'&$top={1}",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri, numberOfItems));
var usersList = JsonConvert.DeserializeObject<UsersList>(jsonResponse); return (usersList.Users);
}

 

Moreover, because the full user’s profile could be big and rich in attributes, querying the list of users you will get back the following attributes by default: businessPhonesdisplayName, givenName, id, jobTitle, mail, mobilePhone, officeLocation, preferredLanguage, surname, and userPrincipalName. You can leverage the OData $select query string parameter to change the default behavior of the service, selecting a custom set of attributes like the code excerpt in Listing does.

 

LISTING Code excerpt that retrieves a list of users with some custom fields

<summary>
This method retrieves the list of users registered in Azure AD with custom fields
</summary>
<param name="fields">The list of fields to retrieve</param>
<param name="numberOfItems">Defines the TOP number of items to retrieve</param>
<returns>The list of users in Azure AD</returns>
public static List<User> ListUsers(String[] fields = null, Int32 numberOfItems = 100) {
String selectFilter = String.Empty;
if (fields != null) {
selectFilter = "&$select=";
foreach (var field in fields) {
selectFilter += HttpUtility.UrlEncode(field) + ",";
}
}
String jsonResponse = MicrosoftGraphHelper.MakeGetRequestForString( String.Format("{0}users?$top={1}{2}",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri,
numberOfItems,
selectFilter));
var usersList = JsonConvert.DeserializeObject<UsersList>(jsonResponse); return (usersList.Users);
}

In Listing you can see a code excerpt that invokes the helper method illustrated in Listing

 

LISTING Code excerpt that invokes the helper method illustrated in Listing

var usersWithCustomAttributes = UsersGroupsHelper.ListUsers( new String[] { "id", "userPrincipalName", "mail",

"department", "country", "preferredLanguage", "onPremisesImmutableId", "onPremisesSecurityIdentifier", "onPremisesSyncEnabled", "userType" },

600);

 

Another interesting option to consider is the capability to leverage some navigation properties of the user object. For example, every user has the manager navigation property that allows you to discover the manager of the current user, if any. Similarly, by using the directReports navigation property, you can see the list of the people managed by a specific user.

 

From a SharePoint Online development perspective, it is important to underline that these properties and organizational relationships refer to those defined in Microsoft Azure Active Directory (Azure AD), and they are not necessarily the ones defined in the User Profile Service (UPS) of Microsoft SharePoint Online (SPO). Having the same values in Azure AD and in the UPS of SPO depends on the configuration of the UPS.

 

Note

If you want to learn more about the fields that are synchronized by default by the UPS in Microsoft SharePoint, you can read the article “Default user profile property mappings in SharePoint Server 2013,” which is available at the following URL: https://technet.microsoft.com/library/hh147510.aspx.

 

If you want to import custom properties in the UPS of SPO, you can refer to the “User Profile Batch Update” sample that is available in the Office 365 Developer Patterns & Practices (PnP) project repository on GitHub. It can be found at the following friendly URL: http://aka.ms/PnPUserProfileBatchUpdateAPI.

In Listing, you can see the definition of a couple of helper methods to retrieve the manager and the direct reports of a user.

 

LISTING Code excerpt to retrieve the manager and the direct reports of a user

<summary>
This method returns the manager of a user
</summary>
<param name="upn">The UPN of the user</param>
<returns>The user's manager</returns>
public static User GetUserManager(String upn) {
String jsonResponse = MicrosoftGraphHelper.MakeGetRequestForString( String.Format("{0}users/{1}/manager",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri, upn));
var user = JsonConvert.DeserializeObject<User>(jsonResponse); return (user);
}
<summary>
This method returns the direct reports of a user
</summary>
<param name="upn">The UPN of the user</param>
<returns>The user's direct reports</returns>
public static List<User> GetUserDirectReports(String upn) {
String jsonResponse = MicrosoftGraphHelper.MakeGetRequestForString( String.Format("b",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri, upn));
var directReports = JsonConvert.DeserializeObject<UsersList>(jsonResponse); return (directReports.Users);
}

 

There are many other navigation properties that you can use to go through the users’ calendars, events, mailboxes and mail folders, drives on OneDrive for Business, and so on. To access these navigation properties, you must have the proper account delegations and proper permissions in Azure AD. Aside from the authorization rules.

 

Just as you can read a list of users, you can get a single user object. You just need to make an HTTP GET request providing, for example, the User Principal Name (UPN) just after the users endpoint. In Listing you can see a sample helper method to accomplish this task.

 

LISTING  Code excerpt to retrieve a single user instance

<summary>
This method retrieves a single user from Azure AD
</summary>
<param name="upn">The UPN of the user to retrieve</param>
<returns>The user retrieved from Azure AD</returns> public static User GetUser(String upn) {
String jsonResponse = MicrosoftGraphHelper.MakeGetRequestForString(
String.Format("{0}users/{1}", MicrosoftGraphHelper.MicrosoftGraphV1BaseUri, upn));
var user = JsonConvert.DeserializeObject<User>(jsonResponse); return (user);
}

 

From a user interface perspective, an interesting property of every user object is the photo. In fact, modern UI applications often show the lists of users not as text but by using their profile pictures. For example, think about what happens in Skype for Business or in the web UI of the Contacts list in Office 365: you have a list of profile pictures, each one wrapped in a circle.

 

If you want to get the profile picture of a specific user, you have to leverage the photo property of that user. Specifically, to get the binary value of the photo, you have to query the $value property of the photo by using the common OData syntax. In Listing, you can see a helper method that retrieves a user’s photo as a stream of bytes.

 

LISTING Code excerpt of a helper method to retrieve a user’s photo as a stream of bytes

<summary>
This method retrieves the photo of a single user from Azure AD
</summary>
<param name="upn">The UPN of the user</param>
<returns>The user's photo retrieved from Azure AD</returns> public static Stream GetUserPhoto(String upn) {
String contentType = "image/png";
var result = MicrosoftGraphHelper.MakeGetRequestForStream( String.Format("{0}users/{1}/photo/$value",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri, upn), contentType);
return (result);
}

 

The resulting stream can be used wherever you like. For example, in the sample application related to this blog, the user’s photo is used to reproduce the current user’s profile picture in the Office 365 suite bar. To achieve this result, the sample application uses a dedicated action in an ASP.NET MVC controller where the stream is returned as a binary file together with a specific image content type (image/png).

 

Groups services

Whenever you think about users in an enterprise scenario, you also have to think about groups to better manage permissions and authorizations and perform general management and governance. In Office 365, groups are managed in Azure AD, like users’ accounts, and you can also manage them through the Microsoft Graph API. In this section, you will learn how to do that.

 

Browsing groups

First, you can query the list of groups by querying the groups endpoint of the Microsoft Graph API. The result will be a collection of objects that have a structure like the one illustrated in Listing.

 

LISTING Code excerpt to define the Group type for consuming the Groups services

/// <summary>
Defines a Group
</summary>
public class Group : BaseModel {
public String Description;
public String DisplayName;
public List<String> GroupTypes;
public String Mail;
public Boolean MailEnabled;
public String MailNickname;
public Nullable<DateTimeOffset> OnPremisesLastSyncDateTime; public String OnPremisesSecurityIdentifier;
public Nullable<Boolean> OnPremisesSyncEnabled; public List<String> ProxyAddresses;
public Boolean SecurityEnabled;
public String Visibility;
public Boolean AllowExternalSenders;
public Boolean AutoSubscribeNewMembers;
public Boolean IsSubscribedByMail;
public Int32 UnseenCount;
}

 

As you can see, there are some descriptive properties, like the DisplayName and the Description, and more functional properties, like those related to the Mail address of the group, the ProxyAddresses, and so on. Moreover, there are the GroupTypes collection property and the Boolean SecurityEnabled property that are useful to disambiguate among the classic security groups (which are mainly used for members’ authorization), the dynamic groups, and the new Office 365 Unified Groups.

 

These enable you to provide a better collaboration experience for users, and they will be covered in the upcoming section. In general, at the time of this writing there are three flavors of groups:

 

Security Groups Groups used for security authorization. They have the GroupTypes collection property empty and the SecurityEnabled property set to true.

 

Unified Groups The Office 365 Groups. They have the GroupTypes collection property with a value of Unified and the SecurityEnabled property set to false.

 

Dynamic Groups Groups with rule-based membership. This capability requires Azure AD Premium in the back end. They have the GroupTypes collection property with a value of DynamicMembership and the SecurityEnabled property set to false.

By playing with these properties and querying the groups endpoint via OData, you can start consuming the groups as you see in Listing.

 

LISTING Code excerpt of a helper method to query for groups in Office 365 and Azure AD

<summary>
This method retrieves the list of groups registered in Azure AD
</summary>
<param name="numberOfItems">Defines the TOP number of items to retrieve</param>
<returns>The list of groups in Azure AD</returns>
public static List<Group> ListGroups(Int32 numberOfItems = 100) { String jsonResponse = MicrosoftGraphHelper.MakeGetRequestForString(
String.Format("{0}groups?$top={1}", MicrosoftGraphHelper.MicrosoftGraphV1BaseUri, numberOfItems));
var groupsList = JsonConvert.DeserializeObject<GroupsList>(jsonResponse); return (groupsList.Groups);
}

You can leverage the $top query string parameter to select a subset of group items. If you want to retrieve a specific set of groups, like the security enabled groups, you can apply a filtering condition by leveraging the $filter query string parameter, as illustrated in Listing.

 

LISTING Code excerpt of a helper method to query for security groups in Office 365 and Azure AD

 <summary>
This method retrieves the list of Security Groups
</summary>
<param name="numberOfItems">Defines the TOP number of items to retrieve</param>
<returns>The list of Security Groups</returns>
public static List<Group> ListSecurityGroups(Int32 numberOfItems = 100) { String jsonResponse = MicrosoftGraphHelper.MakeGetRequestForString(
String.Format("{0}groups?$filter=SecurityEnabled%20eq%20true" + "&$top={1}", MicrosoftGraphHelper.MicrosoftGraphV1BaseUri, numberOfItems));
var groupsList = JsonConvert.DeserializeObject<GroupsList>(jsonResponse); return (groupsList.Groups);
}

Moreover, if you want to retrieve a specific group, you can use the ID of the group object to make a direct HTTP GET request. In Listing, you can see how to do that.

 

LISTING Code excerpt to retrieve a specific group by ID

<summary>
This method retrieves a specific group registered in Azure AD
</summary>
<param name="groupId">The ID of the group</param>
<returns>The group instance</returns>
public static Group GetGroup(String groupId) {
String jsonResponse = MicrosoftGraphHelper.MakeGetRequestForString( String.Format("{0}groups/{1}",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri, groupId));
var group = JsonConvert.DeserializeObject<Group>(jsonResponse); return (group);
}

 

Managing groups

The basic purpose of having groups is to cluster users for authorization and targeting goals. Thus, it is often necessary to retrieve the members of a group or to manage groups’ membership. Every group has some useful navigation properties to accomplish these tasks. For example, you can use the members navigation property to see the members of a group. In Listing, you can see a helper method to do that.

 

LISTING Code excerpt to retrieve the members of a group

<summary>
This method retrieves the list of members of a group
</summary>
<param name="groupId">The ID of the group</param>
<returns>The members of the group</returns>
public static List<User> ListGroupMembers(String groupId) {
String jsonResponse = MicrosoftGraphHelper.MakeGetRequestForString( String.Format("{0}groups/{1}/members",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri, groupId));
var usersList = JsonConvert.DeserializeObject<UsersList>(jsonResponse); return (usersList.Users);
}

 

It is interesting to note that the result of the members navigation property is an array of objects of type User. Thus, you can leverage the capabilities illustrated in the previous section against each User item. For example, you can retrieve the personal picture of every member, or you can retrieve the email addresses, the manager, the direct reports, and so on. If you have proper permissions, you can also access the user’s mailbox, calendars, contacts, drives, and so on.

You can also get a reference to the owners of a group by using the owners navigation property, as illustrated in Listing.

 

LISTING Code excerpt to retrieve the owners of a group

<summary>
This method retrieves the list of owners of a group
</summary>
<param name="groupId">The ID of the group</param>
<returns>The owners of the group</returns>
public static List<User> ListGroupOwners(String groupId) {
String jsonResponse = MicrosoftGraphHelper.MakeGetRequestForString( String.Format("{0}groups/{1}/owners",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri, groupId));
var usersList = JsonConvert.DeserializeObject<UsersList>(jsonResponse); return (usersList.Users);
}

 

Because the owners navigation property returns an array of objects of type User, the same ideas that we applied to the members of a group can be applied to the owners of a group.

 

Managing group membership

You can also use the Microsoft Graph API to add members or owners to or remove them from a group. To achieve this result, you have to target the reference instance of the members and owners navigation properties of a group. This can be done by appending the $ref property to the URL of the target navigation property. Moreover, you will have to provide the ID of the user object you want to add. In Listing, you can see a helper method to add a new member to a group.

 

LISTING Code excerpt of a helper method to add a member to a group

<summary>
This method adds a new member to a group
</summary>
<param name="user">The user to add as a new group's member</param>
<param name="groupId">The ID of the target group</param>
public static void AddMemberToGroup(User user, String groupId) { MicrosoftGraphHelper.MakePostRequest(
String.Format("{0}groups/{1}/members/$ref",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri,
groupId),
new GroupMemberToAdd
{
ObjectId = String.Format("{0}users/{1}/id", MicrosoftGraphHelper.MicrosoftGraphV1BaseUri,
user.UserPrincipalName)
},
"application/json");
}

Notice the GroupMemberToAdd type used to describe the new member to add. It is a type that will serialize a JSON request body like the one illustrated in the following excerpt:

{"@http://odata.id":"https://graph.microsoft.com/v1.0/users/<UPN>/id"}

In Listing  you can see the definition of the GroupMemberToAdd type.

 

LISTING The definition of the GroupMemberToAdd type

<summary>
This type defines a new member to add to a group
</summary>
public class GroupMemberToAdd {
[JsonProperty("@http://odata.id")]
public String ObjectId { get; set; }
}

 

Notice the JsonProperty attribute of the Newtonsoft.Json library applied to the ObjectId property to customize the JSON serialization of the request body, according to the functional requirements of the Microsoft Graph API.

 

If you try to add a member who already exists in the group’s members list, you will get back an HTTP 400 response with an error message like the following:

One or more added object references already exist for the following modified properties: ‘members’.

If you want to remove a member or an owner from a group, you have to make an HTTP DELETE request targeting the specific user object reference inside the entity set from which you want to do the removal. In Listing, you can see the corresponding helper method.

 

LISTING Code excerpt of a helper method to remove a member from a group

<summary>
This method removes a member from a group
</summary>
<param name="user">The user to remove from the group</param>
<param name="groupId">The ID of the target group</param>
public static void RemoveMemberFromGroup(User user, String groupId) { MicrosoftGraphHelper.MakeDeleteRequest(
String.Format("{0}groups/{1}/members/{2}/$ref",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri, groupId, http://user.Id));
}

 

Notice the $ref URL property at the end of the member user’s URL. It is mandatory to reference the specific membership object to delete it.

 

Office 365 Groups services

The Office 365 Groups, also known as Unified Groups, deserve a dedicated section of this blog.  the Office 365 Groups are an exciting new feature of the Office 365 ecosystem. Thus, it is a common business requirement to be able to query, add, update, or delete Office 365 Groups programmatically. In this section, you will learn how to master the most common needs in these fields.

 

Querying Office 365 Groups

First of all, querying the Office 365 Groups is similar to querying other groups, but you have to filter the results based on the GroupType collection property values. As you saw in the previous sections, a GroupType property that contains the Unified value describes an Office 365 Group.

 

However, because the GroupType is a property of type collection of strings, you cannot compare its value with the Unified string. Instead, you have to search for the Unified value in the collection of values contained in the GroupType property. In Listing, you can see how to achieve this result by leveraging the power of OData querying.

 

LISTING Code excerpt of a helper method to query for the list of Office 365 Groups

<summary>
This method retrieves the list of Office 365 Groups
</summary>
<param name="numberOfItems">Defines the TOP number of items to retrieve</param>
<returns>The list of Office 365 Groups</returns>
public static List<Group> ListUnifiedGroups(Int32 numberOfItems = 100) { String jsonResponse = MicrosoftGraphHelper.MakeGetRequestForString(
String.Format("{0}groups?
$filter=groupTypes/any(gt:%20gt%20eq%20'Unified')" +
"&$top={1}", MicrosoftGraphHelper.MicrosoftGraphV1BaseUri, numberOfItems));
var groupsList = JsonConvert.DeserializeObject<GroupsList>(jsonResponse); return (groupsList.Groups);
}
Notice the use of the any function of OData applied to the GroupTypes collection property.
Removing the URL encoding, the search query looks like the following:
$filter=groupTypes/any(gt: gt eq 'Unified')

 

In this example, the arguments of the any function are similar to a lambda expression (from a C# developer perspective), and the gt keyword represents every instance of the values contained in the groupTypes collection. When the instance value equals Unified, the any function will yield the containing group.

 

Typically, every Office 365 Group also has a picture to better describe the group and to make it easier for the users to recognize that group within hundreds or thousands of groups. Through the Microsoft Graph API, you can easily retrieve the picture of a group by consuming the photo property, like you can for a user. In Listing, you can see a helper method to retrieve a group’s picture as a stream of bytes.

 

LISTING Code excerpt of a helper method to retrieve the picture of a group

<summary>
This method retrieves the photo of a group from Azure AD
</summary>
<param name="groupId">The ID of the group</param>
<returns>The group's photo retrieved from Azure AD</returns> public static Stream GetGroupPhoto(String groupId) {
String contentType = "image/png";
var result = MicrosoftGraphHelper.MakeGetRequestForStream( String.Format("{0}groups/{1}/photo/$value",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri, groupId)
contentType);
return (result);
}

The stream can be used wherever you need it to save or show the corresponding image file.

 

Office 365 Groups capabilities

The main reason to access an Office 365 Group is to leverage the capabilities the new Unified Groups model provides. For example, imagine that you want to consume the conversation threads related to a specific Office 365 Group. To do that, you can make an HTTP GET request for the conversations or for the threads navigation properties. In Listing you can find the corresponding sample helper method.

 

LISTING Code excerpt of a helper method to retrieve the conversation threads of an Office 365 Group

<summary>
This method retrieves the list of threads of an Office 365 Group
</summary>
<param name="groupId">The ID of the group</param>
<returns>The threads of an Office 365 Group</returns>
public static List<ConversationThread> ListUnifiedGroupThreads(String groupId) { String jsonResponse = MicrosoftGraphHelper.MakeGetRequestForString(
String.Format("{0}groups/{1}/threads", MicrosoftGraphHelper.MicrosoftGraphV1BaseUri, groupId));
var conversationThreadsList =
JsonConvert.DeserializeObject<ConversationThreadsList>(jsonResponse);
return (conversationThreadsList.Threads)
}

 

The returned list of threads is represented by a collection of objects of custom type ConversationThread, which provides information about the topic, the recipients, any file attachment, and so on. However, a conversation thread is usually interesting because of its messages, which are accessible through the navigation property called posts.

 

By querying that navigation property, you can access every post of the thread, and you can even programmatically reply to the thread by sending a new post message. In Listing, you can see how to consume a single post.

 

LISTING Code excerpt of a helper method to retrieve a single post in a conversation thread of an Office 365 Group

<summary>
This method retrieves a single post of a conversation thread for an Office
365 Group
</summary>
<param name="groupId">The ID of the thread</param>
<param name="threadId">The ID of the thread</param>
<param name="postId">The ID of the post</param>
<returns>The post of the conversation thread for an Office 365 Group</returns>
public static ConversationThreadPost GetUnifiedGroupThreadPost(String groupId, String threadId, String postId) {
String jsonResponse = MicrosoftGraphHelper.MakeGetRequestForString( String.Format("{0}groups/{1}/threads/{2}/posts/{3}",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri,
groupId,
threadId,
postId));
var conversationThreadPost =
JsonConvert.DeserializeObject<ConversationThreadPost>(jsonResponse);
return (conversationThreadPost);
}

 

In Listing you can see how to send a new post into an existing thread. Just invoke the reply action of the thread object, providing at least the body of the message and then providing the categories, any new message recipient, attachment, and whatever else pertains to the post.

 

LISTING Code excerpt of a helper method to reply to a thread of an Office 365 Group

<summary>
This method replies to a thread of an Office 365 Group
</summary>
<param name="groupId">The ID of the thread</param>
<param name="threadId">The ID of the thread</param>
<param name="post">The post to send as the reply</param> public static void ReplyToUnifiedGroupThread(String groupId,
String threadId, ConversationThreadPost post) {
MicrosoftGraphHelper.MakePostRequest(
String.Format("{0}groups/{1}/threads/{2}/reply",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri,
groupId,
threadId), new { post }, "application/json");
}

 

Notice the use of the anonymous type as the content argument for the MakePostRequest method invocation. The reply action signature of a conversation thread accepts a complex type, serialized in JSON, with a post property that represents the post with which to reply. In Listing, you can see a code excerpt that uses the previous helper method.

 

LISTING Code excerpt using the helper method

UnifiedGroupsHelper.ReplyToUnifiedGroupThread(group.Id, threads[0].Id, new Models.ConversationThreadPost {
Body = new Models.ItemBody {
Type = Models.BodyType.Html,
Content = "<html><body><div>This is the body of a post created via"
+
"the Microsoft Graph API!</div></body></html>",
},
NewParticipants = new List<Models.UserInfoContainer>( new Models.UserInfoContainer[] {
new Models.UserInfoContainer {
Recipient = new Models.UserInfo {
Name = "Thesis Scientist",
Address = "ThesisScientist@aaa.com",
}
}
}),
});

As you can see, the sample code excerpt of Listing replies to the thread and includes a new recipient through the NewParticipants collection property of the ConversationThreadPost type.

 

Another interesting capability of an Office 365 Group is the group-related calendar. Using the calendar navigation property of the group endpoint, you can access both the calendar of a Unified Group and the events stored in that calendar. In Listing, you can see the helper method to access a calendar of a Unified Group.

 

LISTING Definition of the helper method to access the calendar of an Office 365 Group.

<summary>
This method retrieves the calendar of an Office 365 Group
</summary>
<param name="groupId">The ID of the group</param>
<returns>The calendar of an Office 365 Group</returns> public static Calendar GetUnifiedGroupCalendar(String groupId) {
String jsonResponse = MicrosoftGraphHelper.MakeGetRequestForString( String.Format("{0}groups/{1}/calendar",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri, groupId));
var calendar = JsonConvert.DeserializeObject<Calendar>(jsonResponse); return (calendar);
}

 

As you can see, the type of the result of the calendar navigation property is identical. Thus, with the calendars of Unified Groups, you can leverage all the functionalities and capabilities. As an example, in Listing you see how to consume the list of calendar events for a specific Office 365 Group.

 

LISTING The helper method to retrieve a calendar view for a calendar of an Office 365 Group

<summary>
Retrieves the events of an Office 365 Group calendar within a specific date range
</summary>
<param name="groupId">The ID of the group</param>
<param name="startDate">The start date of the range</param>
<param name="endDate">The end date of the range</param>
<param name="startIndex">The startIndex (0 based) of the items to retrieve</param>
<returns>A page of up to 10 events</returns>
public static List<Event> ListUnifiedGroupEvents(String groupId, DateTime startDate,
DateTime endDate, Int32 startIndex = 0) {
String jsonResponse = MicrosoftGraphHelper.MakeGetRequestForString( String.Format("{0}groups/{1}/calendarView?startDateTime={2:o}&" +
"endDateTime={3:o}&$skip={4}",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri,
groupId,
startDate.ToUniversalTime(),
endDate.ToUniversalTime(),
startIndex));
var eventList = JsonConvert.DeserializeObject<EventList>(jsonResponse);
return (eventList.Events);
}

 

Again, the result is a collection of JSON objects that correspond to the custom Event type when consuming the calendar services.

One last interesting capability of every Office 365 Group is the OneDrive for Business dedicated folder. From a Microsoft Graph API perspective, you can access the OneDrive for Business drive by using the drive navigation property, which will give you access to a JSON object that defines the owner and the quota of the drive. you will learn more about the drive object.

 

For now, it will suffice to know that you can access the folders and files of an Office 365 Group. In Listing, you can see a code excerpt to access the drive’s general information.

 

LISTING The helper method to retrieve the drive of an Office 365 Group

<summary>
This method retrieves the OneDrive for Business of an Office 365 Group
</summary>
<param name="groupId">The ID of the group</param>
<returns>The OneDrive for Business of an Office 365 Group</returns> public static Drive GetUnifiedGroupDrive(String groupId) {
String jsonResponse = MicrosoftGraphHelper.MakeGetRequestForString(
String.Format("{0}groups/{1}/drive", MicrosoftGraphHelper.MicrosoftGraphV1BaseUri, groupId));
var drive = JsonConvert.DeserializeObject<Drive>(jsonResponse); return (drive);
}

 

Creating or updating Office 365 Groups

Sometime it could be useful to create Unified Groups, not only to consume existing ones. For example, you could have an Office 365 application that creates a Unified Group for managing the approval process of a manual or another type of document, and you want to automate the group creation process and the group deletion once the document is completed and approved. By using the Microsoft Graph API, it is easy to create a new Office 365 Group.

 

To create an Office 365 Group, you just need to make an HTTP POST request against the groups entity set, providing the new group object as JSON serialized content within the body of the request. In Listing, you can see a helper method to achieve this result.

 

LISTING The helper method to create a new Office 365 Group

<summary>
Creates/Adds a new Office 365 Group
</summary>
<param name="group">The group to add/create</param>
<returns>The just added group</returns>
public static Group AddUnifiedGroup(Group group) {
String jsonResponse = MicrosoftGraphHelper.MakePostRequestForString( String.Format("{0}groups",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri), group, "application/json");
var addedGroup = JsonConvert.DeserializeObject<Group>(jsonResponse); return (addedGroup);
}

 

As you see, the helper method uses the MakePostRequestForString helper function because when you add a new group, the response will be the JSON serialization of the newly created group. Notice that there are some mandatory fields for creating a group. These are the displayName, mailEnabled, mailNickname, GroupTypes, and securityEnabled fields.

 

Moreover, to create an Office 365 Group via the Microsoft Graph API, you have to provide a valid access token for delegation of an authorized user. You cannot create Office 365 Groups by providing an app-only access token. In Listing, you can see a code excerpt that invokes the AddUnifiedGroup helper method.

 

LISTING A sample code excerpt that uses the i helper method

var newUnifiedGroup = UnifiedGroupsHelper.AddUnifiedGroup( new Models.Group {
DisplayName = "Created via API",
MailEnabled = true,
SecurityEnabled = false,
GroupTypes = new List<String>(new String[] { "Unified"}), MailNickname = "APICreated",
});

 

Notice that the value for the GroupTypes collection property is Unified. If you like, you can configure many more properties while creating a new Office 365 Group. However, those highlighted in the demo will suffice. When you create a new group, the email address of the group—if any—will be generated automatically by the Exchange Online service, which sits under the cover of the mail capabilities of a group, and you cannot force any explicit value because the mail property of the group is read-only.

 

Because you could have many Office 365 Groups in your tenant, it is a good habit to set up every group with a specific and identifying group icon. Setting the group image/icon is straightforward: you just need to make an HTTP PATCH request against the photo/$value property of the target group. The approach to use is similar to the one for setting a single user’s picture. In Listing, you can see the corresponding helper method.

 

LISTING The definition of a helper method to set the picture of an Office 365 Group

<summary>
Updates the photo of an Office 365 Group
</summary>
<param name="groupId">The ID of the target group</param>
<param name="photo">The byte array of the photo</param>
public static void UpdateUnifiedGroupPhoto(String groupId, Stream photo) { MicrosoftGraphHelper.MakePatchRequestForString(
String.Format("{0}groups/{1}/photo/$value",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri,
groupId),
photo, "image/jpeg");
}

 

The request will contain the image content as a raw stream of bytes, provided with a specific content/type for the request body. The response—if successful—will be an HTTP Status Code with a value of 200 (OK), with no specific text or echo of the request.

 

If you want to delete an existing Office 365 Group, you can make an HTTP DELETE request targeting the URL of the group that you want to delete. In Listing, you can see a code excerpt to accomplish this task.

 

LISTING The definition of a helper method to delete an Office 365 Group

<summary>

Deletes an Office 365 Group

</summary>

<param name="groupId">The ID of the group to delete</param> public static void DeleteUnifiedGroup(String groupId) {

MicrosoftGraphHelper.MakeDeleteRequest(

String.Format("{0}groups/{1}", MicrosoftGraphHelper.MicrosoftGraphV1BaseUri, groupId));

}

 

Summary

In this blog, you learned how to read and manage users and groups in an Office 365 tenant. In particular, you saw how to browse the groups according to group type (Security, Dynamic, and Unified). Furthermore, you saw how to query existing Office 365 Groups and how to browse conversations, threads, calendars, and drives of Unified Groups. Last, you learned how to create, update, and delete an Office 365 Group by using the Microsoft Graph API.

 

File services

OneDrive for Business is one of the most used services of Microsoft Office 365, together with Microsoft Exchange Online. In fact, almost every Office 365 user uses a personal drive on OneDrive for Business and has a mailbox.

 

Working with drives, files, and folders

When consuming OneDrive for Business, the first thing to do is to get a reference to the current user’s drive. As in the previous blogs, you can make an HTTP GET request targeting the drive navigation property of the current user (me). The me/drive property represents the main entry point for the current user’s OneDrive for Business drive. In Listing, you can see the helper method to access the current user’s personal drive.

 

LISTING Code excerpt of the helper method to access the current user’s personal drive

<summary>
This method returns the personal drive of the current user
</summary>
<returns>The current user's personal drive</returns> public static Drive GetUserPersonalDrive() {
String jsonResponse = MicrosoftGraphHelper.MakeGetRequestForString( String.Format("{0}me/drive",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri));
var drive = JsonConvert.DeserializeObject<Drive>(jsonResponse); return (drive);
}

The result will be an instance of a custom Drive type that defines the drive object and can be serialized into and deserialized from the JSON representation of a drive provided by the Microsoft Graph API. The custom Drive type is defined in Listing.

 

LISTING The definition of the custom Drive type for representing a OneDrive for Business drive

<summary>
Defines a drive of OneDrive for Business
</summary>
public class Drive : BaseModel {
<summary>
The type of the current Drive
</summary>
public String DriveType { get; set; }
<summary>
The drive's owner
</summary>
public IdentitySet Owner { get; set; }
<summary>
The storage quota of the drive
</summary>
public Quota Quota { get; set; }
}

As you can see, there is a DriveType property of type String, which assumes a value of business for a OneDrive for Business drive.

 

Note

You may be wondering why there is a property that declares the type of the drive. In the long term, it is reasonable to support multiple types of drives. For example, you could have a OneDrive for Business drive, or you could have a OneDrive Personal drive. Both the flavors of OneDrive can be accessed through a REST API, which is the OneDrive REST API v2.0, and it makes sense to think about a convergence of this API with the Microsoft Graph API.

 

Through the Drive instance, you can also access the Owner of the drive and the Quota numbers of the current drive. For example, by leveraging the Owner property of the drive object, you can access the user who owns that drive directly. This could be useful when you are browsing third parties’ drives (as long as you have proper permissions to do that) and want to traverse the graph to gain a user from her drive object.

 

Browsing for files and folders

Once you have a reference to the drive object, you can access its root folder by making an HTTP GET request for the root navigation property, which represents the root folder of the drive. In Listing, you can see a helper method to get the root folder.

 

LISTING Code excerpt of the helper method to access the root folder of the current user’s personal drive

<summary>
This method returns the root folder of the personal drive of the current
user
</summary>
<returns>The root folder of the current user's personal drive</returns> public static DriveItem GetUserPersonalDriveRoot() {
String jsonResponse = MicrosoftGraphHelper.MakeGetRequestForString( String.Format("{0}me/drive/root",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri));
var folder = JsonConvert.DeserializeObject<DriveItem>(jsonResponse); return (folder);
}

 

The result of querying the root folder is an object that can be serialized/deserialized into a custom type called DriveItem that corresponds to the general-purpose driveItem defined in the metadata documentation of the Microsoft Graph API and is described in Listing If you browse to the Microsoft Graph API metadata endpoint (https://graph.microsoft.com/v1.0/$metadata) and search for the EntityType element with the attribute Name with a value of driveItem, you will see that the driveItem type can hold any kind of drive item: a folder, a generic file, an audio file, a video file, and so on. Based on the instance of driveItem you retrieve, you will get a value for some of the properties declared in the DriveItem type.

 

LISTING The definition of the custom DriveItem type

<summary>
Defines any generic item of a drive
</summary>
public class DriveItem : BaseModel {
public http://System.IO.Stream Content;
public IdentitySet CreatedBy;
public Nullable<DateTimeOffset> CreatedDateTime; public String CTag;
public String Description;
public String ETag;
public IdentitySet LastModifiedBy;
public Nullable<DateTimeOffset> LastModifiedDateTime; public String Name;
public ItemReference ParentReference;
public Nullable<Int64> Size;
public String WebDavUrl;
public String WebUrl;
public Audio Audio;
public Deleted Deleted;
public File File;
public FileSystemInfo FileSystemInfo;
public Folder Folder;
public Image Image;
public GeoCoordinates Location;
public Photo Photo;
public SearchResult SearchResult;
public Shared Shared;
public SpecialFolder SpecialFolder;
public Video Video;
}

 

For example, the root folder of a user’s drive will hold just a few of the available properties. For the root folder and for folders in general, you will have at least the Name, the WebUrl, the CreatedDateTime, and the LastModifiedDateTime.

 

However, when you access a specific drive or folder, you usually are interested in consuming the files contained in that drive or folder. Let’s say that you want to retrieve all the files stored in the root folder of the current user’s drive.  For the root folder of the current user, the relative URL path could be like the following:

/me/drive/root/children

 

But to target any folder and any drive in general—not only the root folder of the current user’s drive—you can use the ID of the drive and the ID of the folder to build a direct path to the collection of children items. So, you will have something like the following relative URL path:

/drives/<DriveID>/items/<FolderId>/children

In Listing you can see a helper method that retrieves the children items of any folder for any target drive.

 

LISTING The helper method to retrieve the children items of a target folder in a specific drive

<summary>
This method returns the children items of a specific folder
</summary>
<param name="driveId">The ID of the target drive</param>
<param name="folderId">The ID of the target folder</param>
<param name="numberOfItems">The number of items to retrieve</param>
<returns>The children items</returns>
public static List<DriveItem> ListFolderChildren(String driveId, String folderId,
Int32 numberOfItems = 100) {
String jsonResponse = MicrosoftGraphHelper.MakeGetRequestForString( String.Format("{0}drives/{1}/items/{2}/children?$top={3}",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri,
driveId,
folderId,
numberOfItems));
var driveItems = JsonConvert.DeserializeObject<DriveItemList>(jsonResponse); return (driveItems.DriveItems);
}

 

By leveraging code recursion, you can then browse all the folders of a drive by using the ID of the folders and by accessing their children items. However, you should keep in mind that by default the Microsoft Graph API will return no more than 200 items for each request. Thus, you will have to leverage the @odata.nextLink property of the result with the $top OData query string parameter if you want to do paging of results and/or if you want to consume all the children items, page by page.

 

Consuming files

When you have identified a file that you want to consume, you can access it directly by ID, using the same syntax that you use for consuming a folder. Thus, the relative URL will look like the following: /drives/<DriveID>/items/<FileId>

 

Of course, a file is made of much more information than a folder. For example, you have the size of the file, the user who created the file and when the file was created, the user who last updated the file and when, the MIME type of the file, and so on. One of the most interesting properties of a file is its content, which can be accessed by using a helper method like the one shown in Listing.

 

LISTING The helper method to consume the content of a file from OneDrive for Business

<summary>
This method returns the content of a specific file by ID
</summary>
<param name="driveId">The ID of the target drive</param>
<param name="fileId">The ID of the target file</param>
<returns>The content of the file as a Stream</returns>
public static Stream GetFileContent(String driveId, String fileId, String contentType) {
Stream fileContent = MicrosoftGraphHelper.MakeGetRequestForStream( String.Format("{0}drives/{1}/items/{2}/content",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri,
driveId,
fileId),
contentType);
return (fileContent);
}

 

The response will be an HTTP Redirect (Status Code 302) to the URL of the file. Thus, you will have to enable HTTP redirection in the HttpClient instance that you used to make the request.

 

However, often you don’t know where a file is, and you want to search for it and then consume its content or properties. In that case, you can use the microsoft.graph.search (or search) action, which can be applied to any folder of OneDrive for Business. In Listing  you can see a function that leverages this search capability.

 

LISTING The helper method to search for files in OneDrive for Business

<summary>
This method searches for a file in the target drive and optional target folder
</summary>
<param name="searchText">The text to search for</param>
<param name="driveId">The ID of the target drive</param>
<param name="folderId">The ID of the target folder, optional</param>
<returns>The list of resulting DriveItem objects, if any</returns> public static List<DriveItem> Search(String searchText, String driveId,
String folderId = null) {
String requestUri = null;
if (!String.IsNullOrEmpty(folderId)) {
requestUri = String.Format("{0}drives/{1}/items/{2}/" +
"search(q='{3}')",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri,
driveId, folderId, searchText);
}
else {
requestUri = String.Format("{0}drives/{1}/root/search(q='{2}')", MicrosoftGraphHelper.MicrosoftGraphV1BaseUri,
driveId, searchText);
}
String jsonResponse =
MicrosoftGraphHelper.MakeGetRequestForString(requestUri);
var driveItems = JsonConvert.DeserializeObject<DriveItemList>(jsonResponse); return (driveItems.DriveItems);
}

 

Notice that the function applies the action to a specific folder URL if the input argument folderId is provided. Otherwise, it applies the action to the root folder of the drive, which will search the content in the entire drive. Aside from that, it is just a common REST query that will return a set of objects of type DriveItem if there is any result corresponding to the provided query text.

 

Nevertheless, you should consider that there could be some delay in finding newly added items due to the indexing engine that works in the background. Thus, don’t be surprised if you do not instantly find any new file that you just uploaded.

 

When consuming files, sometimes it is also useful to present a list of files—for example, the list of search results—including a thumbnail of each file item. Luckily, OneDrive for Business and the Microsoft Graph API provide an out-of-box capability to generate and provide thumbnails for known file types.

 

For every known file type—Office files, video, audio, images, and many others—you can have three different thumbnails: small, medium, and large. Depending on the user interface or the user experience that you want to provide, you can use any of these autogenerated thumbnails.

 

You will need to make an HTTP GET request for the thumbnails navigation property of the DriveItem that you target, and the result will be the set of the three available thumbnails. You can also access a specific thumbnail size directly. In Listing you can see a helper method to get these thumbnails for a specific file item.

 

LISTING The helper method to retrieve thumbnail information for specific files in OneDrive for Business

<summary>
This method returns the thumbnails of a specific file by ID
</summary>
<param name="driveId">The ID of the target drive</param>
<param name="fileId">The ID of the target file</param>
<returns>The file thumbnails for the specific file</returns>
public static ThumbnailSet GetFileThumbnails(String driveId, String fileId) { String jsonResponse = MicrosoftGraphHelper.MakeGetRequestForString(
String.Format("{0}drives/{1}/items/{2}/thumbnails",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri,
driveId,
fileId));
var thumbnails = JsonConvert.DeserializeObject<ThumbnailSetResponse> (jsonResponse);
return (thumbnails.Value.Count > 0 ? thumbnails.Value[0] : null);
}
<summary>
This method returns a thumbnail by size of a specific file by ID
</summary>
<param name="driveId">The ID of the target drive</param>
<param name="fileId">The ID of the target file</param>
<param name="size">The size of the target thumbnail</param>
<returns>The file thumbnails for the specific file</returns> public static Thumbnail GetFileThumbnail(String driveId, String fileId,
ThumbnailSize size) {
String jsonResponse = MicrosoftGraphHelper.MakeGetRequestForString( String.Format("{0}drives/{1}/items/{2}/thumbnails/0/{3}",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri,
driveId,
fileId,
size.ToString().ToLower()));
var thumbnail = JsonConvert.DeserializeObject<Thumbnail>(jsonResponse); return (thumbnail);
}

 

Every thumbnail item will provide information about the height and width of the image file and the URL of the physical image file. If you want, you can even download the image file directly. For example, the URL of a thumbnail could be something like this:

https://<tenant>-

http://my.sharepoint.com/personal/<user>/_api/v2.0/drive/items/<fileId>/thumbnails/0/large/t

 

The URL of the thumbnail file content comes from the base URL of OneDrive for Business, which is related to the URL https://<tenant>-http://my.sharepoint.com. Thus, you cannot request this URL by providing the classic OAuth 2.0 access token that we used so far because that one targets the Microsoft Graph API resource identifier (https://graph.microsoft.com).

 

You have to request an access token specific to every different resource by using the refresh token to request or refresh an access token. So to consume the image file of a thumbnail, you have to switch from the Microsoft Graph API to the OneDrive API, and you have to create a new access token. Aside from that, it will be a common HTTP request for a file stream, as you can see in Listing.

 

LISTING The helper method to retrieve the image file of a thumbnail for specific files in OneDrive for Business

<summary>
This method returns the thumbnails of a specific file by ID
</summary>
<param name="driveId">The ID of the target drive</param>
<param name="fileId">The ID of the target file</param>
<returns>The file thumbnails for the specific file</returns>
public static Stream GetFileThumbnailImage(String driveId, String fileId, ThumbnailSize size) {
String jsonResponse = MicrosoftGraphHelper.MakeGetRequestForString( String.Format("{0}drives/{1}/items/{2}/thumbnails/0/{3}",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri,
driveId,
fileId,
size.ToString().ToLower()));
var thumbnail = JsonConvert.DeserializeObject<Thumbnail>(jsonResponse); var thumbnailImageStream = MicrosoftGraphHelper.MakeGetRequestForStream(
thumbnail.Url,
"image/jpeg");
return (thumbnailImageStream);
}

 

Under the cover of the MakeGetRequestForStream method, the MicrosoftGraphHelper class will disambiguate among the various resource identifiers and request the proper access token to the Microsoft Azure AD OAuth 2.0 endpoint.

 

Uploading and updating files

So far, you have seen how to traverse the folders of a drive, how to browse and search for files, and how to browse and download the thumbnails of files, but you probably will also need to upload new files or update existing files. In this section, you will see how to write contents on OneDrive for Business through the Microsoft Graph API.

 

First, let’s create a new folder that we will use for storing some sample files. Creating a folder is easy and requires almost the same procedure as creating a new file. The only difference is that a folder does not have any specific content, while a file is made mainly of content and content type. In Listing, you can see a function that creates a new folder in a parent folder of OneDrive for Business.

 

LISTING Code excerpt of a helper method to create a new folder in OneDrive for Business

<summary>
This method creates a new folder in OneDrive for Business
</summary>
<param name="driveId">The ID of the target drive</param>
<param name="parentFolderId">The ID of the parent folder</param>
<param name="folder">The new folder object to create</param>
<returns>The just created folder</returns>
public static DriveItem CreateFolder(String driveId, String parentFolderId, DriveItem folder) {
var jsonResponse = MicrosoftGraphHelper.MakePostRequestForString( String.Format("{0}drives/{1}/items/{2}/children",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri,
driveId,
parentFolderId),
folder,
"application/json");
var newFolder = JsonConvert.DeserializeObject<DriveItem>(jsonResponse); return (newFolder);
}

Invoking the helper method illustrated in Listing  is straightforward, and you can see a sample in Listing.

 

LISTING Code excerpt to invoke the helper method 

var newFolder = FilesHelper.CreateFolder(Sewa mobil dari pasar rental mobil terbesar se-Indonesia - Drive.IDhttp://root.Id, new Models.DriveItem {

Name = $"Folder Create via API - {DateTime.Now.GetHashCode()}", Folder = new Models.Folder { },

});

 

Once you have a target folder, you can upload a new file into it. A file is a DriveItem like a folder, and you need to make an HTTP POST request against the children entity set of the parent folder where you want to create the file. However, a DriveItem that represents a file must have the File property assigned instead of the Folder property.

 

Moreover, you will also have to upload the real file content, making an HTTP PUT request against the content property of the file item. In Listing , you can see a helper method that accepts the drive and parent folder in which you want to create a file and the file object of type DriveItem and a

http://System.IO.Stream that will represent the real content for the file.

 

LISTING Helper method to create and upload a file into a target parent folder

<summary>
This method creates and uploads a file into a parent folder
</summary>
<param name="driveId">The ID of the target drive</param>
<param name="parentFolderId">The ID of the parent folder</param>
<param name="file">The file object</param>
<param name="content">The binary stream of the file content</param>
<param name="contentType">The content type of the file</param>
<returns>The just created and uploaded file object</returns>
public static DriveItem UploadFile(String driveId, String parentFolderId, DriveItem file, Stream content, String contentType) {
var jsonResponse = MicrosoftGraphHelper.MakePostRequestForString( String.Format("{0}drives/{1}/items/{2}/children",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri,
driveId,
parentFolderId),
file,
"application/json");
var uploadedFile = JsonConvert.DeserializeObject<DriveItem>(jsonResponse);
try {
MicrosoftGraphHelper.MakePutRequest(
String.Format("{0}drives/{1}/items/{2}/content",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri,
driveId,
http://uploadedFile.Id),
content,
contentType);
}
catch (ApplicationException ex) {
For whatever reason we come here ... the upload failed
and we need to delete the just created file
FilesHelper.DeleteFile(driveId, http://uploadedFile.Id);
And then we re-throw the exception
throw ex;
}
return (uploadedFile);
}

 

Notice that the helper method makes two requests against the Microsoft Graph API. In a real enterprise-level solution, you should provide some kind of logical transaction and a compensating transaction that will remove the file from the parent folder if the content upload fails for any reason. That’s why there is a try catch statement wrapping the content upload stage. So, in case of any failure during content upload, the DriveItem will be deleted.

 

The previous helper method, in case of any failure, uses another helper method to delete a file, which makes an HTTP DELETE request against the target file item. In Listing, you can see the helper method to delete a file in OneDrive for Business.

 

LISTING Helper method to delete a file in OneDrive for Business

<summary>
This method deletes a file in OneDrive for Business
</summary>
<param name="driveId">The ID of the target drive</param>
<param name="fileId">The ID of the target file</param>
public static void DeleteFile(String driveId, String fileId) { MicrosoftGraphHelper.MakeDeleteRequest(
String.Format("{0}drives/{1}/items/{2}",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri,
driveId,
fileId));
}

In Listing, you can see another option that you have to upload a new file, including its content, by making a unique HTTP request. If you make an HTTP PUT request—for example, providing the name of the target file instead of the ID in the target URL of the PUT request— the Graph API will create the file on the fly if it does not exist.

 

LISTING Revised helper method to create and upload a file into a target parent folder

<summary>
This method creates and uploads a file into a parent folder with a unique request
</summary>
<param name="driveId">The ID of the target drive</param>
<param name="parentFolderId">The ID of the parent folder</param>
<param name="file">The file object</param>
<param name="content">The binary stream of the file content</param>
<param name="contentType">The content type of the file</param>
<returns>The just created and uploaded file object</returns>
public static DriveItem UploadFileDirect(String driveId, String parentFolderId, DriveItem file, Stream content, String contentType) {
var jsonResponse = MicrosoftGraphHelper.MakePutRequestForString( String.Format("{0}drives/{1}/items/{2}/children/{3}/content",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri,
driveId,
parentFolderId,
http://file.Name),
content,
contentType);
var uploadedFile = JsonConvert.DeserializeObject<DriveItem>(jsonResponse); return (uploadedFile);
}
In the previous sample, notice the target relative URL, which has a format like the following:
/drives/<DriveID>/items/<FolderId>/children/<TargetFileName>/content

Sometimes it is also useful to update an existing file. Maybe you need to update the file properties, like the file name, or maybe you want to update the file content. Depending on what you need, you could have to make an HTTP POST request against the DriveItem object, or maybe you have to make an HTTP PUT request to overwrite the target file content.

 

In the former scenario, the coding is easy and intuitive. In the latter scenario, it is almost the same as the code sample illustrated in Listing. However, if you have the ID of the target file, it is safer to reference the file by ID than by using its file name because the ID already exists. In Listing, you can see a code excerpt of a helper method that updates the content of an existing file and another helper method that renames an existing file.

 

LISTING Helper methods to update name and/or content of an existing file

<summary>
This method renames an already existing file in OneDrive for Business
</summary>
<param name="driveId">The ID of the target drive</param>
<param name="fileId">The ID of the target file</param>
<param name="newFileName">The new file name</param>
<returns>The updated DriveItem corresponding to the renamed file</returns> public static DriveItem RenameFile(String driveId, String fileId, String newFileName) {
var jsonResponse = MicrosoftGraphHelper.MakePatchRequestForString( String.Format("{0}drives/{1}/items/{2}",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri,
driveId,
fileId),
new DriveItem {
Name = newFileName,
},
"application/json");
var updatedFile = JsonConvert.DeserializeObject<DriveItem>(jsonResponse); return (updatedFile);
}
<summary>
Uploads a new file content on top of an already existing file
</summary>
<param name="driveId">The ID of the target drive</param>
<param name="fileId">The ID of the target file</param>
<param name="content">The binary stream of the file content</param>
<param name="contentType">The content type of the file</param> public static void UpdateFileContent(String driveId, String fileId,
Stream content, String contentType) { MicrosoftGraphHelper.MakePutRequest(
String.Format("{0}drives/{1}/items/{2}/content",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri,
driveId,
fileId),
content,
contentType);
}

 

Updating the content of a file is straightforward. You need to make an HTTP PUT request against the content property of the target DriveItem, providing the new content as a Stream within the request body. You also have to provide a proper value for the request content type according to the content type of the new uploaded content. Last, notice that by using the PUT method, you can upload or update a file with a size up to 4 MB.

 

Renaming a file just requires you to provide a new DriveItem object within an HTTP PATCH request, configuring the Name property with the new name of the file. The same behavior can be used to rename a folder.

 

If you want to move a file or a folder instead of just renaming it, you can leverage the same approach. However, you will have to provide not only the name of the target DriveItem, but also the parentReference property, which represents the path of the parent for the current item. Changing the parent of an item implies moving that item under the new parent. In Listing, you can see a helper method to move a file (or a folder) from one parent to another.

 

LISTING Helper method to move a DriveItem within a OneDrive for Business drive

<summary>
This method moves one item from one parent folder to another
</summary>
<param name="driveId">The ID of the target drive</param>
<param name="driveItemId">The ID of the target file</param>
<param name="newItemName">The new name for the item in the target folder</param>
<param name="newParent">The name of the new target folder</param>
<returns>The moved DriveItem instance</returns>
public static DriveItem MoveDriveItem(String driveId, String driveItemId, String newItemName, String newParent) {
var jsonResponse = MicrosoftGraphHelper.MakePatchRequestForString( String.Format("{0}drives/{1}/items/{2}",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri,
driveId,
driveItemId),
new DriveItem {
Name = newItemName,
ParentReference = new ItemReference {
Path = $"/drive/root:/{newParent}"
}
},
"application/json");
var movedItem = JsonConvert.DeserializeObject<DriveItem>(jsonResponse); return (movedItem);
}

 

Permissions and sharing

One of the most powerful features of the cloud is the capability to share contents with colleagues or third parties easily. In this section, you will see topics related to managing permissions for files and folders, and you will learn how to share a file or a folder with someone.

 

Managing files permissions

A useful use case when handling contents of OneDrive for Business is browsing and managing permissions. Through the permissions navigation property, you can access and update item-level permissions where the target item is any DriveItem, like a file or a folder. In Listing  you can see a code excerpt to get the permissions for a DriveItem.

 

LISTING Helper method to get the permissions for a DriveItem within a OneDrive for Business drive

<summary>
This method returns a list of permissions for a specific DriveItem in OneDrive for
Business
</summary>
<param name="driveItemId">The ID of the DriveItem</param>
<returns>The list of permission for the target object</returns>
public static List<Permission> GetDriveItemPermissions(String driveItemId, String permissionId) {
var jsonResponse = MicrosoftGraphHelper.MakeGetRequestForString( String.Format("{0}me/drive/items/{1}/permissions",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri, driveItemId));
var permission = JsonConvert.DeserializeObject<PermissionList> (jsonResponse);
return (permission.Permissions);
}

Notice that the target in this scenario is the personal drive of the current user (/me/drive). At the time of this writing, you can only manage permissions of personal drives within the context of the current user. The result will be a collection of objects of custom type Permission, which is defined in Listing and which provides information about the target of the permission (GrantedTo), a SharingLink if any, and the permission Roles associated with the current permission.

 

LISTING The definition of the custom type Permission

public class Permission: BaseModel {
public IdentitySet GrantedTo;
public SharingInvitation Invitation;
public ItemReference InheritedFrom;
public SharingLink Link;
public List<String> Roles;
public String ShareId;
}

If you want to retrieve a single object of type Permission, you can do that by directly querying the object by ID. In Listing, there is a helper method that retrieves by ID a single permission for a target DriveItem.

 

LISTING A helper method that retrieves a single permission of a target DriveItem in OneDrive for Business

<summary>
This method returns a permission of a specific DriveItem in OneDrive for Business
</summary>
<param name="driveItemId">The ID of the DriveItem</param>
<param name="permissionId">The ID of the permission</param>
<returns>The permission object</returns>
public static Permission GetDriveItemPermission(String driveItemId, String permissionId) {
var jsonResponse = MicrosoftGraphHelper.MakeGetRequestForString( String.Format("{0}me/drive/items/{1}/permissions/{2}",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri,
driveItemId,
permissionId));
var permission = JsonConvert.DeserializeObject<Permission>(jsonResponse); return (permission);
}

Removing a permission is also a straightforward task. You need to make an HTTP DELETE request targeting the URL of the permission that you want to remove. In Listing there is a helper method to achieve this result.

 

LISTING A helper method that deletes a permission from a target DriveItem in OneDrive for Business

<summary>
This method removes a permission from a target DriveItem
</summary>
<param name="driveItemId">The ID of the DriveItem</param>
<param name="permissionId">The ID of the permission</param>
public static void RemoveDriveItemPermission(String driveItemId, String permissionId) {
MicrosoftGraphHelper.MakeDeleteRequest(
String.Format("{0}me/drive/items/{1}/permissions/{2}",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri
driveItemId,
permissionId));
}

Because this is the fourth blog in which you see this kind of behavior while consuming entity sets through the Microsoft Graph API, you shouldn’t be surprised by the syntax used in Listing

 

Sharing a file

Sharing a file, which means creating a new permission for a target object of type DriveItem, is another interesting use case that can be helpful in real business and enterprise-level solutions. To share a file, you need to invoke with an HTTP POST request the microsoft.graph.createLink (or createLink) action, targeting the URL of the DriveItem that you want to share.

 

Within the body of the HTTP request, you will have to provide a JSON object that defines the features of the sharing link that you want to create. Every sharing link has a type, which can be viewedit, or embed, and a scope, which can be organization or anonymous. In Listing , you can see another helper method that makes it easy to create a sharing link for a target DriveItem.

 

LISTING A helper method that creates a sharing link for a target DriveItem in OneDrive for Business

<summary>
This method creates a sharing link for a target DriveItem
</summary>
<param name="driveItemId">The ID of the DriveItem</param>
<param name="type">The type of the sharing link</param>
<param name="scope">The scope of the sharing link</param>
<returns>The just added permission for the sharing link</returns> public static Permission CreateSharingLink(String driveItemId,
SharingLinkType type, SharingLinkScope scope) {
var jsonResponse = MicrosoftGraphHelper.MakePostRequestForString( String.Format("{0}me/drive/items/{1}/createLink",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri,
driveItemId),
new {
@type = type.ToString().ToLower(),
@scope = scope.ToString().ToLower(),
},
"application/json"
);
var addedPermission = JsonConvert.DeserializeObject<Permission> (jsonResponse);
return (addedPermission);
}

Notice the use of the anonymous type for defining the type and scope of the sharing link. The result of the helper method will be a new object of custom type Permission that defines the newly added sharing permission for the target item.

 

Summary

In this blog, you saw how to browse drives, folders, and files stored within OneDrive for Business. You learned how to consume files’ content and files’ thumbnails. You saw how to upload new files and update existing files. Moreover, you learned how to move a file or a folder around a drive and how to delete an item, whether it is a file or a folder.

 

You also learned how to search for contents in OneDrive for Business. Last, you realized how to manage item-level permissions and how to share a file or a folder by creating a sharing link or by reading a list of permissions. Now you have the knowledge and the power to manage the contents of OneDrive for Business programmatically through the Microsoft Graph API.