Skip to content

Commit

Permalink
SharePoint ACE Activity Handler (#6695)
Browse files Browse the repository at this point in the history
* SharePoint ACE Activity Handler

* adding schema - wip

* all components

* CardViewParameters

* Helper methods for card view parameters

* Handle action and tests

* sync with the changes from local copy

* extra comment

* properties instead of customProperties, sign in card view

* latest changes

* correct working implementation

* remove SharePoint channel

* updated API and comments

* move action type to base class

* updated comments

* comment update

* getting tests schema back

---------

Co-authored-by: Alex Terentiev <[email protected]>
AJIXuMuK and Alex Terentiev authored Nov 1, 2023
1 parent 617394d commit 4d64c20
Showing 66 changed files with 3,736 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Bot.Connector;
using Microsoft.Bot.Schema;
using Microsoft.Bot.Schema.SharePoint;
using Microsoft.Bot.Schema.Teams;
using Newtonsoft.Json.Linq;

namespace Microsoft.Bot.Builder.SharePoint
{
/// <summary>
/// The SharePointActivityHandler is derived from ActivityHandler. It adds support for
/// the SharePoint specific events and interactions.
/// </summary>
public class SharePointActivityHandler : ActivityHandler
{
/// <summary>
/// Invoked when an invoke activity is received from the connector.
/// Invoke activities can be used to communicate many different things.
/// </summary>
/// <param name="turnContext">A strongly-typed context object for this turn.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects
/// or threads to receive notice of cancellation.</param>
/// <returns>A task that represents the work queued to execute.</returns>
/// <remarks>
/// Invoke activities communicate programmatic commands from a client or channel to a bot.
/// The meaning of an invoke activity is defined by the <see cref="IInvokeActivity.Name"/> property,
/// which is meaningful within the scope of a channel.
/// </remarks>
protected override async Task<InvokeResponse> OnInvokeActivityAsync(ITurnContext<IInvokeActivity> turnContext, CancellationToken cancellationToken)
{
try
{
if (turnContext.Activity.Name == null)
{
throw new NotSupportedException();
}
else
{
switch (turnContext.Activity.Name)
{
case "cardExtension/getCardView":
return CreateInvokeResponse(await OnSharePointTaskGetCardViewAsync(turnContext, SafeCast<AceRequest>(turnContext.Activity.Value), cancellationToken).ConfigureAwait(false));

case "cardExtension/getQuickView":
return CreateInvokeResponse(await OnSharePointTaskGetQuickViewAsync(turnContext, SafeCast<AceRequest>(turnContext.Activity.Value), cancellationToken).ConfigureAwait(false));

case "cardExtension/getPropertyPaneConfiguration":
return CreateInvokeResponse(await OnSharePointTaskGetPropertyPaneConfigurationAsync(turnContext, SafeCast<AceRequest>(turnContext.Activity.Value), cancellationToken).ConfigureAwait(false));

case "cardExtension/setPropertyPaneConfiguration":
BaseHandleActionResponse setPropPaneConfigResponse = await OnSharePointTaskSetPropertyPaneConfigurationAsync(turnContext, SafeCast<AceRequest>(turnContext.Activity.Value), cancellationToken).ConfigureAwait(false);
ValidateSetPropertyPaneConfigurationResponse(setPropPaneConfigResponse);
return CreateInvokeResponse(setPropPaneConfigResponse);

case "cardExtension/handleAction":
return CreateInvokeResponse(await OnSharePointTaskHandleActionAsync(turnContext, SafeCast<AceRequest>(turnContext.Activity.Value), cancellationToken).ConfigureAwait(false));
}
}
}
catch (InvokeResponseException e)
{
return e.CreateInvokeResponse();
}

return await base.OnInvokeActivityAsync(turnContext, cancellationToken).ConfigureAwait(false);
}

/// <summary>
/// Override this in a derived class to provide logic for when a card view is fetched.
/// </summary>
/// <param name="turnContext">A strongly-typed context object for this turn.</param>
/// <param name="aceRequest">The ACE 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 Card View Response for the request.</returns>
protected virtual Task<CardViewResponse> OnSharePointTaskGetCardViewAsync(ITurnContext<IInvokeActivity> turnContext, AceRequest aceRequest, CancellationToken cancellationToken)
{
throw new InvokeResponseException(HttpStatusCode.NotImplemented);
}

/// <summary>
/// Override this in a derived class to provide logic for when a quick view is fetched.
/// </summary>
/// <param name="turnContext">A strongly-typed context object for this turn.</param>
/// <param name="aceRequest">The ACE 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 Quick View Response for the request.</returns>
protected virtual Task<QuickViewResponse> OnSharePointTaskGetQuickViewAsync(ITurnContext<IInvokeActivity> turnContext, AceRequest aceRequest, CancellationToken cancellationToken)
{
throw new InvokeResponseException(HttpStatusCode.NotImplemented);
}

/// <summary>
/// Override this in a derived class to provide logic for getting configuration pane properties.
/// </summary>
/// <param name="turnContext">A strongly-typed context object for this turn.</param>
/// <param name="aceRequest">The ACE 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 Property Pane Configuration Response for the request.</returns>
protected virtual Task<GetPropertyPaneConfigurationResponse> OnSharePointTaskGetPropertyPaneConfigurationAsync(ITurnContext<IInvokeActivity> turnContext, AceRequest aceRequest, CancellationToken cancellationToken)
{
throw new InvokeResponseException(HttpStatusCode.NotImplemented);
}

/// <summary>
/// Override this in a derived class to provide logic for setting configuration pane properties.
/// </summary>
/// <param name="turnContext">A strongly-typed context object for this turn.</param>
/// <param name="aceRequest">The ACE 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>Card view or no-op action response.</returns>
/// <remarks>The handler will fail with 500 status code if the response is of type <see cref="QuickViewHandleActionResponse" />.</remarks>
protected virtual Task<BaseHandleActionResponse> OnSharePointTaskSetPropertyPaneConfigurationAsync(ITurnContext<IInvokeActivity> turnContext, AceRequest aceRequest, CancellationToken cancellationToken)
{
throw new InvokeResponseException(HttpStatusCode.NotImplemented);
}

/// <summary>
/// Override this in a derived class to provide logic for handling ACE actions.
/// </summary>
/// <param name="turnContext">A strongly-typed context object for this turn.</param>
/// <param name="aceRequest">The ACE 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 handle action response.</returns>
protected virtual Task<BaseHandleActionResponse> OnSharePointTaskHandleActionAsync(ITurnContext<IInvokeActivity> turnContext, AceRequest aceRequest, CancellationToken cancellationToken)
{
throw new InvokeResponseException(HttpStatusCode.NotImplemented);
}

/// <summary>
/// Safely casts an object to an object of type <typeparamref name="T"/> .
/// </summary>
/// <param name="value">The object to be casted.</param>
/// <returns>The object casted in the new type.</returns>
private static T SafeCast<T>(object value)
{
var obj = value as JObject;
if (obj == null)
{
throw new InvokeResponseException(HttpStatusCode.BadRequest, $"expected type '{value.GetType().Name}'");
}

return obj.ToObject<T>();
}

private void ValidateSetPropertyPaneConfigurationResponse(BaseHandleActionResponse response)
{
if (response is QuickViewHandleActionResponse)
{
throw new InvokeResponseException(HttpStatusCode.InternalServerError, "Response for SetPropertyPaneConfiguration action can't be of QuickView type.");
}
}
}
}
1 change: 1 addition & 0 deletions libraries/Microsoft.Bot.Schema/Microsoft.Bot.Schema.csproj
Original file line number Diff line number Diff line change
@@ -25,6 +25,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="AdaptiveCards" Version="1.2.3" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
</ItemGroup>
</Project>
95 changes: 95 additions & 0 deletions libraries/Microsoft.Bot.Schema/SharePoint/AceData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Text;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Linq;

namespace Microsoft.Bot.Schema.SharePoint
{
/// <summary>
/// SharePoint Ace Data object.
/// </summary>
public class AceData
{
/// <summary>
/// Initializes a new instance of the <see cref="AceData"/> class.
/// </summary>
public AceData()
{
// Do nothing
}

/// <summary>
/// This enum contains the different types of card templates available in the SPFx framework.
/// </summary>
public enum AceCardSize
{
/// <summary>
/// Medium
/// </summary>
Medium,

/// <summary>
/// Large
/// </summary>
Large
}

/// <summary>
/// Gets or Sets the card size of the adaptive card extension of type <see cref="AceCardSize"/> enum.
/// </summary>
/// <value>This value is the size of the adaptive card extension.</value>
[JsonProperty(PropertyName = "cardSize")]
[JsonConverter(typeof(StringEnumConverter))]
public AceCardSize CardSize { get; set; }

/// <summary>
/// Gets or Sets the version of the data of type <see cref="string"/>.
/// </summary>
/// <value>This value is the version of the adaptive card extension.</value>
/// <remarks>Although there is no restriction on the format of this property, it is recommended to use semantic versioning.</remarks>
[JsonProperty(PropertyName = "dataVersion")]
public string DataVersion { get; set; }

/// <summary>
/// Gets or Sets the unique id (Guid) of type <see cref="string"/>.
/// </summary>
/// <value>This value is the ID of the adaptive card extension.</value>
[JsonProperty(PropertyName = "id")]
public string Id { get; set; }

/// <summary>
/// Gets or Sets the title of type <see cref="string"/>.
/// </summary>
/// <value>This value is the title of the adaptive card extension.</value>
[JsonProperty(PropertyName = "title")]
public string Title { get; set; }

/// <summary>
/// Gets or Sets the description of type <see cref="string"/>.
/// </summary>
/// <value>This value is the description of the adaptive card extension.</value>
[JsonProperty(PropertyName = "description")]
public string Description { get; set; }

/// <summary>
/// Gets or Sets the icon property of type <see cref="string"/>.
/// </summary>
/// <value>This value is the icon of the adaptive card extension.</value>
[JsonProperty(PropertyName = "iconProperty")]
public string IconProperty { get; set; }

/// <summary>
/// Gets or Sets the property bag of type <see cref="JObject"/>.
/// </summary>
/// <value>This value is the property bag of the adaptive card extension.</value>
[JsonProperty(PropertyName = "properties")]
#pragma warning disable CA2227
public JObject Properties { get; set; }
}
}
48 changes: 48 additions & 0 deletions libraries/Microsoft.Bot.Schema/SharePoint/AceRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.Text;
using Newtonsoft.Json;

namespace Microsoft.Bot.Schema.SharePoint
{
/// <summary>
/// ACE invoke request payload.
/// </summary>
public class AceRequest
{
/// <summary>
/// Initializes a new instance of the <see cref="AceRequest"/> class.
/// </summary>
public AceRequest()
{
}

/// <summary>
/// Initializes a new instance of the <see cref="AceRequest"/> class.
/// </summary>
/// <param name="data">ACE request data.</param>
/// <param name="properties">ACE properties data.</param>
public AceRequest(object data = default, object properties = default)
{
Data = data;
Properties = properties;
}

/// <summary>
/// Gets or sets user ACE request data.
/// </summary>
/// <value>The ACE request data.</value>
[JsonProperty(PropertyName = "data")]
public object Data { get; set; }

/// <summary>
/// Gets or sets ACE properties data. Free payload with key-value pairs.
/// </summary>
/// <value>ACE Properties object.</value>
[JsonProperty(PropertyName = "properties")]
public object Properties { get; set; }
}
}
28 changes: 28 additions & 0 deletions libraries/Microsoft.Bot.Schema/SharePoint/Actions/BaseAction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.Text;
using Newtonsoft.Json;

namespace Microsoft.Bot.Schema.SharePoint
{
/// <summary>
/// Base Action.
/// </summary>
public class BaseAction
{
[JsonProperty(PropertyName = "type")]
private readonly string type;

/// <summary>
/// Initializes a new instance of the <see cref="BaseAction"/> class.
/// </summary>
/// <param name="actionType">Type of the action.</param>
protected BaseAction(string actionType)
{
this.type = actionType;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using Newtonsoft.Json;

namespace Microsoft.Bot.Schema.SharePoint
{
/// <summary>
/// SharePoint Confirmation Dialog object.
/// </summary>
public class ConfirmationDialog
{
/// <summary>
/// Initializes a new instance of the <see cref="ConfirmationDialog"/> class.
/// </summary>
public ConfirmationDialog()
{
// Do nothing
}

/// <summary>
/// Gets or Sets the title of type <see cref="string"/>.
/// </summary>
/// <value>This value is the title to display.</value>
[JsonProperty(PropertyName = "title")]
public string Title { get; set; }

/// <summary>
/// Gets or Sets the message of type <see cref="string"/>.
/// </summary>
/// <value>This value is the message to display.</value>
[JsonProperty(PropertyName = "message")]
public string Message { get; set; }
}
}
Loading

0 comments on commit 4d64c20

Please sign in to comment.