Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Bot new invoke type: config #6632

Merged
merged 2 commits into from
Jun 9, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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<InvokeResponseBase> 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>
yingduyingdu marked this conversation as resolved.
Show resolved Hide resolved
/// <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<InvokeResponseBase> 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();
}
}
38 changes: 38 additions & 0 deletions libraries/Microsoft.Bot.Schema/Teams/ConfigResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// 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> : InvokeResponseBase
{
/// <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>
/// 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/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();
}
}
43 changes: 43 additions & 0 deletions libraries/Microsoft.Bot.Schema/Teams/InvokeResponseBase.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 Invoke response base including response type.
/// </summary>
public partial class InvokeResponseBase
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason to name it as InvokeResponseBase, will we have a class of InvokeResponse? I saw we have both classes for task.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will align with our class design in the reference repo.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can sync up with you offline for this.

{
/// <summary>
/// Initializes a new instance of the <see cref="InvokeResponseBase"/> class.
/// </summary>
protected InvokeResponseBase()
{
}

/// <summary>
/// Initializes a new instance of the <see cref="InvokeResponseBase"/> class.
/// </summary>
/// <param name="responseType"> response type for invoke.</param>
protected InvokeResponseBase(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; }

/// <summary>
/// Gets or sets response cache Info.
/// </summary>
/// <value> Value of cache info. </value>
[JsonProperty(PropertyName = "cacheInfo")]
public CacheInfo CacheInfo { get; set; }
}
}
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);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is commented out.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated in the next iteration

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);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is commented.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated in the next iteration

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<InvokeResponseBase> OnTeamsConfigFetchAsync(ITurnContext<IInvokeActivity> turnContext, JObject configRequest, CancellationToken cancellationToken)
{
InvokeResponseBase configResponse = new ConfigTaskResponse();
Record.Add(MethodBase.GetCurrentMethod().Name);
return Task.FromResult(configResponse);
}

protected override Task<InvokeResponseBase> OnTeamsConfigSubmitAsync(ITurnContext<IInvokeActivity> turnContext, JObject configRequest, CancellationToken cancellationToken)
{
InvokeResponseBase 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