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

Ensured all Activities stored in transcript have Ids #3701

Merged
merged 8 commits into from
Apr 15, 2020

Conversation

Zerryth
Copy link
Contributor

@Zerryth Zerryth commented Apr 7, 2020

Fixes #3665


As, done in JS's TranscriptMiddlewareLogger already, ensure that all cloned activities in logger have Ids, so that when logged into MemoryTranscriptStore, each Item also has an Id.

If an activity doesn't have an id, MemoryTranscriptStore will generate an Id for that activity/Item for the transcript storage, prefixing it with "g_" for "generated", as done in JS.

Context

  • Not all Activities created have Ids assigned to them (Activity spec in botframework-sdk)
  • ContinuationToken within the MemoryTranscriptStore.GetTranscriptActivitiesAsync relies on last Activity.Id of loaded PagedResult
  • Because of the above 2 bullets, if an Activity without an Id is the last item in the PagedResult, we may end up infinitely loading the same PagedResult over and over again.

Testing

In order to test that activities with null ids are assigned ids properly in transcript storage, I had to create a custom TestAdapter, since the one "off the shelf" automatically fills in an activity id, never allowing for null ids to pass through to the TranscriptMiddlewareLogger layer.

  • Would have created an own separate class, however needed to derive from it, since TestFlow is tightly coupled to TestAdapter (it requires an adapter of specifically type TestAdapter instead of an interface)

@Zerryth Zerryth marked this pull request as ready for review April 8, 2020 23:50
@@ -134,7 +134,27 @@ public async Task OnTurnAsync(ITurnContext turnContext, NextDelegate nextTurn, C
private static IActivity CloneActivity(IActivity activity)
{
activity = JsonConvert.DeserializeObject<Activity>(JsonConvert.SerializeObject(activity, _jsonSettings));
return activity;
var activityWithId = EnsureActivityHasId(activity);

Copy link
Contributor

Choose a reason for hiding this comment

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

Alternatively, you can just: return EnsureActivityHasId(activity);

{
var activityWithId = activity;

if (activity == null)
Copy link
Contributor

Choose a reason for hiding this comment

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

Better to have the check for null before creating activityWithId

/// </summary>
public class AllowNullIdTestAdapter : TestAdapter
{
private bool _sendTraceActivity;
Copy link
Contributor

Choose a reason for hiding this comment

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

readonly?

private readonly object _conversationLock = new object();
private readonly object _activeQueueLock = new object();
private Queue<TaskCompletionSource<IActivity>> _queuedRequests = new Queue<TaskCompletionSource<IActivity>>();
private int _nextId = 0;
Copy link
Contributor

Choose a reason for hiding this comment

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

readonly?

/// </summary>
/// <returns>The next activity in the queue; or null, if the queue is empty.</returns>
/// <remarks>A <see cref="TestFlow"/> object calls this to get the next response from the bot.</remarks>
public new IActivity GetNextReply()
Copy link
Contributor

Choose a reason for hiding this comment

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

This can be private.

/// </summary>
/// <param name="cancellationToken">cancellation Token.</param>
/// <returns>activity when it's available or canceled task if it is canceled.</returns>
public new Task<IActivity> GetNextReplyAsync(CancellationToken cancellationToken = default)
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this method used?

/// <returns>An appropriate message activity.</returns>
/// <remarks>A <see cref="TestFlow"/> object calls this to get a message activity
/// appropriate to the current conversation.</remarks>
public new Activity MakeActivity(string text = null)
Copy link
Contributor

Choose a reason for hiding this comment

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

Can this be private?

Copy link
Contributor

@tomlm tomlm left a comment

Choose a reason for hiding this comment

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

:shipit:

@Zerryth
Copy link
Contributor Author

Zerryth commented Apr 15, 2020

Well then...
@mrivera-ms thank you for reviewing.
All of your points were valid!
Just got merged before I pushed the changes though 😲

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

MemoryTranscriptStore.GetTranscriptActivitiesAsync may end up in infinite loop
3 participants