Skip to content

Commit cb33e75

Browse files
EricDahlvangMichael Richardson
and
Michael Richardson
authored
Teams Adaptive Card Tabs (#5111)
* Teams Adaptive Card Tabs * Change name of TabContext to TabEntityContext * Add tests for tab/fetch and tab/submit * Fix variable name on OnTeamsTabSubmitAsync * Add adaptive schemas and triggers for teams tabs * Add TaskModuleCardResponse * minor fixes * test sync * Address feedback Co-authored-by: Michael Richardson <v-micric@microsoft.com>
1 parent a7c0d98 commit cb33e75

25 files changed

+1545
-675
lines changed

libraries/Microsoft.Bot.Builder.Dialogs.Adaptive.Teams/Microsoft.Bot.Builder.Dialogs.Adaptive.Teams.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@
4040
<None Remove="Schemas\Actions\Teams.GetMeetingParticipant.schema" />
4141
<None Remove="Schemas\TriggerConditions\Microsoft.OnInvokeActivity.schema" />
4242
<None Remove="Schemas\TriggerConditions\Teams.OnChannelRestored.schema" />
43+
<None Remove="Schemas\TriggerConditions\Teams.OnTabFetch.schema" />
44+
<None Remove="Schemas\TriggerConditions\Teams.OnTabSubmit.schema" />
4345
<None Remove="Schemas\TriggerConditions\Teams.OnTeamArchived.schema" />
4446
<None Remove="Schemas\TriggerConditions\Teams.OnTeamDeleted.schema" />
4547
<None Remove="Schemas\TriggerConditions\Teams.OnTeamHardDeleted.schema" />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"$schema": "https://schemas.botframework.com/schemas/component/v1.0/component.schema",
3+
"$role": [ "implements(Microsoft.ITrigger)", "extends(Microsoft.OnCondition)" ],
4+
"title": "On Teams Tab Fetch",
5+
"description": "Actions triggered when a Teams InvokeActivity is received with activity.name='tab/fetch'.",
6+
"type": "object",
7+
"required": [
8+
]
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"$schema": "https://schemas.botframework.com/schemas/component/v1.0/component.schema",
3+
"$role": [ "implements(Microsoft.ITrigger)", "extends(Microsoft.OnCondition)" ],
4+
"title": "On Teams Tab Submit",
5+
"description": "Actions triggered when a Teams InvokeActivity is received with activity.name='tab/submit'.",
6+
"type": "object",
7+
"required": [
8+
]
9+
}

