Added FxCop to Microsoft.BotBuilder and addressed errors. (#4141)

* Added FxCop to Microsoft.BotBuilder and addressed errors.
Enabled the SX1309 (FieldNamesShouldBeginWithUnderscores) stylecop rule and addressed the violations on BotBuilder but excluded it from the other projects for now to avoid collisions.
Enabled CA1309 (Use ordinal StringComparison) to the main ruleset and addressed violations.
Added ($NoWarn) to root dir props to make sure tests take this value.

* Enabled AsyncUsage analyzer and fixed violations wherever ir was possible.

* Refactore Task.Run usages to await/async equivalents.

* Updated Adapters folder to use FxCop 3.0

* Refactored ShowTypingMiddleware to avoid using Task.Run so we don't create unnecessary threads.

* Updated constructor to use _httpClient field instead of the expression.
This commit is contained in:
Gabo Gilabert 2020-06-24 23:02:33 -04:00 коммит произвёл GitHub
Родитель c869f80d50
Коммит 6720f2cec6
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
76 изменённых файлов: 663 добавлений и 441 удалений

Просмотреть файл

@ -6,11 +6,16 @@
<Rule Id="UseAsyncSuffix" Action="Error" /> <Rule Id="UseAsyncSuffix" Action="Error" />
<Rule Id="UseConfigureAwait" Action="Error" /> <Rule Id="UseConfigureAwait" Action="Error" />
</Rules> </Rules>
<Rules AnalyzerId="Microsoft.CodeQuality.Analyzers" RuleNamespace="Microsoft.CodeQuality.Analyzers">
<Rules AnalyzerId="Microsoft.CodeAnalysis.CSharp.Features" RuleNamespace="Microsoft.CodeAnalysis.CSharp.Features"> <Rule Id="CA1054" Action="None" /> <!-- UriParametersShouldNotBeStrings -->
<Rule Id="IDE0003" Action="None" /> <Rule Id="CA1062" Action="None" /> <!-- ValidateArgumentsOfPublicMethods -->
</Rules>
<Rules AnalyzerId="Microsoft.NetCore.Analyzers" RuleNamespace="Microsoft.NetCore.Analyzers">
<Rule Id="CA1303" Action="None" /> <!-- DoNotPassLiteralsAsLocalizedParameters -->
</Rules>
<Rules AnalyzerId="Microsoft.NetCore.CSharp.Analyzers" RuleNamespace="Microsoft.NetCore.CSharp.Analyzers">
<Rule Id="CA1309" Action="Error" /> <!-- Use ordinal StringComparison -->
</Rules> </Rules>
<Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.Analyzers"> <Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.Analyzers">
<Rule Id="SA0001" Action="None" /> <Rule Id="SA0001" Action="None" />
<Rule Id="SA0002" Action="Error" /> <Rule Id="SA0002" Action="Error" />
@ -186,15 +191,6 @@
<Rule Id="SA1648" Action="Error" /> <Rule Id="SA1648" Action="Error" />
<Rule Id="SA1649" Action="Error" /> <Rule Id="SA1649" Action="Error" />
<Rule Id="SA1651" Action="Error" /> <Rule Id="SA1651" Action="Error" />
<Rule Id="SX1309" Action="Error" /> <!-- FieldNamesShouldBeginWithUnderscores -->
</Rules> </Rules>
<Rules AnalyzerId="Microsoft.CodeQuality.Analyzers" RuleNamespace="Microsoft.CodeQuality.Analyzers">
<Rule Id="CA1054" Action="None" /> <!-- UriParametersShouldNotBeStrings -->
<Rule Id="CA1062" Action="None" /> <!-- ValidateArgumentsOfPublicMethods -->
</Rules>
<Rules AnalyzerId="Microsoft.NetCore.Analyzers" RuleNamespace="Microsoft.NetCore.Analyzers">
<Rule Id="CA1303" Action="None" /> <!-- DoNotPassLiteralsAsLocalizedParameters -->
</Rules>
</RuleSet> </RuleSet>

Просмотреть файл

@ -59,7 +59,7 @@
Suppress a warning about upcoming deprecation of PackageLicenseUrl. When embedding licenses are supported, Suppress a warning about upcoming deprecation of PackageLicenseUrl. When embedding licenses are supported,
replace PackageLicenseUrl with PackageLicenseExpression. replace PackageLicenseUrl with PackageLicenseExpression.
--> -->
<NoWarn>NU5125</NoWarn> <NoWarn>$(NoWarn);NU5125</NoWarn>
<PublishRepositoryUrl>true</PublishRepositoryUrl> <PublishRepositoryUrl>true</PublishRepositoryUrl>
<IncludeSymbols>true</IncludeSymbols> <IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat> <SymbolPackageFormat>snupkg</SymbolPackageFormat>

Просмотреть файл

@ -1,8 +1,9 @@
<Project> <Project>
<!-- Contains common properties that apply to projects under the FunctionalTests folder --> <!-- Contains common properties that apply to projects under the FunctionalTests folder -->
<PropertyGroup> <PropertyGroup>
<!-- For tests, we don't generate documentation. Supress related rules. --> <!-- SA0001;CS1573,CS1591,CS1712: For tests, we don't generate documentation. Supress related rules. -->
<NoWarn>$(NoWarn);SA0001;CS1573,CS1591,CS1712</NoWarn> <!-- SX1309: FieldNamesShouldBeginWithUnderscores should be fixed as part of https://github.com/microsoft/botframework-sdk/issues/5933 -->
<NoWarn>$(NoWarn);SA0001;CS1573;CS1591;CS1712;SX1309</NoWarn>
</PropertyGroup> </PropertyGroup>
<!-- This ensures that Directory.Build.props in parent folders are merged with this one --> <!-- This ensures that Directory.Build.props in parent folders are merged with this one -->

Просмотреть файл

@ -1,7 +1,11 @@
<Project> <Project>
<!-- Contains common properties that apply to projects under the Adapters folder --> <!-- Contains common properties that apply to projects under the Adapters folder -->
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.6"> <PackageReference Include="AsyncUsageAnalyzers" Version="1.0.0-alpha003">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="3.0.0">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>

Просмотреть файл

@ -81,7 +81,7 @@ namespace Microsoft.Bot.Builder.Adapters.Twilio
{ {
var messageOptions = TwilioHelper.ActivityToTwilio(activity, _twilioClient.Options.TwilioNumber); var messageOptions = TwilioHelper.ActivityToTwilio(activity, _twilioClient.Options.TwilioNumber);
var res = await _twilioClient.SendMessage(messageOptions, cancellationToken).ConfigureAwait(false); var res = await _twilioClient.SendMessageAsync(messageOptions, cancellationToken).ConfigureAwait(false);
var response = new ResourceResponse() { Id = res, }; var response = new ResourceResponse() { Id = res, };

Просмотреть файл

@ -60,7 +60,7 @@ namespace Microsoft.Bot.Builder.Adapters.Twilio
/// <param name="messageOptions">An object containing the parameters for the message to send.</param> /// <param name="messageOptions">An object containing the parameters for the message to send.</param>
/// <param name="cancellationToken">A cancellation token for the task.</param> /// <param name="cancellationToken">A cancellation token for the task.</param>
/// <returns>The SID of the Twilio message sent.</returns> /// <returns>The SID of the Twilio message sent.</returns>
public virtual async Task<string> SendMessage(CreateMessageOptions messageOptions, CancellationToken cancellationToken) public virtual async Task<string> SendMessageAsync(CreateMessageOptions messageOptions, CancellationToken cancellationToken)
{ {
var messageResource = await MessageResource.CreateAsync(messageOptions).ConfigureAwait(false); var messageResource = await MessageResource.CreateAsync(messageOptions).ConfigureAwait(false);
return messageResource.Sid; return messageResource.Sid;

Просмотреть файл

@ -18,7 +18,8 @@
<PropertyGroup> <PropertyGroup>
<!-- These documentation warnings excludes should be removed as part of https://github.com/microsoft/botbuilder-dotnet/issues/4052 --> <!-- These documentation warnings excludes should be removed as part of https://github.com/microsoft/botbuilder-dotnet/issues/4052 -->
<NoWarn>$(NoWarn);CS1591</NoWarn> <!-- SX1309: FieldNamesShouldBeginWithUnderscores should be fixed as part of https://github.com/microsoft/botframework-sdk/issues/5933 -->
<NoWarn>$(NoWarn);CS1591;SX1309</NoWarn>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>

Просмотреть файл

@ -25,7 +25,8 @@
<PropertyGroup> <PropertyGroup>
<!-- These documentation warnings excludes should be removed as part of https://github.com/microsoft/botbuilder-dotnet/issues/4052 --> <!-- These documentation warnings excludes should be removed as part of https://github.com/microsoft/botbuilder-dotnet/issues/4052 -->
<NoWarn>$(NoWarn);CS1591</NoWarn> <!-- SX1309: FieldNamesShouldBeginWithUnderscores should be fixed as part of https://github.com/microsoft/botframework-sdk/issues/5933 -->
<NoWarn>$(NoWarn);CS1591;SX1309</NoWarn>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

Просмотреть файл

@ -24,7 +24,8 @@
<PropertyGroup> <PropertyGroup>
<!-- These documentation warnings excludes should be removed as part of https://github.com/microsoft/botbuilder-dotnet/issues/4052 --> <!-- These documentation warnings excludes should be removed as part of https://github.com/microsoft/botbuilder-dotnet/issues/4052 -->
<NoWarn>$(NoWarn);CS1591</NoWarn> <!-- SX1309: FieldNamesShouldBeginWithUnderscores should be fixed as part of https://github.com/microsoft/botframework-sdk/issues/5933 -->
<NoWarn>$(NoWarn);CS1591;SX1309</NoWarn>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

Просмотреть файл

@ -23,7 +23,8 @@
<PropertyGroup> <PropertyGroup>
<!-- These documentation warnings excludes should be removed as part of https://github.com/microsoft/botbuilder-dotnet/issues/4052 --> <!-- These documentation warnings excludes should be removed as part of https://github.com/microsoft/botbuilder-dotnet/issues/4052 -->
<NoWarn>$(NoWarn);CS1591</NoWarn> <!-- SX1309: FieldNamesShouldBeginWithUnderscores should be fixed as part of https://github.com/microsoft/botframework-sdk/issues/5933 -->
<NoWarn>$(NoWarn);CS1591;SX1309</NoWarn>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>

Просмотреть файл

@ -29,7 +29,8 @@
<PropertyGroup> <PropertyGroup>
<!-- These documentation warnings excludes should be removed as part of https://github.com/microsoft/botbuilder-dotnet/issues/4052 --> <!-- These documentation warnings excludes should be removed as part of https://github.com/microsoft/botbuilder-dotnet/issues/4052 -->
<NoWarn>$(NoWarn);CS1591</NoWarn> <!-- SX1309: FieldNamesShouldBeginWithUnderscores should be fixed as part of https://github.com/microsoft/botframework-sdk/issues/5933 -->
<NoWarn>$(NoWarn);CS1591;SX1309</NoWarn>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

Просмотреть файл

@ -26,7 +26,8 @@
<PropertyGroup> <PropertyGroup>
<!-- These documentation warnings excludes should be removed as part of https://github.com/microsoft/botbuilder-dotnet/issues/4052 --> <!-- These documentation warnings excludes should be removed as part of https://github.com/microsoft/botbuilder-dotnet/issues/4052 -->
<NoWarn>$(NoWarn);CS1591</NoWarn> <!-- SX1309: FieldNamesShouldBeginWithUnderscores should be fixed as part of https://github.com/microsoft/botframework-sdk/issues/5933 -->
<NoWarn>$(NoWarn);CS1591;SX1309</NoWarn>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

Просмотреть файл

@ -26,7 +26,8 @@
<PropertyGroup> <PropertyGroup>
<!-- These documentation warnings excludes should be removed as part of https://github.com/microsoft/botbuilder-dotnet/issues/4052 --> <!-- These documentation warnings excludes should be removed as part of https://github.com/microsoft/botbuilder-dotnet/issues/4052 -->
<NoWarn>$(NoWarn);CS1591</NoWarn> <!-- SX1309: FieldNamesShouldBeginWithUnderscores should be fixed as part of https://github.com/microsoft/botframework-sdk/issues/5933 -->
<NoWarn>$(NoWarn);CS1591;SX1309</NoWarn>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

Просмотреть файл

@ -25,7 +25,8 @@
<PropertyGroup> <PropertyGroup>
<!-- These documentation warnings excludes should be removed as part of https://github.com/microsoft/botbuilder-dotnet/issues/4052 --> <!-- These documentation warnings excludes should be removed as part of https://github.com/microsoft/botbuilder-dotnet/issues/4052 -->
<NoWarn>$(NoWarn);CS1591</NoWarn> <!-- SX1309: FieldNamesShouldBeginWithUnderscores should be fixed as part of https://github.com/microsoft/botframework-sdk/issues/5933 -->
<NoWarn>$(NoWarn);CS1591;SX1309</NoWarn>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

Просмотреть файл

@ -27,7 +27,8 @@
<PropertyGroup> <PropertyGroup>
<!-- These documentation warnings excludes should be removed as part of https://github.com/microsoft/botbuilder-dotnet/issues/4052 --> <!-- These documentation warnings excludes should be removed as part of https://github.com/microsoft/botbuilder-dotnet/issues/4052 -->
<NoWarn>$(NoWarn);CS1591</NoWarn> <!-- SX1309: FieldNamesShouldBeginWithUnderscores should be fixed as part of https://github.com/microsoft/botframework-sdk/issues/5933 -->
<NoWarn>$(NoWarn);CS1591;SX1309</NoWarn>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

Просмотреть файл

@ -25,7 +25,8 @@
<PropertyGroup> <PropertyGroup>
<!-- These documentation warnings excludes should be removed as part of https://github.com/microsoft/botbuilder-dotnet/issues/4052 --> <!-- These documentation warnings excludes should be removed as part of https://github.com/microsoft/botbuilder-dotnet/issues/4052 -->
<NoWarn>$(NoWarn);CS1591</NoWarn> <!-- SX1309: FieldNamesShouldBeginWithUnderscores should be fixed as part of https://github.com/microsoft/botframework-sdk/issues/5933 -->
<NoWarn>$(NoWarn);CS1591;SX1309</NoWarn>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

Просмотреть файл

@ -23,7 +23,8 @@
<PropertyGroup> <PropertyGroup>
<!-- These documentation warnings excludes should be removed as part of https://github.com/microsoft/botbuilder-dotnet/issues/4052 --> <!-- These documentation warnings excludes should be removed as part of https://github.com/microsoft/botbuilder-dotnet/issues/4052 -->
<NoWarn>$(NoWarn);CS1591</NoWarn> <!-- SX1309: FieldNamesShouldBeginWithUnderscores should be fixed as part of https://github.com/microsoft/botframework-sdk/issues/5933 -->
<NoWarn>$(NoWarn);CS1591;SX1309</NoWarn>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

Просмотреть файл

@ -3,6 +3,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using Microsoft.Bot.Schema; using Microsoft.Bot.Schema;
@ -14,11 +15,15 @@ namespace Microsoft.Bot.Builder
/// The ActivityFactory /// The ActivityFactory
/// to generate text and then uses simple markdown semantics like chatdown to create Activity. /// to generate text and then uses simple markdown semantics like chatdown to create Activity.
/// </summary> /// </summary>
#pragma warning disable CA1052 // Static holder types should be Static or NotInheritable (we can't change this without breaking binary compat)
public class ActivityFactory public class ActivityFactory
#pragma warning restore CA1052 // Static holder types should be Static or NotInheritable
{ {
#pragma warning disable CA1308 // Normalize strings to uppercase (LG is heavily invested in lowercase, ignoring this rule in this class)
private const string LGType = "lgType"; private const string LGType = "lgType";
private static readonly string ErrorPrefix = "[ERROR]"; private const string ErrorPrefix = "[ERROR]";
private static readonly string WarningPrefix = "[WARNING]"; private const string WarningPrefix = "[WARNING]";
private const string AdaptiveCardType = "application/vnd.microsoft.card.adaptive";
private static readonly IList<string> AllActivityTypes = GetAllPublicConstantValues<string>(typeof(ActivityTypes)); private static readonly IList<string> AllActivityTypes = GetAllPublicConstantValues<string>(typeof(ActivityTypes));
private static readonly IList<string> AllActivityProperties = GetAllProperties(typeof(Activity)); private static readonly IList<string> AllActivityProperties = GetAllProperties(typeof(Activity));
@ -36,8 +41,6 @@ namespace Microsoft.Bot.Builder
{ nameof(ReceiptCard).ToLowerInvariant(), ReceiptCard.ContentType }, { nameof(ReceiptCard).ToLowerInvariant(), ReceiptCard.ContentType },
}; };
private static readonly string AdaptiveCardType = "application/vnd.microsoft.card.adaptive";
/// <summary> /// <summary>
/// Generate the activity. /// Generate the activity.
/// Support Both string LG result and structured LG result. /// Support Both string LG result and structured LG result.
@ -47,7 +50,7 @@ namespace Microsoft.Bot.Builder
public static Activity FromObject(object lgResult) public static Activity FromObject(object lgResult)
{ {
var diagnostics = CheckLGResult(lgResult); var diagnostics = CheckLGResult(lgResult);
var errors = diagnostics.Where(u => u.StartsWith(ErrorPrefix)); var errors = diagnostics.Where(u => u.StartsWith(ErrorPrefix, StringComparison.Ordinal));
if (errors.Any()) if (errors.Any())
{ {
@ -64,7 +67,9 @@ namespace Microsoft.Bot.Builder
var lgJsonResult = JObject.FromObject(lgResult); var lgJsonResult = JObject.FromObject(lgResult);
return BuildActivityFromLGStructuredResult(lgJsonResult); return BuildActivityFromLGStructuredResult(lgJsonResult);
} }
#pragma warning disable CA1031 // Do not catch general exception types (we should narrow down the exception being caught but for now we just attempt to build the activity from the text property)
catch catch
#pragma warning restore CA1031 // Do not catch general exception types
{ {
return BuildActivityFromText(lgResult?.ToString()?.Trim()); return BuildActivityFromText(lgResult?.ToString()?.Trim());
} }
@ -84,7 +89,7 @@ namespace Microsoft.Bot.Builder
return new List<string> { BuildDiagnostic("LG output is empty", false) }; return new List<string> { BuildDiagnostic("LG output is empty", false) };
} }
if (!lgStringResult.StartsWith("{") || !lgStringResult.EndsWith("}")) if (!lgStringResult.StartsWith("{", StringComparison.Ordinal) || !lgStringResult.EndsWith("}", StringComparison.Ordinal))
{ {
return new List<string> { BuildDiagnostic("LG output is not a json object, and will fallback to string format.", false) }; return new List<string> { BuildDiagnostic("LG output is not a json object, and will fallback to string format.", false) };
} }
@ -94,7 +99,9 @@ namespace Microsoft.Bot.Builder
{ {
lgStructuredResult = JObject.Parse(lgStringResult); lgStructuredResult = JObject.Parse(lgStringResult);
} }
#pragma warning disable CA1031 // Do not catch general exception types (we should narrow down the exception being caught but for now we just show an error message)
catch catch
#pragma warning restore CA1031 // Do not catch general exception types
{ {
return new List<string> { BuildDiagnostic("LG output is not a json object, and will fallback to string format.", false) }; return new List<string> { BuildDiagnostic("LG output is not a json object, and will fallback to string format.", false) };
} }
@ -108,7 +115,9 @@ namespace Microsoft.Bot.Builder
{ {
lgStructuredResult = JObject.FromObject(lgResult); lgStructuredResult = JObject.FromObject(lgResult);
} }
#pragma warning disable CA1031 // Do not catch general exception types (we should narrow down the exception being caught but for now we just show an error message)
catch catch
#pragma warning restore CA1031 // Do not catch general exception types
{ {
return new List<string> { BuildDiagnostic("LG output is not a json object, and will fallback to string format.", false) }; return new List<string> { BuildDiagnostic("LG output is not a json object, and will fallback to string format.", false) };
} }
@ -414,7 +423,8 @@ namespace Microsoft.Bot.Builder
boolResult = true; boolResult = true;
return true; return true;
} }
else if (boolValue.ToLowerInvariant() == "false")
if (boolValue.ToLowerInvariant() == "false")
{ {
boolResult = false; boolResult = false;
return true; return true;
@ -757,5 +767,6 @@ namespace Microsoft.Bot.Builder
{ {
return type.GetProperties().Select(u => u.Name.ToLowerInvariant()).ToList(); return type.GetProperties().Select(u => u.Name.ToLowerInvariant()).ToList();
} }
#pragma warning restore CA1308 // Normalize strings to uppercase
} }
} }

Просмотреть файл

@ -563,10 +563,12 @@ namespace Microsoft.Bot.Builder
return Task.CompletedTask; return Task.CompletedTask;
} }
#pragma warning disable CA1064 // Exceptions should be public (we can't change this without breaking binary compat, we may consider making this type public in the future)
protected class InvokeResponseException : Exception protected class InvokeResponseException : Exception
#pragma warning restore CA1064 // Exceptions should be public
{ {
private HttpStatusCode _statusCode; private readonly HttpStatusCode _statusCode;
private object _body; private readonly object _body;
public InvokeResponseException(HttpStatusCode statusCode, object body = null) public InvokeResponseException(HttpStatusCode statusCode, object body = null)
{ {
@ -574,9 +576,27 @@ namespace Microsoft.Bot.Builder
_body = body; _body = body;
} }
public InvokeResponseException()
{
}
public InvokeResponseException(string message)
: base(message)
{
}
public InvokeResponseException(string message, Exception innerException)
: base(message, innerException)
{
}
public InvokeResponse CreateInvokeResponse() public InvokeResponse CreateInvokeResponse()
{ {
return new InvokeResponse { Status = (int)_statusCode, Body = _body }; return new InvokeResponse
{
Status = (int)_statusCode,
Body = _body
};
} }
} }
} }

