Get Started with Microsoft Graph API
Guest by Dolga Rares Microsoft Student Partner at University College London
About Me
Hello! My name is Dolga Rares and I am a second year, computer science UCL student. I was passionate about programming and exact sciences from an early age. During high school, I participated in computer science contests and won prizes at regional stages. All these participations made me develop a passion for optimization. During my first year at university I gained experience in software development, and during this summer I built an API and Android library for connecting to a database.
Besides software development, I am interested in machine learning and mixed reality.
My LinkedIn: https://uk.linkedin.com/in/rares-dolga
Introduction:
Microsoft Graph API(v1.0) is perfect for beginners and for senior developers. It provides loads of information and functionality, which is well documented. Even if you have no idea about Graph API, from my experience, I think that this API is the perfect start.
I will guide you through learning about the graph, by building an Android application, that reads your emails and gets access to your Azure drive. It would be great if you have programming experience, but I will try to explain the steps for beginners. Note that you can use the Graph API with any other languages. The principles are the same, and Microsoft even provides SDKs for most common languages. Even though we can use C# for our app, I will show that Microsoft services can be used with other languages as well. Not being restricted just to other Microsoft tools.
Main goals:
· Learn about Microsoft Graph API
· Extract data from your Office 365 account.
· Learn how to integrate Microsoft technologies with other platforms such as Android
· Discover the Microsoft android SDK
· Learn what is a RESTful API
What you should have before starting:
· You should have a Microsoft Office 365 Account so that you can extract data and test the application. If you are a student get it free from https://products.office.com/en-us/student/office-in-education .
Or if you are not in university environment: https://products.office.com/en-gb/buy/office
· You should have Android Studio Installed: https://developer.android.com/studio/index.html
Steps that we will make:
1. What is Microsoft Graph API and how to use it?
2. I will explain the terminology for those who are new to these technologies
3. We will have a practical example of how to use the API in Android
o Register your app with Microsoft
o Create an Android project and add needed libraries and dependencies.
o Create the User Interface
o Create classes for getting an access token from Azure V2.0 endpoint and processing of data
o Read emails from your account and get your drive id
1. Conclusion
2. References
The complete code can be found here: https://github.com/raresdolga/Microsoft_Graph_API
1)What is Microsoft Graph API?
As the name says it is the API that lets you build an app in which users can extract data from Office 365. You can read emails, write emails, get contacts, get users, and take information from Azure Active directory. You can even access the calendar and OneNote. You can build all types of apps that make use of the data and possibilities the graph offers and Xamarin is a great tool for this. However, I will you show that the API is not only compatible with Microsoft tools, but with other platforms as well. Documentation offers support for different languages such as C# (for Xamarin), Angular JS, IOS, PHP, Python, and many others.
So how does it work?
It communicates with your application through URL requests. You ask for particular data that has an identifier. For example, in the URL:” https://graph.microsoft.com/v1.0/users?$top=5 “we ask for users and apply a filter, just first 5 from all of them. A web server parses the information sent over the internet and extracts data from Microsoft’s data centres.
You may wonder if this is secure. Well, it is perfectly protected, as the application must authenticate a user and get the access token. The access token s passed through the URL, so data is given back only if somebody otherized requested it.
A schematic view:
More information about what you can do with the Graph: https://developer.microsoft.com/en-us/graph/docs/concepts/overview
2) Terminology
What is a RESTful API?
API = Application Programming Interface. If you do not know what is an interface, imagine it like a bridge of communication between different software. API defines a set of functions that you can call in your program to get/send some information to an existing software. For example, in Microsoft Graph API you can get personal information by this request: “https://graph.microsoft.com/v1.0/me/”. You may wonder why this is an URL and not a function call in some language. In fact, this is the RESTful API, which works by sending requests over the internet to a server. The server processes the request you sent in the URL and sends back a response. Usually, or at least in Microsoft’s Graph API case, it is in JSON (JavaScript object notation) format.
What is an SDK and why do we use it?
SDK stands for Software Development Kit. It is a set of classes (In Java’s case) and methods defined for a specific purpose. Microsoft Android SDK has the purpose of communicating with the API. We use it because it provides an optimal implementation for sending and receiving information from the RESTful API. You can write your own classes for that, but you will need to construct the URL and make HTTP request from java code. Also, the response from the internet is not coming directly, it is a flow of information and you need to implement an asynchronous task. This means that you should start a process (transforming data) before information arrives. With the SDK you just make function calls which are easier to understand.
3) Let’s start the app:
1) How the app should look?
2) Create an Empty Application
· Create a new android project and name it how you want:
It should have the following characteristics:
Please remember to choose an Empty Activity as a starting point.
· Add libraries
Our project will depend on one library and on one SDK. The MSAL (Microsoft Authentication Library) library provides the authentication token from Azure v2 endpoint. The SDK will make our project easier to code and in an efficient way.
In the build.grandle (Module app) please add the following in the dependency:
compile ( 'com.microsoft.identity.client:msal:0.1.+' ) {
exclude group: 'com.android.support' , module: 'appcompat-v7'
}
// Include the SDK as a dependency
compile 'com.microsoft.graph:msgraph-sdk-android:1.3.2'
// Include the gson dependency
compile( 'com.google.code.gson:gson:2.3.1' )
Above dependencies add:
epository {
jcenter()
}
Also modify minifyEnable to true. ->For faster performance.
Now we need to add the necessary permissions into the Manifest File.
Add this code between package = “your package...” and “>”:
Above dependencies add:
repository {
jcenter()
}
Also modify minifyEnable to true. ->For faster performance.
Now we need to add the necessary permissions into the Manifest File.
Add this code between package = “your package...” and “>”:
xmlns: tools = https://schemas.android.com/tools
After the closing symbol “>” add the permissions you need:
<uses-permission android :name= "android.permission.INTERNET" />
<uses-permission android :name= "android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android :name= "android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android :name= "android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-sdk tools :overrideLibrary= "com.microsoft.identity.msal" />
Now we just have to allow our activity to open a browser window and add the redirect URL for getting the token.
Under the </activity> closing tag, add this code:
<activity
android :name= "com.microsoft.identity.client.BrowserTabActivity" >
<intent-filter>
<action android :name= "android.intent.action.VIEW" />
<category android :name= "android.intent.category.BROWSABLE" />
<category android :name= "android.intent.category.DEFAULT" />
<category android :name= "android.intent.category.LAUNCHER" />
<!--Add in your scheme/host from registered redirect URI-->
<data android :scheme= "msalc6eb0d0a-28fa-4424-8e6f-62b0f865181d"
android :host= "auth" />
</intent-filter>
</activity>
3) Register your app with Microsoft:
Go to: https://developer.microsoft.com/en-us/graph/quick-start. Chose Android and follow the steps. If you do not already have an account, please sign in.
After you registered your app, the Microsoft page should look like this:
You should go to: “app registration page” in step 3. There click “Add URI” near Custom Redirect URIs, and paste this: msalc6eb0d0a-28fa-4424-8e6f-62b0f865181d://auth
Notice that this is the redirect URL that we used in our Manifest file!
Click Save at the bottom of the page, and you are ready to code!
Create the User Interface:
Add this code in the XML file of the activity. (res->layout->activity_connect.xml – depends on the name of your activity). This code should be placed between Relative layouts tags):
<Button
android:text="Sign Out"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/sign_out"
android:layout_alignParentEnd="true" />
<ProgressBar
style="@android:style/Widget.Material.Light.ProgressBar.Large"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/progressBar"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true" />
<Button
android:text="Sign In"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/sign_in"
android:layout_alignParentTop="true"
android:layout_alignParentStart="true"
android:layout_marginStart="20dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/driveText"
android:text="sddsdsdsds"
android:layout_below="@+id/sign_in"
android:layout_alignStart="@+id/sign_in"
android:layout_marginTop="56dp" />
<Button
android:text="Me"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/info_me"
android:layout_alignParentTop="true"
android:layout_alignParentStart="true" />
<Button
android:text="Your Emails"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/read_email"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true" />
<WebView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/myEmail"
android:layout_below="@+id/sign_out"
android:layout_alignParentBottom="true"
android:layout_alignParentStart="true">
</WebView>
<Button
android:text="Next"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/next"
style="@style/Widget.AppCompat.Button.Small"
android:layout_alignParentTop="true"
android:layout_toStartOf="@+id/sign_out" />
<Button
android:text="Prev"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/prev"
style="@style/Widget.AppCompat.Button.Small"
android:layout_alignParentTop="true"
android:layout_toStartOf="@+id/next" />
Writing the code:
The code has a structure, one class for a specific task. The general overview of the app:
Authenticate the user.
Create Your Graph class, that represent an abstraction for Microsoft Graph
Construct the Activity class. This will represent the logic behind the User Interface and will handle the request to the Graph
Create a class that help you make your own requests, apart from those provided by the SDK.
Create the authentication class:
This class should have a single instance as we do not want to have different objects that ask for access to the account at the same time. We achieve this by making the constructor private and having an instance that creates an object if it does not exist or returns the existing one.
The purpose of this class is to obtain an authentication token from Azure. We can achieve this in two ways.
Silently – without the user action. In this case, we already asked for permissions before and they were accepted. Also, there was no change in the account of the user, like password change.
Interactively – we involve users in the process by asking for permissions (read email). This is done the sign in is done for the first time or something changed in the user credentials. Also, if the user signed out from the app previously, this type of authentication is needed.
Please put your package name at the top and replace the class name with the name you used. Preferably, use the same name as in my code. Also, do not forget to import the necessary packages.
public class Authentication {
//URL constants
private static String app_id = "c6eb0d0a-28fa-4424-8e6f-62b0f865181d" ;
private String basicUrl = "https://graph.microsoft.com/" ;
private String [] scope = { "Mail.ReadWrite" , "User.ReadBasic.All" , "Mail.Read" };
private String graph_endPoint = "https://graph.microsoft.com/v1.0/me" ;
// App variables and constants
private String TAG = "ConnectAct" ;
private volatile static Authentication single;
private static PublicClientApplication myApp = null;
private AuthenticationResult authR = null;
private ConnectAct connect_activ;
private Authentication() {
}
public static Authentication getInstance() {
if (single == null) {
synchronized (Authentication.class) {
if(single == null) {
single = new Authentication();
if (myApp == null) {
myApp = new PublicClientApplication(ConnectAct.getAppContext(), app_id);
}
}
}
}
return single;
}
public String getAccessToken(){
return authR.getAccessToken();
}
public PublicClientApplication getAppClient(){
return myApp;
}
public void aquire_acessTokenInteractive(Activity activity, final ConnectAct passedActivity){
connect_activ = passedActivity;
myApp.acquireToken(activity, scope, getInteractiveCallBack());
}
public void acquire_accessTokenSilent(User user, boolean forceRefresh, ConnectAct passedActivity) {
connect_activ = passedActivity;
myApp.acquireTokenSilentAsync(scope, user, null, forceRefresh, getSilentCallBack());
}
public void disconnect(){
List<User> users = null;
try {
users = myApp.getUsers();
if(users != null) {
for (User u : users) {
myApp.remove(u);
}
}
} catch(Exception e){
e.printStackTrace();
}
}
public AuthenticationCallback getSilentCallBack(){
return new AuthenticationCallback() {
@Override
public void onSuccess(AuthenticationResult result) {
authR = result;
Log.v(TAG, "This is the token got silently: " + authR.getAccessToken());
}
@Override
public void onError(MsalException exception) {
exception.printStackTrace();
}
@Override
public void onCancel() {
Log.d(TAG, "logged in canceled" );
}
};
}
public AuthenticationCallback getInteractiveCallBack(){
//anonymous class
return new AuthenticationCallback(){
@Override
public void onSuccess(AuthenticationResult result){
authR = result;
Log.v(TAG, "interactive token" +authR.getAccessToken());
connect_activ.onSuccessAuth();
}
@Override
public void onError(MsalException e){
connect_activ.onErrorAuth();
}
@Override
public void onCancel(){
connect_activ.onCancelAuth();
}
};
}
}
Create your graph class:
This graph must implement the interface provided by Microsoft SDK. The class must have a single instance and is unique in the sense that when the object is created it contains the authentication token provided by the above class. Remember that is an abstraction of the real graph. We use it to call methods implemented in the SDK as we would build a request to the API.
public class Graph implements IAuthenticationProvider {
private volatile static Graph singleInst;
private IGraphServiceClient graphServiceClient = null;
private Graph(){
}
public static Graph getInstance() {
if (singleInst == null) {
synchronized (Authentication.class) {
if(singleInst == null) {
singleInst = new Graph();
}
}
}
return singleInst;
}
public synchronized IGraphServiceClient getGraphServieClient(){
if(graphServiceClient == null){
IClientConfig myConfig = DefaultClientConfig.createWithAuthenticationProvider(this);
graphServiceClient = new GraphServiceClient.Builder().fromConfig(myConfig).buildClient();
}
return graphServiceClient;
}
@Override
public void authenticateRequest(IHttpRequest request) {
try {
request.addHeader( "Authorization" , "Bearer " + Authentication.getInstance().getAccessToken());
Log.v( "Connect" , "Request: " + request.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
}
Create your main activity:
This class handles what happens when a user clicks a button and interacts with the app. For example, when “YOUR EMAILS” button is clicked, a request that asks the Graph API for recently emails will be made.
Here I will show you a pattern between how the request URL looks and how the request made with the SDK looks:
The code for the class:
public class ConnectAct extends AppCompatActivity {
private String TAG = "ConnectAct" ;
Button signIn;
Button signOut;
Button me;
ProgressBar pb;
TextView driveT;
Button getEmails;
WebView emailsTo_read;
Button next, prev;
private static Context appContext = null;
private List<Message> globalEm = null;
private int iterator = 1;
IGraphServiceClient myConfigured_graph;
private AuthenticationResult authR;
private Authentication authManager = null;
private ConnectAct outerObj = this;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_connect);
appContext = this.getApplicationContext();
authManager = Authentication.getInstance();
myConfigured_graph = Graph.getInstance().getGraphServieClient();
//UI implementation
signIn = (Button) findViewById(R.id.sign_in);
signOut = (Button) findViewById(R.id.sign_out);
me = (Button) findViewById(R.id.info_me);
prev = (Button) findViewById(R.id.prev);
next = (Button) findViewById(R.id.next);
pb = (ProgressBar) findViewById(R.id.progressBar);
getEmails = (Button) findViewById(R.id.read_email);
emailsTo_read = (WebView) findViewById(R.id.myEmail);
driveT = (TextView) findViewById(R.id.driveText);
setGui();
signIn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
pb.setVisibility(View.VISIBLE);
try {
List<User> users = authManager.getAppClient().getUsers();
// try a silent acquire token
if (users != null && users.size() == 1) {
authManager.acquire_accessTokenSilent(users.get(0), false, outerObj);
updateGui();
} else {
authManager.aquire_acessTokenInteractive(outerObj, outerObj);
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
signOut.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
authManager.disconnect();
Toast.makeText(getBaseContext(), "You had signed out!" , Toast.LENGTH_LONG);
setGui();
}
});
me.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v){
pb.setVisibility(View.VISIBLE);
driveT.setVisibility(View.VISIBLE);
emailsTo_read.setVisibility(View.GONE);
getEmails.setVisibility(View.VISIBLE);
next.setVisibility(View.GONE);
prev.setVisibility(View.GONE);
myConfigured_graph.getMe()
.getDrive()
.buildRequest()
.get(new ICallback<Drive>() {
@Override
public void success(final Drive result) {
final String msg = "Found Drive, the ID = " + result.id;
driveT.setText(msg);
pb.setVisibility(View.GONE);
}
@Override
public void failure(ClientException e){
e.printStackTrace();
driveT.setText( "An error occurred, please try again" );
pb.setVisibility(View.GONE);
}
});
}
});
getEmails.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
pb.setVisibility(View.VISIBLE);
myConfigured_graph.getMe().getMessages().buildRequest().get(new ICallback<IMessageCollectionPage>() {
@Override
public void success(final IMessageCollectionPage result) {
emailsTo_read.setVisibility(View.VISIBLE);
driveT.setVisibility(View.GONE);
getEmails.setVisibility(View.GONE);List <Message> emails = result.getCurrentPage();
setEmailList(emails);
navigateEmails(0);
pb.setVisibility(View.GONE);
next.setVisibility(View.VISIBLE);
prev.setVisibility(View.VISIBLE);
}
@Override
public void failure(ClientException e) {
e.printStackTrace();
Toast.makeText(getAppContext(), "An error occurred, please try again" ,Toast.LENGTH_LONG);
pb.setVisibility(View.GONE);
}
});
}
});
next.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v){
navigateEmails(++iterator);
}
});
prev.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v){
navigateEmails(--iterator);
}
});
}
/* Handles the redirect from the System Browser */
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
authManager.getAppClient().handleInteractiveRequestRedirect(requestCode, resultCode, data);
}
public Activity getOutActivity(){
return this;
}
public static Context getAppContext(){
return appContext;
}
public void onSuccessAuth(){
updateGui();
}
public void onErrorAuth(){
Toast.makeText(getBaseContext(), "Sorry Something went wrong" , Toast.LENGTH_LONG);
}
public void onCancelAuth(){
}
private void updateGui(){
pb.setVisibility(View.INVISIBLE);
me.setVisibility(View.VISIBLE);
signIn.setVisibility(View.GONE);
me.setVisibility(View.VISIBLE);
getEmails.setVisibility(View.VISIBLE);
}
private void setGui(){
iterator = 1;
driveT.setText(null);
signIn.setVisibility(View.VISIBLE);
pb.setVisibility(View.GONE);
me.setVisibility(View.GONE);
getEmails.setVisibility(View.GONE);
driveT.setVisibility(View.GONE);
next.setVisibility(View.GONE);
prev.setVisibility(View.GONE);
emailsTo_read.setVisibility(View.GONE);
globalEm = null;
}
private void setEmailList(List<Message> messages){
globalEm = messages;
}
private void navigateEmails( int i){
if(i >= globalEm.size() || i < 0){
Toast.makeText(getBaseContext(), "No more emails to find" , Toast.LENGTH_LONG);
Log.v(TAG,i + "" );
if(iterator >= globalEm.size())
iterator--;
else<br> iterator ++;
}
else {
Message m = globalEm.get(i);
emailsTo_read.loadData(m.body.content.toString(), "text/html; charset=utf-8" , "UTF-8" );
Log.v(TAG, i + "" );
}
}
}
If you ask yourself what is with the lambda functions or what they are I will quickly explain.
We basically construct a class in a method. Instead of passing an object of that class we call new on a class that we
define in the same place. For example, in this section:
myConfigured_graph.getMe().getMessages().buildRequest().get(new ICallback<IMessageCollectionPage>() {
@Override
public void success(final IMessageCollectionPage result) {
List <Message> emails = result.getCurrentPage();
setEmailList(emails);
}
@Override
public void failure(ClientException e) {
e.printStackTrace();
Toast.makeText(getAppContext(),"An error occurred, please try again",Toast.LENGTH_LONG);
pb.setVisibility(View.GONE);
} });
Here we implement the request with Microsoft SDK. We must manage the situation when the HTTP request fails and succeed. In the last case, we need to handle the information.
This is the most important part as here we call the Microsoft Graph API to give you the information you asked.
Create your own requests:
This class purpose is to implement the functionality from the superclass in SDK that handles requests. You should override a method that passes your own request. For example, you pass the HTTP for a request that is not already implemented in the SDK. The HTTP should be a string.
public class YourOwnRequest extends BaseRequest {
public YourOwnRequest(final String requestUrl, final IBaseClient client, final java.util.List<Option> requestOptions){
super(requestUrl,client,requestOptions,Void.class);
}
public String get() throws ClientException {
return send(HttpMethod.GET, null);
}
}
An example of the call would be:
YourOwnRequest request = new YourOwnRequest( "https://graph.microsoft.com/v1.0/custom" , myConfigured_graph, new ArrayList<Option>());
request.get();
The Final App
Conclusion:
Now I hope that you understand the principles of Microsoft Graph API, by making this practical example. As you can see it is easy to use and you can build a lot of functionality with it. Now you can build more complicated applications that make use of the other functionalities that are provided.
Hope you understand and enjoy!
The complete code can be found here: https://github.com/raresdolga/Microsoft_Graph_API
References:
Microsoft Graph API: https://developer.microsoft.com/en-us/graph/docs/concepts/overview
Microsoft Android SDK: https://github.com/microsoftgraph/msgraph-sdk-android
Microsoft Azure V2.0 Endpoint: /en-us/azure/active-directory/develop/active-directory-appmodel-v2-overview