Introduction:
- Are you looking for a way to connect Microsoft Teams with your Liferay portal? If so, you’ve come to the right place.
- Microsoft Teams is an incredibly useful platform for joining meetings and keeping track of events.
- It is powered by Microsoft Graph APIs, which allow users to create meetings and events with ease.
- The Microsoft Graph APIs provide a comprehensive range of capabilities to manage and control the events, such as creating, updating, and deleting events.
- They also allow for customization of the events, such as setting up reminders, adding notes, and inviting participants.
- M365 and Microsoft Graph are required to use Microsoft APIs.
- M365’s admin portal allows us to modify permissions and uses.
Prerequisites:
- Liferay (Any version)
- An Microsoft account
- Knowledge of API
- Knowledge of HTTP request and response.
Environmental Requirements:
- Browser
- Liferay
- Eclipse ID
- Gradle
Follow these steps to create a Microsoft admin account and assign permissions:
- The first step is to create a Microsoft developer account. For that go to “https://developer.microsoft.com/en-us/microsoft-365/dev-program“.
![Create Microsoft developer account at link | Microsoft Teams integration with Liferay Create Microsoft developer account at link](https://www.ignek.com/wp-content/uploads/2024/03/Create-Microsoft-developer-account-at-link-1024x582.webp)
- Here you can see the “join now” button. When you click on it, it will redirect you to the Microsoft sign-in page. Just sign in with your Microsoft account.
- You can see the page below. Here you get a 90 day free developer subscription for testing purposes.
![Explore Microsoft sign in for developers | Microsoft Teams integration with Liferay Explore Microsoft sign-in for developers](https://www.ignek.com/wp-content/uploads/2024/03/Explore-Microsoft-sign-in-for-developers-1024x576.webp)
- Here you can find your admin account email address. Just copy it and save it.
- Now go to “https://admin.microsoft.com/” and sign in with the admin email address. If you don’t have the password then you can reset it.
- After signing in, you can see the below message. So, click on “Next” to set up authentication, or you can just skip for now by tapping “Ask later”.
![Sign in to view important message | Microsoft Teams integration with Liferay Sign in to view important message](https://www.ignek.com/wp-content/uploads/2024/03/Sign-in-to-view-important-message.webp)
- Now the admin dashboard looks like the below image.
![View the updated admin dashboard image | Microsoft Teams integration with Liferay View the updated admin dashboard image](https://www.ignek.com/wp-content/uploads/2024/03/View-the-updated-admin-dashboard-image-1024x585.webp)
- Now go to the new tab and hit “https://azure.microsoft.com/“.
![Azure home page | Microsoft Teams integration with Liferay Azure home page](https://www.ignek.com/wp-content/uploads/2024/03/Azure-home-page-1024x585.webp)
- Now sign in using the admin email address. After signing in you can see the home page like below.
![Azure admin panel | Microsoft Teams integration with Liferay Azure admin panel](https://www.ignek.com/wp-content/uploads/2024/03/Azure-admin-panel-1024x582.webp)
- Now go to “Manage Azure Active Directory” using the “View” button.
![Access Azure AD | Microsoft Teams integration with Liferay Access Azure AD](https://www.ignek.com/wp-content/uploads/2024/03/Access-Azure-AD-1024x555.webp)
- Now go to “App registrations”. And create an app using “New registration”.
![App registrations panel in Azure | Microsoft Teams integration with Liferay App registrations panel in Azure](https://www.ignek.com/wp-content/uploads/2024/03/App-registrations-panel-in-Azure-1024x500.webp)
- Now enter your app name and select Supported Account types as “Accounts in any organizational directory (Any Azure AD directory – Multitenant) and personal Microsoft accounts (e.g. Skype, Xbox)” and leave Redirect URI as blank.
![Create app name and account types | Microsoft Teams integration with Liferay Create app name and account types](https://www.ignek.com/wp-content/uploads/2024/03/Create-app-name-and-account-types-1024x586.webp)
- After successful registration, you can see the below page.
![View page after registration success | Microsoft Teams integration with Liferay View page after registration success](https://www.ignek.com/wp-content/uploads/2024/03/View-page-after-registration-success-1024x583.webp)
- The next step is to collect some information and store it: ClientId and TenantId. And go to “Add a certificate or secret”.
![Collect ClientId and TenantId information | Microsoft Teams integration with Liferay Collect ClientId and TenantId information](https://www.ignek.com/wp-content/uploads/2024/03/Collect-ClientId-and-TenantId-information-1024x583.webp)
- Now create a client secret using the “New client secret” button.
![Generate client secret via new button | Microsoft Teams integration with Liferay Generate client secret via new button](https://www.ignek.com/wp-content/uploads/2024/03/Generate-client-secret-via-new-button-1024x555.webp)
- Fill in details as per needs. And click on add.
![Details related to client secret | Microsoft Teams integration with Liferay Details related to client secret](https://www.ignek.com/wp-content/uploads/2024/03/Details-related-to-client-secret-1024x510.webp)
- Now store the “value” (client secret) of the created client secret because we can’t see it after that.
- Now go to API permissions from the side panel.
![Access API permissions | Microsoft Teams integration with Liferay Access API permissions](https://www.ignek.com/wp-content/uploads/2024/03/Access-API-permissions-1024x503.webp)
- Click on “Add a permission”.
![Request permissions for APIs | Microsoft Teams integration with Liferay Request permissions for APIs](https://www.ignek.com/wp-content/uploads/2024/03/Request-permissions-for-APIs-1024x501.webp)
- Click on “Microsoft Graph” and after that, click on “Delegated permissions”.
![Delegated permissions in Microsoft Graph | Microsoft Teams integration with Liferay Delegated permissions in Microsoft Graph](https://www.ignek.com/wp-content/uploads/2024/03/Delegated-permissions-in-Microsoft-Graph-1024x503.webp)
- Search “calendars” and select all the checkboxes. And click on “Add permissions”.
![Add permissions related to calendars | Microsoft Teams integration with Liferay Add permissions related to calendars](https://www.ignek.com/wp-content/uploads/2024/03/Add-permissions-related-to-calendars-1024x500.webp)
- Like that, add all permissions as mentioned in the below list.
- Delegated
- Calendars.Read
- Calendars.Read.Shared
- Calendars.ReadBasic
- Calendars.ReadWrite
- Calendars.ReadWrite.Shared
- Mail.Read
- Mail.Read.Shared
- Mail.ReadBasic
- Mail.ReadBasic.Shared
- Mail.ReadWrite
- Mail.ReadWrite.Shared
- Mail.Send
- Mail.Send.Shared
- MailboxSettings.Read
- MailboxSettings.ReadWrite
- OnlineMeetings.Read
- OnlineMeetings.ReadWrite
- User.Read
- Application
- Calendars.Read
- Calendars.ReadBasic.All
- Calendars.ReadWrite
- Mail.Read
- Mail.ReadBasic.All
- Mail.ReadWrite
- Mail.Send
- MailboxSettings.Read
- MailboxSettings.ReadWrite
- OnlineMeetings.Read.All
- OnlineMeetings.ReadWrite.All
- Delegated
![List of permissions for calendar and email | Microsoft Teams integration with Liferay List of permissions for calendar and email](https://www.ignek.com/wp-content/uploads/2024/03/List-of-permissions-for-calendar-and-email-1024x502.webp)
- Then click on “Grant Admin consent for MSFT” and click on “Yes”.
![Grant MSFT admin consent | Microsoft Teams integration with Liferay Grant MSFT admin consent](https://www.ignek.com/wp-content/uploads/2024/03/Grant-MSFT-admin-consent-1024x500.webp)
- Go back one step and you will see the screen below.
![MSFT app registration screen | Microsoft Teams integration with Liferay MSFT app registration screen](https://www.ignek.com/wp-content/uploads/2024/03/MSFT-app-registration-screen-1024x503.webp)
- From the side panel, select “Users”.
![Azure AD users list | Microsoft Teams integration with Liferay Azure AD users list](https://www.ignek.com/wp-content/uploads/2024/03/Azure-AD-users-list-1024x470.webp)
- Here, click on your name. In my case “Dixit Baravaliya”.
![Detailed view of admin user | Microsoft Teams integration with Liferay Detailed view of admin user](https://www.ignek.com/wp-content/uploads/2024/03/Detailed-view-of-admin-user-1024x501.webp)
- Here you can find “Object ID”. Just copy it and store it.
- Now in the new tab go to “https://admin.microsoft.com/” and sign in with the admin email address.
![Microsoft teams admin center | Microsoft Teams integration with Liferay Microsoft teams admin center](https://www.ignek.com/wp-content/uploads/2024/03/Microsoft-teams-admin-center-1024x501.webp)
- Go to Meetings -> Meeting Policies from the side panel.
![Manage Meeting Policies screen | Microsoft Teams integration with Liferay Manage Meeting Policies screen](https://www.ignek.com/wp-content/uploads/2024/03/Manage-Meeting-Policies-screen-1024x502.webp)
- Choose “Global (Org-wide default)”.
![Global Org wide default detailed screen | Microsoft Teams integration with Liferay Global Org-wide default detailed screen](https://www.ignek.com/wp-content/uploads/2024/03/Global-Org-wide-default-detailed-screen-1024x502.webp)
- Then, in Meeting Join & Lobby -> Who can bypass the lobby, select Everyone. In this way, everyone can join the meeting even if the organizer hasn’t given his or her permission.
![Managing everyone can bypass lobby Meeting Policies | Microsoft Teams integration with Liferay Managing everyone can bypass lobby Meeting Policies](https://www.ignek.com/wp-content/uploads/2024/03/Managing-everyone-can-bypass-lobby-Meeting-Policies-1024x502.webp)
- The APIs of Microsoft Graph are ready for use.
Liferay Rest Builder
Here I’m creating one rest builder to perform create, update, get, and delete operations on Microsoft events.Follow these steps to create a Liferay rest builder and call Microsoft APIs:- First, Create Liferay Workspace and a rest builder (If you don’t know how to create a rest builder then take reference from here).
- Here I have created “microsoft-teams-rest” named Rest Builder.
![Liferay Workspace and Microsoft Teams Rest builder | Microsoft Teams integration with Liferay Liferay Workspace and Microsoft Teams Rest builder](https://www.ignek.com/wp-content/uploads/2024/03/Liferay-Workspace-and-Microsoft-Teams-Rest-builder-1024x583.webp)
- Go to the microsoft-teams-rest-impl -> rest-openapi.yaml file and write the below text.
// rest-openapi.yaml
info:
description: "MicrosoftTeamsRest REST API"
license:
name: "Apache 2.0"
url: "http://www.apache.org/licenses/LICENSE-2.0.html"
title: "MicrosoftTeamsRest"
version: v1.0
openapi: 3.0.1
paths:
"/update-microsoft-teams-link":
post:
operationId: updateMicrosoftTeamsLink
requestBody:
content:
application/json:
schema:
$ref: "#/components/schemas/Teams"
application/xml:
schema:
$ref: "#/components/schemas/Teams"
responses:
200:
content:
application/json:
schema:
$ref: "#/components/schemas/Teams"
application/xml:
schema:
$ref: "#/components/schemas/Teams"
description: ""
tags: ["Teams"]
"/get-microsoft-teams-link":
get:
operationId: getMicrosoftTeamsLink
parameters:
- in: query
name: eventId
required: true
schema:
type: string
responses:
200:
content:
application/json:
schema:
$ref: "#/components/schemas/Teams"
application/xml:
schema:
$ref: "#/components/schemas/Teams"
description: ""
404:
description: "Teams not found"
500:
description: "Internal server error"
tags: ["Teams"]
"/delete-microsoft-teams-link":
delete:
operationId: deleteMicrosoftTeamsLink
parameters:
- in: query
name: eventId
required: true
schema:
type: string
responses:
200:
content:
application/json:
schema:
$ref: "#/components/schemas/Teams"
application/xml:
schema:
$ref: "#/components/schemas/Teams"
description: ""
tags: ["Teams"]
components:
schemas:
Teams:
description: adding or updating microsoft teams link
properties:
eventId:
description: The eventId.
type: string
title:
description: The title.
type: string
description:
description: The description.
type: string
setDate:
description: The setDate in "yyyy-MM-dd HH:mm" format.
type: string
endDate:
description: The endDate in "yyyy-MM-dd HH:mm" format.
type: string
timeZone:
description: The timeZone.
type: string
location:
description: The location.
type: string
teamsLink:
description: The teamsLink.
type: string
emailAddress:
description: The emailAddress.
type: array
items:
type: string
status:
$ref: "#/components/schemas/Status"
Status:
properties:
statusMessage:
type: string
statusCode:
type: integer
- Now, build rest of it, and you will see the below files. Open the “TeamsResourceImpl” file.
![Exploring TeamsResourceImpl file in depth | Microsoft Teams integration with Liferay Exploring TeamsResourceImpl file in-depth](https://www.ignek.com/wp-content/uploads/2024/03/Exploring-TeamsResourceImpl-file-in-depth-1024x584.webp)
- Add methods in the “TeamsResourceImpl” class file (updateMicrosoftTeamsLink, getMicrosoftTeamsLink, and deleteMicrosoftTeamsLink).
![New functions in TeamsResourceImpl class | Microsoft Teams integration with Liferay New functions in TeamsResourceImpl class](https://www.ignek.com/wp-content/uploads/2024/03/New-functions-in-TeamsResourceImpl-class-1024x584.webp)
- Now go to modules -> microsoft-teams-rest -> microsoft-teams-rest-impl -> build.gradle. And replace with below lines.
// build.gradle
dependencies {
compile project(":modules:microsoft-teams-rest:microsoft-teams-rest-api")
compileOnly group: "com.liferay.portal", name: "release.portal.api"
compileOnly group: "javax.annotation", name: "javax.annotation-api", version: "1.3.2"
compileOnly group: "javax.validation", name: "validation-api", version: "2.0.1.Final"
compileOnly group: "javax.ws.rs", name: "javax.ws.rs-api"
compile group: 'org.apache.httpcomponents', name: 'httpclient'
compile group: 'org.apache.httpcomponents', name: 'httpcore'
implementation group: 'com.google.code.gson', name: 'gson', version: '2.9.0'
restBuilder group: "com.liferay", name: "com.liferay.portal.tools.rest.builder", version: "1.0.222"
}
group = "microsoft.teams.rest"
- Now go to modules -> microsoft-teams-rest -> microsoft-teams-rest-impl -> bnd.bnd (source) file and replace with below lines.
// bnd.bnd
Bundle-Name: microsoft-teams-rest-impl
Bundle-SymbolicName: microsoft.teams.rest.impl
Bundle-Version: 1.0.0
-privatepackage: \
com.google.gson.*
- Now, create a package “com.ignek.microsoft.teams” in the “microsoft-teams-rest-impl/src/main/java” folder.
![Package creation in designated folder | Microsoft Teams integration with Liferay Package creation in designated folder](https://www.ignek.com/wp-content/uploads/2024/03/Package-creation-in-designated-folder-1024x583.webp)
- Now, add an interface “MicrosoftTeamsService” and a class “MicrosoftTeamsServiceImpl” which implements “MicrosoftTeamsService”.
![Implement MicrosoftTeamsService and MicrosoftTeamsServiceImpl | Microsoft Teams integration with Liferay Implement MicrosoftTeamsService and MicrosoftTeamsServiceImpl](https://www.ignek.com/wp-content/uploads/2024/03/Implement-MicrosoftTeamsService-and-MicrosoftTeamsServiceImpl-1024x581.webp)
- Now, create a package “com.ignek.microsoft.teams.model” in the “microsoft-teams-rest-impl/src/main/java” folder. and create a class “ResponseDTO” that implements serializable.
// ResponseDTO.java
package com.ignek.microsoft.teams.model;
import java.io.Serializable;
import javax.ws.rs.core.Response;
public class ResponseDTO implements Serializable {
protected static Response.Status STATUS_OK = Response.Status.OK;
protected static Response.Status STATUS_ERROR = Response.Status.INTERNAL_SERVER_ERROR;
private final int status;
private final String message;
private ResponseDTO(int status, String message) {
this.status = status;
this.message = message;
}
protected ResponseDTO(Response.Status status) {
this(status.getStatusCode(), status.getReasonPhrase());
}
protected ResponseDTO(Response.Status status, String message) {
this(status.getStatusCode(), status.getReasonPhrase() + ": " + message);
}
public static ResponseDTO ok() {
return new ResponseDTO(STATUS_OK);
}
public static ResponseDTO fail() {
return new ResponseDTO(STATUS_ERROR);
}
public static ResponseDTO error(String message) {
return new ResponseDTO(STATUS_ERROR, essage);
}
public int getStatus() {
return status;
}
public String getMessage() {
return message;
}
}
- Now, In the same package “com.ignek.microsoft.teams.model” create a class “MicrosoftTeamsResponse” that extends ResponseDTO.
// MicrosoftTeamsResponse.java
package com.ignek.microsoft.teams.model;
import com.liferay.petra.string.StringPool;
public class MicrosoftTeamsResponse extends ResponseDTO {
private final String eventId;
private final String link;
private MicrosoftTeamsResponse(String link, String eventId) {
super(STATUS_OK);
this.eventId = eventId;
this.link = link;
}
private MicrosoftTeamsResponse(String message) {
super(STATUS_ERROR, message);
this.eventId = StringPool.BLANK;
this.link = StringPool.BLANK;
}
public static MicrosoftTeamsResponse of(String link, String eventId) {
return new MicrosoftTeamsResponse(link, eventId);
}
public static MicrosoftTeamsResponse fail(String message) {
return new MicrosoftTeamsResponse(message);
}
public String getEventId() {
return eventId;
}
public String getLink() {
return link;
}
}
- Now, create a package “com.ignek.http.client.methods” in the “microsoft-teams-rest-impl/src/main/java” folder. and create an interface “HTTPRequestService” and a class “HTTPRequestServiceImpl” that implements “HTTPRequestService”.
![Create package and class for HTTP requests | Microsoft Teams integration with Liferay Create package and class for HTTP requests](https://www.ignek.com/wp-content/uploads/2024/03/Create-package-and-class-for-HTTP-requests-1024x584.webp)
- In “HTTPRequestService”, add the below code.
// HTTPRequestService.java
package com.ignek.http.client.methods;
import java.util.Map;
import org.apache.http.entity.ContentType;
public interface HTTPRequestService {
String sendHTTPPostRequest(String httpURL, Map headerMap, String requestBody, ContentType contentType);
String sendHTTPPatchRequest(String httpURL, Map headerMap, String requestBody, ContentType contentType);
String sendHTTPGetRequest(String httpURL, Map headerMap);
void sendHTTPDeleteRequest(String httpURL, Map headerMap);
}
- In “HTTPRequestServiceImpl”, add the below code.
// HTTPRequestServiceImpl.java
package com.ignek.http.client.methods;
import java.util.Map;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPatch;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import org.osgi.service.component.annotations.Component;
import com.liferay.petra.string.StringPool;
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.util.Validator;
@Component(immediate = true, service = HTTPRequestService.class)
public class HTTPRequestServiceImpl implements HTTPRequestService {
@Override
public String sendHTTPPostRequest(String httpURL, Map headerMap, String requestBody, ContentType contentType) {
HttpPost httpPost = new HttpPost(httpURL);
HttpEntity entity = new StringEntity(requestBody, contentType);
httpPost.setEntity(entity);
return sendHTTPRequest(httpPost, headerMap);
}
@Override
public String sendHTTPPatchRequest(String httpURL, Map headerMap, String requestBody, ContentType contentType) {
HttpPatch httpPatch = new HttpPatch(httpURL);
HttpEntity entity = new StringEntity(requestBody, contentType);
httpPatch.setEntity(entity);
return sendHTTPRequest(httpPatch, headerMap);
}
@Override
public String sendHTTPGetRequest(String httpURL, Map headerMap) {
HttpGet httpGet = new HttpGet(httpURL);
return sendHTTPRequest(httpGet, headerMap);
}
@Override
public void sendHTTPDeleteRequest(String httpURL, Map headerMap) {
HttpDelete httpDelete = new HttpDelete(httpURL);
sendHTTPRequest(httpDelete, headerMap);
}
private String sendHTTPRequest(HttpUriRequest httpUriRequest, Map headerMap) {
try (CloseableHttpClient client = HttpClientBuilder.create().build()) {
headerMap.forEach((key, value) -> {
httpUriRequest.setHeader(key, value);
});
HttpResponse response = client.execute(httpUriRequest);
return Validator.isNotNull(response) ? EntityUtils.toString(response.getEntity()) : StringPool.BLANK;
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return StringPool.BLANK;
}
private static final Log log = LogFactoryUtil.getLog(HTTPRequestServiceImpl.class);
}
- Now, go to the “MicrosoftTeamsService” file. And write the below code.
// MicrosoftTeamsService.java
package com.ignek.microsoft.teams;
import com.ignek.microsoft.teams.model.MicrosoftTeamsResponse;
public interface MicrosoftTeamsService {
MicrosoftTeamsResponse updateMicrosoftTeamsEvent(String eventId, String title, String location, String[] emails,
String description, String startTime, String endTime, String timeZone);
String getMicrosoftTeamsLink(String eventId);
void deleteMicrosoftTeamsEvent(String eventId);
}
- Now, go to the “MicrosoftTeamsServiceImpl” file. And write the below code.
// MicrosoftTeamsServiceImpl.java
package com.ignek.microsoft.teams;
import java.io.Serializable;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import org.apache.http.entity.ContentType;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.ignek.http.client.methods.HTTPRequestService;
import com.ignek.microsoft.teams.model.MicrosoftTeamsResponse;
import com.liferay.petra.string.StringPool;
import com.liferay.portal.kernel.json.JSONArray;
import com.liferay.portal.kernel.json.JSONFactoryUtil;
import com.liferay.portal.kernel.json.JSONObject;
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.util.Validator;
@Component(immediate = true, service = MicrosoftTeamsService.class)
public class MicrosoftTeamsServiceImpl implements MicrosoftTeamsService {
@Reference
private HTTPRequestService httpRequestService;
@Override
public MicrosoftTeamsResponse updateMicrosoftTeamsEvent(String eventId, String title, String location, String[] emails,
String description, String startTime, String endTime, String timeZone) {
Map headerMap = new HashMap<>();
try {
eventId = eventId.replace(StringPool.QUOTE, StringPool.BLANK);
String accessToken = getAccessToken();
String httpURL = MICROSOFT_CREATE_EVENT_URL.replace("${objectId}", OBJECT_ID)
+ (Validator.isNotNull(eventId) ? StringPool.SLASH + eventId : StringPool.BLANK);
headerMap.put("Host", "graph.microsoft.com");
headerMap.put("Authorization", "Bearer " + accessToken.replace(StringPool.QUOTE, StringPool.BLANK));
headerMap.put("Content-type", ContentType.APPLICATION_JSON.toString());
String requestBody = createRequestBody(title, location, emails, description, startTime, endTime, timeZone);
String responceBody = Validator.isNotNull(eventId)
? httpRequestService.sendHTTPPatchRequest(httpURL, headerMap, requestBody, ContentType.APPLICATION_JSON)
: httpRequestService.sendHTTPPostRequest(httpURL, headerMap, requestBody, ContentType.APPLICATION_JSON);
eventId = getEntityValueFromStringJson(responceBody, "id").replace(StringPool.QUOTE, StringPool.BLANK);
String joinURL = getEntityValueFromStringJson(getEntityValueFromStringJson(responceBody, "onlineMeeting"), "joinUrl")
.replace(StringPool.QUOTE, StringPool.BLANK);
return MicrosoftTeamsResponse.of(joinURL, eventId);
} catch (Exception e) {
log.error(e.getMessage(), e);
return MicrosoftTeamsResponse.fail(e.getMessage());
}
}
@Override
public String getMicrosoftTeamsLink(String eventId) {
String httpURL = MICROSOFT_CREATE_EVENT_URL.replace("${objectId}", OBJECT_ID) + StringPool.SLASH + eventId;
String accessToken = getAccessToken();
Map headerMap = new HashMap<>();
headerMap.put("Host", "graph.microsoft.com");
headerMap.put("Authorization", "Bearer " + accessToken.replace(StringPool.QUOTE, StringPool.BLANK));
String responceBody = httpRequestService.sendHTTPGetRequest(httpURL, headerMap);
return getEntityValueFromStringJson(getEntityValueFromStringJson(responceBody, "onlineMeeting"),
"joinUrl").replace(StringPool.QUOTE, StringPool.BLANK);
}
@Override
public void deleteMicrosoftTeamsEvent(String eventId) {
String httpURL = MICROSOFT_CREATE_EVENT_URL.replace("${objectId}", OBJECT_ID) + StringPool.SLASH + eventId;
String accessToken = getAccessToken();
Map headerMap = new HashMap<>();
headerMap.put("Host", "graph.microsoft.com");
headerMap.put("Authorization", "Bearer " + accessToken.replace(StringPool.QUOTE, StringPool.BLANK));
httpRequestService.sendHTTPDeleteRequest(httpURL, headerMap);
}
private String createRequestBody(String title, String location, String[] emails, String description, String startDate,
String endDate, String timeZone) {
JSONObject jsonObject = JSONFactoryUtil.createJSONObject();
jsonObject.put("subject", title);
jsonObject.put("body", createJSONObject(new String[] { "contentType", "content" }, new String[] { "HTML", description }));
jsonObject.put("start", createJSONObject(new String[] { "dateTime", "timeZone" }, new String[] { startDate, timeZone }));
jsonObject.put("end", createJSONObject(new String[] { "dateTime", "timeZone" }, new String[] { endDate, timeZone }));
jsonObject.put("location", createJSONObject(new String[] { "displayName" }, new String[] { location }));
jsonObject.put("isOnlineMeeting", Boolean.TRUE.toString());
jsonObject.put("onlineMeetingProvider", "teamsForBusiness");
JSONArray array = JSONFactoryUtil.createJSONArray();
for (String email : emails) {
JSONObject emailAddressObject = JSONFactoryUtil.createJSONObject();
emailAddressObject.put("emailAddress", createJSONObject(new String[] { "address" }, new String[] { email }));
array.put(emailAddressObject);
}
jsonObject.put("attendees", array);
return jsonObject.toString();
}
private JSONObject createJSONObject(String[] keys, String[] values) {
JSONObject jsonObject = JSONFactoryUtil.createJSONObject();
for (String key : keys) {
jsonObject.put(key, values[Arrays.asList(keys).indexOf(key)]);
}
return jsonObject;
}
private String getAccessToken() {
Map requestBodyMap = new HashMap<>();
requestBodyMap.put("client_id", CLIENT_ID);
requestBodyMap.put("client_secret", CLIENT_SECRET);
requestBodyMap.put("grant_type", "client_credentials");
requestBodyMap.put("scope", "https://graph.microsoft.com/.default");
String postURL = MICROSOFT_TOKEN_URL.replace("${tenantId}", TENANT_ID);
Map headerMap = new HashMap<>();
headerMap.put("Content-type", ContentType.APPLICATION_FORM_URLENCODED.toString());
headerMap.put("Host", "login.microsoftonline.com");
String response = httpRequestService.sendHTTPPostRequest(postURL, headerMap, getRequestBody(requestBodyMap),
ContentType.APPLICATION_FORM_URLENCODED);
return getEntityValueFromStringJson(response, "access_token");
}
private static String getRequestBody(Map map) {
return map.toString().replace(StringPool.OPEN_CURLY_BRACE, StringPool.BLANK)
.replace(StringPool.CLOSE_CURLY_BRACE, StringPool.BLANK)
.replace(StringPool.COMMA + StringPool.SPACE, StringPool.AMPERSAND);
}
private String getEntityValueFromStringJson(String json, String entityName) {
Gson gson = new Gson();
JsonObject object = gson.fromJson(json, JsonObject.class);
return object.get(entityName).toString();
}
private static final String CLIENT_ID = "b5ade99e-d2ef-4a7c-a9b0-7ad27xxxxx";
private static final String CLIENT_SECRET = "q~e8Q~NvaLAqcbpNodOpirJz_-PnTDxxxxxx";
private static final String TENANT_ID = "ea9b5a29-8a4b-4697-a846-475xxxxx";
private static final String OBJECT_ID = "7d379439-6f76-43a9-895e-71xxxxxxx";
private static final String MICROSOFT_TOKEN_URL = "https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token";
private static final String MICROSOFT_CREATE_EVENT_URL = "https://graph.microsoft.com/v1.0/users/${objectId}/events";
private static final Log log = LogFactoryUtil.getLog(MicrosoftTeamsServiceImpl.class);
}
- In above file please change the value of CLIENT_ID, CLIENT_SECRET, TENANT_ID, and OBJECT_ID with your data which we have stored while creating the microsoft account and application.
- Now, go to the “TeamsResourceImpl” file. And write the below code.
// TeamsResourceImpl.java
package microsoft.teams.rest.internal.resource.v1_0;
import javax.validation.constraints.NotNull;
import javax.ws.rs.core.Response;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ServiceScope;
import com.ignek.microsoft.teams.MicrosoftTeamsService;
import com.ignek.microsoft.teams.model.MicrosoftTeamsResponse;
import microsoft.teams.rest.dto.v1_0.Status;
import microsoft.teams.rest.dto.v1_0.Teams;
import microsoft.teams.rest.resource.v1_0.TeamsResource;
/**
* @author ignek
*/
@Component(properties = "OSGI-INF/liferay/rest/v1_0/teams.properties", scope = ServiceScope.PROTOTYPE, service = TeamsResource.class)
public class TeamsResourceImpl extends BaseTeamsResourceImpl {
@Reference
private MicrosoftTeamsService microsoftTeamsService;
@Override
public Teams updateMicrosoftTeamsLink(Teams teams) throws Exception {
String eventId = teams.getEventId();
String title = teams.getTitle();
String description = teams.getDescription();
String location = teams.getLocation();
String setDate = teams.getSetDate();
String endDate = teams.getEndDate();
String timeZone = teams.getTimeZone();
String[] emailAddresses = teams.getEmailAddress();
MicrosoftTeamsResponse microsoftTeamsResponse = microsoftTeamsService.updateMicrosoftTeamsEvent(eventId, title, location,
emailAddresses, description, setDate, endDate, timeZone);
String teamsLink = microsoftTeamsResponse.getLink();
eventId = microsoftTeamsResponse.getEventId();
Status status = new Status();
status.setStatusCode(microsoftTeamsResponse.getStatus());
status.setStatusMessage(microsoftTeamsResponse.getMessage());
teams.setStatus(status);
teams.setEventId(eventId);
teams.setTeamsLink(teamsLink);
return teams;
}
@Override
public Teams getMicrosoftTeamsLink(@NotNull String eventId) throws Exception {
Teams teams = new Teams();
String teamsLink = microsoftTeamsService.getMicrosoftTeamsLink(eventId);
teams.setTeamsLink(teamsLink);
teams.setEventId(eventId);
return teams;
}
@Override
public Teams deleteMicrosoftTeamsLink(@NotNull String eventId) throws Exception {
Teams teams = new Teams();
Status status = new Status();
try {
microsoftTeamsService.deleteMicrosoftTeamsEvent(eventId);
status.setStatusCode(Response.Status.OK.getStatusCode());
status.setStatusMessage("Deleted Successfully");
} catch (Exception e) {
status.setStatusCode(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode());
status.setStatusMessage(e.getMessage());
}
teams.setStatus(status);
return teams;
}
}
- Now, deploy the rest module and you can test the above rest-apis using graphQL.
- Here, you can find graphQL for adding or updating Microsoft events. For adding an event you have to pass “eventId” as blank and for updating an event you have to pass the eventId of that event.
mutation ($teams: InputTeams!) {
updateMicrosoftTeamsLink(teams: $teams) {
eventId
teamsLink
status {
statusCode
statusMessage
}
}
}
/* Query Variables */
{
"teams": {
"title": "My first teams link",
"description": "test microsoft teams link",
"emailAddress": [
"test@yopmail.com",
"test1@yopmail.com"
],
"setDate": "2023-05-16 13:15",
"endDate": "2023-05-16 14:15",
"eventId": "",
"location": "my house",
"timeZone": "Pacific Standard Time"
}
}
![GraphQL for Microsoft events management | Microsoft Teams integration with Liferay GraphQL for Microsoft events management](https://www.ignek.com/wp-content/uploads/2024/03/GraphQL-for-Microsoft-events-management-1024x583.webp)
- Here, you can find graphQL for getting Microsoft event and teams link.
{
microsoftTeamsLink(eventId: "") {
teamsLink
eventId
}
}
![GraphQL for Access Microsoft event and Teams links | Microsoft Teams integration with Liferay GraphQL for Access Microsoft event and Teams links](https://www.ignek.com/wp-content/uploads/2024/03/GraphQL-for-Access-Microsoft-event-and-Teams-links-1024x581.webp)
- Here, you can find graphQL for deleting Microsoft events.
mutation {
deleteMicrosoftTeamsLink(eventId: "") {
status {
statusCode
statusMessage
}
}
}
![GraphQL for deleting Microsoft events | Microsoft Teams integration with Liferay GraphQL for deleting Microsoft events](https://www.ignek.com/wp-content/uploads/2024/03/GraphQL-for-deleting-Microsoft-events-1024x584.webp)
Note*: Microsoft APIs return many parameters in their responses. As an eventId, we are taking “id” and as a team link, we are taking “joinUrl”.
Here is an example response:
{
"@odata.context":"https://graph.microsoft.com/v1.0/$metadata#users('cd209b0b-3f83-4c35-82d2-d88a61820480')/events/$entity",
"@odata.etag":"W/\"ZlnW4RIAV06KYYwlrfNZvQAALfZeRQ==\"",
"id":"AAMkAGI1AAAt8AHjAAA=",
"createdDateTime":"2017-04-15T03:00:50.7579581Z",
"lastModifiedDateTime":"2017-04-15T03:00:51.245372Z",
"changeKey":"ZlnW4RIAV06KYYwlrfNZvQAALfZeRQ==",
"categories":[
],
"originalStartTimeZone":"Pacific Standard Time",
"originalEndTimeZone":"Pacific Standard Time",
"iCalUId":"040000008200E00074C5B7101A82E00800000000DA2B357D94B5D201000000000000000010000000EC4597557F0CB34EA4CC2887EA7B17C3",
"reminderMinutesBeforeStart":15,
"isReminderOn":true,
"hasAttachments":false,
"hideAttendees": false,
"subject":"Let's go brunch",
"bodyPreview":"Does noon work for you?",
"importance":"normal",
"sensitivity":"normal",
"isAllDay":false,
"isCancelled":false,
"isDraft": false,
"isOrganizer":true,
"responseRequested":true,
"seriesMasterId":null,
"showAs":"busy",
"type":"singleInstance",
"webLink":"https://outlook.office365.com/owa/?itemid=AAMkAGI1AAAt9AHjAAA%3D&exvsurl=1&path=/calendar/item",
"onlineMeetingUrl":null,
"isOnlineMeeting": true,
"onlineMeetingProvider": "teamsForBusiness",
"allowNewTimeProposals": true,
"responseStatus":{
"response":"organizer",
"time":"0001-01-01T00:00:00Z"
},
"body":{
"contentType":"html",
"content":"Does late morning work for you?"
},
"start":{
"dateTime":"2017-04-15T11:00:00.0000000",
"timeZone":"Pacific Standard Time"
},
"end":{
"dateTime":"2017-04-15T12:00:00.0000000",
"timeZone":"Pacific Standard Time"
},
"location": {
"displayName": "Harry's Bar",
"locationType": "default",
"uniqueId": "Harry's Bar",
"uniqueIdType": "private"
},
"locations": [
{
"displayName": "Harry's Bar",
"locationType": "default",
"uniqueIdType": "unknown"
}
],
"recurrence":null,
"attendees":[
{
"type":"required",
"status":{
"response":"none",
"time":"0001-01-01T00:00:00Z"
},
"emailAddress":{
"name":"Samantha Booth",
"address":"samanthab@contoso.onmicrosoft.com"
}
}
],
"organizer":{
"emailAddress":{
"name":"Dana Swope",
"address":"danas@contoso.onmicrosoft.com"
}
},
"onlineMeeting": {
"joinUrl": "https://teams.microsoft.com/l/meetup-join/19%3ameeting_NzIyNzhlMGEtM2YyZC00ZmY0LTlhNzUtZmZjNWFmZGNlNzE2%40thread.v2/0?
context=%7b%22Tid%22%3a%2272f988bf-86f1-41af-91ab-2d7cd011db47%22%2c%22Oid%22%3a%22bc55b173-cff6-457d-b7a1-64bda7d7581a%22%7d"
}
}