Просмотреть файл

@ -3,6 +3,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.Linq; using System.Linq;
using System.Security.Claims; using System.Security.Claims;
using System.Threading; using System.Threading;
@ -125,8 +126,10 @@ namespace Microsoft.Bot.Builder.Adapters
ChannelId = "test", ChannelId = "test",
ServiceUrl = "https://test.com", ServiceUrl = "https://test.com",
Conversation = new ConversationAccount(false, name, name), Conversation = new ConversationAccount(false, name, name),
User = new ChannelAccount(id: user.ToLower(), name: user), #pragma warning disable CA1308 // Normalize strings to uppercase (it is safe to use lowercase here, this is just for display purposes)
Bot = new ChannelAccount(id: bot.ToLower(), name: bot), User = new ChannelAccount(id: user.ToLowerInvariant(), name: user),
Bot = new ChannelAccount(id: bot.ToLowerInvariant(), name: bot),
#pragma warning restore CA1308 // Normalize strings to uppercase
Locale = "en-us" Locale = "en-us"
}; };
} }
@ -174,7 +177,7 @@ namespace Microsoft.Bot.Builder.Adapters
activity.Conversation = Conversation.Conversation; activity.Conversation = Conversation.Conversation;
activity.ServiceUrl = Conversation.ServiceUrl; activity.ServiceUrl = Conversation.ServiceUrl;
var id = activity.Id = (_nextId++).ToString(); var id = activity.Id = (_nextId++).ToString(CultureInfo.InvariantCulture);
} }
if (activity.Timestamp == null || activity.Timestamp == default(DateTimeOffset)) if (activity.Timestamp == null || activity.Timestamp == default(DateTimeOffset))
@ -370,9 +373,12 @@ namespace Microsoft.Bot.Builder.Adapters
{ {
ActiveQueue.Clear(); ActiveQueue.Clear();
var update = Activity.CreateConversationUpdateActivity(); var update = Activity.CreateConversationUpdateActivity();
update.Conversation = new ConversationAccount() { Id = Guid.NewGuid().ToString("n") }; update.ChannelId = channelId;
var context = new TurnContext(this, (Activity)update); update.Conversation = new ConversationAccount { Id = Guid.NewGuid().ToString("n") };
return callback(context, cancellationToken); using (var context = new TurnContext(this, (Activity)update))
{
return callback(context, cancellationToken);
}
} }
/// <summary> /// <summary>
@ -435,7 +441,7 @@ namespace Microsoft.Bot.Builder.Adapters
Recipient = Conversation.Bot, Recipient = Conversation.Bot,
Conversation = Conversation.Conversation, Conversation = Conversation.Conversation,
ServiceUrl = Conversation.ServiceUrl, ServiceUrl = Conversation.ServiceUrl,
Id = (_nextId++).ToString(), Id = (_nextId++).ToString(CultureInfo.InvariantCulture),
Text = text, Text = text,
}; };
@ -892,9 +898,9 @@ namespace Microsoft.Bot.Builder.Adapters
var rhs = obj as UserTokenKey; var rhs = obj as UserTokenKey;
if (rhs != null) if (rhs != null)
{ {
return string.Equals(this.ConnectionName, rhs.ConnectionName) && return string.Equals(this.ConnectionName, rhs.ConnectionName, StringComparison.Ordinal) &&
string.Equals(this.UserId, rhs.UserId) && string.Equals(this.UserId, rhs.UserId, StringComparison.Ordinal) &&
string.Equals(this.ChannelId, rhs.ChannelId); string.Equals(this.ChannelId, rhs.ChannelId, StringComparison.Ordinal);
} }
return base.Equals(obj); return base.Equals(obj);
@ -917,7 +923,7 @@ namespace Microsoft.Bot.Builder.Adapters
var rhs = obj as ExchangableTokenKey; var rhs = obj as ExchangableTokenKey;
if (rhs != null) if (rhs != null)
{ {
return string.Equals(this.ExchangableItem, rhs.ExchangableItem) && return string.Equals(this.ExchangableItem, rhs.ExchangableItem, StringComparison.Ordinal) &&
base.Equals(obj); base.Equals(obj);
} }

Просмотреть файл

