Skip to content

Commit

Permalink
Support Bot new invoke type: config (#6632)
Browse files Browse the repository at this point in the history
* Support bot new invoke type: config

* address comments

---------

Co-authored-by: Ying Du <yingdu@microsoft.com>
  • Loading branch information
yingduyingdu and Ying Du authored Jun 9, 2023
1 parent 850944e commit 18524da
Show file tree
Hide file tree
Showing 14 changed files with 431 additions and 0 deletions.
32 changes: 32 additions & 0 deletions libraries/Microsoft.Bot.Builder/Teams/TeamsActivityHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,12 @@ protected override async Task<InvokeResponse> OnInvokeActivityAsync(ITurnContext
case "tab/submit":
return CreateInvokeResponse(await OnTeamsTabSubmitAsync(turnContext, SafeCast<TabSubmit>(turnContext.Activity.Value), cancellationToken).ConfigureAwait(false));

case "config/fetch":
return CreateInvokeResponse(await OnTeamsConfigFetchAsync(turnContext, turnContext.Activity.Value as JObject, cancellationToken).ConfigureAwait(false));

case "config/submit":
return CreateInvokeResponse(await OnTeamsConfigSubmitAsync(turnContext, turnContext.Activity.Value as JObject, cancellationToken).ConfigureAwait(false));

default:
return await base.OnInvokeActivityAsync(turnContext, cancellationToken).ConfigureAwait(false);
}
Expand Down Expand Up @@ -431,6 +437,32 @@ protected virtual Task<TabResponse> OnTeamsTabSubmitAsync(ITurnContext<IInvokeAc
throw new InvokeResponseException(HttpStatusCode.NotImplemented);
}

/// <summary>
/// Override this in a derived class to provide logic for when a config is fetched.
/// </summary>
/// <param name="turnContext">A strongly-typed context object for this turn.</param>
/// <param name="configData">The config fetch invoke request value payload.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects
/// or threads to receive notice of cancellation.</param>
/// <returns>A Config Response for the request.</returns>
protected virtual Task<ConfigResponseBase> OnTeamsConfigFetchAsync(ITurnContext<IInvokeActivity> turnContext, JObject configData, CancellationToken cancellationToken)
{
throw new InvokeResponseException(HttpStatusCode.NotImplemented);
}

/// <summary>
/// Override this in a derived class to provide logic for when a config is submitted.
/// </summary>
/// <param name="turnContext">A strongly-typed context object for this turn.</param>
/// <param name="configData">The config fetch invoke request value payload.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects
/// or threads to receive notice of cancellation.</param>
/// <returns>A Config Response for the request.</returns>
protected virtual Task<ConfigResponseBase> OnTeamsConfigSubmitAsync(ITurnContext<IInvokeActivity> turnContext, JObject configData, CancellationToken cancellationToken)
{
throw new InvokeResponseException(HttpStatusCode.NotImplemented);
}

/// <summary>
/// Invoked when a conversation update activity is received from the channel.
/// Conversation update activities are useful when it comes to responding to users being added to or removed from the channel.
Expand Down
43 changes: 43 additions & 0 deletions libraries/Microsoft.Bot.Schema/Teams/BotConfigAuth.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using Newtonsoft.Json;

namespace Microsoft.Bot.Schema.Teams
{
/// <summary>
/// Specifies bot config auth, including type and suggestedActions.
/// </summary>
public partial class BotConfigAuth
{
/// <summary>
/// Initializes a new instance of the <see cref="BotConfigAuth"/> class.
/// </summary>
public BotConfigAuth()
{
CustomInit();
}

/// <summary>
/// Gets or sets type of bot config auth.
/// </summary>
/// <value>
/// The type of bot config auth.
/// </value>
[JsonProperty(PropertyName = "type")]
public string Type { get; set; } = "auth";

/// <summary>
/// Gets or sets suggested actions.
/// </summary>
/// <value>
/// The suggested actions of bot config auth.
/// </value>
[JsonProperty(PropertyName = "suggestedActions")]
public SuggestedActions SuggestedActions { get; set; }

/// <summary>
/// An initialization method that performs custom operations like setting defaults.
/// </summary>
partial void CustomInit();
}
}
24 changes: 24 additions & 0 deletions libraries/Microsoft.Bot.Schema/Teams/ConfigAuthResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

namespace Microsoft.Bot.Schema.Teams
{
/// <summary>
/// Envelope for Config Auth Response.
/// </summary>
public partial class ConfigAuthResponse : ConfigResponse<BotConfigAuth>
{
/// <summary>
/// Initializes a new instance of the <see cref="ConfigAuthResponse"/> class.
/// </summary>
public ConfigAuthResponse()
{
CustomInit();
}

/// <summary>
/// An initialization method that performs custom operations like setting defaults.
/// </summary>
partial void CustomInit();
}
}
45 changes: 45 additions & 0 deletions libraries/Microsoft.Bot.Schema/Teams/ConfigResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

namespace Microsoft.Bot.Schema.Teams
{
using Newtonsoft.Json;

/// <summary>
/// Envelope for Config Response Payload.
/// </summary>
/// <typeparam name="T">The first generic type parameter.</typeparam>.
public partial class ConfigResponse<T> : ConfigResponseBase
{
/// <summary>
/// Initializes a new instance of the <see cref="ConfigResponse{T}"/> class.
/// </summary>
public ConfigResponse()
: base("config")
{
CustomInit();
}

/// <summary>
/// Gets or sets the response to the config message.
/// Possible values for the config type include: 'auth'or 'task'.
/// </summary>
/// <value>
/// Response to a config request.
/// </value>
[JsonProperty(PropertyName = "config")]
public T Config { get; set; }

/// <summary>
/// Gets or sets response cache Info.
/// </summary>
/// <value> Value of cache info. </value>
[JsonProperty(PropertyName = "cacheInfo")]
public CacheInfo CacheInfo { get; set; }

/// <summary>
/// An initialization method that performs custom operations like setting defaults.
/// </summary>
partial void CustomInit();
}
}
36 changes: 36 additions & 0 deletions libraries/Microsoft.Bot.Schema/Teams/ConfigResponseBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using Newtonsoft.Json;

namespace Microsoft.Bot.Schema.Teams
{
/// <summary>
/// Specifies Invoke response base including response type.
/// </summary>
public partial class ConfigResponseBase
{
/// <summary>
/// Initializes a new instance of the <see cref="ConfigResponseBase"/> class.
/// </summary>
protected ConfigResponseBase()
{
}

/// <summary>
/// Initializes a new instance of the <see cref="ConfigResponseBase"/> class.
/// </summary>
/// <param name="responseType"> response type for invoke.</param>
protected ConfigResponseBase(string responseType)
{
ResponseType = responseType;
}

/// <summary>
/// Gets or sets response type invoke request.
/// </summary>
/// <value> Invoke request response type.</value>
[JsonProperty("responseType")]
public string ResponseType { get; set; }
}
}
24 changes: 24 additions & 0 deletions libraries/Microsoft.Bot.Schema/Teams/ConfigTaskResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

namespace Microsoft.Bot.Schema.Teams
{
/// <summary>
/// Envelope for Config Task Response.
/// </summary>
public partial class ConfigTaskResponse : ConfigResponse<TaskModuleResponseBase>
{
/// <summary>
/// Initializes a new instance of the <see cref="ConfigTaskResponse"/> class.
/// </summary>
public ConfigTaskResponse()
{
CustomInit();
}

/// <summary>
/// An initialization method that performs custom operations like setting defaults.
/// </summary>
partial void CustomInit();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,66 @@ void CaptureSend(Activity[] arg)
Assert.Equal(501, ((InvokeResponse)activitiesToSend[0].Value).Status);
}

[Fact]
public async Task TestConfigFetch()
{
// Arrange
var activity = new Activity
{
Type = ActivityTypes.Invoke,
Name = "config/fetch",
Value = JObject.Parse(@"{""data"":{""key"":""value"",""type"":""config / fetch""},""context"":{""theme"":""default""}}"),
};

Activity[] activitiesToSend = null;
void CaptureSend(Activity[] arg)
{
activitiesToSend = arg;
}

var turnContext = new TurnContext(new SimpleAdapter(CaptureSend), activity);

// Act
var bot = new TestActivityHandler();
await ((IBot)bot).OnTurnAsync(turnContext);

// Assert
Assert.NotNull(activitiesToSend);
Assert.Single(activitiesToSend);
Assert.IsType<InvokeResponse>(activitiesToSend[0].Value);
Assert.Equal(501, ((InvokeResponse)activitiesToSend[0].Value).Status);
}

[Fact]
public async Task TestConfigSubmit()
{
// Arrange
var activity = new Activity
{
Type = ActivityTypes.Invoke,
Name = "config/submit",
Value = JObject.Parse(@"{""data"":{""key"":""value"",""type"":""config / submit""},""context"":{""theme"":""default""}}"),
};

Activity[] activitiesToSend = null;
void CaptureSend(Activity[] arg)
{
activitiesToSend = arg;
}

var turnContext = new TurnContext(new SimpleAdapter(CaptureSend), activity);

// Act
var bot = new TestActivityHandler();
await ((IBot)bot).OnTurnAsync(turnContext);

// Assert
Assert.NotNull(activitiesToSend);
Assert.Single(activitiesToSend);
Assert.IsType<InvokeResponse>(activitiesToSend[0].Value);
Assert.Equal(501, ((InvokeResponse)activitiesToSend[0].Value).Status);
}

[Fact]
public async Task TestFileConsentAcceptImplemented()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1052,6 +1052,72 @@ void CaptureSend(Activity[] arg)
Assert.Equal(200, ((InvokeResponse)activitiesToSend[0].Value).Status);
}

[Fact]
public async Task TestConfigFetch()
{
// Arrange
var activity = new Activity
{
Type = ActivityTypes.Invoke,
Name = "config/fetch",
Value = JObject.Parse(@"{""data"":{""key"":""value"",""type"":""config / fetch""},""context"":{""theme"":""default""}}"),
};

Activity[] activitiesToSend = null;
void CaptureSend(Activity[] arg)
{
activitiesToSend = arg;
}

var turnContext = new TurnContext(new SimpleAdapter(CaptureSend), activity);

// Act
var bot = new TestActivityHandler();
await ((IBot)bot).OnTurnAsync(turnContext);

// Assert
Assert.Equal(2, bot.Record.Count);
Assert.Equal("OnInvokeActivityAsync", bot.Record[0]);
Assert.Equal("OnTeamsConfigFetchAsync", bot.Record[1]);
Assert.NotNull(activitiesToSend);
Assert.Single(activitiesToSend);
Assert.IsType<InvokeResponse>(activitiesToSend[0].Value);
Assert.Equal(200, ((InvokeResponse)activitiesToSend[0].Value).Status);
}

[Fact]
public async Task TestConfigSubmit()
{
// Arrange
var activity = new Activity
{
Type = ActivityTypes.Invoke,
Name = "config/submit",
Value = JObject.Parse(@"{""data"":{""key"":""value"",""type"":""config / submit""},""context"":{""theme"":""default""}}"),
};

Activity[] activitiesToSend = null;
void CaptureSend(Activity[] arg)
{
activitiesToSend = arg;
}

var turnContext = new TurnContext(new SimpleAdapter(CaptureSend), activity);

// Act
var bot = new TestActivityHandler();
await ((IBot)bot).OnTurnAsync(turnContext);

// Assert
Assert.Equal(2, bot.Record.Count);
Assert.Equal("OnInvokeActivityAsync", bot.Record[0]);
Assert.Equal("OnTeamsConfigSubmitAsync", bot.Record[1]);
Assert.NotNull(activitiesToSend);
Assert.Single(activitiesToSend);
Assert.IsType<InvokeResponse>(activitiesToSend[0].Value);
Assert.Equal(200, ((InvokeResponse)activitiesToSend[0].Value).Status);
}

[Fact]
public async Task TestSigninVerifyState()
{
Expand Down Expand Up @@ -1609,6 +1675,20 @@ protected override Task<TabResponse> OnTeamsTabSubmitAsync(ITurnContext<IInvokeA
return Task.FromResult(new TabResponse());
}

protected override Task<ConfigResponseBase> OnTeamsConfigFetchAsync(ITurnContext<IInvokeActivity> turnContext, JObject configRequest, CancellationToken cancellationToken)
{
ConfigResponseBase configResponse = new ConfigTaskResponse();
Record.Add(MethodBase.GetCurrentMethod().Name);
return Task.FromResult(configResponse);
}

protected override Task<ConfigResponseBase> OnTeamsConfigSubmitAsync(ITurnContext<IInvokeActivity> turnContext, JObject configRequest, CancellationToken cancellationToken)
{
ConfigResponseBase configResponse = new ConfigTaskResponse();
Record.Add(MethodBase.GetCurrentMethod().Name);
return Task.FromResult(configResponse);
}

protected override Task OnEventActivityAsync(ITurnContext<IEventActivity> turnContext, CancellationToken cancellationToken)
{
Record.Add(MethodBase.GetCurrentMethod().Name);
Expand Down
Loading

0 comments on commit 18524da

Please sign in to comment.