libraries/Microsoft.Bot.Builder.Dialogs.Adaptive.Teams/TeamsComponentRegistration.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ public virtual IEnumerable<DeclarativeType> GetDeclarativeTypes(ResourceExplorer
3838
yield return new DeclarativeType<OnTeamsTeamRenamed>(OnTeamsTeamRenamed.Kind);
3939
yield return new DeclarativeType<OnTeamsTeamRestored>(OnTeamsTeamRestored.Kind);
4040
yield return new DeclarativeType<OnTeamsTeamUnarchived>(OnTeamsTeamUnarchived.Kind);
41+
yield return new DeclarativeType<OnTeamsTabFetch>(OnTeamsTabFetch.Kind);
42+
yield return new DeclarativeType<OnTeamsTabSubmit>(OnTeamsTabSubmit.Kind);
4143

4244
// Actions
4345
yield return new DeclarativeType<GetMeetingParticipant>(GetMeetingParticipant.Kind);
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Licensed under the MIT License.
2+
// Copyright (c) Microsoft Corporation. All rights reserved.
3+
4+
using System.Collections.Generic;
5+
using System.Runtime.CompilerServices;
6+
using AdaptiveExpressions;
7+
using Microsoft.Bot.Builder.Dialogs.Adaptive.Conditions;
8+
using Microsoft.Bot.Connector;
9+
using Newtonsoft.Json;
10+
11+
namespace Microsoft.Bot.Builder.Dialogs.Adaptive.Teams
12+
{
13+
/// <summary>
14+
/// Actions triggered when a Teams InvokeActivity is received with activity.name='tab/fetch'.
15+
/// </summary>
16+
public class OnTeamsTabFetch : OnInvokeActivity
17+
{
18+
[JsonProperty("$kind")]
19+
public new const string Kind = "Teams.OnTabFetch";
20+
21+
[JsonConstructor]
22+
public OnTeamsTabFetch(List<Dialog> actions = null, string condition = null, [CallerFilePath] string callerPath = "", [CallerLineNumber] int callerLine = 0)
23+
: base(actions: actions, condition: condition, callerPath: callerPath, callerLine: callerLine)
24+
{
25+
}
26+
27+
/// <inheritdoc/>
28+
protected override Expression CreateExpression()
29+
{
30+
// if name is 'tab/fetch'
31+
return Expression.AndExpression(Expression.Parse($"{TurnPath.Activity}.ChannelId == '{Channels.Msteams}' && {TurnPath.Activity}.name == 'tab/fetch'"), base.CreateExpression());
32+
}
33+
}
34+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Licensed under the MIT License.
2+
// Copyright (c) Microsoft Corporation. All rights reserved.
3+
4+
using System.Collections.Generic;
5+
using System.Runtime.CompilerServices;
6+
using AdaptiveExpressions;
7+
using Microsoft.Bot.Builder.Dialogs.Adaptive.Conditions;
8+
using Microsoft.Bot.Connector;
9+
using Newtonsoft.Json;
10+
11+
namespace Microsoft.Bot.Builder.Dialogs.Adaptive.Teams
12+
{
13+
/// <summary>
14+
/// Actions triggered when a Teams InvokeActivity is received with activity.name='tab/submit'.
15+
/// </summary>
16+
public class OnTeamsTabSubmit : OnInvokeActivity
17+
{
18+
[JsonProperty("$kind")]
19+
public new const string Kind = "Teams.OnTabSubmit";
20+
21+
[JsonConstructor]
22+
public OnTeamsTabSubmit(List<Dialog> actions = null, string condition = null, [CallerFilePath] string callerPath = "", [CallerLineNumber] int callerLine = 0)
23+
: base(actions: actions, condition: condition, callerPath: callerPath, callerLine: callerLine)
24+
{
25+
}
26+
27+
/// <inheritdoc/>
28+
protected override Expression CreateExpression()
29+
{
30+
// if name is 'tab/submit'
31+
return Expression.AndExpression(Expression.Parse($"{TurnPath.Activity}.ChannelId == '{Channels.Msteams}' && {TurnPath.Activity}.name == 'tab/submit'"), base.CreateExpression());
32+
}
33+
}
34+
}

libraries/Microsoft.Bot.Builder/Teams/TeamsActivityHandler.cs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,12 @@ protected override async Task<InvokeResponse> OnInvokeActivityAsync(ITurnContext
8282

8383
case "task/submit":
8484
return CreateInvokeResponse(await OnTeamsTaskModuleSubmitAsync(turnContext, SafeCast<TaskModuleRequest>(turnContext.Activity.Value), cancellationToken).ConfigureAwait(false));
85+
86+
case "tab/fetch":
87+
return CreateInvokeResponse(await OnTeamsTabFetchAsync(turnContext, SafeCast<TabRequest>(turnContext.Activity.Value), cancellationToken).ConfigureAwait(false));
88+
89+
case "tab/submit":
90+
return CreateInvokeResponse(await OnTeamsTabSubmitAsync(turnContext, SafeCast<TabSubmit>(turnContext.Activity.Value), cancellationToken).ConfigureAwait(false));
8591

8692
default:
8793
return await base.OnInvokeActivityAsync(turnContext, cancellationToken).ConfigureAwait(false);
@@ -383,6 +389,32 @@ protected virtual Task<TaskModuleResponse> OnTeamsTaskModuleSubmitAsync(ITurnCon
383389
throw new InvokeResponseException(HttpStatusCode.NotImplemented);
384390
}
385391

392+
/// <summary>
393+
/// Override this in a derived class to provide logic for when a tab is fetched.
394+
/// </summary>
395+
/// <param name="turnContext">A strongly-typed context object for this turn.</param>
396+
/// <param name="tabRequest">The tab invoke request value payload.</param>
397+
/// <param name="cancellationToken">A cancellation token that can be used by other objects
398+
/// or threads to receive notice of cancellation.</param>
399+
/// <returns>A Tab Response for the request.</returns>
400+
protected virtual Task<TabResponse> OnTeamsTabFetchAsync(ITurnContext<IInvokeActivity> turnContext, TabRequest tabRequest, CancellationToken cancellationToken)
401+
{
402+
throw new InvokeResponseException(HttpStatusCode.NotImplemented);
403+
}
404+
405+
/// <summary>
406+
/// Override this in a derived class to provide logic for when a tab is submitted.
407+
/// </summary>
408+
/// <param name="turnContext">A strongly-typed context object for this turn.</param>
409+
/// <param name="tabSubmit">The tab submit invoke request value payload.</param>
410+
/// <param name="cancellationToken">A cancellation token that can be used by other objects
411+
/// or threads to receive notice of cancellation.</param>
412+
/// <returns>A Tab Response for the request.</returns>
413+
protected virtual Task<TabResponse> OnTeamsTabSubmitAsync(ITurnContext<IInvokeActivity> turnContext, TabSubmit tabSubmit, CancellationToken cancellationToken)
414+
{
415+
throw new InvokeResponseException(HttpStatusCode.NotImplemented);
416+
}
417+
386418
/// <summary>
387419
/// Invoked when a conversation update activity is received from the channel.
388420
/// Conversation update activities are useful when it comes to responding to users being added to or removed from the channel.
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
namespace Microsoft.Bot.Schema.Teams
5+
{
6+
using Newtonsoft.Json;
7+
8+
/// <summary>
9+
/// Current tab request context, i.e., the current theme.
10+
/// </summary>
11+
public partial class TabContext
12+
{
13+
/// <summary>
14+
/// Initializes a new instance of the <see cref="TabContext"/> class.
15+
/// </summary>
16+
public TabContext()
17+
{
18+
CustomInit();
19+
}
20+
21+
/// <summary>
22+
/// Gets or sets the current user's theme.
23+
/// </summary>
24+
/// <value>
25+
/// The current user's theme.
26+
/// </value>
27+
[JsonProperty(PropertyName = "theme")]
28+
public string Theme { get; set; }
29+
30+
/// <summary>
31+
/// An initialization method that performs custom operations like setting defaults.
32+
/// </summary>
33+
partial void CustomInit();
34+
}
35+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
namespace Microsoft.Bot.Schema.Teams
5+
{
6+
using Newtonsoft.Json;
7+
8+
/// <summary>
9+
/// Current TabRequest entity context, or 'tabEntityId'.
10+
/// </summary>
11+
public partial class TabEntityContext
12+
{
13+
/// <summary>
14+
/// Initializes a new instance of the <see cref="TabEntityContext"/> class.
15+
/// </summary>
16+
public TabEntityContext()
17+
{
18+
CustomInit();
19+
}
20+
21+
/// <summary>
22+
/// Gets or sets the entity id of the tab.
23+
/// </summary>
24+
/// <value>
25+
/// The entity id of the tab.
26+
/// </value>
27+
[JsonProperty(PropertyName = "tabEntityId")]
28+
public string TabEntityId { get; set; }
29+
30+
/// <summary>
31+
/// An initialization method that performs custom operations like setting defaults.
32+
/// </summary>
33+
partial void CustomInit();
34+
}
35+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
namespace Microsoft.Bot.Schema.Teams
5+
{
6+
using Newtonsoft.Json;
7+
8+
/// <summary>
9+
/// Invoke ('tab/fetch') request value payload.
10+
/// </summary>
11+
public partial class TabRequest
12+
{
13+
/// <summary>
14+
/// Initializes a new instance of the <see cref="TabRequest"/> class.
15+
/// </summary>
16+
public TabRequest()
17+
{
18+
CustomInit();
19+
}
20+
21+
/// <summary>
22+
/// Gets or sets current tab entity request context.
23+
/// </summary>
24+
/// <value>
25+
/// Tab context for this <see cref="TabRequest"/>.
26+
/// </value>
27+
[JsonProperty(PropertyName = "tabContext")]
28+
public TabEntityContext TabEntityContext { get; set; }
29+
30+
/// <summary>
31+
/// Gets or sets current user context, i.e., the current theme.
32+
/// </summary>
33+
/// <value>
34+
/// Current user context, i.e., the current theme.
35+
/// </value>
36+
[JsonProperty(PropertyName = "context")]
37+
public TabContext Context { get; set; }
38+
39+
/// <summary>
40+
/// Gets or sets state, which is the magic code for OAuth Flow.
41+
/// </summary>
42+
/// <value>
43+
/// State, which is the magic code for OAuth Flow.
44+
/// </value>
45+
[JsonProperty(PropertyName = "state")]
46+
public string State { get; set; }
47+
48+
/// <summary>
49+
/// An initialization method that performs custom operations like setting defaults.
50+
/// </summary>
51+
partial void CustomInit();
52+
}
53+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
namespace Microsoft.Bot.Schema.Teams
5+
{
6+
using Newtonsoft.Json;
7+
8+
/// <summary>
9+
/// Envelope for Card Tab Response Payload.
10+
/// </summary>
11+
public partial class TabResponse
12+
{
13+
/// <summary>
14+
/// Initializes a new instance of the <see cref="TabResponse"/> class.
15+
/// </summary>
16+
public TabResponse()
17+
{
18+
CustomInit();
19+
}
20+
21+
/// <summary>
22+
/// Gets or sets the response to the tab/fetch message.
23+
/// Possible values for the tab type include: 'continue', 'auth' or 'silentAuth'.
24+
/// </summary>
25+
/// <value>
26+
/// Cards in response to a <see cref="TabRequest"/>.
27+
/// </value>
28+
[JsonProperty(PropertyName = "tab")]
29+
public TabResponsePayload Tab { get; set; }
30+
31+
/// <summary>
32+
/// An initialization method that performs custom operations like setting defaults.
33+
/// </summary>
34+
partial void CustomInit();
35+
}
36+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
namespace Microsoft.Bot.Schema.Teams
5+
{
6+
using Newtonsoft.Json;
7+
8+
/// <summary>
9+
/// Envelope for cards for a Tab request.
10+
/// </summary>
11+
public partial class TabResponseCard
12+
{
13+
/// <summary>
14+
/// Initializes a new instance of the <see cref="TabResponseCard"/> class.
15+
/// </summary>
16+
public TabResponseCard()
17+
{
18+
CustomInit();
19+
}
20+
21+
/// <summary>
22+
/// Gets or sets adaptive card for this card tab response.
23+
/// </summary>
24+
/// <value>
25+
/// Cards for this <see cref="TabResponse"/>.
26+
/// </value>
27+
[JsonProperty(PropertyName = "card")]
28+
public object Card { get; set; }
29+
30+
/// <summary>
31+
/// An initialization method that performs custom operations like setting defaults.
32+
/// </summary>
33+
partial void CustomInit();
34+
}
35+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
namespace Microsoft.Bot.Schema.Teams
5+
{
6+
using System.Collections.Generic;
7+
using Newtonsoft.Json;
8+
9+
/// <summary>
10+
/// Envelope for cards for a <see cref="TabResponse"/>.
11+
/// </summary>
12+
public partial class TabResponseCards
13+
{
14+
/// <summary>
15+
/// Initializes a new instance of the <see cref="TabResponseCards"/> class.
16+
/// </summary>
17+
public TabResponseCards()
18+
{
19+
CustomInit();
20+
}
21+
22+
/// <summary>
23+
/// Gets or sets adaptive cards for this card tab response.
24+
/// </summary>
25+
/// <value>
26+
/// Cards for this <see cref="TabResponse"/>.
27+
/// </value>
28+
[JsonProperty(PropertyName = "cards")]
29+
#pragma warning disable CA2227 // Collection properties should be read only
30+
public IList<object> Cards { get; set; }
31+
#pragma warning restore CA2227 // Collection properties should be read only
32+
33+
/// <summary>
34+
/// An initialization method that performs custom operations like setting defaults.
35+
/// </summary>
36+
partial void CustomInit();
37+
}
38+
}

0 commit comments

Comments
 (0)