@ -97,7 +97,7 @@ namespace Microsoft.Bot.Builder.Adapters
{ {
if (userSays == null) if (userSays == null)
{ {
throw new ArgumentNullException("You have to pass a userSays parameter"); throw new ArgumentNullException(nameof(userSays), "You have to pass a userSays parameter");
} }
return new TestFlow( return new TestFlow(
@ -145,7 +145,7 @@ namespace Microsoft.Bot.Builder.Adapters
{ {
if (userActivity == null) if (userActivity == null)
{ {
throw new ArgumentNullException("You have to pass an Activity"); throw new ArgumentNullException(nameof(userActivity), "You have to pass an Activity");
} }
return new TestFlow( return new TestFlow(
@ -314,7 +314,9 @@ namespace Microsoft.Bot.Builder.Adapters
/// <param name="timeout">The amount of time in milliseconds within which a response is expected.</param> /// <param name="timeout">The amount of time in milliseconds within which a response is expected.</param>
/// <returns>A new <see cref="TestFlow"/> object that appends this assertion to the modeled exchange.</returns> /// <returns>A new <see cref="TestFlow"/> object that appends this assertion to the modeled exchange.</returns>
/// <remarks>This method does not modify the original <see cref="TestFlow"/> object.</remarks> /// <remarks>This method does not modify the original <see cref="TestFlow"/> object.</remarks>
#pragma warning disable CA1801 // Review unused parameters (we can't remove this withouth breaking binary compat)
public TestFlow AssertReply(Action<IActivity> validateActivity, [CallerMemberName] string description = null, uint timeout = 3000) public TestFlow AssertReply(Action<IActivity> validateActivity, [CallerMemberName] string description = null, uint timeout = 3000)
#pragma warning restore CA1801 // Review unused parameters
{ {
return new TestFlow( return new TestFlow(
async () => async () =>
@ -530,9 +532,9 @@ namespace Microsoft.Bot.Builder.Adapters
timeout); timeout);
} }
private bool IsReply(IActivity activity) private static bool IsReply(IActivity activity)
{ {
return string.Equals("bot", activity.From?.Role, StringComparison.InvariantCultureIgnoreCase); return string.Equals("bot", activity.From?.Role, StringComparison.OrdinalIgnoreCase);
} }
} }
} }

Просмотреть файл

@ -10,7 +10,9 @@ namespace Microsoft.Bot.Builder
/// <summary> /// <summary>
/// Provides methods for debugging Bot Builder code. /// Provides methods for debugging Bot Builder code.
/// </summary> /// </summary>
#pragma warning disable CA1052 // Static holder types should be Static or NotInheritable (we can't change this without breaking binary compat)
public class BotAssert public class BotAssert
#pragma warning restore CA1052 // Static holder types should be Static or NotInheritable
{ {
/// <summary> /// <summary>
/// Checks that an activity object is not <c>null</c>. /// Checks that an activity object is not <c>null</c>.

Просмотреть файл

@ -363,7 +363,7 @@ namespace Microsoft.Bot.Builder
} }
} }
var connectorClient = await CreateConnectorClientAsync(reference.ServiceUrl, claimsIdentity, audience, cancellationToken).ConfigureAwait(false); var connectorClient = await CreateConnectorClientAsync(reference.ServiceUrl, claimsIdentity, audience).ConfigureAwait(false);
context.TurnState.Add(connectorClient); context.TurnState.Add(connectorClient);
await RunPipelineAsync(context, callback, cancellationToken).ConfigureAwait(false); await RunPipelineAsync(context, callback, cancellationToken).ConfigureAwait(false);
@ -441,7 +441,7 @@ namespace Microsoft.Bot.Builder
// The OAuthScope is also stored on the TurnState to get the correct AppCredentials if fetching a token is required. // The OAuthScope is also stored on the TurnState to get the correct AppCredentials if fetching a token is required.
var scope = SkillValidation.IsSkillClaim(claimsIdentity.Claims) ? JwtTokenValidation.GetAppIdFromClaims(claimsIdentity.Claims) : GetBotFrameworkOAuthScope(); var scope = SkillValidation.IsSkillClaim(claimsIdentity.Claims) ? JwtTokenValidation.GetAppIdFromClaims(claimsIdentity.Claims) : GetBotFrameworkOAuthScope();
context.TurnState.Add(OAuthScopeKey, scope); context.TurnState.Add(OAuthScopeKey, scope);
var connectorClient = await CreateConnectorClientAsync(activity.ServiceUrl, claimsIdentity, scope, cancellationToken).ConfigureAwait(false); var connectorClient = await CreateConnectorClientAsync(activity.ServiceUrl, claimsIdentity, scope).ConfigureAwait(false);
context.TurnState.Add(connectorClient); context.TurnState.Add(connectorClient);
context.TurnState.Add(callback); context.TurnState.Add(callback);
@ -552,7 +552,9 @@ namespace Microsoft.Bot.Builder
var oAuthScope = turnContext.TurnState.Get<string>(OAuthScopeKey); var oAuthScope = turnContext.TurnState.Get<string>(OAuthScopeKey);
_ = (await GetAppCredentialsAsync(appId, oAuthScope).ConfigureAwait(false)).GetTokenAsync(); _ = (await GetAppCredentialsAsync(appId, oAuthScope).ConfigureAwait(false)).GetTokenAsync();
} }
#pragma warning disable CA1031 // Do not catch general exception types (we just catch and log the exception here)
catch (Exception ex) catch (Exception ex)
#pragma warning restore CA1031 // Do not catch general exception types
{ {
Logger.LogError("Failed to fetch token before processing outgoing activity. " + ex.Message); Logger.LogError("Failed to fetch token before processing outgoing activity. " + ex.Message);
} }
@ -1153,28 +1155,28 @@ namespace Microsoft.Bot.Builder
/// <param name="cancellationToken">A cancellation token that can be used by other objects /// <param name="cancellationToken">A cancellation token that can be used by other objects
/// or threads to receive notice of cancellation.</param> /// or threads to receive notice of cancellation.</param>
/// <returns>If the task completes, the exchanged token is returned.</returns> /// <returns>If the task completes, the exchanged token is returned.</returns>
public virtual async Task<TokenResponse> ExchangeTokenAsync(ITurnContext turnContext, AppCredentials oAuthAppCredentials, string connectionName, string userId, TokenExchangeRequest exchangeRequest, CancellationToken cancellationToken = default(CancellationToken)) public virtual async Task<TokenResponse> ExchangeTokenAsync(ITurnContext turnContext, AppCredentials oAuthAppCredentials, string connectionName, string userId, TokenExchangeRequest exchangeRequest, CancellationToken cancellationToken = default)
{ {
BotAssert.ContextNotNull(turnContext); BotAssert.ContextNotNull(turnContext);
if (string.IsNullOrWhiteSpace(connectionName)) if (string.IsNullOrWhiteSpace(connectionName))
{ {
LogAndThrowException(new ArgumentException(nameof(connectionName))); LogAndThrowException(new ArgumentException($"{nameof(connectionName)} is null or empty", nameof(connectionName)));
} }
if (string.IsNullOrWhiteSpace(userId)) if (string.IsNullOrWhiteSpace(userId))
{ {
LogAndThrowException(new ArgumentException(nameof(userId))); LogAndThrowException(new ArgumentException($"{nameof(userId)} is null or empty", nameof(userId)));
} }
if (exchangeRequest == null) if (exchangeRequest == null)
{ {
LogAndThrowException(new ArgumentException(nameof(exchangeRequest))); LogAndThrowException(new ArgumentException($"{nameof(exchangeRequest)} is null or empty", nameof(exchangeRequest)));
} }
if (string.IsNullOrWhiteSpace(exchangeRequest.Token) && string.IsNullOrWhiteSpace(exchangeRequest.Uri)) if (string.IsNullOrWhiteSpace(exchangeRequest.Token) && string.IsNullOrWhiteSpace(exchangeRequest.Uri))
{ {
LogAndThrowException(new ArgumentException(nameof(exchangeRequest), "Either a Token or Uri property is required on the TokenExchangeRequest")); LogAndThrowException(new ArgumentException("Either a Token or Uri property is required on the TokenExchangeRequest", nameof(exchangeRequest)));
} }
var activity = turnContext.Activity; var activity = turnContext.Activity;
@ -1191,13 +1193,11 @@ namespace Microsoft.Bot.Builder
{ {
return tokenResponse; return tokenResponse;
} }
else
{
LogAndThrowException(new InvalidOperationException($"ExchangeAsyncAsync returned improper result: {result.GetType()}"));
// even though LogAndThrowException always throws, compiler gives an error about not all code paths returning a value. LogAndThrowException(new InvalidOperationException($"ExchangeAsyncAsync returned improper result: {result.GetType()}"));
return null;
} // even though LogAndThrowException always throws, compiler gives an error about not all code paths returning a value.
return null;
} }
/// <summary> /// <summary>
@ -1309,7 +1309,7 @@ namespace Microsoft.Bot.Builder
Task<IConnectorClient> IConnectorClientBuilder.CreateConnectorClientAsync(string serviceUrl, ClaimsIdentity claimsIdentity, string audience, CancellationToken cancellationToken) Task<IConnectorClient> IConnectorClientBuilder.CreateConnectorClientAsync(string serviceUrl, ClaimsIdentity claimsIdentity, string audience, CancellationToken cancellationToken)
{ {
return CreateConnectorClientAsync(serviceUrl, claimsIdentity, audience, cancellationToken); return CreateConnectorClientAsync(serviceUrl, claimsIdentity, audience);
} }
/// <summary> /// <summary>
@ -1321,7 +1321,7 @@ namespace Microsoft.Bot.Builder
protected virtual async Task<OAuthClient> CreateOAuthApiClientAsync(ITurnContext turnContext, AppCredentials oAuthAppCredentials) protected virtual async Task<OAuthClient> CreateOAuthApiClientAsync(ITurnContext turnContext, AppCredentials oAuthAppCredentials)
{ {
if (!OAuthClientConfig.EmulateOAuthCards && if (!OAuthClientConfig.EmulateOAuthCards &&
string.Equals(turnContext.Activity.ChannelId, Channels.Emulator, StringComparison.InvariantCultureIgnoreCase) && string.Equals(turnContext.Activity.ChannelId, Channels.Emulator, StringComparison.OrdinalIgnoreCase) &&
(await CredentialProvider.IsAuthenticationDisabledAsync().ConfigureAwait(false))) (await CredentialProvider.IsAuthenticationDisabledAsync().ConfigureAwait(false)))
{ {
OAuthClientConfig.EmulateOAuthCards = true; OAuthClientConfig.EmulateOAuthCards = true;
@ -1335,7 +1335,7 @@ namespace Microsoft.Bot.Builder
var appCredentials = oAuthAppCredentials ?? await GetAppCredentialsAsync(appId, oAuthScope).ConfigureAwait(false); var appCredentials = oAuthAppCredentials ?? await GetAppCredentialsAsync(appId, oAuthScope).ConfigureAwait(false);
if (!OAuthClientConfig.EmulateOAuthCards && if (!OAuthClientConfig.EmulateOAuthCards &&
string.Equals(turnContext.Activity.ChannelId, Channels.Emulator, StringComparison.InvariantCultureIgnoreCase) && string.Equals(turnContext.Activity.ChannelId, Channels.Emulator, StringComparison.OrdinalIgnoreCase) &&
(await CredentialProvider.IsAuthenticationDisabledAsync().ConfigureAwait(false))) (await CredentialProvider.IsAuthenticationDisabledAsync().ConfigureAwait(false)))
{ {
OAuthClientConfig.EmulateOAuthCards = true; OAuthClientConfig.EmulateOAuthCards = true;
@ -1421,6 +1421,28 @@ namespace Microsoft.Bot.Builder
return ChannelProvider != null && ChannelProvider.IsGovernment() ? new MicrosoftGovernmentAppCredentials(appId, appPassword, HttpClient, Logger, oAuthScope) : new MicrosoftAppCredentials(appId, appPassword, HttpClient, Logger, oAuthScope); return ChannelProvider != null && ChannelProvider.IsGovernment() ? new MicrosoftGovernmentAppCredentials(appId, appPassword, HttpClient, Logger, oAuthScope) : new MicrosoftAppCredentials(appId, appPassword, HttpClient, Logger, oAuthScope);
} }
/// <summary>
/// Gets the AppId of the Bot out of the TurnState.
/// </summary>
/// <param name="turnContext">The context object for the turn.</param>
/// <returns>Bot's AppId.</returns>
private static string GetBotAppId(ITurnContext turnContext)
{
var botIdentity = (ClaimsIdentity)turnContext.TurnState.Get<IIdentity>(BotIdentityKey);
if (botIdentity == null)
{
throw new InvalidOperationException("An IIdentity is required in TurnState for this operation.");
}
var appId = botIdentity.Claims.FirstOrDefault(claim => claim.Type == AuthenticationConstants.AudienceClaim)?.Value;
if (string.IsNullOrWhiteSpace(appId))
{
throw new InvalidOperationException("Unable to get the bot AppId from the audience claim.");
}
return appId;
}
/// <summary> /// <summary>
/// Generates the CallerId property for the activity based on /// Generates the CallerId property for the activity based on
/// https://github.com/microsoft/botframework-obi/blob/master/protocols/botframework-activity/botframework-activity.md#appendix-v---caller-id-values. /// https://github.com/microsoft/botframework-obi/blob/master/protocols/botframework-activity/botframework-activity.md#appendix-v---caller-id-values.
@ -1463,10 +1485,9 @@ namespace Microsoft.Bot.Builder
/// <param name="serviceUrl">The service URL.</param> /// <param name="serviceUrl">The service URL.</param>
/// <param name="claimsIdentity">The claims claimsIdentity.</param> /// <param name="claimsIdentity">The claims claimsIdentity.</param>
/// <param name="audience">The target audience for the connector.</param> /// <param name="audience">The target audience for the connector.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>ConnectorClient instance.</returns> /// <returns>ConnectorClient instance.</returns>
/// <exception cref="NotSupportedException">ClaimsIdentity cannot be null. Pass Anonymous ClaimsIdentity if authentication is turned off.</exception> /// <exception cref="NotSupportedException">ClaimsIdentity cannot be null. Pass Anonymous ClaimsIdentity if authentication is turned off.</exception>
private async Task<IConnectorClient> CreateConnectorClientAsync(string serviceUrl, ClaimsIdentity claimsIdentity, string audience, CancellationToken cancellationToken = default) private async Task<IConnectorClient> CreateConnectorClientAsync(string serviceUrl, ClaimsIdentity claimsIdentity, string audience)
{ {
if (claimsIdentity == null) if (claimsIdentity == null)
{ {
@ -1497,7 +1518,7 @@ namespace Microsoft.Bot.Builder
GetBotFrameworkOAuthScope(); GetBotFrameworkOAuthScope();
} }
appCredentials = await GetAppCredentialsAsync(botId, scope, cancellationToken).ConfigureAwait(false); appCredentials = await GetAppCredentialsAsync(botId, scope).ConfigureAwait(false);
} }
return CreateConnectorClient(serviceUrl, appCredentials); return CreateConnectorClient(serviceUrl, appCredentials);
@ -1544,9 +1565,8 @@ namespace Microsoft.Bot.Builder
/// </summary> /// </summary>
/// <param name="appId">The application identifier (AAD ID for the bot).</param> /// <param name="appId">The application identifier (AAD ID for the bot).</param>
/// <param name="oAuthScope">The scope for the token. Skills use the skill's app ID. </param> /// <param name="oAuthScope">The scope for the token. Skills use the skill's app ID. </param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>App credentials.</returns> /// <returns>App credentials.</returns>
private async Task<AppCredentials> GetAppCredentialsAsync(string appId, string oAuthScope, CancellationToken cancellationToken = default) private async Task<AppCredentials> GetAppCredentialsAsync(string appId, string oAuthScope)
{ {
if (string.IsNullOrWhiteSpace(appId)) if (string.IsNullOrWhiteSpace(appId))
{ {
@ -1575,28 +1595,6 @@ namespace Microsoft.Bot.Builder
return appCredentials; return appCredentials;
} }
/// <summary>
/// Gets the AppId of the Bot out of the TurnState.
/// </summary>
/// <param name="turnContext">The context object for the turn.</param>
/// <returns>Bot's AppId.</returns>
private string GetBotAppId(ITurnContext turnContext)
{
var botIdentity = (ClaimsIdentity)turnContext.TurnState.Get<IIdentity>(BotIdentityKey);
if (botIdentity == null)
{
throw new InvalidOperationException("An IIdentity is required in TurnState for this operation.");
}
var appId = botIdentity.Claims.FirstOrDefault(claim => claim.Type == AuthenticationConstants.AudienceClaim)?.Value;
if (string.IsNullOrWhiteSpace(appId))
{
throw new InvalidOperationException("Unable to get the bot AppId from the audience claim.");
}
return appId;
}
/// <summary> /// <summary>
/// This method returns the correct Bot Framework OAuthScope for AppCredentials. /// This method returns the correct Bot Framework OAuthScope for AppCredentials.
/// </summary> /// </summary>
@ -1608,7 +1606,7 @@ namespace Microsoft.Bot.Builder
} }
/// <summary> /// <summary>
/// Logs and throws an expcetion. /// Logs and throws an exception.
/// </summary> /// </summary>
/// <param name="ex"> Exception instance to throw.</param> /// <param name="ex"> Exception instance to throw.</param>
/// <param name="source"> Source method for the exception.</param> /// <param name="source"> Source method for the exception.</param>
@ -1630,7 +1628,7 @@ namespace Microsoft.Bot.Builder
{ {
public async Task OnTurnAsync(ITurnContext turnContext, NextDelegate next, CancellationToken cancellationToken = default) public async Task OnTurnAsync(ITurnContext turnContext, NextDelegate next, CancellationToken cancellationToken = default)
{ {
if (Channels.Msteams.Equals(turnContext.Activity.ChannelId, StringComparison.InvariantCultureIgnoreCase) && turnContext.Activity.Conversation != null && string.IsNullOrEmpty(turnContext.Activity.Conversation.TenantId) && turnContext.Activity.ChannelData != null) if (Channels.Msteams.Equals(turnContext.Activity.ChannelId, StringComparison.OrdinalIgnoreCase) && turnContext.Activity.Conversation != null && string.IsNullOrEmpty(turnContext.Activity.Conversation.TenantId) && turnContext.Activity.ChannelData != null)
{ {
var teamsChannelData = JObject.FromObject(turnContext.Activity.ChannelData); var teamsChannelData = JObject.FromObject(turnContext.Activity.ChannelData);
if (teamsChannelData["tenant"]?["id"] != null) if (teamsChannelData["tenant"]?["id"] != null)

Просмотреть файл

@ -134,7 +134,7 @@ namespace Microsoft.Bot.Builder
{ key, cachedState.State }, { key, cachedState.State },
}; };
await _storage.WriteAsync(changes).ConfigureAwait(false); await _storage.WriteAsync(changes).ConfigureAwait(false);
cachedState.Hash = cachedState.ComputeHash(cachedState.State); cachedState.Hash = CachedBotState.ComputeHash(cachedState.State);
return; return;
} }
} }
@ -230,7 +230,9 @@ namespace Microsoft.Bot.Builder
/// <remarks>If the task is successful, the result contains the property value, otherwise it will be default(T).</remarks> /// <remarks>If the task is successful, the result contains the property value, otherwise it will be default(T).</remarks>
/// <exception cref="ArgumentNullException"><paramref name="turnContext"/> or /// <exception cref="ArgumentNullException"><paramref name="turnContext"/> or
/// <paramref name="propertyName"/> is <c>null</c>.</exception> /// <paramref name="propertyName"/> is <c>null</c>.</exception>
#pragma warning disable CA1801 // Review unused parameters (we can't change this without breaking binary compat)
protected Task<T> GetPropertyValueAsync<T>(ITurnContext turnContext, string propertyName, CancellationToken cancellationToken = default(CancellationToken)) protected Task<T> GetPropertyValueAsync<T>(ITurnContext turnContext, string propertyName, CancellationToken cancellationToken = default(CancellationToken))
#pragma warning restore CA1801 // Review unused parameters
{ {
BotAssert.ContextNotNull(turnContext); BotAssert.ContextNotNull(turnContext);
@ -287,7 +289,9 @@ namespace Microsoft.Bot.Builder
/// <returns>A task that represents the work queued to execute.</returns> /// <returns>A task that represents the work queued to execute.</returns>
/// <exception cref="ArgumentNullException"><paramref name="turnContext"/> or /// <exception cref="ArgumentNullException"><paramref name="turnContext"/> or
/// <paramref name="propertyName"/> is <c>null</c>.</exception> /// <paramref name="propertyName"/> is <c>null</c>.</exception>
#pragma warning disable CA1801 // Review unused parameters (we can't change this without breaking binary compat)
protected Task DeletePropertyValueAsync(ITurnContext turnContext, string propertyName, CancellationToken cancellationToken = default(CancellationToken)) protected Task DeletePropertyValueAsync(ITurnContext turnContext, string propertyName, CancellationToken cancellationToken = default(CancellationToken))
#pragma warning restore CA1801 // Review unused parameters
{ {
BotAssert.ContextNotNull(turnContext); BotAssert.ContextNotNull(turnContext);
@ -312,7 +316,9 @@ namespace Microsoft.Bot.Builder
/// <returns>A task that represents the work queued to execute.</returns> /// <returns>A task that represents the work queued to execute.</returns>
/// <exception cref="ArgumentNullException"><paramref name="turnContext"/> or /// <exception cref="ArgumentNullException"><paramref name="turnContext"/> or
/// <paramref name="propertyName"/> is <c>null</c>.</exception> /// <paramref name="propertyName"/> is <c>null</c>.</exception>
#pragma warning disable CA1801 // Review unused parameters (we can't change this without breaking binary compat)
protected Task SetPropertyValueAsync(ITurnContext turnContext, string propertyName, object value, CancellationToken cancellationToken = default(CancellationToken)) protected Task SetPropertyValueAsync(ITurnContext turnContext, string propertyName, object value, CancellationToken cancellationToken = default(CancellationToken))
#pragma warning restore CA1801 // Review unused parameters
{ {
BotAssert.ContextNotNull(turnContext); BotAssert.ContextNotNull(turnContext);
@ -329,7 +335,9 @@ namespace Microsoft.Bot.Builder
/// <summary> /// <summary>
/// Internal cached bot state. /// Internal cached bot state.
/// </summary> /// </summary>
#pragma warning disable CA1034 // Nested types should not be visible (we can't change this without breaking binary compat)
public class CachedBotState public class CachedBotState
#pragma warning restore CA1034 // Nested types should not be visible
{ {
internal CachedBotState(IDictionary<string, object> state = null) internal CachedBotState(IDictionary<string, object> state = null)
{ {
@ -337,19 +345,21 @@ namespace Microsoft.Bot.Builder
Hash = ComputeHash(State); Hash = ComputeHash(State);
} }
#pragma warning disable CA2227 // Collection properties should be read only (we can't change this without breaking binary compat)
public IDictionary<string, object> State { get; set; } public IDictionary<string, object> State { get; set; }
#pragma warning restore CA2227 // Collection properties should be read only
internal string Hash { get; set; } internal string Hash { get; set; }
internal static string ComputeHash(object obj)
{
return JsonConvert.SerializeObject(obj);
}
internal bool IsChanged() internal bool IsChanged()
{ {
return Hash != ComputeHash(State); return Hash != ComputeHash(State);
} }
internal string ComputeHash(object obj)
{
return JsonConvert.SerializeObject(obj);
}
} }
/// <summary> /// <summary>

Просмотреть файл

@ -27,7 +27,9 @@ namespace Microsoft.Bot.Builder
/// Gets or sets the BotStates list for the BotStateSet. /// Gets or sets the BotStates list for the BotStateSet.
/// </summary> /// </summary>
/// <value>The BotState objects managed by this class.</value> /// <value>The BotState objects managed by this class.</value>
#pragma warning disable CA2227 // Collection properties should be read only (we can't change this without breaking binary compat)
public List<BotState> BotStates { get; set; } = new List<BotState>(); public List<BotState> BotStates { get; set; } = new List<BotState>();
#pragma warning restore CA2227 // Collection properties should be read only
/// <summary> /// <summary>
/// Adds a bot state object to the set. /// Adds a bot state object to the set.

Просмотреть файл

@ -4,10 +4,6 @@
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
namespace Microsoft.Bot.Builder namespace Microsoft.Bot.Builder
{ {
@ -33,9 +29,11 @@ namespace Microsoft.Bot.Builder
/// ComponentRegistration.Add(new MyComponentRegistration()); /// ComponentRegistration.Add(new MyComponentRegistration());
/// </code> /// </code>
/// </remarks> /// </remarks>
#pragma warning disable CA1052 // Static holder types should be Static or NotInheritable (we can't change this without breaking binary compat)
public class ComponentRegistration public class ComponentRegistration
#pragma warning restore CA1052 // Static holder types should be Static or NotInheritable
{ {
private static ConcurrentDictionary<Type, ComponentRegistration> components = new ConcurrentDictionary<Type, ComponentRegistration>(); private static readonly ConcurrentDictionary<Type, ComponentRegistration> _components = new ConcurrentDictionary<Type, ComponentRegistration>();
/// <summary> /// <summary>
/// Gets list of all ComponentRegistration objects registered. /// Gets list of all ComponentRegistration objects registered.
@ -43,7 +41,7 @@ namespace Microsoft.Bot.Builder
/// <value> /// <value>
/// A numeration of ComponentRegistration objects. /// A numeration of ComponentRegistration objects.
/// </value> /// </value>
public static IEnumerable<object> Components => components.Values; public static IEnumerable<object> Components => _components.Values;
/// <summary> /// <summary>
/// Add a component which implements registration methods. /// Add a component which implements registration methods.
@ -52,7 +50,7 @@ namespace Microsoft.Bot.Builder
/// <param name="componentRegistration">componentRegistration.</param> /// <param name="componentRegistration">componentRegistration.</param>
public static void Add(ComponentRegistration componentRegistration) public static void Add(ComponentRegistration componentRegistration)
{ {
components[componentRegistration.GetType()] = componentRegistration; _components[componentRegistration.GetType()] = componentRegistration;
} }
} }
} }

Просмотреть файл

@ -37,8 +37,9 @@ namespace Microsoft.Bot.Builder
/// <see cref="Schema.ConversationAccount.Id"/> is missing.</exception> /// <see cref="Schema.ConversationAccount.Id"/> is missing.</exception>
protected override string GetStorageKey(ITurnContext turnContext) protected override string GetStorageKey(ITurnContext turnContext)
{ {
var channelId = turnContext.Activity.ChannelId ?? throw new ArgumentNullException("invalid activity-missing channelId"); var channelId = turnContext.Activity.ChannelId ?? throw new InvalidOperationException("invalid activity-missing channelId");
var conversationId = turnContext.Activity.Conversation?.Id ?? throw new ArgumentNullException("invalid activity-missing Conversation.Id"); var conversationId = turnContext.Activity.Conversation?.Id ?? throw new InvalidOperationException("invalid activity-missing Conversation.Id");
#pragma warning restore CA2208 // Instantiate argument exceptions correctly
return $"{channelId}/conversations/{conversationId}"; return $"{channelId}/conversations/{conversationId}";
} }
} }

Просмотреть файл

@ -20,15 +20,15 @@ namespace Microsoft.Bot.Builder
/// </remarks> /// </remarks>
public class FileTranscriptLogger : ITranscriptStore public class FileTranscriptLogger : ITranscriptStore
{ {
private static JsonSerializerSettings jsonSettings = new JsonSerializerSettings() private static readonly JsonSerializerSettings _jsonSettings = new JsonSerializerSettings()
{ {
Formatting = Formatting.Indented, Formatting = Formatting.Indented,
NullValueHandling = NullValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore,
}; };
private string folder; private readonly string _folder;
private bool unitTestMode; private readonly bool _unitTestMode;
private HashSet<string> started = new HashSet<string>(); private readonly HashSet<string> _started = new HashSet<string>();
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="FileTranscriptLogger"/> class. /// Initializes a new instance of the <see cref="FileTranscriptLogger"/> class.
@ -49,8 +49,8 @@ namespace Microsoft.Bot.Builder
Directory.CreateDirectory(folder); Directory.CreateDirectory(folder);
} }
this.folder = folder; this._folder = folder;
this.unitTestMode = unitTestMode; this._unitTestMode = unitTestMode;
} }
/// <summary> /// <summary>
@ -62,7 +62,7 @@ namespace Microsoft.Bot.Builder
{ {
if (activity == null) if (activity == null)
{ {
throw new ArgumentNullException(nameof(Activity)); throw new ArgumentNullException(nameof(activity));
} }
var transcriptFile = GetTranscriptFile(activity.ChannelId, activity.Conversation.Id); var transcriptFile = GetTranscriptFile(activity.ChannelId, activity.Conversation.Id);
@ -81,40 +81,40 @@ namespace Microsoft.Bot.Builder
{ {
try try
{ {
if ((this.unitTestMode == true && !started.Contains(transcriptFile)) || !File.Exists(transcriptFile)) if ((this._unitTestMode == true && !_started.Contains(transcriptFile)) || !File.Exists(transcriptFile))
{ {
System.Diagnostics.Trace.TraceInformation($"file://{transcriptFile.Replace("\\", "/")}"); Trace.TraceInformation($"file://{transcriptFile.Replace("\\", "/")}");
started.Add(transcriptFile); _started.Add(transcriptFile);
List<Activity> transcript = new List<Activity>() { (Activity)activity };
using (var stream = File.OpenWrite(transcriptFile)) using (var stream = File.OpenWrite(transcriptFile))
{ {
using (var writer = new StreamWriter(stream) as TextWriter) using (var writer = new StreamWriter(stream) as TextWriter)
{ {
await writer.WriteAsync($"[{JsonConvert.SerializeObject(activity, jsonSettings)}]").ConfigureAwait(false); await writer.WriteAsync($"[{JsonConvert.SerializeObject(activity, _jsonSettings)}]").ConfigureAwait(false);
return; return;
} }
} }
} }
else
switch (activity.Type)
{ {
switch (activity.Type) case ActivityTypes.MessageDelete:
{ await MessageDeleteAsync(activity, transcriptFile).ConfigureAwait(false);
case ActivityTypes.MessageDelete: return;
await MessageDeleteAsync(activity, transcriptFile).ConfigureAwait(false);
return;
case ActivityTypes.MessageUpdate: case ActivityTypes.MessageUpdate:
await MessageUpdateAsync(activity, transcriptFile).ConfigureAwait(false); await MessageUpdateAsync(activity, transcriptFile).ConfigureAwait(false);
return; return;
default: default:
// append // append
await LogActivityAsync(activity, transcriptFile).ConfigureAwait(false); await LogActivityAsync(activity, transcriptFile).ConfigureAwait(false);
return; return;
}
} }
} }
#pragma warning disable CA1031 // Do not catch general exception types (we ignore the exception and we retry)
catch (Exception) catch (Exception)
#pragma warning restore CA1031 // Do not catch general exception types
{ {
// try again // try again
} }
@ -178,42 +178,9 @@ namespace Microsoft.Bot.Builder
return Array.Empty<Activity>(); return Array.Empty<Activity>();
} }
private string GetTranscriptFile(string channelId, string conversationId) private static async Task LogActivityAsync(IActivity activity, string transcriptFile)
{ {
if (channelId == null) var json = $",\n{JsonConvert.SerializeObject(activity, _jsonSettings)}]";
{
throw new ArgumentNullException(channelId);
}
if (conversationId == null)
{
throw new ArgumentNullException(nameof(conversationId));
}
var channelFolder = GetChannelFolder(channelId);
string transcriptFile = Path.Combine(channelFolder, conversationId + ".transcript");
return transcriptFile;
}
private string GetChannelFolder(string channelId)
{
if (channelId == null)
{
throw new ArgumentNullException(channelId);
}
var channelFolder = Path.Combine(folder, channelId);
if (!Directory.Exists(channelFolder))
{
Directory.CreateDirectory(channelFolder);
}
return channelFolder;
}
private async Task LogActivityAsync(IActivity activity, string transcriptFile)
{
var json = $",\n{JsonConvert.SerializeObject(activity, jsonSettings)}]";
using (var stream = File.Open(transcriptFile, FileMode.OpenOrCreate)) using (var stream = File.Open(transcriptFile, FileMode.OpenOrCreate))
{ {
@ -229,7 +196,7 @@ namespace Microsoft.Bot.Builder
} }
} }
private async Task MessageUpdateAsync(IActivity activity, string transcriptFile) private static async Task MessageUpdateAsync(IActivity activity, string transcriptFile)
{ {
// load all activities // load all activities
var transcript = await LoadTranscriptAsync(transcriptFile).ConfigureAwait(false); var transcript = await LoadTranscriptAsync(transcriptFile).ConfigureAwait(false);
@ -244,7 +211,7 @@ namespace Microsoft.Bot.Builder
updatedActivity.LocalTimestamp = originalActivity.LocalTimestamp; updatedActivity.LocalTimestamp = originalActivity.LocalTimestamp;
updatedActivity.Timestamp = originalActivity.Timestamp; updatedActivity.Timestamp = originalActivity.Timestamp;
transcript[i] = updatedActivity; transcript[i] = updatedActivity;
var json = JsonConvert.SerializeObject(transcript, jsonSettings); var json = JsonConvert.SerializeObject(transcript, _jsonSettings);
using (var stream = File.OpenWrite(transcriptFile)) using (var stream = File.OpenWrite(transcriptFile))
{ {
using (var writer = new StreamWriter(stream) as TextWriter) using (var writer = new StreamWriter(stream) as TextWriter)
@ -257,7 +224,7 @@ namespace Microsoft.Bot.Builder
} }
} }
private async Task MessageDeleteAsync(IActivity activity, string transcriptFile) private static async Task MessageDeleteAsync(IActivity activity, string transcriptFile)
{ {
// load all activities // load all activities
var transcript = await LoadTranscriptAsync(transcriptFile).ConfigureAwait(false); var transcript = await LoadTranscriptAsync(transcriptFile).ConfigureAwait(false);
@ -283,7 +250,7 @@ namespace Microsoft.Bot.Builder
ServiceUrl = originalActivity.ServiceUrl, ServiceUrl = originalActivity.ServiceUrl,
ReplyToId = originalActivity.ReplyToId, ReplyToId = originalActivity.ReplyToId,
}; };
var json = JsonConvert.SerializeObject(transcript, jsonSettings); var json = JsonConvert.SerializeObject(transcript, _jsonSettings);
using (var stream = File.OpenWrite(transcriptFile)) using (var stream = File.OpenWrite(transcriptFile))
{ {
using (var writer = new StreamWriter(stream) as TextWriter) using (var writer = new StreamWriter(stream) as TextWriter)
@ -295,5 +262,38 @@ namespace Microsoft.Bot.Builder
} }
} }
} }
private string GetTranscriptFile(string channelId, string conversationId)
{
if (channelId == null)
{
throw new ArgumentNullException(channelId);
}
if (conversationId == null)
{
throw new ArgumentNullException(nameof(conversationId));
}
var channelFolder = GetChannelFolder(channelId);
string transcriptFile = Path.Combine(channelFolder, conversationId + ".transcript");
return transcriptFile;
}
private string GetChannelFolder(string channelId)
{
if (channelId == null)
{
throw new ArgumentNullException(channelId);
}
var channelFolder = Path.Combine(_folder, channelId);
if (!Directory.Exists(channelFolder))
{
Directory.CreateDirectory(channelFolder);
}
return channelFolder;
}
} }
} }

