Skip to content

Commit

Permalink
feat(sdk): notification sdk enhancement for .NET
Browse files Browse the repository at this point in the history
  • Loading branch information
qinezh committed Apr 21, 2023
1 parent c63e836 commit 057fb5f
Show file tree
Hide file tree
Showing 14 changed files with 583 additions and 93 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
namespace Microsoft.TeamsFx.Test.Conversation
{
using Microsoft.Bot.Schema;
using Microsoft.TeamsFx.Conversation;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Threading.Tasks;

[Obsolete]
internal class DefaultConversationReferenceStoreTest
{
private readonly InMemoryStorage _storage;
private readonly DefaultConversationReferenceStore _store;

public DefaultConversationReferenceStoreTest()
{
_storage = new InMemoryStorage();
_store = new DefaultConversationReferenceStore(_storage);
}

[TestMethod]
public async Task Add_NonExistItem()
{
var storage = new InMemoryStorage();
var store = new DefaultConversationReferenceStore(storage);

const string key = "key";
const string activityId = "add_nonexistitem";
var reference = new ConversationReference
{
ActivityId = activityId
};
var isAdded = await store.Add(key, reference, new ConversationReferenceStoreAddOptions { });

Assert.IsTrue(isAdded);
Assert.Equals(activityId, storage.Items[key].ActivityId);
}

[TestMethod]
public async Task Add_OverwriteExistItem()
{
var storage = new InMemoryStorage();
var store = new DefaultConversationReferenceStore(storage);

const string key = "key";
const string oldActivityId = "add_existitem_old";
const string newActivityId = "add_existitem_new";

storage.Items[key] = new ConversationReference
{
ActivityId = oldActivityId
};

var reference = new ConversationReference
{
ActivityId = newActivityId
};
var isAdded = await store.Add(key, reference, new ConversationReferenceStoreAddOptions { Overwrite = true });

Assert.IsTrue(isAdded);
Assert.Equals(newActivityId, storage.Items[key].ActivityId);
}

[TestMethod]
public async Task Add_NotOverwriteExistItem()
{
var storage = new InMemoryStorage();
var store = new DefaultConversationReferenceStore(storage);

const string key = "key";
const string oldActivityId = "add_existitem_old";
const string newActivityId = "add_existitem_new";

storage.Items[key] = new ConversationReference
{
ActivityId = oldActivityId
};

var reference = new ConversationReference
{
ActivityId = newActivityId
};
var isAdded = await store.Add(key, reference, new ConversationReferenceStoreAddOptions { Overwrite = false });

Assert.IsFalse(isAdded);
Assert.Equals(oldActivityId, storage.Items[key].ActivityId);
}

[TestMethod]
public async Task Add_NotOverwriteExistItemByDefault()
{
var storage = new InMemoryStorage();
var store = new DefaultConversationReferenceStore(storage);

const string key = "key";
const string oldActivityId = "add_existitem_old";
const string newActivityId = "add_existitem_new";

storage.Items[key] = new ConversationReference
{
ActivityId = oldActivityId
};

var reference = new ConversationReference
{
ActivityId = newActivityId
};
var isAdded = await store.Add(key, reference, new ConversationReferenceStoreAddOptions { });

Assert.IsFalse(isAdded);
Assert.Equals(oldActivityId, storage.Items[key].ActivityId);
}

[TestMethod]
public async Task Remove_ExistItem()
{
var storage = new InMemoryStorage();
var store = new DefaultConversationReferenceStore(storage);

const string key = "key";
const string activityId = "remove_existitem";

var reference = new ConversationReference
{
ActivityId = activityId
};
storage.Items[key] = reference;

var isRemoved = await store.Remove(key, reference);
Assert.IsTrue(isRemoved);
Assert.IsTrue(!storage.Items.ContainsKey(key));
}

[TestMethod]
public async Task Remove_NonExistItem()
{
var storage = new InMemoryStorage();
var store = new DefaultConversationReferenceStore(storage);

const string key = "key";
const string activityId = "remove_existitem";

var reference = new ConversationReference
{
ActivityId = activityId
};

var isRemoved = await store.Remove(key, reference);
Assert.IsFalse(isRemoved);
}

[TestMethod]
public async Task List()
{
var storage = new InMemoryStorage();
var store = new DefaultConversationReferenceStore(storage);

storage.Items.Add("key1", new ConversationReference { ActivityId = "activity-1" });
storage.Items.Add("key2", new ConversationReference { ActivityId = "activity-2" });
storage.Items.Add("key3", new ConversationReference { ActivityId = "activity-3" });

var items = await store.List();
Assert.AreEqual(3, items.Data.Length);
Assert.IsNull(items.ContinuationToken);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Microsoft.Bot.Schema;
using Microsoft.TeamsFx.Conversation;

[Obsolete]
public sealed class InMemoryStorage : INotificationTargetStorage
{
public InMemoryStorage()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ namespace Microsoft.TeamsFx.Test.Conversation
using Microsoft.VisualStudio.TestTools.UnitTesting;

[TestClass]
[Obsolete]
public class LocalFileStorageTest
{
private const string testDir = "./test-local";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using System.Text.Json;

[TestClass]
[Obsolete]
public class NotificationMiddlewareTest
{
private readonly InMemoryStorage _storage;
Expand All @@ -18,7 +19,8 @@ public class NotificationMiddlewareTest
public NotificationMiddlewareTest()
{
_storage = new InMemoryStorage();
_middleware = new NotificationMiddleware(_storage);
var store = new DefaultConversationReferenceStore(_storage);
_middleware = new NotificationMiddleware(store);
}

[TestMethod]
Expand Down Expand Up @@ -185,12 +187,12 @@ public async Task OnTurnAsync_HandleCurrentBotMessaged_Channel()
await _middleware.OnTurnAsync(mockContext.Object, (ctx) => Task.CompletedTask, CancellationToken.None);

Assert.AreEqual(1, _storage.Items.Count);
var reference = _storage.Items.GetValueOrDefault($"_a_{teamId}", null);
var reference = _storage.Items.GetValueOrDefault($"_a_{conversationId}", null);
Assert.IsNotNull(reference);
Assert.AreEqual(activityId, reference.ActivityId);
Assert.AreEqual("x", reference.ChannelId);
Assert.AreEqual("a", reference.Conversation.TenantId);
Assert.AreEqual(teamId, reference.Conversation.Id);
Assert.AreEqual(conversationId, reference.Conversation.Id);
}

[TestMethod]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

namespace Microsoft.TeamsFx.Conversation
{
using Microsoft.Bot.Schema;

[Obsolete]
internal sealed class DefaultConversationReferenceStore : IConversationReferenceStore
{
private readonly INotificationTargetStorage _storage;

public DefaultConversationReferenceStore(INotificationTargetStorage storage)
{
_storage = storage;
}

public async Task<bool> Add(
string key,
ConversationReference reference,
ConversationReferenceStoreAddOptions options,
CancellationToken cancellationToken = default)
{
if (string.IsNullOrEmpty(key))
{
throw new ArgumentException($"{nameof(key)} can't be null or empty.");
}

if (reference == null)
{
throw new ArgumentNullException(nameof(reference));
}

if (options == null)
{
throw new ArgumentNullException(nameof(options));
}

var overwrite = options.Overwrite ?? false;
if (overwrite)
{
await _storage.Write(key, reference, cancellationToken).ConfigureAwait(false);
return true;
}

var result = await _storage.Read(key, cancellationToken).ConfigureAwait(false);
if (result == null)
{
await _storage.Write(key, reference, cancellationToken).ConfigureAwait(false);
return true;
}

return false;
}

public async Task<bool> Remove(
string key,
ConversationReference reference,
CancellationToken cancellationToken = default)
{
if (string.IsNullOrEmpty(key))
{
throw new ArgumentException($"{nameof(key)} can't be null or empty.");
}

if (reference == null)
{
throw new ArgumentNullException(nameof(reference));
}

var result = await _storage.Read(key, cancellationToken).ConfigureAwait(false);
if (result == null)
{
return false;
}

await _storage.Delete(key, cancellationToken).ConfigureAwait(false);
return true;
}

public async Task<PagedData<ConversationReference>> List(
int? pageSize = null,
string continuationToken = null,
CancellationToken cancellationToken = default)
{
var data = await _storage.List(cancellationToken).ConfigureAwait(false);
return new PagedData<ConversationReference> {
Data = data,
ContinuationToken = null,
};
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

namespace Microsoft.TeamsFx.Conversation
{
using Microsoft.Bot.Schema;

/// <summary>
/// Interface for a store provider that manages notification target references.
/// </summary>
public interface IConversationReferenceStore
{
/// <summary>
/// Add a conversation reference to the store. If overwrite, update the existing one, otherwise add when not exist.
/// </summary>
/// <param name="key">The target key.</param>
/// <param name="reference">The conversation reference to be added.</param>
/// <param name="options">The options to add the conversation reference.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>true if added or updated, false if not changed.</returns>
public Task<bool> Add(
string key,
ConversationReference reference,
ConversationReferenceStoreAddOptions options,
CancellationToken cancellationToken = default);

/// <summary>
/// Remove a conversation reference from the store.
/// </summary>
/// <param name="key">The target key.</param>
/// <param name="reference">The conversation reference to be removed.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>true if exist and removed, false if not changed.</returns>
public Task<bool> Remove(
string key,
ConversationReference reference,
CancellationToken cancellationToken = default);


/// <summary>
/// List stored conversation reference by page.
/// </summary>
/// <param name="pageSize">The page size.</param>
/// <param name="continuationToken">The continuation token.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>A paged list of conversation references.</returns>
public Task<PagedData<ConversationReference>> List(
int? pageSize = default,
string continuationToken = default,
CancellationToken cancellationToken = default);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

namespace Microsoft.TeamsFx.Conversation
{
/// <summary>
/// The options to add a conversation reference.
/// </summary>
public class ConversationReferenceStoreAddOptions
{
/// <summary>
/// Gets or sets a value indicating whether to overwrite the existing conversation reference.
/// </summary>
public bool? Overwrite { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
/// <summary>
/// Interface for a storage provider that stores and retrieves notification target references.
/// </summary>
[Obsolete($"Use {nameof(IConversationReferenceStore)} to customize the way to persist bot notification connections instead.")]
public interface INotificationTargetStorage
{
/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ namespace Microsoft.TeamsFx.Conversation

using Microsoft.Bot.Schema;

[Obsolete]
internal sealed class LocalFileStorage : INotificationTargetStorage
{
private const string LocalFileName = ".notification.localstore.json";
Expand Down
Loading

0 comments on commit 057fb5f

Please sign in to comment.