From c6577334f90ed7787083a8db2ea194df71e9c713 Mon Sep 17 00:00:00 2001 From: tracyboehrer Date: Wed, 21 Oct 2020 13:41:34 -0500 Subject: [PATCH] Teams Meeting API --- .../bot/builder/teams/TeamsInfo.java | 60 +++++++++++++++ .../connector/rest/RestTeamsOperations.java | 53 ++++++++++++- .../bot/connector/teams/TeamsOperations.java | 14 ++++ .../com/microsoft/bot/schema/Activity.java | 36 ++++++++- .../schema/teams/MeetingParticipantInfo.java | 46 +++++++++++ .../bot/schema/teams/TeamsChannelData.java | 55 +++++++++++-- .../bot/schema/teams/TeamsMeetingInfo.java | 45 +++++++++++ .../schema/teams/TeamsMeetingParticipant.java | 77 +++++++++++++++++++ 8 files changed, 376 insertions(+), 10 deletions(-) create mode 100644 libraries/bot-schema/src/main/java/com/microsoft/bot/schema/teams/MeetingParticipantInfo.java create mode 100644 libraries/bot-schema/src/main/java/com/microsoft/bot/schema/teams/TeamsMeetingInfo.java create mode 100644 libraries/bot-schema/src/main/java/com/microsoft/bot/schema/teams/TeamsMeetingParticipant.java diff --git a/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/teams/TeamsInfo.java b/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/teams/TeamsInfo.java index 091774d2f..65f65c780 100644 --- a/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/teams/TeamsInfo.java +++ b/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/teams/TeamsInfo.java @@ -21,6 +21,7 @@ import com.microsoft.bot.schema.teams.TeamsChannelAccount; import com.microsoft.bot.schema.teams.TeamsChannelData; import com.microsoft.bot.schema.teams.TeamsPagedMembersResult; +import com.microsoft.bot.schema.teams.TeamsMeetingParticipant; import org.apache.commons.lang3.StringUtils; import java.util.ArrayList; @@ -209,6 +210,65 @@ public static CompletableFuture getPagedMembers( return getPagedMembers(getConnectorClient(turnContext), conversationId, continuationToken); } + /** + * Gets the details for the given meeting participant. This only works in teams meeting scoped conversations. + * @param turnContext The TurnContext that the meeting, participant, and tenant ids are pulled from. + * @return TeamsParticipantChannelAccount + */ + public static CompletableFuture getMeetingParticipant( + TurnContext turnContext + ) { + return getMeetingParticipant(turnContext, null, null, null); + } + + /** + * Gets the details for the given meeting participant. This only works in teams meeting scoped conversations. + * @param turnContext Turn context. + * @param meetingId The meeting id, or null to get from Activities TeamsChannelData + * @param participantId The participant id, or null to get from Activities TeamsChannelData + * @param tenantId The tenant id, or null to get from Activities TeamsChannelData + * @return Team participant channel account. + */ + public static CompletableFuture getMeetingParticipant( + TurnContext turnContext, + String meetingId, + String participantId, + String tenantId + ) { + if (StringUtils.isEmpty(meetingId)) { + meetingId = turnContext.getActivity().teamsGetMeetingInfo() != null + ? turnContext.getActivity().teamsGetMeetingInfo().getId() + : null; + } + if (StringUtils.isEmpty(meetingId)) { + return illegalArgument("TeamsInfo.getMeetingParticipant: method requires a meetingId"); + } + + if (StringUtils.isEmpty(participantId)) { + participantId = turnContext.getActivity().getFrom() != null + ? turnContext.getActivity().getFrom().getAadObjectId() + : null; + } + if (StringUtils.isEmpty(participantId)) { + return illegalArgument("TeamsInfo.getMeetingParticipant: method requires a participantId"); + } + + if (StringUtils.isEmpty(tenantId)) { + tenantId = turnContext.getActivity().teamsGetChannelData() != null + ? turnContext.getActivity().teamsGetChannelData().getTenant().getId() + : null; + } + if (StringUtils.isEmpty(tenantId)) { + return illegalArgument("TeamsInfo.getMeetingParticipant: method requires a tenantId"); + } + + return getTeamsConnectorClient(turnContext).getTeams().fetchParticipant( + meetingId, + participantId, + tenantId + ); + } + private static CompletableFuture> getMembers( ConnectorClient connectorClient, String conversationId diff --git a/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/rest/RestTeamsOperations.java b/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/rest/RestTeamsOperations.java index ce799566e..5d79d6f4f 100644 --- a/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/rest/RestTeamsOperations.java +++ b/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/rest/RestTeamsOperations.java @@ -11,9 +11,11 @@ import com.microsoft.bot.rest.ServiceResponse; import com.microsoft.bot.schema.teams.ConversationList; import com.microsoft.bot.schema.teams.TeamDetails; +import com.microsoft.bot.schema.teams.TeamsMeetingParticipant; import okhttp3.ResponseBody; import retrofit2.Response; import retrofit2.Retrofit; +import retrofit2.http.GET; import retrofit2.http.Header; import retrofit2.http.Headers; import retrofit2.http.POST; @@ -22,6 +24,7 @@ import java.io.IOException; import java.net.HttpURLConnection; import java.util.concurrent.CompletableFuture; +import retrofit2.http.Query; /** * msrest impl of TeamsOperations. @@ -117,6 +120,44 @@ private ServiceResponse fetchTeamDetailsDelegate( .build(response); } + /** + * Fetches Teams meeting participant details. + * @param meetingId Teams meeting id + * @param participantId Teams meeting participant id + * @param tenantId Teams meeting tenant id + * @return TeamsParticipantChannelAccount + */ + public CompletableFuture fetchParticipant( + String meetingId, + String participantId, + String tenantId + ) { + return service.fetchParticipant( + meetingId, participantId, tenantId, client.getAcceptLanguage(), client.getUserAgent() + ) + .thenApply(responseBodyResponse -> { + try { + return fetchParticipantDelegate(responseBodyResponse).body(); + } catch (ErrorResponseException e) { + throw e; + } catch (Throwable t) { + throw new ErrorResponseException("fetchParticipant", responseBodyResponse); + } + }); + } + + private ServiceResponse fetchParticipantDelegate( + Response response + ) throws ErrorResponseException, IOException, IllegalArgumentException { + return client.restClient() + .responseBuilderFactory() + .newInstance(client.serializerAdapter()) + .register(HttpURLConnection.HTTP_OK, new TypeToken() { + }.getType()) + .registerError(ErrorResponseException.class) + .build(response); + } + /** * The interface defining all the services for TeamsOperations to be used by * Retrofit to perform actually REST calls. @@ -140,6 +181,16 @@ CompletableFuture> fetchTeamDetails( @Header("accept-language") String acceptLanguage, @Header("User-Agent") String userAgent ); - } + @Headers({ "Content-Type: application/json; charset=utf-8", + "x-ms-logging-context: com.microsoft.bot.schema.Teams fetchParticipant" }) + @GET("v1/meetings/{meetingId}/participants/{participantId}") + CompletableFuture> fetchParticipant( + @Path("meetingId") String meetingId, + @Path("participantId") String participantId, + @Query("tenantId") String tenantId, + @Header("accept-language") String acceptLanguage, + @Header("User-Agent") String userAgent + ); + } } diff --git a/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/teams/TeamsOperations.java b/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/teams/TeamsOperations.java index 848b241cb..85889c789 100644 --- a/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/teams/TeamsOperations.java +++ b/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/teams/TeamsOperations.java @@ -13,6 +13,7 @@ import com.microsoft.bot.schema.teams.ConversationList; import com.microsoft.bot.schema.teams.TeamDetails; +import com.microsoft.bot.schema.teams.TeamsMeetingParticipant; import java.util.concurrent.CompletableFuture; /** @@ -34,4 +35,17 @@ public interface TeamsOperations { * @return The TeamDetails */ CompletableFuture fetchTeamDetails(String teamId); + + /** + * Fetches Teams meeting participant details. + * @param meetingId Teams meeting id + * @param participantId Teams meeting participant id + * @param tenantId Teams meeting tenant id + * @return TeamsMeetingParticipant + */ + CompletableFuture fetchParticipant( + String meetingId, + String participantId, + String tenantId + ); } diff --git a/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/Activity.java b/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/Activity.java index aff2b263e..c90ade421 100644 --- a/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/Activity.java +++ b/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/Activity.java @@ -16,6 +16,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.microsoft.bot.schema.teams.TeamsMeetingInfo; import org.apache.commons.lang3.StringUtils; import java.time.LocalDateTime; @@ -50,9 +51,6 @@ public class Activity { @JsonProperty(value = "localTimestamp") @JsonInclude(JsonInclude.Include.NON_EMPTY) - // @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = - // "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'") - // 2019-10-07T09:49:37-05:00 private OffsetDateTime localTimestamp; @JsonProperty(value = "localTimezone") @@ -1757,6 +1755,22 @@ public String teamsGetChannelId() { return teamsChannelId; } + /** + * Gets the TeamsChannelData. + * @return TeamsChannelData + */ + public TeamsChannelData teamsGetChannelData() { + TeamsChannelData teamsChannelData; + + try { + teamsChannelData = getChannelData(TeamsChannelData.class); + } catch (JsonProcessingException jpe) { + teamsChannelData = null; + } + + return teamsChannelData; + } + /** * Get unique identifier representing a team. * @@ -1842,6 +1856,22 @@ public void teamsNotifyUser(boolean alertInMeeting, String externalResourceUrl) teamsChannelData.setNotification(new NotificationInfo(true, externalResourceUrl)); } + /** + * Gets the TeamsMeetingInfo object from the current activity. + * @return The current activity's team's meeting, or null. + */ + public TeamsMeetingInfo teamsGetMeetingInfo() { + TeamsChannelData teamsChannelData; + + try { + teamsChannelData = getChannelData(TeamsChannelData.class); + } catch (JsonProcessingException jpe) { + teamsChannelData = null; + } + + return teamsChannelData != null ? teamsChannelData.getMeeting() : null; + } + /** * Returns this activity as a Message Activity; or null, if this is not that type of activity. * diff --git a/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/teams/MeetingParticipantInfo.java b/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/teams/MeetingParticipantInfo.java new file mode 100644 index 000000000..c3b8597db --- /dev/null +++ b/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/teams/MeetingParticipantInfo.java @@ -0,0 +1,46 @@ +package com.microsoft.bot.schema.teams; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Teams meeting participant details. + */ +public class MeetingParticipantInfo { + @JsonProperty(value = "role") + private String role; + + @JsonProperty(value = "inMeeting") + private boolean inMeeting; + + /** + * Gets the participant's role in the meeting. + * @return The participant's role in the meeting. + */ + public String getRole() { + return role; + } + + /** + * Sets the participant's role in the meeting. + * @param withRole The participant's role in the meeting. + */ + public void setRole(String withRole) { + role = withRole; + } + + /** + * Gets a value indicating whether the participant is in the meeting or not. + * @return The value indicating if the participant is in the meeting. + */ + public boolean isInMeeting() { + return inMeeting; + } + + /** + * Sets a value indicating whether the participant is in the meeting or not. + * @param withInMeeting The value indicating if the participant is in the meeting. + */ + public void setInMeeting(boolean withInMeeting) { + inMeeting = withInMeeting; + } +} diff --git a/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/teams/TeamsChannelData.java b/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/teams/TeamsChannelData.java index 27448a2a8..166f07b44 100644 --- a/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/teams/TeamsChannelData.java +++ b/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/teams/TeamsChannelData.java @@ -3,7 +3,12 @@ package com.microsoft.bot.schema.teams; +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.JsonNode; +import java.util.HashMap; +import java.util.Map; /** * Channel data specific to messages received in Microsoft Teams. @@ -18,24 +23,23 @@ public class TeamsChannelData { @JsonProperty(value = "channel") private ChannelInfo channel; - /// Gets or sets type of event. @JsonProperty(value = "eventType") private String eventType; - /// Gets or sets information about the team in which the message was - /// sent @JsonProperty(value = "team") private TeamInfo team; - /// Gets or sets notification settings for the message @JsonProperty(value = "notification") private NotificationInfo notification; - /// Gets or sets information about the tenant in which the message was - /// sent @JsonProperty(value = "tenant") private TenantInfo tenant; + @JsonProperty(value = "meeting") + private TeamsMeetingInfo meeting; + + private HashMap properties = new HashMap<>(); + /** * Get unique identifier representing a channel. * @@ -163,6 +167,45 @@ public void setTenant(TenantInfo withTenant) { this.tenant = withTenant; } + /** + * Information about the meeting in which the message was sent. + * @return The meeting info + */ + public TeamsMeetingInfo getMeeting() { + return meeting; + } + + /** + * Sets information about the meeting in which the message was sent. + * @param withMeeting The meeting info + */ + public void setMeeting(TeamsMeetingInfo withMeeting) { + meeting = withMeeting; + } + + /** + * Holds the overflow properties that aren't first class properties in the + * object. This allows extensibility while maintaining the object. + * + * @return Map of additional properties. + */ + @JsonAnyGetter + public Map getProperties() { + return this.properties; + } + + /** + * Holds the overflow properties that aren't first class properties in the + * object. This allows extensibility while maintaining the object. + * + * @param key The key of the property to set. + * @param withValue The value for the property. + */ + @JsonAnySetter + public void setProperties(String key, JsonNode withValue) { + this.properties.put(key, withValue); + } + /** * A new instance of TeamChannelData. * diff --git a/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/teams/TeamsMeetingInfo.java b/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/teams/TeamsMeetingInfo.java new file mode 100644 index 000000000..ec659ab68 --- /dev/null +++ b/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/teams/TeamsMeetingInfo.java @@ -0,0 +1,45 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.bot.schema.teams; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Describes a Teams Meeting. + */ +public class TeamsMeetingInfo { + @JsonProperty(value = "id") + private String id; + + /** + * New Teams meeting. + */ + public TeamsMeetingInfo() { + + } + + /** + * New Teams meeting with ID. + * @param withId Unique identifier representing a teams meeting. + */ + public TeamsMeetingInfo(String withId) { + id = withId; + } + + /** + * Gets the unique identifier representing a meeting. + * @return The meeting id + */ + public String getId() { + return id; + } + + /** + * Sets the unique identifier representing a meeting. + * @param withId The meeting id + */ + public void setId(String withId) { + id = withId; + } +} diff --git a/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/teams/TeamsMeetingParticipant.java b/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/teams/TeamsMeetingParticipant.java new file mode 100644 index 000000000..63b4b0742 --- /dev/null +++ b/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/teams/TeamsMeetingParticipant.java @@ -0,0 +1,77 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.bot.schema.teams; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.microsoft.bot.schema.ConversationAccount; + +/** + * Teams participant channel account detailing user Azure Active Directory and meeting + * participant details. + */ +public class TeamsMeetingParticipant { + @JsonProperty(value = "user") + private TeamsChannelAccount user; + + @JsonProperty(value = "meeting") + private MeetingParticipantInfo meeting; + + @JsonProperty(value = "conversation") + private ConversationAccount conversation; + + /** + * Create TeamsParticipantChannelAccount. + */ + public TeamsMeetingParticipant() { + + } + + /** + * Gets the participant's user information. + * @return The participant's user information. + */ + public TeamsChannelAccount getUser() { + return user; + } + + /** + * Sets the participant's user information. + * @param withUser The participant's user information. + */ + public void setUser(TeamsChannelAccount withUser) { + user = withUser; + } + + /** + * Gets the participant's meeting information. + * @return The participant's role in the meeting. + */ + public MeetingParticipantInfo getMeeting() { + return meeting; + } + + /** + * Sets the participant's meeting information. + * @param withMeeting The participant's role in the meeting. + */ + public void setMeeting(MeetingParticipantInfo withMeeting) { + meeting = withMeeting; + } + + /** + * Gets the Conversation Account for the meeting. + * @return The Conversation Account for the meeting. + */ + public ConversationAccount getConversation() { + return conversation; + } + + /** + * Sets the Conversation Account for the meeting. + * @param withConversation The Conversation Account for the meeting. + */ + public void setConversation(ConversationAccount withConversation) { + conversation = withConversation; + } +}