Просмотреть файл

@ -24,24 +24,39 @@ namespace Microsoft.Bot.Builder
{ {
// This is a mock secure SendToConversation to grab the exact HTTP headers. // This is a mock secure SendToConversation to grab the exact HTTP headers.
// If you have no appId and no secret this code will run but not produce an Authorization header. // If you have no appId and no secret this code will run but not produce an Authorization header.
var captureHandler = new CaptureRequestHandler(); using (var captureHandler = new CaptureRequestHandler())
var client = new ConnectorClient(connector.BaseUri, connector.Credentials, captureHandler); {
var activity = new Activity { Type = ActivityTypes.Message, Conversation = new ConversationAccount { Id = "capture" } }; using (var client = new ConnectorClient(connector.BaseUri, connector.Credentials, captureHandler))
client.Conversations.SendToConversation(activity); {
var headers = captureHandler.Request.Headers; var activity = new Activity
healthResults.Authorization = headers.Authorization?.ToString(); {
healthResults.UserAgent = headers.UserAgent?.ToString(); Type = ActivityTypes.Message,
Conversation = new ConversationAccount { Id = "capture" }
};
client.Conversations.SendToConversation(activity);
var headers = captureHandler.Request.Headers;
healthResults.Authorization = headers.Authorization?.ToString();
healthResults.UserAgent = headers.UserAgent?.ToString();
}
}
} }
#pragma warning disable CA1031 // Do not catch general exception types (ignoring, see comment in catch block)
catch (Exception) catch (Exception)
#pragma warning restore CA1031 // Do not catch general exception types
{ {
// This exception happens when you have a valid appId but invalid or blank secret. // This exception happens when you have a valid appId but invalid or blank secret.
// No callbacks will be possible, although the bot maybe healthy in other respects. // No callbacks will be possible, although the bot maybe healthy in other respects.
} }
} }
var successMessage = "Health check succeeded."; var successMessage = "Health check succeeded.";
healthResults.Messages = healthResults.Authorization != null ? new[] { successMessage } : new[] { successMessage, "Callbacks are not authorized." }; healthResults.Messages = healthResults.Authorization != null
? new[] { successMessage }
: new[]
{
successMessage,
"Callbacks are not authorized."
};
return new HealthCheckResponse { HealthResults = healthResults }; return new HealthCheckResponse { HealthResults = healthResults };
} }

Просмотреть файл

@ -50,6 +50,8 @@ namespace Microsoft.Bot.Builder
/// </remarks> /// </remarks>
/// <seealso cref="ITurnContext"/> /// <seealso cref="ITurnContext"/>
/// <seealso cref="Bot.Schema.IActivity"/> /// <seealso cref="Bot.Schema.IActivity"/>
#pragma warning disable CA1716 // Identifiers should not match keywords (we can't change this without breaking binary compat)
Task OnTurnAsync(ITurnContext turnContext, NextDelegate next, CancellationToken cancellationToken = default(CancellationToken)); Task OnTurnAsync(ITurnContext turnContext, NextDelegate next, CancellationToken cancellationToken = default(CancellationToken));
#pragma warning restore CA1716 // Identifiers should not match keywords
} }
} }

Просмотреть файл

@ -133,6 +133,32 @@ namespace Microsoft.Bot.Builder
} }
} }
private static string OpenCommand(InspectionSessionsByStatus sessions, ConversationReference conversationReference)
{
var sessionId = Guid.NewGuid().ToString();
sessions.OpenedSessions.Add(sessionId, conversationReference);
return sessionId;
}
private static bool AttachCommand(string attachId, InspectionSessionsByStatus sessions, string sessionId)
{
if (sessions.OpenedSessions.TryGetValue(sessionId, out var inspectionSessionState))
{
sessions.AttachedSessions[attachId] = inspectionSessionState;
sessions.OpenedSessions.Remove(sessionId);
return true;
}
return false;
}
private static string GetAttachId(Activity activity)
{
// If we are running in a Microsoft Teams Team the conversation Id will reflect a particular thread the bot is in.
// So if we are in a Team then we will associate the "attach" with the Team Id rather than the more restrictive conversation Id.
return activity.TeamsGetTeamInfo()?.Id ?? activity.Conversation.Id;
}
private async Task ProcessOpenCommandAsync(ITurnContext turnContext, CancellationToken cancellationToken) private async Task ProcessOpenCommandAsync(ITurnContext turnContext, CancellationToken cancellationToken)
{ {
var accessor = _inspectionState.CreateProperty<InspectionSessionsByStatus>(nameof(InspectionSessionsByStatus)); var accessor = _inspectionState.CreateProperty<InspectionSessionsByStatus>(nameof(InspectionSessionsByStatus));
@ -166,25 +192,6 @@ namespace Microsoft.Bot.Builder
await _inspectionState.SaveChangesAsync(turnContext, false, cancellationToken).ConfigureAwait(false); await _inspectionState.SaveChangesAsync(turnContext, false, cancellationToken).ConfigureAwait(false);
} }
private string OpenCommand(InspectionSessionsByStatus sessions, ConversationReference conversationReference)
{
var sessionId = Guid.NewGuid().ToString();
sessions.OpenedSessions.Add(sessionId, conversationReference);
return sessionId;
}
private bool AttachCommand(string attachId, InspectionSessionsByStatus sessions, string sessionId)
{
if (sessions.OpenedSessions.TryGetValue(sessionId, out var inspectionSessionState))
{
sessions.AttachedSessions[attachId] = inspectionSessionState;
sessions.OpenedSessions.Remove(sessionId);
return true;
}
return false;
}
private async Task<InspectionSession> FindSessionAsync(ITurnContext turnContext, CancellationToken cancellationToken) private async Task<InspectionSession> FindSessionAsync(ITurnContext turnContext, CancellationToken cancellationToken)
{ {
var accessor = _inspectionState.CreateProperty<InspectionSessionsByStatus>(nameof(InspectionSessionsByStatus)); var accessor = _inspectionState.CreateProperty<InspectionSessionsByStatus>(nameof(InspectionSessionsByStatus));
@ -218,12 +225,5 @@ namespace Microsoft.Bot.Builder
openSessions.AttachedSessions.Remove(GetAttachId(turnContext.Activity)); openSessions.AttachedSessions.Remove(GetAttachId(turnContext.Activity));
await _inspectionState.SaveChangesAsync(turnContext, false, cancellationToken).ConfigureAwait(false); await _inspectionState.SaveChangesAsync(turnContext, false, cancellationToken).ConfigureAwait(false);
} }
private string GetAttachId(Activity activity)
{
// If we are running in a Microsoft Teams Team the conversation Id will reflect a particular thread the bot is in.
// So if we are in a Team then we will associate the "attach" with the Team Id rather than the more restrictive conversation Id.
return activity.TeamsGetTeamInfo()?.Id ?? activity.Conversation.Id;
}
} }
} }

Просмотреть файл

@ -1,5 +1,5 @@
// Copyright (c) Microsoft. All rights reserved. // Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. // Licensed under the MIT License.
using System; using System;
using System.Net.Http; using System.Net.Http;
@ -12,11 +12,11 @@ using Microsoft.Extensions.Logging;
namespace Microsoft.Bot.Builder namespace Microsoft.Bot.Builder
{ {
internal class InspectionSession internal class InspectionSession : IDisposable
{ {
private readonly ConnectorClient _connectorClient;
private readonly ConversationReference _conversationReference; private readonly ConversationReference _conversationReference;
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly ConnectorClient _connectorClient;
public InspectionSession(ConversationReference conversationReference, MicrosoftAppCredentials credentials, HttpClient httpClient, ILogger logger) public InspectionSession(ConversationReference conversationReference, MicrosoftAppCredentials credentials, HttpClient httpClient, ILogger logger)
{ {
@ -31,15 +31,22 @@ namespace Microsoft.Bot.Builder
try try
{ {
var resourceResponse = await _connectorClient.Conversations.SendToConversationAsync(activity, cancellationToken).ConfigureAwait(false); await _connectorClient.Conversations.SendToConversationAsync(activity, cancellationToken).ConfigureAwait(false);
} }
catch (Exception err) #pragma warning disable CA1031 // Do not catch general exception types (we just log the exception in this case and return false)
catch (Exception ex)
#pragma warning restore CA1031 // Do not catch general exception types
{ {
_logger.LogWarning($"Exception '{err.ToString()}' while attempting to call Emulator for inspection, check it is running, and you have correct credentials in the Emulator and the InspectionMiddleware."); _logger.LogWarning($"Exception '{ex}' while attempting to call Emulator for inspection, check it is running, and you have correct credentials in the Emulator and the InspectionMiddleware.");
return false; return false;
} }
return true; return true;
} }
public void Dispose()
{
_connectorClient?.Dispose();
}
} }
} }

Просмотреть файл

@ -19,7 +19,7 @@ namespace Microsoft.Bot.Builder
Logger = logger ?? NullLogger.Instance; Logger = logger ?? NullLogger.Instance;
} }
protected ILogger Logger { get; private set; } protected ILogger Logger { get; }
async Task IMiddleware.OnTurnAsync(ITurnContext turnContext, NextDelegate next, CancellationToken cancellationToken) async Task IMiddleware.OnTurnAsync(ITurnContext turnContext, NextDelegate next, CancellationToken cancellationToken)
{ {
@ -58,7 +58,7 @@ namespace Microsoft.Bot.Builder
catch (Exception e) catch (Exception e)
{ {
await InvokeTraceExceptionAsync(turnContext, e.TraceActivity(), cancellationToken).ConfigureAwait(false); await InvokeTraceExceptionAsync(turnContext, e.TraceActivity(), cancellationToken).ConfigureAwait(false);
throw e; throw;
} }
} }
@ -80,9 +80,11 @@ namespace Microsoft.Bot.Builder
{ {
return await InboundAsync(turnContext, traceActivity, cancellationToken).ConfigureAwait(false); return await InboundAsync(turnContext, traceActivity, cancellationToken).ConfigureAwait(false);
} }
catch (Exception e) #pragma warning disable CA1031 // Do not catch general exception types (exception are logged and ignored)
catch (Exception ex)
#pragma warning restore CA1031 // Do not catch general exception types
{ {
Logger.LogWarning($"Exception in inbound interception {e.Message}"); Logger.LogWarning($"Exception in inbound interception {ex.Message}");
return (true, false); return (true, false);
} }
} }
@ -93,9 +95,11 @@ namespace Microsoft.Bot.Builder
{ {
await OutboundAsync(turnContext, traceActivities, cancellationToken).ConfigureAwait(false); await OutboundAsync(turnContext, traceActivities, cancellationToken).ConfigureAwait(false);
} }
catch (Exception e) #pragma warning disable CA1031 // Do not catch general exception types (exception are logged and ignored)
catch (Exception ex)
#pragma warning restore CA1031 // Do not catch general exception types
{ {
Logger.LogWarning($"Exception in outbound interception {e.Message}"); Logger.LogWarning($"Exception in outbound interception {ex.Message}");
} }
} }
@ -110,9 +114,11 @@ namespace Microsoft.Bot.Builder
{ {
await TraceStateAsync(turnContext, cancellationToken).ConfigureAwait(false); await TraceStateAsync(turnContext, cancellationToken).ConfigureAwait(false);
} }
catch (Exception e) #pragma warning disable CA1031 // Do not catch general exception types (exception are logged and ignored)
catch (Exception ex)
#pragma warning restore CA1031 // Do not catch general exception types
{ {
Logger.LogWarning($"Exception in state interception {e.Message}"); Logger.LogWarning($"Exception in state interception {ex.Message}");
} }
} }
@ -122,9 +128,11 @@ namespace Microsoft.Bot.Builder
{ {
await OutboundAsync(turnContext, new Activity[] { traceActivity }, cancellationToken).ConfigureAwait(false); await OutboundAsync(turnContext, new Activity[] { traceActivity }, cancellationToken).ConfigureAwait(false);
} }
catch (Exception e) #pragma warning disable CA1031 // Do not catch general exception types (exception are logged and ignored)
catch (Exception ex)
#pragma warning restore CA1031 // Do not catch general exception types
{ {
Logger.LogWarning($"Exception in exception interception {e.Message}"); Logger.LogWarning($"Exception in exception interception {ex.Message}");
} }
} }
} }

Просмотреть файл

@ -11,8 +11,10 @@ namespace Microsoft.Bot.Builder.Integration
{ {
public static JsonSerializerSettings Create() public static JsonSerializerSettings Create()
{ {
var connector = new ConnectorClient(new Uri("http://localhost/")); using (var connector = new ConnectorClient(new Uri("http://localhost/")))
return connector.DeserializationSettings; {
return connector.DeserializationSettings;
}
} }
} }
} }

Просмотреть файл

@ -26,6 +26,8 @@ namespace Microsoft.Bot.Builder
/// Any extra properties to include in the results. /// Any extra properties to include in the results.
/// </value> /// </value>
[JsonExtensionData(ReadData = true, WriteData = true)] [JsonExtensionData(ReadData = true, WriteData = true)]
#pragma warning disable CA2227 // Collection properties should be read only (we can't change this without breaking binary compat)
public IDictionary<string, object> Properties { get; set; } = new Dictionary<string, object>(); public IDictionary<string, object> Properties { get; set; } = new Dictionary<string, object>();
#pragma warning restore CA2227 // Collection properties should be read only
} }
} }

Просмотреть файл

@ -3,6 +3,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Newtonsoft.Json; using Newtonsoft.Json;
@ -160,7 +161,7 @@ namespace Microsoft.Bot.Builder
throw new Exception($"Etag conflict.\r\n\r\nOriginal: {newStoreItem.ETag}\r\nCurrent: {oldStateETag}"); throw new Exception($"Etag conflict.\r\n\r\nOriginal: {newStoreItem.ETag}\r\nCurrent: {oldStateETag}");
} }
newState["eTag"] = (_eTag++).ToString(); newState["eTag"] = (_eTag++).ToString(CultureInfo.InvariantCulture);
} }
_memory[change.Key] = newState; _memory[change.Key] = newState;

Просмотреть файл

