SAPUI5 Integration with Outlook REST APIs
Reference: https://docs.microsoft.com/en-us/outlook/rest/javascript-tutorial
The purpose of this guide is to walk through the process of creating a simple single-page SAPUI5 app that retrieves messages from Office 365 or Outlook.com. The source code in this repository is what you should end up with if you follow the steps outlined here.
We will use the Microsoft Graph to access Outlook mail. Microsoft recommends using the Microsoft Graph to access Outlook mail, calendar, and contacts. You should use the Outlook APIs directly (via https://outlook.office.com/api
) only if you require a feature that is not available on the Graph endpoints.
Create the app
Create an empty directory where you want to create your app. For the purposes of this guide I will assume the name of the directory is sapui5-outlook-app
, but feel free to use any name you like.
Since our app will be client-side JavaScript-based, we need an HTTP server to serve our HTML and JavaScript files. Feel free to use your favorite HTTP server for this guide. To make things simple, this guide will include steps to use http-server to quickly create a development web server on the command line.
Setup the web server
Before you begin you’ll need to have Node.js installed.
- Open a command prompt and set the current directory to the
sapui5-outlook-app
directory. - Enter the following command to install
http-server
:npm install http-server -g
- Enter the following command to start the server:
http-server
- Open a browser and browse to
http://localhost:8080
. If the server is working correctly, you should see something like the following:
This confirms that the server is working, so we’re ready to start coding. You can leave the server running. If you do stop it at some point, be sure to restart it using the http-server
command in the sapui5-outlook-app
directory.
Register the app
Head over to the Application Registration Portal to quickly get an application ID.
- Using the Sign in link, sign in with either your Microsoft account (Outlook.com), or your work or school account (Office 365).
- Click the Add an app button. Enter
sapui5-outlook-app
for the name and click Create application. - Locate the Platforms section, and click Add Platform. Choose Web, then enter
http://localhost:8080
under Redirect URIs. Make sure that there is a check next to ALlow Implicit Flow. - Click Save to complete the registration. Copy the Application Id and save it. We’ll need it soon.
Thus you will have an application id and redirect url which we’ll use in our index.html file to redirect to outlook login page for this app.
Designing the app
Our app will be very simple. When a user visits the site, they will see a link to log in and view their email. Clicking that link will take them to the Azure login page where they can login with their Office 365 or Outlook.com account and grant access to our app. Finally, they will be redirected back to our app, which will display a list of the most recent email in the user’s inbox.
Let’s begin by adding an HTML page to our app. Using your favorite editor, create a file named index.html
in the sapui5-outlook-app
directory, and add the code in this repository.
Description
Here is the description of important code extracts-
Implementing OAuth2
Our goal in this section is to make the link on our home page initiate the OAuth2 Implicit Grant flow with Azure AD. This flow is designed for SPA apps, and will allow us to get an access token without needing to exchange a client secret or do extra requests to a token issuing endpoint.
The implicit flow is pretty simple. The user browses to an authentication link hosted by Azure, where they sign in and grant access to our app. Azure redirects back to the app with the result. Assuming the user successfully signed in and granted consent, the result contains an access token and ID token.
Our first step is to generate the authentication link.
// App configuration
var authEndpoint = 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize?';
var redirectUri = 'http://localhost:8080';
var appId = 'YOUR APP ID HERE';
var scopes = 'openid profile User.Read Mail.Read';
Replace the YOUR APP ID HERE
value with the application ID you generated as part of the app registration process.
The buildAuthUrl
function
function buildAuthUrl() {
// Generate random values for state and nonce
sessionStorage.authState = guid();
sessionStorage.authNonce = guid();
var authParams = {
response_type: 'id_token token',
client_id: appId,
redirect_uri: redirectUri,
scope: scopes,
state: sessionStorage.authState,
nonce: sessionStorage.authNonce,
response_mode: 'fragment'
};
url = authEndpoint + $.param(authParams);
window.location.href = url;
}
The guid
function
function guid() {
var buf = new Uint16Array(8);
cryptObj.getRandomValues(buf);
function s4(num) {
var ret = num.toString(16);
while (ret.length < 4) {
ret = '0' + ret;
}
return ret;
}
return s4(buf[0]) + s4(buf[1]) + '-' + s4(buf[2]) + '-' + s4(buf[3]) + '-' +
s4(buf[4]) + '-' + s4(buf[5]) + s4(buf[6]) + s4(buf[7]);
}
Now update the renderWelcome
function to call the buildAuthUrl
function and set the href
attribute of the sign in button to the result. We are creating a demo view of tokens in renderWelcome
when login is successful.
function renderWelcome(isAuthed) {
if (isAuthed) {
var app = new sap.m.App();
var oPage = new sap.m.Page({
title: "Outlook Integration With SAPUI5",
content: [
new sap.m.MessageToast.show("Successfully Logged In!", {
duration: 1000,
my: "center center",
at: "center center",
}),
new sap.m.VBox({
items:[
new sap.m.MessageStrip({
text: "Access Token",
type: "Information"
}),
new sap.m.TextArea({
value: myAccessToken,
growing: true,
width: "100%",
editable: false
}),
new sap.m.MessageStrip({
text: "ID Token",
type: "Information"
}),
new sap.m.TextArea({
value: myTokenID,
growing: true,
width: "100%",
editable: false
}),
new sap.m.MessageStrip({
text: tokenExpire,
type: "Warning"
})
]
})
],
headerContent:[
new sap.m.Button({
text: "My Inbox",
type: "Accept",
press: function(){
var emailAddress = "YOUR MAIL ID";
//hardcoding it if you want code to get current user's email please refer to outlook page reference
getUserInboxMessages(emailAddress);
function getAccessToken(callback) {
var now = new Date().getTime();
var isExpired = now > parseInt(sessionStorage.tokenExpires);
// Do we have a token already?
if (sessionStorage.accessToken && !isExpired) {
// Just return what we have
if (callback) {
callback(sessionStorage.accessToken);
}
}
else {
console.log("Token Expired! Re-login required!");
}
}
// function getUserEmailAddress(callback)
function getUserInboxMessages(emailAddress, callback) {
getAccessToken(function(accessToken) {
if (accessToken) {
// Create a Graph client
var client = MicrosoftGraph.Client.init({
authProvider: (done) => {
// Just return the token
done(null, accessToken);
}
});
// Get the 10 newest messages
client
.api('/me/mailfolders/inbox/messages')
.header('X-AnchorMailbox', emailAddress)
.top(10)
.select('subject,from,receivedDateTime,bodyPreview')
.orderby('receivedDateTime DESC')
.get((err, res) => {
if (err) {
callback(null, err);
}
else {
var oList = new sap.m.List({
});
var oMail = new sap.m.CustomListItem({
content:[
new sap.m.HBox({
items:[
new sap.m.CheckBox({
}),
new sap.m.VBox({
items:[
new sap.m.Label({
text: "{from/emailAddress/name}",
width: "100%"
}).addStyleClass("profileName"),
new sap.m.Label({
text: "{receivedDateTime}"
}).addStyleClass("designation")
]
}),
new sap.m.HBox({
items:[
new sap.m.VBox({
items:[
new sap.m.Label({
text: "{subject}",
design: "Bold",
width: "40%"
}).addStyleClass("subjectLabel"),
new sap.m.Label({
text: "{bodyPreview}",
width: "40%"
}).addStyleClass("shortText")
]
})
]
})
]
})
]
});
var oJSONDataModel;
oJSONDataModel= new sap.ui.model.json.JSONModel();
oJSONDataModel.setData(res.value);
oList.setModel(oJSONDataModel);
oList.bindItems('/',oMail);
var oInboxPage = new sap.m.Page({
title: "Outlook Integration With SAPUI5",
content:[
oList
]
});
app.addPage(oInboxPage);
app.to(oInboxPage);
}
});
}
else {
var error = {
responseText: 'Could not retrieve access token' };
callback(null, error);
}
});
}
}
}),
new sap.m.Button({
icon: "sap-icon://log",
type: "Reject"
})
]
});
app.addPage(oPage);
app.placeAt("content");
}
else {
buildAuthUrl();
}
}
The handleTokenResponse
function
function handleTokenResponse(hash) {
// clear tokens
sessionStorage.removeItem('accessToken');
sessionStorage.removeItem('idToken');
var tokenresponse = parseHashParams(hash);
// Check that state is what we sent in sign in request
if (tokenresponse.state != sessionStorage.authState) {
sessionStorage.removeItem('authState');
sessionStorage.removeItem('authNonce');
// Report error
window.location.hash = '#error=Invalid+state&error_description=The+state+in+the+authorization+response+did+not+match+the+expected+value.+Please+try+signing+in+again.';
return;
}
sessionStorage.authState = '';
sessionStorage.accessToken = tokenresponse.access_token;
// Get the number of seconds the token is valid for,
// Subract 5 minutes (300 sec) to account for differences in clock settings
// Convert to milliseconds
var expiresin = (parseInt(tokenresponse.expires_in) - 300) * 1000;
var now = new Date();
var expireDate = new Date(now.getTime() + expiresin);
sessionStorage.tokenExpires = expireDate.getTime();
sessionStorage.idToken = tokenresponse.id_token;
// Redirect to home page
window.location.href = redirectUri;
}
This function clears any cached tokens, then parses the token response. It checks that the state
parameter matches what we originally sent, calculates the expiration time for the token, then saves the tokens in the session. Before we test this code, we need to implement the parseHashParams
function. Add this function after the guid
function.
The parseHashParams
function
function parseHashParams(hash) {
var params = hash.slice(1).split('&');
var paramarray = {
};
params.forEach(function(param) {
param = param.split('=');
paramarray[param[0]] = param[1];
});
return paramarray;
}
Using the Mail API
Now that we can get an access token, we’re in a good position to do something with the Mail API. Let’s start by downloading the Microsoft Graph JavaScript Client Library. We already added a reference to it in the HTML, so all we need to do is download it. Go to https://github.com/microsoftgraph/msgraph-sdk-javascript and download the source. Copy the graph-js-sdk-web.js
into the sapui5-outlook-app
directory.
The getAccessToken
function
function getAccessToken(callback) {
var now = new Date().getTime();
var isExpired = now > parseInt(sessionStorage.tokenExpires);
// Do we have a token already?
if (sessionStorage.accessToken && !isExpired) {
// Just return what we have
if (callback) {
callback(sessionStorage.accessToken);
}
}
else {
console.log("Token Expired! Re-login required!");
}
}
We’ll use this method anytime we need to use the access token. The function checks if the token is about to expire. If it isn’t, then it just returns the token from the session. If it is, then it refreshes the token.
The getUserInboxMessages
function
function getUserInboxMessages(emailAddress, callback) {
getAccessToken(function(accessToken) {
if (accessToken) {
// Create a Graph client
var client = MicrosoftGraph.Client.init({
authProvider: (done) => {
// Just return the token
done(null, accessToken);
}
});
// Get the 10 newest messages
client
.api('/me/mailfolders/inbox/messages')
.header('X-AnchorMailbox', emailAddress)
.top(10)
.select('subject,from,receivedDateTime,bodyPreview')
.orderby('receivedDateTime DESC')
.get((err, res) => {
if (err) {
callback(null, err);
}
else {
var oList = new sap.m.List({
});
var oMail = new sap.m.CustomListItem({
content:[
new sap.m.HBox({
items:[
new sap.m.CheckBox({
}),
new sap.m.VBox({
items:[
new sap.m.Label({
text: "{from/emailAddress/name}",
width: "100%"
}).addStyleClass("profileName"),
new sap.m.Label({
text: "{receivedDateTime}"
}).addStyleClass("designation")
]
}),
new sap.m.HBox({
items:[
new sap.m.VBox({
items:[
new sap.m.Label({
text: "{subject}",
design: "Bold",
width: "40%"
}).addStyleClass("subjectLabel"),
new sap.m.Label({
text: "{bodyPreview}",
width: "40%"
}).addStyleClass("shortText")
]
})
]
})
]
})
]
});
var oJSONDataModel;
oJSONDataModel= new sap.ui.model.json.JSONModel();
oJSONDataModel.setData(res.value);
oList.setModel(oJSONDataModel);
oList.bindItems('/',oMail);
var oInboxPage = new sap.m.Page({
title: "Outlook Integration With SAPUI5",
content:[
oList
]
});
app.addPage(oInboxPage);
app.to(oInboxPage);
}
});
}
else {
var error = {
responseText: 'Could not retrieve access token' };
callback(null, error);
}
});
}
}
}),
new sap.m.Button({
icon: "sap-icon://log",
type: "Reject"
})
]
});
app.addPage(oPage);
app.placeAt("content");
}
else {
buildAuthUrl();
}
}
This uses the Graph client that we used in getUserEmailAddress
, but this time the call is a little more complicated. It uses query parameters to control the results we get back.
- Using the
top
method limits the results to the first 10 messages. - Using the
select
method controls which fields are returned for each message. In this case we only request thesubject
,from
,receivedDateTime
, andbodyPreview
fields. - Using the
orderby
method sorts the results by thereceivedDateTime
field in descending order (newest first).
After receiving the response it is further used for data binding in our SAPUI5 mailbox template.
oJSONDataModel.setData(res.value);
Reference: https://docs.microsoft.com/en-us/outlook/rest/javascript-tutorial
Code Repository: https://github.com/3bhu1/3bhu1.github.io/