@ -29,7 +29,7 @@ namespace Microsoft.Bot.Builder
{ {
if (activity == null) if (activity == null)
{ {
throw new ArgumentNullException("activity cannot be null for LogActivity()"); throw new ArgumentNullException(nameof(activity), "activity cannot be null for LogActivity()");
} }
lock (_channels) lock (_channels)
@ -142,7 +142,7 @@ namespace Microsoft.Bot.Builder
.Take(20) .Take(20)
.ToArray(); .ToArray();
if (pagedResult.Items.Count() == 20) if (pagedResult.Items.Length == 20)
{ {
pagedResult.ContinuationToken = pagedResult.Items.Last().Id; pagedResult.ContinuationToken = pagedResult.Items.Last().Id;
} }
@ -155,7 +155,7 @@ namespace Microsoft.Bot.Builder
.Take(20) .Take(20)
.ToArray(); .ToArray();
if (pagedResult.Items.Count() == 20) if (pagedResult.Items.Length == 20)
{ {
pagedResult.ContinuationToken = pagedResult.Items.Last().Id; pagedResult.ContinuationToken = pagedResult.Items.Last().Id;
} }
@ -232,7 +232,7 @@ namespace Microsoft.Bot.Builder
.Take(20) .Take(20)
.ToArray(); .ToArray();
if (pagedResult.Items.Count() == 20) if (pagedResult.Items.Length == 20)
{ {
pagedResult.ContinuationToken = pagedResult.Items.Last().Id; pagedResult.ContinuationToken = pagedResult.Items.Last().Id;
} }
@ -250,7 +250,7 @@ namespace Microsoft.Bot.Builder
.Take(20) .Take(20)
.ToArray(); .ToArray();
if (pagedResult.Items.Count() == 20) if (pagedResult.Items.Length == 20)
{ {
pagedResult.ContinuationToken = pagedResult.Items.Last().Id; pagedResult.ContinuationToken = pagedResult.Items.Last().Id;
} }

Просмотреть файл

@ -34,6 +34,10 @@
<PackageReference Include="Microsoft.Bot.Streaming" Condition=" '$(IsBuildServer)' != '' " Version="$(ReleasePackageVersion)" /> <PackageReference Include="Microsoft.Bot.Streaming" Condition=" '$(IsBuildServer)' != '' " Version="$(ReleasePackageVersion)" />
<PackageReference Include="Microsoft.Bot.Connector" Condition=" '$(IsBuildServer)' == '' " Version="$(LocalPackageVersion)" /> <PackageReference Include="Microsoft.Bot.Connector" Condition=" '$(IsBuildServer)' == '' " Version="$(LocalPackageVersion)" />
<PackageReference Include="Microsoft.Bot.Connector" Condition=" '$(IsBuildServer)' != '' " Version="$(ReleasePackageVersion)" /> <PackageReference Include="Microsoft.Bot.Connector" Condition=" '$(IsBuildServer)' != '' " Version="$(ReleasePackageVersion)" />
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="3.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.1.0" /> <PackageReference Include="Microsoft.Extensions.Logging" Version="2.1.0" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" /> <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
</ItemGroup> </ItemGroup>

Просмотреть файл

@ -1,6 +1,8 @@
// Copyright (c) Microsoft Corporation. All rights reserved. // Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. // Licensed under the MIT License.
using System;
namespace Microsoft.Bot.Builder namespace Microsoft.Bot.Builder
{ {
/// <summary> /// <summary>
@ -15,7 +17,9 @@ namespace Microsoft.Bot.Builder
/// <value> /// <value>
/// The array of items. /// The array of items.
/// </value> /// </value>
public T[] Items { get; set; } = new T[0]; #pragma warning disable CA1819 // Properties should not return arrays (can't change this without breaking binary compat)
public T[] Items { get; set; } = Array.Empty<T>();
#pragma warning restore CA1819 // Properties should not return arrays
/// <summary> /// <summary>
/// Gets or sets a token for retrieving the next page of results. /// Gets or sets a token for retrieving the next page of results.

Просмотреть файл

@ -39,9 +39,9 @@ namespace Microsoft.Bot.Builder
/// </exception> /// </exception>
protected override string GetStorageKey(ITurnContext turnContext) protected override string GetStorageKey(ITurnContext turnContext)
{ {
var channelId = turnContext.Activity.ChannelId ?? throw new ArgumentNullException("invalid activity-missing channelId"); var channelId = turnContext.Activity.ChannelId ?? throw new InvalidOperationException("invalid activity-missing channelId");
var conversationId = turnContext.Activity.Conversation?.Id ?? throw new ArgumentNullException("invalid activity-missing Conversation.Id"); var conversationId = turnContext.Activity.Conversation?.Id ?? throw new InvalidOperationException("invalid activity-missing Conversation.Id");
var userId = turnContext.Activity.From?.Id ?? throw new ArgumentNullException("invalid activity-missing From.Id"); var userId = turnContext.Activity.From?.Id ?? throw new InvalidOperationException("invalid activity-missing From.Id");
return $"{channelId}/conversations/{conversationId}/users/{userId}"; return $"{channelId}/conversations/{conversationId}/users/{userId}";
} }
} }

Просмотреть файл

@ -38,7 +38,9 @@ namespace Microsoft.Bot.Builder
/// Mapping from intent to information about the intent. /// Mapping from intent to information about the intent.
/// </value> /// </value>
[JsonProperty("intents")] [JsonProperty("intents")]
#pragma warning disable CA2227 // Collection properties should be read only (we can't change this without breaking binary compat)
public IDictionary<string, IntentScore> Intents { get; set; } = new Dictionary<string, IntentScore>(); public IDictionary<string, IntentScore> Intents { get; set; } = new Dictionary<string, IntentScore>();
#pragma warning restore CA2227 // Collection properties should be read only
/// <summary> /// <summary>
/// Gets or sets the recognized top-level entities. /// Gets or sets the recognized top-level entities.
@ -47,7 +49,9 @@ namespace Microsoft.Bot.Builder
/// Object with each top-level recognized entity as a key. /// Object with each top-level recognized entity as a key.
/// </value> /// </value>
[JsonProperty("entities")] [JsonProperty("entities")]
#pragma warning disable CA2227 // Collection properties should be read only (we can't change this without breaking binary compat)
public JObject Entities { get; set; } = new JObject(); public JObject Entities { get; set; } = new JObject();
#pragma warning restore CA2227 // Collection properties should be read only
/// <summary> /// <summary>
/// Gets or sets properties that are not otherwise defined by the <see cref="RecognizerResult"/> type but that /// Gets or sets properties that are not otherwise defined by the <see cref="RecognizerResult"/> type but that
@ -58,7 +62,9 @@ namespace Microsoft.Bot.Builder
/// the JSON object is deserialized, but are instead stored in this property. Such properties /// the JSON object is deserialized, but are instead stored in this property. Such properties
/// will be written to a JSON object when the instance is serialized.</remarks> /// will be written to a JSON object when the instance is serialized.</remarks>
[JsonExtensionData(ReadData = true, WriteData = true)] [JsonExtensionData(ReadData = true, WriteData = true)]
#pragma warning disable CA2227 // Collection properties should be read only (we can't change this without breaking binary compat)
public IDictionary<string, object> Properties { get; set; } = new Dictionary<string, object>(); public IDictionary<string, object> Properties { get; set; } = new Dictionary<string, object>();
#pragma warning restore CA2227 // Collection properties should be read only
/// <inheritdoc /> /// <inheritdoc />
public void Convert(dynamic result) public void Convert(dynamic result)

Просмотреть файл

@ -24,7 +24,7 @@ namespace Microsoft.Bot.Builder
if (result.Intents == null) if (result.Intents == null)
{ {
throw new ArgumentNullException(nameof(result.Intents)); throw new InvalidOperationException(nameof(result.Intents));
} }
var topIntent = (string.Empty, 0.0d); var topIntent = (string.Empty, 0.0d);

Просмотреть файл

@ -10,7 +10,7 @@ namespace Microsoft.Bot.Builder
public class RegisterClassMiddleware<T> : IMiddleware public class RegisterClassMiddleware<T> : IMiddleware
where T : class where T : class
{ {
private string key; private readonly string _key;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="RegisterClassMiddleware{T}"/> class. /// Initializes a new instance of the <see cref="RegisterClassMiddleware{T}"/> class.
@ -29,7 +29,7 @@ namespace Microsoft.Bot.Builder
public RegisterClassMiddleware(T service, string key) public RegisterClassMiddleware(T service, string key)
{ {
this.Service = service; this.Service = service;
this.key = key; this._key = key;
} }
/// <summary> /// <summary>
@ -53,9 +53,9 @@ namespace Microsoft.Bot.Builder
public async Task OnTurnAsync(ITurnContext turnContext, NextDelegate nextTurn, CancellationToken cancellationToken) public async Task OnTurnAsync(ITurnContext turnContext, NextDelegate nextTurn, CancellationToken cancellationToken)
{ {
// Register service // Register service
if (this.key != null) if (this._key != null)
{ {
turnContext.TurnState.Add(this.key, this.Service); turnContext.TurnState.Add(this._key, this.Service);
} }
else else
{ {

Просмотреть файл

@ -55,26 +55,27 @@ namespace Microsoft.Bot.Builder
/// <seealso cref="Bot.Schema.IActivity"/> /// <seealso cref="Bot.Schema.IActivity"/>
public async Task OnTurnAsync(ITurnContext turnContext, NextDelegate next, CancellationToken cancellationToken) public async Task OnTurnAsync(ITurnContext turnContext, NextDelegate next, CancellationToken cancellationToken)
{ {
CancellationTokenSource cts = null; using (var cts = new CancellationTokenSource())
try
{ {
// If the incoming activity is a MessageActivity, start a timer to periodically send the typing activity Task typingTask = null;
if (turnContext.Activity.Type == ActivityTypes.Message) try
{ {
cts = new CancellationTokenSource(); // If the incoming activity is a MessageActivity, start a timer to periodically send the typing activity.
cancellationToken.Register(() => cts.Cancel()); if (turnContext.Activity.Type == ActivityTypes.Message)
{
// do not await task - we want this to run in the background and we will cancel it when its done
typingTask = SendTypingAsync(turnContext, _delay, _period, cts.Token);
}
// do not await task - we want this to run in the background and we will cancel it when its done await next(cancellationToken).ConfigureAwait(false);
var task = Task.Run(() => SendTypingAsync(turnContext, _delay, _period, cts.Token), cancellationToken);
} }
finally
await next(cancellationToken).ConfigureAwait(false);
}
finally
{
if (cts != null)
{ {
cts.Cancel(); if (typingTask != null && !typingTask.IsCanceled)
{
// Cancel the typing loop.
cts.Cancel();
}
} }
} }
} }
@ -96,7 +97,7 @@ namespace Microsoft.Bot.Builder
await Task.Delay(period, cancellationToken).ConfigureAwait(false); await Task.Delay(period, cancellationToken).ConfigureAwait(false);
} }
} }
catch (TaskCanceledException) catch (OperationCanceledException)
{ {
// do nothing // do nothing
} }

Просмотреть файл

@ -62,7 +62,9 @@ namespace Microsoft.Bot.Builder.Streaming
/// <value> /// <value>
/// The request handlers for this adapter. /// The request handlers for this adapter.
/// </value> /// </value>
#pragma warning disable CA2227 // Collection properties should be read only (we can't change this without breaking binary compat)
protected IList<StreamingRequestHandler> RequestHandlers { get; set; } = new List<StreamingRequestHandler>(); protected IList<StreamingRequestHandler> RequestHandlers { get; set; } = new List<StreamingRequestHandler>();
#pragma warning restore CA2227 // Collection properties should be read only
/// <summary> /// <summary>
/// Primary adapter method for processing activities sent from streaming channel. /// Primary adapter method for processing activities sent from streaming channel.
@ -128,7 +130,7 @@ namespace Microsoft.Bot.Builder.Streaming
// Check to see if any of this adapter's StreamingRequestHandlers is associated with this conversation. // Check to see if any of this adapter's StreamingRequestHandlers is associated with this conversation.
var possibleHandlers = RequestHandlers.Where(x => x.ServiceUrl == activity.ServiceUrl).Where(y => y.HasConversation(activity.Conversation.Id)); var possibleHandlers = RequestHandlers.Where(x => x.ServiceUrl == activity.ServiceUrl).Where(y => y.HasConversation(activity.Conversation.Id));
if (possibleHandlers.Count() > 0) if (possibleHandlers.Any())
{ {
if (possibleHandlers.Count() > 1) if (possibleHandlers.Count() > 1)
{ {
@ -147,32 +149,32 @@ namespace Microsoft.Bot.Builder.Streaming
return await possibleHandlers.First().SendActivityAsync(activity, cancellationToken).ConfigureAwait(false); return await possibleHandlers.First().SendActivityAsync(activity, cancellationToken).ConfigureAwait(false);
} }
else
if (ConnectedBot != null)
{ {
if (ConnectedBot != null) // This is a proactive message that will need a new streaming connection opened.
// The ServiceUrl of a streaming connection follows the pattern "urn:[ChannelName]:[Protocol]:[Host]".
#pragma warning disable CA2000 // Dispose objects before losing scope (we can't fix this without closing the socket connection, this should be addressed after we make StreamingRequestHandler disposable and we dispose the connector )
var connection = new ClientWebSocket();
#pragma warning restore CA2000 // Dispose objects before losing scope
var uri = activity.ServiceUrl.Split(':');
var protocol = uri[uri.Length - 2];
var host = uri[uri.Length - 1];
await connection.ConnectAsync(new Uri(protocol + host + "/api/messages"), cancellationToken).ConfigureAwait(false);
var handler = new StreamingRequestHandler(ConnectedBot, this, connection, Logger);
if (RequestHandlers == null)
{ {
// This is a proactive message that will need a new streaming connection opened. RequestHandlers = new List<StreamingRequestHandler>();
// The ServiceUrl of a streaming connection follows the pattern "urn:[ChannelName]:[Protocol]:[Host]".
var connection = new ClientWebSocket();
var uri = activity.ServiceUrl.Split(':');
var protocol = uri[uri.Length - 2];
var host = uri[uri.Length - 1];
await connection.ConnectAsync(new Uri(protocol + host + "/api/messages"), cancellationToken).ConfigureAwait(false);
var handler = new StreamingRequestHandler(ConnectedBot, this, connection, Logger);
if (RequestHandlers == null)
{
RequestHandlers = new List<StreamingRequestHandler>();
}
RequestHandlers.Add(handler);
return await handler.SendActivityAsync(activity, cancellationToken).ConfigureAwait(false);
} }
return null; RequestHandlers.Add(handler);
return await handler.SendActivityAsync(activity, cancellationToken).ConfigureAwait(false);
} }
return null;
} }
/// <summary> /// <summary>
@ -228,7 +230,7 @@ namespace Microsoft.Bot.Builder.Streaming
// information unique to streaming connections. Now that we know that this is a streaming // information unique to streaming connections. Now that we know that this is a streaming
// activity, process it in the streaming pipeline. // activity, process it in the streaming pipeline.
// Process streaming activity. // Process streaming activity.
return await SendStreamingActivityAsync(activity).ConfigureAwait(false); return await SendStreamingActivityAsync(activity, cancellationToken).ConfigureAwait(false);
} }
/// <summary> /// <summary>
@ -239,7 +241,9 @@ namespace Microsoft.Bot.Builder.Streaming
var emptyCredentials = (ChannelProvider != null && ChannelProvider.IsGovernment()) ? var emptyCredentials = (ChannelProvider != null && ChannelProvider.IsGovernment()) ?
MicrosoftGovernmentAppCredentials.Empty : MicrosoftGovernmentAppCredentials.Empty :
MicrosoftAppCredentials.Empty; MicrosoftAppCredentials.Empty;
#pragma warning disable CA2000 // Dispose objects before losing scope (We need to make ConnectorClient disposable to fix this, ignoring it for now)
var streamingClient = new StreamingHttpClient(requestHandler, Logger); var streamingClient = new StreamingHttpClient(requestHandler, Logger);
#pragma warning restore CA2000 // Dispose objects before losing scope
var connectorClient = new ConnectorClient(new Uri(activity.ServiceUrl), emptyCredentials, customHttpClient: streamingClient); var connectorClient = new ConnectorClient(new Uri(activity.ServiceUrl), emptyCredentials, customHttpClient: streamingClient);
return connectorClient; return connectorClient;
} }

Просмотреть файл

@ -14,7 +14,7 @@ namespace Microsoft.Bot.Builder.Streaming
{ {
internal class StreamingHttpClient : HttpClient internal class StreamingHttpClient : HttpClient
{ {
private StreamingRequestHandler _requestHandler; private readonly StreamingRequestHandler _requestHandler;
private readonly ILogger _logger; private readonly ILogger _logger;
/// <summary> /// <summary>
@ -29,19 +29,19 @@ namespace Microsoft.Bot.Builder.Streaming
_logger = logger ?? NullLogger.Instance; _logger = logger ?? NullLogger.Instance;
} }
public override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken = default) public override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{ {
var streamingRequest = new StreamingRequest var streamingRequest = new StreamingRequest
{ {
Path = request.RequestUri.OriginalString.Substring(request.RequestUri.OriginalString.IndexOf("/v3")), Path = request.RequestUri.OriginalString.Substring(request.RequestUri.OriginalString.IndexOf("/v3", StringComparison.Ordinal)),
Verb = request.Method.ToString(), Verb = request.Method.ToString(),
}; };
streamingRequest.SetBody(request.Content); streamingRequest.SetBody(request.Content);
return await this.SendRequestAsync<HttpResponseMessage>(streamingRequest, cancellationToken).ConfigureAwait(false); return await SendRequestAsync<HttpResponseMessage>(streamingRequest, cancellationToken).ConfigureAwait(false);
} }
public async Task<ReceiveResponse> SendAsync(StreamingRequest streamingRequest, CancellationToken cancellationToken = default) => await this._requestHandler.SendStreamingRequestAsync(streamingRequest, cancellationToken).ConfigureAwait(false); public async Task<ReceiveResponse> SendAsync(StreamingRequest streamingRequest, CancellationToken cancellationToken = default) => await _requestHandler.SendStreamingRequestAsync(streamingRequest, cancellationToken).ConfigureAwait(false);
private async Task<T> SendRequestAsync<T>(StreamingRequest request, CancellationToken cancellation = default) private async Task<T> SendRequestAsync<T>(StreamingRequest request, CancellationToken cancellation = default)
{ {
@ -59,9 +59,11 @@ namespace Microsoft.Bot.Builder.Streaming
return serverResponse.ReadBodyAsJson<T>(); return serverResponse.ReadBodyAsJson<T>();
} }
} }
#pragma warning disable CA1031 // Do not catch general exception types (we just log the exception and continue)
catch (Exception ex) catch (Exception ex)
#pragma warning restore CA1031 // Do not catch general exception types
{ {
this._logger.LogError(ex.ToString()); _logger.LogError(ex.ToString());
} }
return default; return default;

Просмотреть файл

@ -4,6 +4,7 @@
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
@ -30,8 +31,8 @@ namespace Microsoft.Bot.Builder.Streaming
private readonly IStreamingActivityProcessor _activityProcessor; private readonly IStreamingActivityProcessor _activityProcessor;
private readonly string _userAgent; private readonly string _userAgent;
private readonly IDictionary<string, DateTime> _conversations; private readonly IDictionary<string, DateTime> _conversations;
private readonly IStreamingTransportServer _server;
private IStreamingTransportServer _server;
private bool _serverIsConnected; private bool _serverIsConnected;
/// <summary> /// <summary>
@ -92,7 +93,9 @@ namespace Microsoft.Bot.Builder.Streaming
/// <value> /// <value>
/// The URL of the channel endpoint this StreamingRequestHandler receives requests from. /// The URL of the channel endpoint this StreamingRequestHandler receives requests from.
/// </value> /// </value>
#pragma warning disable CA1056 // Uri properties should not be strings (we can't change this without breaking binary compat)
public string ServiceUrl { get; private set; } public string ServiceUrl { get; private set; }
#pragma warning restore CA1056 // Uri properties should not be strings
/// <summary> /// <summary>
/// Begins listening for incoming requests over this StreamingRequestHandler's server. /// Begins listening for incoming requests over this StreamingRequestHandler's server.
@ -123,7 +126,7 @@ namespace Microsoft.Bot.Builder.Streaming
/// the conversation was added to this <see cref="StreamingRequestHandler"/>.</returns> /// the conversation was added to this <see cref="StreamingRequestHandler"/>.</returns>
public DateTime ConversationAddedTime(string conversationId) public DateTime ConversationAddedTime(string conversationId)
{ {
if (!_conversations.TryGetValue(conversationId, out DateTime addedTime)) if (!_conversations.TryGetValue(conversationId, out var addedTime))
{ {
addedTime = DateTime.MinValue; addedTime = DateTime.MinValue;
} }
@ -146,7 +149,7 @@ namespace Microsoft.Bot.Builder.Streaming
var response = new StreamingResponse(); var response = new StreamingResponse();
// We accept all POSTs regardless of path, but anything else requires special treatment. // We accept all POSTs regardless of path, but anything else requires special treatment.
if (!string.Equals(request?.Verb, StreamingRequest.POST, StringComparison.InvariantCultureIgnoreCase)) if (!string.Equals(request?.Verb, StreamingRequest.POST, StringComparison.OrdinalIgnoreCase))
{ {
return HandleCustomPaths(request, response); return HandleCustomPaths(request, response);
} }
@ -157,7 +160,9 @@ namespace Microsoft.Bot.Builder.Streaming
{ {
body = request.ReadBodyAsString(); body = request.ReadBodyAsString();
} }
#pragma warning disable CA1031 // Do not catch general exception types (we log the exception and continue execution)
catch (Exception ex) catch (Exception ex)
#pragma warning restore CA1031 // Do not catch general exception types
{ {
response.StatusCode = (int)HttpStatusCode.BadRequest; response.StatusCode = (int)HttpStatusCode.BadRequest;
_logger.LogError("Request body missing or malformed: " + ex.Message); _logger.LogError("Request body missing or malformed: " + ex.Message);
@ -225,7 +230,9 @@ namespace Microsoft.Bot.Builder.Streaming
} }
} }
} }
#pragma warning disable CA1031 // Do not catch general exception types (we logging the error and we send it back in the body of the response)
catch (Exception ex) catch (Exception ex)
#pragma warning restore CA1031 // Do not catch general exception types
{ {
response.StatusCode = (int)HttpStatusCode.InternalServerError; response.StatusCode = (int)HttpStatusCode.InternalServerError;
response.SetBody(ex.ToString()); response.SetBody(ex.ToString());
@ -279,7 +286,9 @@ namespace Microsoft.Bot.Builder.Streaming
return serverResponse.ReadBodyAsJson<ResourceResponse>(); return serverResponse.ReadBodyAsJson<ResourceResponse>();
} }
} }
#pragma warning disable CA1031 // Do not catch general exception types (this should probably be addressed later, but for now we just log the error and continue the execution)
catch (Exception ex) catch (Exception ex)
#pragma warning restore CA1031 // Do not catch general exception types
{ {
_logger.LogError(ex.Message); _logger.LogError(ex.Message);
} }
@ -309,7 +318,9 @@ namespace Microsoft.Bot.Builder.Streaming
return serverResponse.ReadBodyAsJson<ReceiveResponse>(); return serverResponse.ReadBodyAsJson<ReceiveResponse>();
} }
} }
#pragma warning disable CA1031 // Do not catch general exception types (this should probably be addressed later, but for now we just log the error and continue the execution)
catch (Exception ex) catch (Exception ex)
#pragma warning restore CA1031 // Do not catch general exception types
{ {
_logger.LogError(ex.Message); _logger.LogError(ex.Message);
} }
@ -327,15 +338,21 @@ namespace Microsoft.Bot.Builder.Streaming
/// https://github.com/Microsoft/botbuilder-dotnet/blob/d342cd66d159a023ac435aec0fdf791f93118f5f/doc/UserAgents.md. /// https://github.com/Microsoft/botbuilder-dotnet/blob/d342cd66d159a023ac435aec0fdf791f93118f5f/doc/UserAgents.md.
/// </summary> /// </summary>
/// <returns>A string containing versioning information.</returns> /// <returns>A string containing versioning information.</returns>
private static string GetUserAgent() => private static string GetUserAgent()
string.Format( {
"Microsoft-BotFramework/3.1 Streaming-Extensions/1.0 BotBuilder/{0} ({1}; {2}; {3})", using (var connectorClient = new ConnectorClient(new Uri("http://localhost")))
ConnectorClient.GetClientVersion(new ConnectorClient(new Uri("http://localhost"))), {
ConnectorClient.GetASPNetVersion(), return string.Format(
ConnectorClient.GetOsVersion(), CultureInfo.InvariantCulture,
ConnectorClient.GetArchitecture()); "Microsoft-BotFramework/3.1 Streaming-Extensions/1.0 BotBuilder/{0} ({1}; {2}; {3})",
ConnectorClient.GetClientVersion(connectorClient),
ConnectorClient.GetASPNetVersion(),
ConnectorClient.GetOsVersion(),
ConnectorClient.GetArchitecture());
}
}
private IEnumerable<HttpContent> UpdateAttachmentStreams(Activity activity) private static IEnumerable<HttpContent> UpdateAttachmentStreams(Activity activity)
{ {
if (activity == null || activity.Attachments == null) if (activity == null || activity.Attachments == null)
{ {
@ -379,8 +396,8 @@ namespace Microsoft.Bot.Builder.Streaming
return response; return response;
} }
if (string.Equals(request.Verb, StreamingRequest.GET, StringComparison.InvariantCultureIgnoreCase) && if (string.Equals(request.Verb, StreamingRequest.GET, StringComparison.OrdinalIgnoreCase) &&
string.Equals(request.Path, "/api/version", StringComparison.InvariantCultureIgnoreCase)) string.Equals(request.Path, "/api/version", StringComparison.OrdinalIgnoreCase))
{ {
response.StatusCode = (int)HttpStatusCode.OK; response.StatusCode = (int)HttpStatusCode.OK;
response.SetBody(new VersionInfo() { UserAgent = _userAgent }); response.SetBody(new VersionInfo() { UserAgent = _userAgent });

Просмотреть файл

@ -20,13 +20,17 @@ namespace Microsoft.Bot.Builder.Teams
public static async Task<TeamDetails> GetTeamDetailsAsync(ITurnContext turnContext, string teamId = null, CancellationToken cancellationToken = default) public static async Task<TeamDetails> GetTeamDetailsAsync(ITurnContext turnContext, string teamId = null, CancellationToken cancellationToken = default)
{ {
var t = teamId ?? turnContext.Activity.TeamsGetTeamInfo()?.Id ?? throw new InvalidOperationException("This method is only valid within the scope of MS Teams Team."); var t = teamId ?? turnContext.Activity.TeamsGetTeamInfo()?.Id ?? throw new InvalidOperationException("This method is only valid within the scope of MS Teams Team.");
#pragma warning disable CA2000 // Dispose objects before losing scope (we need to review this, disposing the connectorClient may have unintended consequences)
return await GetTeamsConnectorClient(turnContext).Teams.FetchTeamDetailsAsync(t, cancellationToken).ConfigureAwait(false); return await GetTeamsConnectorClient(turnContext).Teams.FetchTeamDetailsAsync(t, cancellationToken).ConfigureAwait(false);
#pragma warning restore CA2000 // Dispose objects before losing scope
} }
public static async Task<IList<ChannelInfo>> GetTeamChannelsAsync(ITurnContext turnContext, string teamId = null, CancellationToken cancellationToken = default) public static async Task<IList<ChannelInfo>> GetTeamChannelsAsync(ITurnContext turnContext, string teamId = null, CancellationToken cancellationToken = default)
{ {
var t = teamId ?? turnContext.Activity.TeamsGetTeamInfo()?.Id ?? throw new InvalidOperationException("This method is only valid within the scope of MS Teams Team."); var t = teamId ?? turnContext.Activity.TeamsGetTeamInfo()?.Id ?? throw new InvalidOperationException("This method is only valid within the scope of MS Teams Team.");
#pragma warning disable CA2000 // Dispose objects before losing scope (we need to review this, disposing the connectorClient may have unintended consequences)
var channelList = await GetTeamsConnectorClient(turnContext).Teams.FetchChannelListAsync(t, cancellationToken).ConfigureAwait(false); var channelList = await GetTeamsConnectorClient(turnContext).Teams.FetchChannelListAsync(t, cancellationToken).ConfigureAwait(false);
#pragma warning restore CA2000 // Dispose objects before losing scope
return channelList.Conversations; return channelList.Conversations;
} }
@ -102,7 +106,7 @@ namespace Microsoft.Bot.Builder.Teams
if (turnContext.Activity == null) if (turnContext.Activity == null)
{ {
throw new ArgumentNullException(nameof(turnContext.Activity)); throw new InvalidOperationException(nameof(turnContext.Activity));
} }
if (string.IsNullOrEmpty(teamsChannelId)) if (string.IsNullOrEmpty(teamsChannelId))

Просмотреть файл

@ -324,7 +324,9 @@ namespace Microsoft.Bot.Builder
/// <param name="activity">The Activity object deleted by bot.</param> /// <param name="activity">The Activity object deleted by bot.</param>
/// <param name="additionalProperties">Additional properties to add to the event.</param> /// <param name="additionalProperties">Additional properties to add to the event.</param>
/// <returns>The properties and their values to log when the bot deletes a message it sent previously.</returns> /// <returns>The properties and their values to log when the bot deletes a message it sent previously.</returns>
#pragma warning disable CA1822 // Mark members as static (can't change this without breaking binary compat)
protected Task<Dictionary<string, string>> FillDeleteEventPropertiesAsync(IMessageDeleteActivity activity, Dictionary<string, string> additionalProperties = null) protected Task<Dictionary<string, string>> FillDeleteEventPropertiesAsync(IMessageDeleteActivity activity, Dictionary<string, string> additionalProperties = null)
#pragma warning restore CA1822 // Mark members as static
{ {
var properties = new Dictionary<string, string>() var properties = new Dictionary<string, string>()
{ {

Просмотреть файл

@ -13,7 +13,6 @@ using Microsoft.Bot.Connector;
using Microsoft.Bot.Connector.Authentication; using Microsoft.Bot.Connector.Authentication;
using Microsoft.Bot.Schema; using Microsoft.Bot.Schema;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Newtonsoft.Json.Linq;
namespace Microsoft.Bot.Builder namespace Microsoft.Bot.Builder
{ {
@ -23,69 +22,63 @@ namespace Microsoft.Bot.Builder
public static void CheckForOAuthCards(BotFrameworkAdapter adapter, ILogger logger, ITurnContext turnContext, Activity activity, CancellationToken cancellationToken) public static void CheckForOAuthCards(BotFrameworkAdapter adapter, ILogger logger, ITurnContext turnContext, Activity activity, CancellationToken cancellationToken)
{ {
if (activity == null || activity.Attachments == null) if (activity?.Attachments == null)
{ {
return; return;
} }
var pollTokenTasks = new List<Task>();
foreach (var attachment in activity.Attachments.Where(a => a.ContentType == OAuthCard.ContentType)) foreach (var attachment in activity.Attachments.Where(a => a.ContentType == OAuthCard.ContentType))
{ {
OAuthCard oauthCard = attachment.Content as OAuthCard; if (attachment.Content is OAuthCard oauthCard)
if (oauthCard != null)
{ {
if (string.IsNullOrWhiteSpace(oauthCard.ConnectionName)) if (string.IsNullOrWhiteSpace(oauthCard.ConnectionName))
{ {
throw new InvalidOperationException("The OAuthPrompt's ConnectionName property is missing a value."); throw new InvalidOperationException("The OAuthPrompt's ConnectionName property is missing a value.");
} }
// Poll as a background task // Poll as a background task and add to the list (we don't call await here, we await all the calls together later).
Task.Run(() => PollForTokenAsync(adapter, logger, turnContext, activity, oauthCard.ConnectionName, cancellationToken)) pollTokenTasks.Add(PollForTokenAsync(adapter, logger, turnContext, oauthCard.ConnectionName, cancellationToken));
.ContinueWith(t =>
{
if (t.Exception != null)
{
logger.LogError(t.Exception.InnerException ?? t.Exception, "PollForTokenAsync threw an exception", oauthCard.ConnectionName);
}
});
} }
} }
if (pollTokenTasks.Any())
{
// Wait for all the poll operations to complete.
Task.WaitAll(pollTokenTasks.ToArray());
}
} }
private static async Task PollForTokenAsync(BotFrameworkAdapter adapter, ILogger logger, ITurnContext turnContext, Activity activity, string connectionName, CancellationToken cancellationToken) private static async Task PollForTokenAsync(BotFrameworkAdapter adapter, ILogger logger, ITurnContext turnContext, string connectionName, CancellationToken cancellationToken)
{ {
TokenResponse tokenResponse = null; try
bool shouldEndPolling = false;
var pollingTimeout = TurnStateConstants.OAuthLoginTimeoutValue;
var pollingRequestsInterval = PollingInterval;
var loginTimeout = turnContext.TurnState.Get<object>(TurnStateConstants.OAuthLoginTimeoutKey);
bool sentToken = false;
// Override login timeout with value set from the OAuthPrompt or by the developer
if (turnContext.TurnState.ContainsKey(TurnStateConstants.OAuthLoginTimeoutKey))
{ {
pollingTimeout = (TimeSpan)turnContext.TurnState.Get<object>(TurnStateConstants.OAuthLoginTimeoutKey); var shouldEndPolling = false;
} var pollingTimeout = TurnStateConstants.OAuthLoginTimeoutValue;
var pollingRequestsInterval = PollingInterval;
var sentToken = false;
var stopwatch = Stopwatch.StartNew(); // Override login timeout with value set from the OAuthPrompt or by the developer
var oauthClient = turnContext.TurnState.Get<OAuthClient>(); if (turnContext.TurnState.ContainsKey(TurnStateConstants.OAuthLoginTimeoutKey))
while (stopwatch.Elapsed < pollingTimeout && !shouldEndPolling)
{
tokenResponse = await adapter.GetUserTokenAsync(turnContext, oauthClient?.Credentials as AppCredentials, connectionName, null, cancellationToken).ConfigureAwait(false);
if (tokenResponse != null)
{ {
// This can be used to short-circuit the polling loop. pollingTimeout = (TimeSpan)turnContext.TurnState.Get<object>(TurnStateConstants.OAuthLoginTimeoutKey);
if (tokenResponse.Properties != null) }
var stopwatch = Stopwatch.StartNew();
var oauthClient = turnContext.TurnState.Get<OAuthClient>();
while (stopwatch.Elapsed < pollingTimeout && !shouldEndPolling)
{
var tokenResponse = await adapter.GetUserTokenAsync(turnContext, oauthClient?.Credentials as AppCredentials, connectionName, null, cancellationToken).ConfigureAwait(false);
if (tokenResponse != null)
{ {
JToken tokenPollingSettingsToken = null; // This can be used to short-circuit the polling loop.
TokenPollingSettings tokenPollingSettings = null; if (tokenResponse.Properties != null)
tokenResponse.Properties.TryGetValue(TurnStateConstants.TokenPollingSettingsKey, out tokenPollingSettingsToken);
if (tokenPollingSettingsToken != null)
{ {
tokenPollingSettings = tokenPollingSettingsToken.ToObject<TokenPollingSettings>(); tokenResponse.Properties.TryGetValue(TurnStateConstants.TokenPollingSettingsKey, out var tokenPollingSettingsToken);
var tokenPollingSettings = tokenPollingSettingsToken?.ToObject<TokenPollingSettings>();
if (tokenPollingSettings != null) if (tokenPollingSettings != null)
{ {
logger.LogInformation($"PollForTokenAsync received new polling settings: timeout={tokenPollingSettings.Timeout}, interval={tokenPollingSettings.Interval}", tokenPollingSettings); logger.LogInformation($"PollForTokenAsync received new polling settings: timeout={tokenPollingSettings.Timeout}, interval={tokenPollingSettings.Interval}", tokenPollingSettings);
@ -93,34 +86,40 @@ namespace Microsoft.Bot.Builder
pollingRequestsInterval = tokenPollingSettings.Interval > 0 ? TimeSpan.FromMilliseconds(tokenPollingSettings.Interval) : pollingRequestsInterval; // Only overrides if it is set. pollingRequestsInterval = tokenPollingSettings.Interval > 0 ? TimeSpan.FromMilliseconds(tokenPollingSettings.Interval) : pollingRequestsInterval; // Only overrides if it is set.
} }
} }
// once there is a token, send it to the bot and stop polling
if (tokenResponse.Token != null)
{
var tokenResponseActivityEvent = CreateTokenResponse(turnContext.Activity.GetConversationReference(), tokenResponse.Token, connectionName);
var identity = turnContext.TurnState.Get<IIdentity>(BotAdapter.BotIdentityKey) as ClaimsIdentity;
var callback = turnContext.TurnState.Get<BotCallbackHandler>();
await adapter.ProcessActivityAsync(identity, tokenResponseActivityEvent, callback, cancellationToken).ConfigureAwait(false);
shouldEndPolling = true;
sentToken = true;
logger.LogInformation("PollForTokenAsync completed with a token", turnContext.Activity);
}
} }
// once there is a token, send it to the bot and stop polling if (!shouldEndPolling)
if (tokenResponse.Token != null)
{ {
var tokenResponseActivityEvent = CreateTokenResponse(turnContext.Activity.GetConversationReference(), tokenResponse.Token, connectionName); await Task.Delay(pollingRequestsInterval, cancellationToken).ConfigureAwait(false);
var identity = turnContext.TurnState.Get<IIdentity>(BotFrameworkAdapter.BotIdentityKey) as ClaimsIdentity;
var callback = turnContext.TurnState.Get<BotCallbackHandler>();
await adapter.ProcessActivityAsync(identity, tokenResponseActivityEvent, callback, cancellationToken).ConfigureAwait(false);
shouldEndPolling = true;
sentToken = true;
logger.LogInformation("PollForTokenAsync completed with a token", turnContext.Activity);
} }
} }
if (!shouldEndPolling) if (!sentToken)
{ {
await Task.Delay(pollingRequestsInterval).ConfigureAwait(false); logger.LogInformation("PollForTokenAsync completed without receiving a token", turnContext.Activity);
} }
}
if (!sentToken) stopwatch.Stop();
}
#pragma warning disable CA1031 // Do not catch general exception types (for new we just log the exception and continue)
catch (Exception ex)
#pragma warning restore CA1031 // Do not catch general exception types
{ {
logger.LogInformation("PollForTokenAsync completed without receiving a token", turnContext.Activity); logger.LogError(ex, "PollForTokenAsync threw an exception", connectionName);
} }
stopwatch.Stop();
} }
private static Activity CreateTokenResponse(ConversationReference relatesTo, string token, string connectionName) private static Activity CreateTokenResponse(ConversationReference relatesTo, string token, string connectionName)

Просмотреть файл

@ -13,9 +13,9 @@ namespace Microsoft.Bot.Builder
/// </summary> /// </summary>
public class TraceTranscriptLogger : ITranscriptLogger public class TraceTranscriptLogger : ITranscriptLogger
{ {
private static JsonSerializerSettings serializationSettings = new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.Indented }; private static readonly JsonSerializerSettings _serializationSettings = new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.Indented };
private bool traceActivity; private readonly bool _traceActivity;
public TraceTranscriptLogger() public TraceTranscriptLogger()
: this(true) : this(true)
@ -24,7 +24,7 @@ namespace Microsoft.Bot.Builder
public TraceTranscriptLogger(bool traceActivity) public TraceTranscriptLogger(bool traceActivity)
{ {
this.traceActivity = traceActivity; this._traceActivity = traceActivity;
} }
/// <summary> /// <summary>
@ -35,9 +35,9 @@ namespace Microsoft.Bot.Builder
public Task LogActivityAsync(IActivity activity) public Task LogActivityAsync(IActivity activity)
{ {
BotAssert.ActivityNotNull(activity); BotAssert.ActivityNotNull(activity);
if (traceActivity) if (_traceActivity)
{ {
Trace.TraceInformation(JsonConvert.SerializeObject(activity, serializationSettings)); Trace.TraceInformation(JsonConvert.SerializeObject(activity, _serializationSettings));
} }
else else
{ {

Просмотреть файл

@ -4,6 +4,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Bot.Schema; using Microsoft.Bot.Schema;
@ -16,8 +17,8 @@ namespace Microsoft.Bot.Builder
/// </summary> /// </summary>
public class TranscriptLoggerMiddleware : IMiddleware public class TranscriptLoggerMiddleware : IMiddleware
{ {
private static JsonSerializerSettings _jsonSettings = new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore }; private static readonly JsonSerializerSettings _jsonSettings = new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore };
private ITranscriptLogger logger; private readonly ITranscriptLogger _logger;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="TranscriptLoggerMiddleware"/> class. /// Initializes a new instance of the <see cref="TranscriptLoggerMiddleware"/> class.
@ -25,7 +26,7 @@ namespace Microsoft.Bot.Builder
/// <param name="transcriptLogger">The conversation store to use.</param> /// <param name="transcriptLogger">The conversation store to use.</param>
public TranscriptLoggerMiddleware(ITranscriptLogger transcriptLogger) public TranscriptLoggerMiddleware(ITranscriptLogger transcriptLogger)
{ {
logger = transcriptLogger ?? throw new ArgumentNullException("TranscriptLoggerMiddleware requires a ITranscriptLogger implementation. "); _logger = transcriptLogger ?? throw new ArgumentNullException(nameof(transcriptLogger), "TranscriptLoggerMiddleware requires a ITranscriptLogger implementation. ");
} }
/// <summary> /// <summary>
@ -108,26 +109,34 @@ namespace Microsoft.Bot.Builder
await nextTurn(cancellationToken).ConfigureAwait(false); await nextTurn(cancellationToken).ConfigureAwait(false);
// flush transcript at end of turn // flush transcript at end of turn
var logTasks = new List<Task>();
while (transcript.Count > 0) while (transcript.Count > 0)
{ {
// Process the queue and log all the activities in parallel.
var activity = transcript.Dequeue(); var activity = transcript.Dequeue();
// As we are deliberately not using await, disable the associated warning. // Add the logging task to the list (we don't call await here, we await all the calls together later).
#pragma warning disable 4014 logTasks.Add(TryLogActivityAsync(_logger, activity));
logger.LogActivityAsync(activity).ContinueWith( }
task =>
{ if (logTasks.Any())
try {
{ // Wait for all the activities to be logged before continuing.
task.Wait(); await Task.WhenAll(logTasks).ConfigureAwait(false);
} }
catch (Exception err) }
{
Trace.TraceError($"Transcript logActivity failed with {err}"); private static async Task TryLogActivityAsync(ITranscriptLogger logger, IActivity activity)
} {
}, try
cancellationToken); {
#pragma warning restore 4014 await logger.LogActivityAsync(activity).ConfigureAwait(false);
}
#pragma warning disable CA1031 // Do not catch general exception types (this should probably be addressed later, but for now we just log the error and continue the execution)
catch (Exception ex)
#pragma warning restore CA2008 // Do not create tasks without passing a TaskScheduler
{
Trace.TraceError($"Transcript logActivity failed with {ex}");
} }
} }
@ -145,7 +154,7 @@ namespace Microsoft.Bot.Builder
if (activity == null) if (activity == null)
{ {
throw new ArgumentNullException("Cannot check or add Id on a null Activity."); throw new ArgumentNullException(nameof(activity), "Cannot check or add Id on a null Activity.");
} }
if (activity.Id == null) if (activity.Id == null)
@ -157,7 +166,7 @@ namespace Microsoft.Bot.Builder
return activityWithId; return activityWithId;
} }
private void LogActivity(Queue<IActivity> transcript, IActivity activity) private static void LogActivity(Queue<IActivity> transcript, IActivity activity)
{ {
if (activity.Timestamp == null) if (activity.Timestamp == null)
{ {

Просмотреть файл

@ -401,6 +401,16 @@ namespace Microsoft.Bot.Builder
/// </summary> /// </summary>
public void Dispose() public void Dispose()
{ {
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
// Dispose any disposable objects owned by the class here.
}
} }
private async Task<ResourceResponse> UpdateActivityInternalAsync( private async Task<ResourceResponse> UpdateActivityInternalAsync(
@ -412,11 +422,11 @@ namespace Microsoft.Bot.Builder
BotAssert.ActivityNotNull(activity); BotAssert.ActivityNotNull(activity);
if (updateHandlers == null) if (updateHandlers == null)
{ {
throw new ArgumentException(nameof(updateHandlers)); throw new ArgumentException($"{nameof(updateHandlers)} is null.", nameof(updateHandlers));
} }
// No middleware to run. // No middleware to run.
if (updateHandlers.Count() == 0) if (!updateHandlers.Any())
{ {
if (callAtBottom != null) if (callAtBottom != null)
{ {
@ -452,11 +462,11 @@ namespace Microsoft.Bot.Builder
if (deleteHandlers == null) if (deleteHandlers == null)
{ {
throw new ArgumentException(nameof(deleteHandlers)); throw new ArgumentException($"{nameof(deleteHandlers)} is null", nameof(deleteHandlers));
} }
// No middleware to run. // No middleware to run.
if (deleteHandlers.Count() == 0) if (!deleteHandlers.Any())
{ {
if (callAtBottom != null) if (callAtBottom != null)
{ {

Просмотреть файл

@ -133,6 +133,16 @@ namespace Microsoft.Bot.Builder
public void Dispose() public void Dispose()
{ {
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
// Dispose any disposable objects owned by the class here.
}
} }
} }
} }

Просмотреть файл

@ -37,8 +37,8 @@ namespace Microsoft.Bot.Builder
/// <see cref="Schema.ConversationAccount.Id"/> is missing.</exception> /// <see cref="Schema.ConversationAccount.Id"/> is missing.</exception>
protected override string GetStorageKey(ITurnContext turnContext) protected override string GetStorageKey(ITurnContext turnContext)
{ {
var channelId = turnContext.Activity.ChannelId ?? throw new ArgumentNullException("invalid activity-missing channelId"); var channelId = turnContext.Activity.ChannelId ?? throw new InvalidOperationException("invalid activity-missing channelId");
var userId = turnContext.Activity.From?.Id ?? throw new ArgumentNullException("invalid activity-missing From.Id"); var userId = turnContext.Activity.From?.Id ?? throw new InvalidOperationException("invalid activity-missing From.Id");
return $"{channelId}/users/{userId}"; return $"{channelId}/users/{userId}";
} }
} }

Просмотреть файл

@ -23,7 +23,8 @@
<PropertyGroup> <PropertyGroup>
<!-- These documentation warnings excludes should be removed as part of https://github.com/microsoft/botbuilder-dotnet/issues/4052 --> <!-- These documentation warnings excludes should be removed as part of https://github.com/microsoft/botbuilder-dotnet/issues/4052 -->
<NoWarn>$(NoWarn);CS1591</NoWarn> <!-- SX1309: FieldNamesShouldBeginWithUnderscores should be fixed as part of https://github.com/microsoft/botframework-sdk/issues/5933 -->
<NoWarn>$(NoWarn);CS1591;SX1309</NoWarn>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

Просмотреть файл

@ -54,7 +54,9 @@ namespace Microsoft.Bot.Connector.Authentication
/// <returns> /// <returns>
/// A valid ClaimsIdentity. /// A valid ClaimsIdentity.
/// </returns> /// </returns>
#pragma warning disable UseAsyncSuffix // Use Async suffix (can't change this without breaking binary compat)
public static async Task<ClaimsIdentity> AuthenticateChannelToken(string authHeader, ICredentialProvider credentials, HttpClient httpClient, string channelId) public static async Task<ClaimsIdentity> AuthenticateChannelToken(string authHeader, ICredentialProvider credentials, HttpClient httpClient, string channelId)
#pragma warning restore UseAsyncSuffix // Use Async suffix
{ {
return await AuthenticateChannelToken(authHeader, credentials, httpClient, channelId, new AuthenticationConfiguration()).ConfigureAwait(false); return await AuthenticateChannelToken(authHeader, credentials, httpClient, channelId, new AuthenticationConfiguration()).ConfigureAwait(false);
} }
@ -75,7 +77,9 @@ namespace Microsoft.Bot.Connector.Authentication
/// <returns> /// <returns>
/// A valid ClaimsIdentity. /// A valid ClaimsIdentity.
/// </returns> /// </returns>
#pragma warning disable UseAsyncSuffix // Use Async suffix (can't change this without breaking binary compat)
public static async Task<ClaimsIdentity> AuthenticateChannelToken(string authHeader, ICredentialProvider credentials, HttpClient httpClient, string channelId, AuthenticationConfiguration authConfig) public static async Task<ClaimsIdentity> AuthenticateChannelToken(string authHeader, ICredentialProvider credentials, HttpClient httpClient, string channelId, AuthenticationConfiguration authConfig)
#pragma warning restore UseAsyncSuffix // Use Async suffix
{ {
if (authConfig == null) if (authConfig == null)
{ {
@ -145,7 +149,9 @@ namespace Microsoft.Bot.Connector.Authentication
/// setup and teardown, so a shared HttpClient is recommended.</param> /// setup and teardown, so a shared HttpClient is recommended.</param>
/// <param name="channelId">The ID of the channel to validate.</param> /// <param name="channelId">The ID of the channel to validate.</param>
/// <returns>ClaimsIdentity.</returns> /// <returns>ClaimsIdentity.</returns>
#pragma warning disable UseAsyncSuffix // Use Async suffix (can't change this without breaking binary compat)
public static async Task<ClaimsIdentity> AuthenticateChannelToken(string authHeader, ICredentialProvider credentials, string serviceUrl, HttpClient httpClient, string channelId) public static async Task<ClaimsIdentity> AuthenticateChannelToken(string authHeader, ICredentialProvider credentials, string serviceUrl, HttpClient httpClient, string channelId)
#pragma warning restore UseAsyncSuffix // Use Async suffix
{ {
return await AuthenticateChannelToken(authHeader, credentials, serviceUrl, httpClient, channelId, new AuthenticationConfiguration()).ConfigureAwait(false); return await AuthenticateChannelToken(authHeader, credentials, serviceUrl, httpClient, channelId, new AuthenticationConfiguration()).ConfigureAwait(false);
} }
@ -162,7 +168,9 @@ namespace Microsoft.Bot.Connector.Authentication
/// <param name="channelId">The ID of the channel to validate.</param> /// <param name="channelId">The ID of the channel to validate.</param>
/// <param name="authConfig">The authentication configuration.</param> /// <param name="authConfig">The authentication configuration.</param>
/// <returns>ClaimsIdentity.</returns> /// <returns>ClaimsIdentity.</returns>
#pragma warning disable UseAsyncSuffix // Use Async suffix (can't change this without breaking binary compat)
public static async Task<ClaimsIdentity> AuthenticateChannelToken(string authHeader, ICredentialProvider credentials, string serviceUrl, HttpClient httpClient, string channelId, AuthenticationConfiguration authConfig) public static async Task<ClaimsIdentity> AuthenticateChannelToken(string authHeader, ICredentialProvider credentials, string serviceUrl, HttpClient httpClient, string channelId, AuthenticationConfiguration authConfig)
#pragma warning restore UseAsyncSuffix // Use Async suffix
{ {
if (authConfig == null) if (authConfig == null)
{ {

Просмотреть файл

@ -92,7 +92,9 @@ namespace Microsoft.Bot.Connector.Authentication
/// <remarks> /// <remarks>
/// A token issued by the Bot Framework will FAIL this check. Only Emulator tokens will pass. /// A token issued by the Bot Framework will FAIL this check. Only Emulator tokens will pass.
/// </remarks> /// </remarks>
#pragma warning disable UseAsyncSuffix // Use Async suffix (can't change this without breaking binary compat)
public static async Task<ClaimsIdentity> AuthenticateEmulatorToken(string authHeader, ICredentialProvider credentials, IChannelProvider channelProvider, HttpClient httpClient, string channelId) public static async Task<ClaimsIdentity> AuthenticateEmulatorToken(string authHeader, ICredentialProvider credentials, IChannelProvider channelProvider, HttpClient httpClient, string channelId)
#pragma warning restore UseAsyncSuffix // Use Async suffix
{ {
return await AuthenticateEmulatorToken(authHeader, credentials, channelProvider, httpClient, channelId, new AuthenticationConfiguration()).ConfigureAwait(false); return await AuthenticateEmulatorToken(authHeader, credentials, channelProvider, httpClient, channelId, new AuthenticationConfiguration()).ConfigureAwait(false);
} }
@ -114,7 +116,9 @@ namespace Microsoft.Bot.Connector.Authentication
/// <remarks> /// <remarks>
/// A token issued by the Bot Framework will FAIL this check. Only Emulator tokens will pass. /// A token issued by the Bot Framework will FAIL this check. Only Emulator tokens will pass.
/// </remarks> /// </remarks>
#pragma warning disable UseAsyncSuffix // Use Async suffix (can't change this without breaking binary compat)
public static async Task<ClaimsIdentity> AuthenticateEmulatorToken(string authHeader, ICredentialProvider credentials, IChannelProvider channelProvider, HttpClient httpClient, string channelId, AuthenticationConfiguration authConfig) public static async Task<ClaimsIdentity> AuthenticateEmulatorToken(string authHeader, ICredentialProvider credentials, IChannelProvider channelProvider, HttpClient httpClient, string channelId, AuthenticationConfiguration authConfig)
#pragma warning restore UseAsyncSuffix // Use Async suffix
{ {
if (authConfig == null) if (authConfig == null)
{ {

Просмотреть файл

@ -42,7 +42,9 @@ namespace Microsoft.Bot.Connector.Authentication
/// setup and teardown, so a shared HttpClient is recommended.</param> /// setup and teardown, so a shared HttpClient is recommended.</param>
/// <param name="channelId">The ID of the channel to validate.</param> /// <param name="channelId">The ID of the channel to validate.</param>
/// <returns>ClaimsIdentity.</returns> /// <returns>ClaimsIdentity.</returns>
#pragma warning disable UseAsyncSuffix // Use Async suffix (can't change this without breaking binary compat)
public static async Task<ClaimsIdentity> AuthenticateChannelToken(string authHeader, ICredentialProvider credentials, IChannelProvider channelProvider, string serviceUrl, HttpClient httpClient, string channelId) public static async Task<ClaimsIdentity> AuthenticateChannelToken(string authHeader, ICredentialProvider credentials, IChannelProvider channelProvider, string serviceUrl, HttpClient httpClient, string channelId)
#pragma warning restore UseAsyncSuffix // Use Async suffix
{ {
return await AuthenticateChannelToken(authHeader, credentials, channelProvider, serviceUrl, httpClient, channelId, new AuthenticationConfiguration()).ConfigureAwait(false); return await AuthenticateChannelToken(authHeader, credentials, channelProvider, serviceUrl, httpClient, channelId, new AuthenticationConfiguration()).ConfigureAwait(false);
} }
@ -60,7 +62,9 @@ namespace Microsoft.Bot.Connector.Authentication
/// <param name="channelId">The ID of the channel to validate.</param> /// <param name="channelId">The ID of the channel to validate.</param>
/// <param name="authConfig">The authentication configuration.</param> /// <param name="authConfig">The authentication configuration.</param>
/// <returns>ClaimsIdentity.</returns> /// <returns>ClaimsIdentity.</returns>
#pragma warning disable UseAsyncSuffix // Use Async suffix (can't change this without breaking binary compat)
public static async Task<ClaimsIdentity> AuthenticateChannelToken(string authHeader, ICredentialProvider credentials, IChannelProvider channelProvider, string serviceUrl, HttpClient httpClient, string channelId, AuthenticationConfiguration authConfig) public static async Task<ClaimsIdentity> AuthenticateChannelToken(string authHeader, ICredentialProvider credentials, IChannelProvider channelProvider, string serviceUrl, HttpClient httpClient, string channelId, AuthenticationConfiguration authConfig)
#pragma warning restore UseAsyncSuffix // Use Async suffix
{ {
if (authConfig == null) if (authConfig == null)
{ {
@ -82,7 +86,9 @@ namespace Microsoft.Bot.Connector.Authentication
return identity; return identity;
} }
#pragma warning disable UseAsyncSuffix // Use Async suffix (can't change this without breaking binary compat)
public static async Task ValidateIdentity(ClaimsIdentity identity, ICredentialProvider credentials, string serviceUrl) public static async Task ValidateIdentity(ClaimsIdentity identity, ICredentialProvider credentials, string serviceUrl)
#pragma warning restore UseAsyncSuffix // Use Async suffix
{ {
if (identity == null) if (identity == null)
{ {

Просмотреть файл

@ -44,7 +44,9 @@ namespace Microsoft.Bot.Connector.Authentication
/// setup and teardown, so a shared HttpClient is recommended.</param> /// setup and teardown, so a shared HttpClient is recommended.</param>
/// <param name="channelId">The ID of the channel to validate.</param> /// <param name="channelId">The ID of the channel to validate.</param>
/// <returns>ClaimsIdentity.</returns> /// <returns>ClaimsIdentity.</returns>
#pragma warning disable UseAsyncSuffix // Use Async suffix (can't change this without breaking binary compat)
public static async Task<ClaimsIdentity> AuthenticateChannelToken(string authHeader, ICredentialProvider credentials, string serviceUrl, HttpClient httpClient, string channelId) public static async Task<ClaimsIdentity> AuthenticateChannelToken(string authHeader, ICredentialProvider credentials, string serviceUrl, HttpClient httpClient, string channelId)
#pragma warning restore UseAsyncSuffix // Use Async suffix
{ {
return await AuthenticateChannelToken(authHeader, credentials, serviceUrl, httpClient, channelId, new AuthenticationConfiguration()).ConfigureAwait(false); return await AuthenticateChannelToken(authHeader, credentials, serviceUrl, httpClient, channelId, new AuthenticationConfiguration()).ConfigureAwait(false);
} }
@ -61,7 +63,9 @@ namespace Microsoft.Bot.Connector.Authentication
/// <param name="channelId">The ID of the channel to validate.</param> /// <param name="channelId">The ID of the channel to validate.</param>
/// <param name="authConfig">The authentication configuration.</param> /// <param name="authConfig">The authentication configuration.</param>
/// <returns>ClaimsIdentity.</returns> /// <returns>ClaimsIdentity.</returns>
#pragma warning disable UseAsyncSuffix // Use Async suffix (can't change this without breaking binary compat)
public static async Task<ClaimsIdentity> AuthenticateChannelToken(string authHeader, ICredentialProvider credentials, string serviceUrl, HttpClient httpClient, string channelId, AuthenticationConfiguration authConfig) public static async Task<ClaimsIdentity> AuthenticateChannelToken(string authHeader, ICredentialProvider credentials, string serviceUrl, HttpClient httpClient, string channelId, AuthenticationConfiguration authConfig)
#pragma warning restore UseAsyncSuffix // Use Async suffix
{ {
if (authConfig == null) if (authConfig == null)
{ {
@ -88,7 +92,9 @@ namespace Microsoft.Bot.Connector.Authentication
/// <param name="credentials">The user defined set of valid credentials, such as the AppId.</param> /// <param name="credentials">The user defined set of valid credentials, such as the AppId.</param>
/// <param name="serviceUrl">The service url from the request.</param> /// <param name="serviceUrl">The service url from the request.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns> /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
#pragma warning disable UseAsyncSuffix // Use Async suffix (can't change this without breaking binary compat)
public static async Task ValidateIdentity(ClaimsIdentity identity, ICredentialProvider credentials, string serviceUrl) public static async Task ValidateIdentity(ClaimsIdentity identity, ICredentialProvider credentials, string serviceUrl)
#pragma warning restore UseAsyncSuffix // Use Async suffix
{ {
if (identity == null) if (identity == null)
{ {

Просмотреть файл

@ -30,7 +30,9 @@ namespace Microsoft.Bot.Connector.Authentication
/// <returns>A task that represents the work queued to execute.</returns> /// <returns>A task that represents the work queued to execute.</returns>
/// <remarks>If the task completes successfully, the result contains the claims-based /// <remarks>If the task completes successfully, the result contains the claims-based
/// identity for the request.</remarks> /// identity for the request.</remarks>
#pragma warning disable UseAsyncSuffix // Use Async suffix (can't change this without breaking binary compat)
public static async Task<ClaimsIdentity> AuthenticateRequest(IActivity activity, string authHeader, ICredentialProvider credentials, IChannelProvider provider, HttpClient httpClient = null) public static async Task<ClaimsIdentity> AuthenticateRequest(IActivity activity, string authHeader, ICredentialProvider credentials, IChannelProvider provider, HttpClient httpClient = null)
#pragma warning restore UseAsyncSuffix // Use Async suffix
{ {
return await AuthenticateRequest(activity, authHeader, credentials, provider, new AuthenticationConfiguration(), httpClient).ConfigureAwait(false); return await AuthenticateRequest(activity, authHeader, credentials, provider, new AuthenticationConfiguration(), httpClient).ConfigureAwait(false);
} }
@ -48,7 +50,9 @@ namespace Microsoft.Bot.Connector.Authentication
/// <returns>A task that represents the work queued to execute.</returns> /// <returns>A task that represents the work queued to execute.</returns>
/// <remarks>If the task completes successfully, the result contains the claims-based /// <remarks>If the task completes successfully, the result contains the claims-based
/// identity for the request.</remarks> /// identity for the request.</remarks>
#pragma warning disable UseAsyncSuffix // Use Async suffix (can't change this without breaking binary compat)
public static async Task<ClaimsIdentity> AuthenticateRequest(IActivity activity, string authHeader, ICredentialProvider credentials, IChannelProvider provider, AuthenticationConfiguration authConfig, HttpClient httpClient = null) public static async Task<ClaimsIdentity> AuthenticateRequest(IActivity activity, string authHeader, ICredentialProvider credentials, IChannelProvider provider, AuthenticationConfiguration authConfig, HttpClient httpClient = null)
#pragma warning restore UseAsyncSuffix // Use Async suffix
{ {
if (authConfig == null) if (authConfig == null)
{ {
@ -89,7 +93,9 @@ namespace Microsoft.Bot.Connector.Authentication
/// <returns>A task that represents the work queued to execute.</returns> /// <returns>A task that represents the work queued to execute.</returns>
/// <remarks>If the task completes successfully, the result contains the claims-based /// <remarks>If the task completes successfully, the result contains the claims-based
/// identity for the request.</remarks> /// identity for the request.</remarks>
#pragma warning disable UseAsyncSuffix // Use Async suffix (can't change this without breaking binary compat)
public static async Task<ClaimsIdentity> ValidateAuthHeader(string authHeader, ICredentialProvider credentials, IChannelProvider channelProvider, string channelId, string serviceUrl = null, HttpClient httpClient = null) public static async Task<ClaimsIdentity> ValidateAuthHeader(string authHeader, ICredentialProvider credentials, IChannelProvider channelProvider, string channelId, string serviceUrl = null, HttpClient httpClient = null)
#pragma warning restore UseAsyncSuffix // Use Async suffix
{ {
return await ValidateAuthHeader(authHeader, credentials, channelProvider, channelId, new AuthenticationConfiguration(), serviceUrl, httpClient).ConfigureAwait(false); return await ValidateAuthHeader(authHeader, credentials, channelProvider, channelId, new AuthenticationConfiguration(), serviceUrl, httpClient).ConfigureAwait(false);
} }
@ -107,7 +113,9 @@ namespace Microsoft.Bot.Connector.Authentication
/// <returns>A task that represents the work queued to execute.</returns> /// <returns>A task that represents the work queued to execute.</returns>
/// <remarks>If the task completes successfully, the result contains the claims-based /// <remarks>If the task completes successfully, the result contains the claims-based
/// identity for the request.</remarks> /// identity for the request.</remarks>
#pragma warning disable UseAsyncSuffix // Use Async suffix (can't change this without breaking binary compat)
public static async Task<ClaimsIdentity> ValidateAuthHeader(string authHeader, ICredentialProvider credentials, IChannelProvider channelProvider, string channelId, AuthenticationConfiguration authConfig, string serviceUrl = null, HttpClient httpClient = null) public static async Task<ClaimsIdentity> ValidateAuthHeader(string authHeader, ICredentialProvider credentials, IChannelProvider channelProvider, string channelId, AuthenticationConfiguration authConfig, string serviceUrl = null, HttpClient httpClient = null)
#pragma warning restore UseAsyncSuffix // Use Async suffix
{ {
if (string.IsNullOrEmpty(authHeader)) if (string.IsNullOrEmpty(authHeader))
{ {
@ -121,7 +129,7 @@ namespace Microsoft.Bot.Connector.Authentication
httpClient = httpClient ?? _httpClient; httpClient = httpClient ?? _httpClient;
var identity = await AuthenticateToken(authHeader, credentials, channelProvider, channelId, authConfig, serviceUrl, httpClient).ConfigureAwait(false); var identity = await AuthenticateTokenAsync(authHeader, credentials, channelProvider, channelId, authConfig, serviceUrl, httpClient).ConfigureAwait(false);
await ValidateClaimsAsync(authConfig, identity.Claims).ConfigureAwait(false); await ValidateClaimsAsync(authConfig, identity.Claims).ConfigureAwait(false);
@ -210,7 +218,7 @@ namespace Microsoft.Bot.Connector.Authentication
// [0] = "Bearer" // [0] = "Bearer"
// [1] = "[Big Long String]" // [1] = "[Big Long String]"
var authScheme = parts[0]; var authScheme = parts[0];
if (!authScheme.Equals("Bearer", StringComparison.InvariantCultureIgnoreCase)) if (!authScheme.Equals("Bearer", StringComparison.OrdinalIgnoreCase))
{ {
// The scheme MUST be "Bearer" // The scheme MUST be "Bearer"
return false; return false;
@ -222,7 +230,7 @@ namespace Microsoft.Bot.Connector.Authentication
/// <summary> /// <summary>
/// Authenticates the auth header token from the request. /// Authenticates the auth header token from the request.
/// </summary> /// </summary>
private static async Task<ClaimsIdentity> AuthenticateToken(string authHeader, ICredentialProvider credentials, IChannelProvider channelProvider, string channelId, AuthenticationConfiguration authConfig, string serviceUrl, HttpClient httpClient) private static async Task<ClaimsIdentity> AuthenticateTokenAsync(string authHeader, ICredentialProvider credentials, IChannelProvider channelProvider, string channelId, AuthenticationConfiguration authConfig, string serviceUrl, HttpClient httpClient)
{ {
if (SkillValidation.IsSkillToken(authHeader)) if (SkillValidation.IsSkillToken(authHeader))
{ {

Просмотреть файл

@ -9,7 +9,9 @@ namespace Microsoft.Bot.Connector.Authentication
{ {
public static class Retry public static class Retry
{ {
#pragma warning disable UseAsyncSuffix // Use Async suffix (can't change this without breaking binary compat)
public static async Task<TResult> Run<TResult>(Func<Task<TResult>> task, Func<Exception, int, RetryParams> retryExceptionHandler) public static async Task<TResult> Run<TResult>(Func<Task<TResult>> task, Func<Exception, int, RetryParams> retryExceptionHandler)
#pragma warning restore UseAsyncSuffix // Use Async suffix
{ {
RetryParams retry; RetryParams retry;
var exceptions = new List<Exception>(); var exceptions = new List<Exception>();

Просмотреть файл

@ -42,7 +42,7 @@ namespace Microsoft.Bot.Connector.Authentication
/// <returns>True if this channel provider represents a channel on US Government Azure.</returns> /// <returns>True if this channel provider represents a channel on US Government Azure.</returns>
public bool IsGovernment() public bool IsGovernment()
{ {
return string.Equals(GovernmentAuthenticationConstants.ChannelService, ChannelService, StringComparison.InvariantCultureIgnoreCase); return string.Equals(GovernmentAuthenticationConstants.ChannelService, ChannelService, StringComparison.OrdinalIgnoreCase);
} }
/// <summary> /// <summary>

Просмотреть файл

@ -94,7 +94,7 @@ namespace Microsoft.Bot.Connector.Authentication
} }
var audience = claimsList.FirstOrDefault(claim => claim.Type == AuthenticationConstants.AudienceClaim)?.Value; var audience = claimsList.FirstOrDefault(claim => claim.Type == AuthenticationConstants.AudienceClaim)?.Value;
if (string.IsNullOrWhiteSpace(audience) || AuthenticationConstants.ToBotFromChannelTokenIssuer.Equals(audience, StringComparison.InvariantCulture)) if (string.IsNullOrWhiteSpace(audience) || AuthenticationConstants.ToBotFromChannelTokenIssuer.Equals(audience, StringComparison.OrdinalIgnoreCase))
{ {
// The audience is https://api.botframework.com and not an appId. // The audience is https://api.botframework.com and not an appId.
return false; return false;
@ -124,7 +124,9 @@ namespace Microsoft.Bot.Connector.Authentication
/// <param name="channelId">The ID of the channel to validate.</param> /// <param name="channelId">The ID of the channel to validate.</param>
/// <param name="authConfig">The authentication configuration.</param> /// <param name="authConfig">The authentication configuration.</param>
/// <returns>A <see cref="ClaimsIdentity"/> instance if the validation is successful.</returns> /// <returns>A <see cref="ClaimsIdentity"/> instance if the validation is successful.</returns>
#pragma warning disable UseAsyncSuffix // Use Async suffix (can't change this without breaking binary compat)
public static async Task<ClaimsIdentity> AuthenticateChannelToken(string authHeader, ICredentialProvider credentials, IChannelProvider channelProvider, HttpClient httpClient, string channelId, AuthenticationConfiguration authConfig) public static async Task<ClaimsIdentity> AuthenticateChannelToken(string authHeader, ICredentialProvider credentials, IChannelProvider channelProvider, HttpClient httpClient, string channelId, AuthenticationConfiguration authConfig)
#pragma warning restore UseAsyncSuffix // Use Async suffix
{ {
if (authConfig == null) if (authConfig == null)
{ {

Просмотреть файл

@ -24,7 +24,8 @@
<PropertyGroup> <PropertyGroup>
<!-- These documentation warnings excludes should be removed as part of https://github.com/microsoft/botbuilder-dotnet/issues/4052 --> <!-- These documentation warnings excludes should be removed as part of https://github.com/microsoft/botbuilder-dotnet/issues/4052 -->
<NoWarn>$(NoWarn);CS1591</NoWarn> <!-- SX1309: FieldNamesShouldBeginWithUnderscores should be fixed as part of https://github.com/microsoft/botframework-sdk/issues/5933 -->
<NoWarn>$(NoWarn);CS1591;SX1309</NoWarn>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@ -38,6 +39,10 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="AsyncUsageAnalyzers" Version="1.0.0-alpha003">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.1.0" /> <PackageReference Include="Microsoft.Extensions.Logging" Version="2.1.0" />
<PackageReference Include="Microsoft.IdentityModel.Clients.ActiveDirectory" Version="5.2.4" /> <PackageReference Include="Microsoft.IdentityModel.Clients.ActiveDirectory" Version="5.2.4" />
<PackageReference Include="Microsoft.IdentityModel.Protocols.OpenIdConnect" Version="5.6.0" /> <PackageReference Include="Microsoft.IdentityModel.Protocols.OpenIdConnect" Version="5.6.0" />

Просмотреть файл

@ -23,10 +23,15 @@
<PropertyGroup> <PropertyGroup>
<!-- These documentation warnings excludes should be removed as part of https://github.com/microsoft/botbuilder-dotnet/issues/4052 --> <!-- These documentation warnings excludes should be removed as part of https://github.com/microsoft/botbuilder-dotnet/issues/4052 -->
<NoWarn>$(NoWarn);CS1573;CS1591</NoWarn> <!-- SX1309: FieldNamesShouldBeginWithUnderscores should be fixed as part of https://github.com/microsoft/botframework-sdk/issues/5933 -->
<NoWarn>$(NoWarn);CS1573;CS1591;SX1309</NoWarn>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="AsyncUsageAnalyzers" Version="1.0.0-alpha003">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="3.0.0"> <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="3.0.0">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

Просмотреть файл

@ -25,8 +25,9 @@
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<!-- These documentation warnings excludes should be removed as part of https://github.com/microsoft/botbuilder-dotnet/issues/4052 --> <!-- CS1591: These documentation warnings excludes should be removed as part of https://github.com/microsoft/botbuilder-dotnet/issues/4052 -->
<NoWarn>$(NoWarn);CS1591</NoWarn> <!-- SX1309: FieldNamesShouldBeginWithUnderscores should be fixed as part of https://github.com/microsoft/botframework-sdk/issues/5933 -->
<NoWarn>$(NoWarn);CS1591;SX1309</NoWarn>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

Просмотреть файл

@ -12,7 +12,6 @@ namespace Microsoft.Bot.Streaming
/// </summary> /// </summary>
public abstract class RequestHandler public abstract class RequestHandler
{ {
#pragma warning disable IDE0034
/// <summary> /// <summary>
/// The method that must be implemented in order to handle incoming requests. /// The method that must be implemented in order to handle incoming requests.
/// </summary> /// </summary>
@ -22,6 +21,5 @@ namespace Microsoft.Bot.Streaming
/// <param name="cancellationToken">Cancellation token.</param> /// <param name="cancellationToken">Cancellation token.</param>
/// <returns>A <see cref="Task"/> that will produce a <see cref="StreamingResponse"/> on successful completion.</returns> /// <returns>A <see cref="Task"/> that will produce a <see cref="StreamingResponse"/> on successful completion.</returns>
public abstract Task<StreamingResponse> ProcessRequestAsync(ReceiveRequest request, ILogger<RequestHandler> logger, object context = null, CancellationToken cancellationToken = default(CancellationToken)); public abstract Task<StreamingResponse> ProcessRequestAsync(ReceiveRequest request, ILogger<RequestHandler> logger, object context = null, CancellationToken cancellationToken = default(CancellationToken));
#pragma warning restore IDE0034
} }
} }

Просмотреть файл

@ -24,7 +24,8 @@
<PropertyGroup> <PropertyGroup>
<!-- These documentation warnings excludes should be removed as part of https://github.com/microsoft/botbuilder-dotnet/issues/4052 --> <!-- These documentation warnings excludes should be removed as part of https://github.com/microsoft/botbuilder-dotnet/issues/4052 -->
<NoWarn>$(NoWarn);CS1591</NoWarn> <!-- SX1309: FieldNamesShouldBeginWithUnderscores should be fixed as part of https://github.com/microsoft/botframework-sdk/issues/5933 -->
<NoWarn>$(NoWarn);CS1591;SX1309</NoWarn>
</PropertyGroup> </PropertyGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp3.1'"> <ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp3.1'">

Просмотреть файл

@ -25,7 +25,8 @@
<PropertyGroup> <PropertyGroup>
<!-- These documentation warnings excludes should be removed as part of https://github.com/microsoft/botbuilder-dotnet/issues/4052 --> <!-- These documentation warnings excludes should be removed as part of https://github.com/microsoft/botbuilder-dotnet/issues/4052 -->
<NoWarn>$(NoWarn);CS1591</NoWarn> <!-- SX1309: FieldNamesShouldBeginWithUnderscores should be fixed as part of https://github.com/microsoft/botframework-sdk/issues/5933 -->
<NoWarn>$(NoWarn);CS1591;SX1309</NoWarn>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>

Просмотреть файл

@ -23,7 +23,8 @@
<PropertyGroup> <PropertyGroup>
<!-- These documentation warnings excludes should be removed as part of https://github.com/microsoft/botbuilder-dotnet/issues/4052 --> <!-- These documentation warnings excludes should be removed as part of https://github.com/microsoft/botbuilder-dotnet/issues/4052 -->
<NoWarn>$(NoWarn);CS1591</NoWarn> <!-- SX1309: FieldNamesShouldBeginWithUnderscores should be fixed as part of https://github.com/microsoft/botframework-sdk/issues/5933 -->
<NoWarn>$(NoWarn);CS1591;SX1309</NoWarn>
</PropertyGroup> </PropertyGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp3.1'"> <ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp3.1'">

Просмотреть файл

@ -20,7 +20,8 @@
<PropertyGroup> <PropertyGroup>
<!-- These documentation warnings excludes should be removed as part of https://github.com/microsoft/botbuilder-dotnet/issues/4052 --> <!-- These documentation warnings excludes should be removed as part of https://github.com/microsoft/botbuilder-dotnet/issues/4052 -->
<NoWarn>$(NoWarn);CS1591</NoWarn> <!-- SX1309: FieldNamesShouldBeginWithUnderscores should be fixed as part of https://github.com/microsoft/botframework-sdk/issues/5933 -->
<NoWarn>$(NoWarn);CS1591;SX1309</NoWarn>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">

Просмотреть файл

@ -42,7 +42,7 @@ namespace Microsoft.Bot.Builder.Adapters.Twilio.Tests
const string resourceIdentifier = "Mocked Resource Identifier"; const string resourceIdentifier = "Mocked Resource Identifier";
var twilioApi = new Mock<TwilioClientWrapper>(_testOptions); var twilioApi = new Mock<TwilioClientWrapper>(_testOptions);
twilioApi.Setup(x => x.SendMessage(It.IsAny<CreateMessageOptions>(), default)).Returns(Task.FromResult(resourceIdentifier)); twilioApi.Setup(x => x.SendMessageAsync(It.IsAny<CreateMessageOptions>(), default)).Returns(Task.FromResult(resourceIdentifier));
var twilioAdapter = new TwilioAdapter(twilioApi.Object); var twilioAdapter = new TwilioAdapter(twilioApi.Object);
var resourceResponses = await twilioAdapter.SendActivitiesAsync(null, new Activity[] { activity.Object }, default).ConfigureAwait(false); var resourceResponses = await twilioAdapter.SendActivitiesAsync(null, new Activity[] { activity.Object }, default).ConfigureAwait(false);
@ -61,7 +61,7 @@ namespace Microsoft.Bot.Builder.Adapters.Twilio.Tests
const string resourceIdentifier = "Mocked Resource Identifier"; const string resourceIdentifier = "Mocked Resource Identifier";
var twilioApi = new Mock<TwilioClientWrapper>(_testOptions); var twilioApi = new Mock<TwilioClientWrapper>(_testOptions);
twilioApi.Setup(x => x.SendMessage(It.IsAny<CreateMessageOptions>(), default)).Returns(Task.FromResult(resourceIdentifier)); twilioApi.Setup(x => x.SendMessageAsync(It.IsAny<CreateMessageOptions>(), default)).Returns(Task.FromResult(resourceIdentifier));
var twilioAdapter = new TwilioAdapter(twilioApi.Object); var twilioAdapter = new TwilioAdapter(twilioApi.Object);
var resourceResponses = await twilioAdapter.SendActivitiesAsync(null, new Activity[] { activity.Object }, default).ConfigureAwait(false); var resourceResponses = await twilioAdapter.SendActivitiesAsync(null, new Activity[] { activity.Object }, default).ConfigureAwait(false);

Просмотреть файл

@ -1,8 +1,9 @@
<Project> <Project>
<!-- Contains common properties that apply to projects under the Tests folder --> <!-- Contains common properties that apply to projects under the Tests folder -->
<PropertyGroup> <PropertyGroup>
<!-- For tests, we don't generate documentation. Supress related rules. --> <!-- SA0001;CS1573,CS1591,CS1712: For tests, we don't generate documentation. Supress related rules. -->
<NoWarn>$(NoWarn);SA0001;CS1573,CS1591,CS1712</NoWarn> <!-- SX1309: FieldNamesShouldBeginWithUnderscores should be fixed as part of https://github.com/microsoft/botframework-sdk/issues/5933 -->
<NoWarn>$(NoWarn);SA0001;CS1573;CS1591;CS1712;SX1309</NoWarn>
</PropertyGroup> </PropertyGroup>
<!-- This ensures that Directory.Build.props in parent folders are merged with this one --> <!-- This ensures that Directory.Build.props in parent folders are merged with this one -->

Просмотреть файл

@ -16,24 +16,6 @@ namespace Microsoft.Bot.Builder.Tests.Adapters
{ {
public class TestAdapterTests public class TestAdapterTests
{ {
public async Task MyBotLogic(ITurnContext turnContext, CancellationToken cancellationToken)
{
switch (turnContext.Activity.AsMessageActivity().Text)
{
case "count":
await turnContext.SendActivityAsync(turnContext.Activity.CreateReply("one"));
await turnContext.SendActivityAsync(turnContext.Activity.CreateReply("two"));
await turnContext.SendActivityAsync(turnContext.Activity.CreateReply("three"));
break;
case "ignore":
break;
default:
await turnContext.SendActivityAsync(
turnContext.Activity.CreateReply($"echo:{turnContext.Activity.AsMessageActivity().Text}"));
break;
}
}
[Fact] [Fact]
public async Task TestAdapter_ExceptionTypesOnTest() public async Task TestAdapter_ExceptionTypesOnTest()
{ {
@ -579,5 +561,23 @@ namespace Microsoft.Bot.Builder.Tests.Adapters
Assert.Equal(targetChannel, receivedChannelId); Assert.Equal(targetChannel, receivedChannelId);
Assert.Equal(targetChannel, reply.ChannelId); Assert.Equal(targetChannel, reply.ChannelId);
} }
private async Task MyBotLogic(ITurnContext turnContext, CancellationToken cancellationToken)
{
switch (turnContext.Activity.AsMessageActivity().Text)
{
case "count":
await turnContext.SendActivityAsync(turnContext.Activity.CreateReply("one"));
await turnContext.SendActivityAsync(turnContext.Activity.CreateReply("two"));
await turnContext.SendActivityAsync(turnContext.Activity.CreateReply("three"));
break;
case "ignore":
break;
default:
await turnContext.SendActivityAsync(
turnContext.Activity.CreateReply($"echo:{turnContext.Activity.AsMessageActivity().Text}"));
break;
}
}
} }
} }

Просмотреть файл

@ -610,7 +610,7 @@ namespace Microsoft.Bot.Builder.Tests
var context = TestUtilities.CreateEmptyContext(); var context = TestUtilities.CreateEmptyContext();
context.Activity.From = null; context.Activity.From = null;
var testProperty = userState.CreateProperty<TestPocoState>("test"); var testProperty = userState.CreateProperty<TestPocoState>("test");
await Assert.ThrowsAsync<ArgumentNullException>(() => testProperty.GetAsync(context)); await Assert.ThrowsAsync<InvalidOperationException>(() => testProperty.GetAsync(context));
} }
[Fact] [Fact]
@ -621,7 +621,7 @@ namespace Microsoft.Bot.Builder.Tests
var context = TestUtilities.CreateEmptyContext(); var context = TestUtilities.CreateEmptyContext();
context.Activity.Conversation = null; context.Activity.Conversation = null;
var testProperty = userState.CreateProperty<TestPocoState>("test"); var testProperty = userState.CreateProperty<TestPocoState>("test");
await Assert.ThrowsAsync<ArgumentNullException>(() => testProperty.GetAsync(context)); await Assert.ThrowsAsync<InvalidOperationException>(() => testProperty.GetAsync(context));
} }
[Fact] [Fact]
@ -633,7 +633,7 @@ namespace Microsoft.Bot.Builder.Tests
context.Activity.Conversation = null; context.Activity.Conversation = null;
context.Activity.From = null; context.Activity.From = null;
var testProperty = userState.CreateProperty<TestPocoState>("test"); var testProperty = userState.CreateProperty<TestPocoState>("test");
await Assert.ThrowsAsync<ArgumentNullException>(() => testProperty.GetAsync(context)); await Assert.ThrowsAsync<InvalidOperationException>(() => testProperty.GetAsync(context));
} }
[Fact] [Fact]
@ -644,7 +644,7 @@ namespace Microsoft.Bot.Builder.Tests
var context = TestUtilities.CreateEmptyContext(); var context = TestUtilities.CreateEmptyContext();
context.Activity.Conversation = null; context.Activity.Conversation = null;
var testProperty = userState.CreateProperty<TestPocoState>("test"); var testProperty = userState.CreateProperty<TestPocoState>("test");
await Assert.ThrowsAsync<ArgumentNullException>(() => testProperty.GetAsync(context)); await Assert.ThrowsAsync<InvalidOperationException>(() => testProperty.GetAsync(context));
} }
[Fact] [Fact]