From 51b364553d6fd91a10b3fa799ab5ac02b702423a Mon Sep 17 00:00:00 2001 From: Jenya Y Date: Tue, 3 May 2016 01:09:07 +0300 Subject: [PATCH] Added VTFS webhooks project with basic modeling of payload --- CustomDictionary.xml | 4 + WebHooks.sln | 29 ++- .../VstsReceiver/App_Start/WebApiConfig.cs | 24 +++ samples/VstsReceiver/Global.asax | 1 + samples/VstsReceiver/Global.asax.cs | 12 ++ .../VstsReceiver/Properties/AssemblyInfo.cs | 34 +++ samples/VstsReceiver/VstsReceiver.csproj | 161 ++++++++++++++ samples/VstsReceiver/Web.Debug.config | 30 +++ samples/VstsReceiver/Web.Release.config | 31 +++ samples/VstsReceiver/Web.config | 58 +++++ .../WebHooks/VstsWebHookHandler.cs | 95 ++++++++ samples/VstsReceiver/index.html | 27 +++ samples/VstsReceiver/packages.config | 10 + .../Extensions/HttpConfigurationExtensions.cs | 30 +++ .../Handlers/VstsWebHookHandlerBase.cs | 147 +++++++++++++ ...soft.AspNet.WebHooks.Receivers.VSTS.csproj | 137 ++++++++++++ ...soft.AspNet.WebHooks.Receivers.VSTS.nuspec | 20 ++ .../Payloads/BasePayload.cs | 81 +++++++ .../Payloads/BaseResource.cs | 13 ++ .../Payloads/BaseWorkItemResource.cs | 45 ++++ .../Payloads/BuildCompletedDefinition.cs | 50 +++++ .../Payloads/BuildCompletedDrop.cs | 38 ++++ .../Payloads/BuildCompletedLog.cs | 32 +++ .../Payloads/BuildCompletedPayload.cs | 12 ++ .../Payloads/BuildCompletedQueueDefinition.cs | 38 ++++ .../Payloads/BuildCompletedRequest.cs | 32 +++ .../Payloads/BuildCompletedResource.cs | 128 +++++++++++ .../Payloads/CodeCheckedInPayload.cs | 12 ++ .../Payloads/CodeCheckedInResource.cs | 50 +++++ .../Payloads/PayloadMessage.cs | 31 +++ .../Payloads/PayloadResourceContainer.cs | 19 ++ .../Payloads/PayloadResourceContainers.cs | 31 +++ .../Payloads/ResourceUser.cs | 44 ++++ .../Payloads/TeamRoomMessagePostedPayload.cs | 12 ++ .../Payloads/TeamRoomMessagePostedResource.cs | 50 +++++ .../Payloads/WorkItemCommentedOnPayload.cs | 12 ++ .../Payloads/WorkItemCommentedOnResource.cs | 12 ++ .../Payloads/WorkItemCreatedPayload.cs | 12 ++ .../Payloads/WorkItemCreatedResource.cs | 12 ++ .../Payloads/WorkItemDeletedPayload.cs | 12 ++ .../Payloads/WorkItemDeletedResource.cs | 12 ++ .../Payloads/WorkItemFields.cs | 98 +++++++++ .../Payloads/WorkItemLink.cs | 19 ++ .../Payloads/WorkItemLinks.cs | 61 ++++++ .../Payloads/WorkItemRestoredPayload.cs | 12 ++ .../Payloads/WorkItemRestoredResource.cs | 12 ++ .../Payloads/WorkItemUpdatedFieldValue.cs | 26 +++ .../Payloads/WorkItemUpdatedFields.cs | 68 ++++++ .../Payloads/WorkItemUpdatedPayload.cs | 12 ++ .../Payloads/WorkItemUpdatedResource.cs | 38 ++++ .../Payloads/WorkItemUpdatedRevision.cs | 38 ++++ .../Properties/AssemblyInfo.cs | 6 + .../VstsReceiverResources.Designer.cs | 81 +++++++ .../Properties/VstsReceiverResources.resx | 126 +++++++++++ .../WebHooks/VstsWebHookReceiver.cs | 87 ++++++++ .../packages.config | 7 + .../App.config | 8 + .../Common/Extensions.cs | 16 ++ .../Handlers/VstsWebHookHandlerBaseTests.cs | 168 +++++++++++++++ .../Messages/bad.noEventType.json | 3 + .../Messages/bad.notMappedEventType.json | 5 + .../Messages/build.complete.json | 83 +++++++ .../Messages/message.posted.json | 36 ++++ .../Messages/tfvc.checkin.json | 40 ++++ .../Messages/workitem.commented.json | 52 +++++ .../Messages/workitem.created.json | 51 +++++ .../Messages/workitem.deleted.json | 49 +++++ .../Messages/workitem.restored.json | 53 +++++ .../Messages/workitem.updated.json | 92 ++++++++ ...AspNet.WebHooks.Receivers.VSTS.Test.csproj | 139 ++++++++++++ .../Payloads/BuildCompletedPayloadTests.cs | 124 +++++++++++ .../Payloads/CodeCheckedInPayloadTests.cs | 76 +++++++ .../TeamRoomMessagePostedPayloadTests.cs | 70 ++++++ .../WorkItemCommentedOnPayloadTests.cs | 88 ++++++++ .../Payloads/WorkItemCreatedPayloadTests.cs | 87 ++++++++ .../Payloads/WorkItemDeletedPayloadTests.cs | 85 ++++++++ .../Payloads/WorkItemRestoredPayloadTests.cs | 89 ++++++++ .../Payloads/WorkItemUpdatedPayloadTests.cs | 141 ++++++++++++ .../Properties/AssemblyInfo.cs | 8 + .../WebHooks/VstsWebHookReceiverTests.cs | 203 ++++++++++++++++++ .../packages.config | 14 ++ tools/SkipStrongNames.xml | 2 + 82 files changed, 4112 insertions(+), 1 deletion(-) create mode 100644 samples/VstsReceiver/App_Start/WebApiConfig.cs create mode 100644 samples/VstsReceiver/Global.asax create mode 100644 samples/VstsReceiver/Global.asax.cs create mode 100644 samples/VstsReceiver/Properties/AssemblyInfo.cs create mode 100644 samples/VstsReceiver/VstsReceiver.csproj create mode 100644 samples/VstsReceiver/Web.Debug.config create mode 100644 samples/VstsReceiver/Web.Release.config create mode 100644 samples/VstsReceiver/Web.config create mode 100644 samples/VstsReceiver/WebHooks/VstsWebHookHandler.cs create mode 100644 samples/VstsReceiver/index.html create mode 100644 samples/VstsReceiver/packages.config create mode 100644 src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Extensions/HttpConfigurationExtensions.cs create mode 100644 src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Handlers/VstsWebHookHandlerBase.cs create mode 100644 src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Microsoft.AspNet.WebHooks.Receivers.VSTS.csproj create mode 100644 src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Microsoft.AspNet.WebHooks.Receivers.VSTS.nuspec create mode 100644 src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/BasePayload.cs create mode 100644 src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/BaseResource.cs create mode 100644 src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/BaseWorkItemResource.cs create mode 100644 src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/BuildCompletedDefinition.cs create mode 100644 src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/BuildCompletedDrop.cs create mode 100644 src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/BuildCompletedLog.cs create mode 100644 src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/BuildCompletedPayload.cs create mode 100644 src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/BuildCompletedQueueDefinition.cs create mode 100644 src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/BuildCompletedRequest.cs create mode 100644 src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/BuildCompletedResource.cs create mode 100644 src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/CodeCheckedInPayload.cs create mode 100644 src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/CodeCheckedInResource.cs create mode 100644 src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/PayloadMessage.cs create mode 100644 src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/PayloadResourceContainer.cs create mode 100644 src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/PayloadResourceContainers.cs create mode 100644 src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/ResourceUser.cs create mode 100644 src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/TeamRoomMessagePostedPayload.cs create mode 100644 src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/TeamRoomMessagePostedResource.cs create mode 100644 src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemCommentedOnPayload.cs create mode 100644 src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemCommentedOnResource.cs create mode 100644 src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemCreatedPayload.cs create mode 100644 src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemCreatedResource.cs create mode 100644 src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemDeletedPayload.cs create mode 100644 src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemDeletedResource.cs create mode 100644 src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemFields.cs create mode 100644 src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemLink.cs create mode 100644 src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemLinks.cs create mode 100644 src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemRestoredPayload.cs create mode 100644 src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemRestoredResource.cs create mode 100644 src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemUpdatedFieldValue.cs create mode 100644 src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemUpdatedFields.cs create mode 100644 src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemUpdatedPayload.cs create mode 100644 src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemUpdatedResource.cs create mode 100644 src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemUpdatedRevision.cs create mode 100644 src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Properties/AssemblyInfo.cs create mode 100644 src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Properties/VstsReceiverResources.Designer.cs create mode 100644 src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Properties/VstsReceiverResources.resx create mode 100644 src/Microsoft.AspNet.WebHooks.Receivers.VSTS/WebHooks/VstsWebHookReceiver.cs create mode 100644 src/Microsoft.AspNet.WebHooks.Receivers.VSTS/packages.config create mode 100644 test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/App.config create mode 100644 test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Common/Extensions.cs create mode 100644 test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Handlers/VstsWebHookHandlerBaseTests.cs create mode 100644 test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Messages/bad.noEventType.json create mode 100644 test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Messages/bad.notMappedEventType.json create mode 100644 test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Messages/build.complete.json create mode 100644 test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Messages/message.posted.json create mode 100644 test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Messages/tfvc.checkin.json create mode 100644 test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Messages/workitem.commented.json create mode 100644 test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Messages/workitem.created.json create mode 100644 test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Messages/workitem.deleted.json create mode 100644 test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Messages/workitem.restored.json create mode 100644 test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Messages/workitem.updated.json create mode 100644 test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test.csproj create mode 100644 test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Payloads/BuildCompletedPayloadTests.cs create mode 100644 test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Payloads/CodeCheckedInPayloadTests.cs create mode 100644 test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Payloads/TeamRoomMessagePostedPayloadTests.cs create mode 100644 test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Payloads/WorkItemCommentedOnPayloadTests.cs create mode 100644 test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Payloads/WorkItemCreatedPayloadTests.cs create mode 100644 test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Payloads/WorkItemDeletedPayloadTests.cs create mode 100644 test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Payloads/WorkItemRestoredPayloadTests.cs create mode 100644 test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Payloads/WorkItemUpdatedPayloadTests.cs create mode 100644 test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Properties/AssemblyInfo.cs create mode 100644 test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/WebHooks/VstsWebHookReceiverTests.cs create mode 100644 test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/packages.config diff --git a/CustomDictionary.xml b/CustomDictionary.xml index 054168f..ac4dd49 100644 --- a/CustomDictionary.xml +++ b/CustomDictionary.xml @@ -8,6 +8,7 @@ apis awaitable bitbucket + changeset deployer dequeueing dequeues @@ -17,12 +18,14 @@ git instagram kudu + kanban mvc nuget paypal salesforce trello ver + vsts zapier zendesk 你好 @@ -42,6 +45,7 @@ CRM SSO + VSTS diff --git a/WebHooks.sln b/WebHooks.sln index 0a6860b..950d656 100644 --- a/WebHooks.sln +++ b/WebHooks.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 -VisualStudioVersion = 14.0.24720.0 +VisualStudioVersion = 14.0.25123.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Src", "Src", "{929F44D0-A040-4DC3-A22F-4C5829C05D44}" EndProject @@ -152,6 +152,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNet.WebHooks.R EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNet.WebHooks.Receivers.Zendesk.Test", "test\Microsoft.AspNet.WebHooks.Receivers.Zendesk.Test\Microsoft.AspNet.WebHooks.Receivers.Zendesk.Test.csproj", "{D4AE6D4C-004F-4392-9EE1-772444719449}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNet.WebHooks.Receivers.VSTS", "src\Microsoft.AspNet.WebHooks.Receivers.VSTS\Microsoft.AspNet.WebHooks.Receivers.VSTS.csproj", "{6B501D55-2AA3-4757-A185-1277BB881DE8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNet.WebHooks.Receivers.VSTS.Test", "test\Microsoft.AspNet.WebHooks.Receivers.VSTS.Test\Microsoft.AspNet.WebHooks.Receivers.VSTS.Test.csproj", "{B27CC8B5-BF38-434B-B5CE-42ACEC40624C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VstsReceiver", "samples\VstsReceiver\VstsReceiver.csproj", "{88FB9535-BBC9-48FE-AC34-4C3E9A4073F3}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution CodeAnalysis|Any CPU = CodeAnalysis|Any CPU @@ -555,6 +561,24 @@ Global {D4AE6D4C-004F-4392-9EE1-772444719449}.Debug|Any CPU.Build.0 = Debug|Any CPU {D4AE6D4C-004F-4392-9EE1-772444719449}.Release|Any CPU.ActiveCfg = Release|Any CPU {D4AE6D4C-004F-4392-9EE1-772444719449}.Release|Any CPU.Build.0 = Release|Any CPU + {6B501D55-2AA3-4757-A185-1277BB881DE8}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU + {6B501D55-2AA3-4757-A185-1277BB881DE8}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU + {6B501D55-2AA3-4757-A185-1277BB881DE8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6B501D55-2AA3-4757-A185-1277BB881DE8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6B501D55-2AA3-4757-A185-1277BB881DE8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6B501D55-2AA3-4757-A185-1277BB881DE8}.Release|Any CPU.Build.0 = Release|Any CPU + {B27CC8B5-BF38-434B-B5CE-42ACEC40624C}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU + {B27CC8B5-BF38-434B-B5CE-42ACEC40624C}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU + {B27CC8B5-BF38-434B-B5CE-42ACEC40624C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B27CC8B5-BF38-434B-B5CE-42ACEC40624C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B27CC8B5-BF38-434B-B5CE-42ACEC40624C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B27CC8B5-BF38-434B-B5CE-42ACEC40624C}.Release|Any CPU.Build.0 = Release|Any CPU + {88FB9535-BBC9-48FE-AC34-4C3E9A4073F3}.CodeAnalysis|Any CPU.ActiveCfg = Release|Any CPU + {88FB9535-BBC9-48FE-AC34-4C3E9A4073F3}.CodeAnalysis|Any CPU.Build.0 = Release|Any CPU + {88FB9535-BBC9-48FE-AC34-4C3E9A4073F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {88FB9535-BBC9-48FE-AC34-4C3E9A4073F3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {88FB9535-BBC9-48FE-AC34-4C3E9A4073F3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {88FB9535-BBC9-48FE-AC34-4C3E9A4073F3}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -626,6 +650,9 @@ Global {BD0AB018-7229-42BF-B607-5D46E9F1F2EA} = {E957C8D9-B4A0-488B-838F-BAB4DE080A76} {DCD550FB-FE5E-4684-AA52-C09FE5F8FDDA} = {929F44D0-A040-4DC3-A22F-4C5829C05D44} {D4AE6D4C-004F-4392-9EE1-772444719449} = {9575CB90-BC4B-43BB-8AEA-82C53FDA4187} + {6B501D55-2AA3-4757-A185-1277BB881DE8} = {929F44D0-A040-4DC3-A22F-4C5829C05D44} + {B27CC8B5-BF38-434B-B5CE-42ACEC40624C} = {9575CB90-BC4B-43BB-8AEA-82C53FDA4187} + {88FB9535-BBC9-48FE-AC34-4C3E9A4073F3} = {E957C8D9-B4A0-488B-838F-BAB4DE080A76} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution EnterpriseLibraryConfigurationToolBinariesPathV6 = packages\EnterpriseLibrary.TransientFaultHandling.6.0.1304.0\lib\portable-net45+win+wp8;packages\EnterpriseLibrary.TransientFaultHandling.Data.6.0.1304.1\lib\NET45 diff --git a/samples/VstsReceiver/App_Start/WebApiConfig.cs b/samples/VstsReceiver/App_Start/WebApiConfig.cs new file mode 100644 index 0000000..b7f2251 --- /dev/null +++ b/samples/VstsReceiver/App_Start/WebApiConfig.cs @@ -0,0 +1,24 @@ +using System.Web.Http; + +namespace VstsReceiver +{ + public static class WebApiConfig + { + public static void Register(HttpConfiguration config) + { + // Web API configuration and services + + // Web API routes + config.MapHttpAttributeRoutes(); + + config.Routes.MapHttpRoute( + name: "DefaultApi", + routeTemplate: "api/{controller}/{id}", + defaults: new { id = RouteParameter.Optional } + ); + + // Initialize Vsts WebHook receiver + config.InitializeReceiveVstsWebHooks(); + } + } +} diff --git a/samples/VstsReceiver/Global.asax b/samples/VstsReceiver/Global.asax new file mode 100644 index 0000000..d6279fa --- /dev/null +++ b/samples/VstsReceiver/Global.asax @@ -0,0 +1 @@ +<%@ Application Codebehind="Global.asax.cs" Inherits="VstsReceiver.WebApiApplication" Language="C#" %> diff --git a/samples/VstsReceiver/Global.asax.cs b/samples/VstsReceiver/Global.asax.cs new file mode 100644 index 0000000..d7e0575 --- /dev/null +++ b/samples/VstsReceiver/Global.asax.cs @@ -0,0 +1,12 @@ +using System.Web.Http; + +namespace VstsReceiver +{ + public class WebApiApplication : System.Web.HttpApplication + { + protected void Application_Start() + { + GlobalConfiguration.Configure(WebApiConfig.Register); + } + } +} diff --git a/samples/VstsReceiver/Properties/AssemblyInfo.cs b/samples/VstsReceiver/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..0f60383 --- /dev/null +++ b/samples/VstsReceiver/Properties/AssemblyInfo.cs @@ -0,0 +1,34 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("VstsReceivers")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("VststReceivers")] +[assembly: AssemblyCopyright("Copyright © 2015")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("3f9f62dd-c365-4fec-a10e-5314d111ebf5")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/samples/VstsReceiver/VstsReceiver.csproj b/samples/VstsReceiver/VstsReceiver.csproj new file mode 100644 index 0000000..62e6648 --- /dev/null +++ b/samples/VstsReceiver/VstsReceiver.csproj @@ -0,0 +1,161 @@ + + + + + + + Debug + AnyCPU + + + 2.0 + {88FB9535-BBC9-48FE-AC34-4C3E9A4073F3} + {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} + Library + Properties + VstsReceiver + VstsReceiver + v4.5.1 + true + + + + + + + + + + + true + full + false + bin\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\ + TRACE + prompt + 4 + + + + ..\..\bin\CodeAnalysis\Microsoft.AspNet.WebHooks.Receivers.VSTS.dll + + + ..\..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.0\lib\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll + True + + + + + + ..\..\packages\Microsoft.AspNet.WebApi.Client.5.2.2\lib\net45\System.Net.Http.Formatting.dll + True + + + + + + + + + + ..\..\packages\Microsoft.AspNet.WebApi.Core.5.2.2\lib\net45\System.Web.Http.dll + True + + + ..\..\packages\Microsoft.AspNet.WebApi.WebHost.5.2.2\lib\net45\System.Web.Http.WebHost.dll + True + + + + + + + + + + + + ..\..\packages\Newtonsoft.Json.6.0.4\lib\net45\Newtonsoft.Json.dll + + + + + + + + + + + Global.asax + + + + + + + + Web.config + + + Web.config + + + + + + {f7dd0935-6320-4efc-9464-d5a2d2b8c2f7} + Microsoft.AspNet.WebHooks.Common + + + {8ced31fb-32f2-4ffb-9997-452fb9728577} + Microsoft.AspNet.WebHooks.Receivers + + + + 10.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + + + + + + True + True + 15572 + / + http://localhost:50009 + False + False + + + False + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + \ No newline at end of file diff --git a/samples/VstsReceiver/Web.Debug.config b/samples/VstsReceiver/Web.Debug.config new file mode 100644 index 0000000..2e302f9 --- /dev/null +++ b/samples/VstsReceiver/Web.Debug.config @@ -0,0 +1,30 @@ + + + + + + + + + + \ No newline at end of file diff --git a/samples/VstsReceiver/Web.Release.config b/samples/VstsReceiver/Web.Release.config new file mode 100644 index 0000000..c358444 --- /dev/null +++ b/samples/VstsReceiver/Web.Release.config @@ -0,0 +1,31 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/samples/VstsReceiver/Web.config b/samples/VstsReceiver/Web.config new file mode 100644 index 0000000..70ef6f1 --- /dev/null +++ b/samples/VstsReceiver/Web.config @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/VstsReceiver/WebHooks/VstsWebHookHandler.cs b/samples/VstsReceiver/WebHooks/VstsWebHookHandler.cs new file mode 100644 index 0000000..8c35a56 --- /dev/null +++ b/samples/VstsReceiver/WebHooks/VstsWebHookHandler.cs @@ -0,0 +1,95 @@ +using Microsoft.AspNet.WebHooks; +using Microsoft.AspNet.WebHooks.Payloads; +using Newtonsoft.Json.Linq; +using System.Threading.Tasks; + +namespace VstsReceiver.WebHooks +{ + /// + /// This handler processes WebHooks from Visual Studio Team Services and leverages the base handler. + /// For details about Visual Studio Team Services WebHooks, see https://www.visualstudio.com/en-us/get-started/integrate/service-hooks/webhooks-and-vso-vs. + /// + public class VstsWebHookHandler : VstsWebHookHandlerBase + { + /// + /// We use so just have to override the methods we want to process WebHooks for. + /// This one processes the WebHook. + /// + public override Task ExecuteAsync(WebHookHandlerContext context, BuildCompletedPayload payload) + { + return Task.FromResult(true); + } + + /// + /// We use so just have to override the methods we want to process WebHooks for. + /// This one processes the WebHook. + /// + public override Task ExecuteAsync(WebHookHandlerContext context, TeamRoomMessagePostedPayload payload) + { + return Task.FromResult(true); + } + + /// + /// We use so just have to override the methods we want to process WebHooks for. + /// This one processes the WebHook. + /// + public override Task ExecuteAsync(WebHookHandlerContext context, WorkItemCreatedPayload payload) + { + return Task.FromResult(true); + } + + /// + /// We use so just have to override the methods we want to process WebHooks for. + /// This one processes the WebHook. + /// + public override Task ExecuteAsync(WebHookHandlerContext context, WorkItemCommentedOnPayload payload) + { + return Task.FromResult(true); + } + + /// + /// We use so just have to override the methods we want to process WebHooks for. + /// This one processes the WebHook. + /// + public override Task ExecuteAsync(WebHookHandlerContext context, CodeCheckedInPayload payload) + { + return Task.FromResult(true); + } + + /// + /// We use so just have to override the methods we want to process WebHooks for. + /// This one processes the WebHook. + /// + public override Task ExecuteAsync(WebHookHandlerContext context, WorkItemDeletedPayload payload) + { + return Task.FromResult(true); + } + + /// + /// We use so just have to override the methods we want to process WebHooks for. + /// This one processes the WebHook. + /// + public override Task ExecuteAsync(WebHookHandlerContext context, WorkItemRestoredPayload payload) + { + return Task.FromResult(true); + } + + /// + /// We use so just have to override the methods we want to process WebHooks for. + /// This one processes the WebHook. + /// + public override Task ExecuteAsync(WebHookHandlerContext context, WorkItemUpdatedPayload payload) + { + return Task.FromResult(true); + } + + /// + /// We use so just have to override the methods we want to process WebHooks for. + /// This one processes the payload for unknown eventType. + /// + public override Task ExecuteAsync(WebHookHandlerContext context, JObject payload) + { + return Task.FromResult(true); + } + } +} diff --git a/samples/VstsReceiver/index.html b/samples/VstsReceiver/index.html new file mode 100644 index 0000000..6853f8f --- /dev/null +++ b/samples/VstsReceiver/index.html @@ -0,0 +1,27 @@ + + + + Microsoft ASP.NET WebHooks Visual Studio Team Services Receiver + + + +

Microsoft ASP.NET WebHooks Visual Studio Team Services Receiver

+ +

+ The VSTS WebHook Receiver provides support for receiving WebHooks from Visual Studio Team Services. A sample WebHook URI is:

+ +
https://<host>/api/webhooks/incoming/vsts?code=83699ec7c1d794c0c780e49a5c72972590571fd8
+ +

+ For security reasons the WebHook URI must be an https URI and contain a 'code' query parameter with the + same value as configured in the MS_WebHookReceiverSecret_VSTS application setting. + The 'code' parameter must be between 32 and 128 characters long. Optionally you can use IDs + to differentiate between multiple WebHooks, for example 'code0, id1=code1, id2=code2'. +

+ +

+ Please see Visual Studio Team Services WebHooks + for more information. +

+ + diff --git a/samples/VstsReceiver/packages.config b/samples/VstsReceiver/packages.config new file mode 100644 index 0000000..9cc9e59 --- /dev/null +++ b/samples/VstsReceiver/packages.config @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Extensions/HttpConfigurationExtensions.cs b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Extensions/HttpConfigurationExtensions.cs new file mode 100644 index 0000000..130ee0c --- /dev/null +++ b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Extensions/HttpConfigurationExtensions.cs @@ -0,0 +1,30 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.ComponentModel; +using Microsoft.AspNet.WebHooks.Config; + +namespace System.Web.Http +{ + /// + /// Extension methods for . + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public static class HttpConfigurationExtensions + { + /// + /// Initializes support for receiving WebHooks generated by Visual Studio Team Services. + /// + /// A sample WebHook URI is 'https://<host>/api/webhooks/incoming/vsts/{id}?code={code}'. + /// For security reasons the WebHook URI must be an https URI and contain a 'code' query parameter with the + /// same value as configured in the 'MS_WebHookReceiverSecret_Tfs' application setting, optionally using IDs + /// to differentiate between multiple WebHooks, for example 'secret0, id1=secret1, id2=secret2'. + /// The 'code' parameter must be between 32 and 128 characters long. + /// + /// The current config. + public static void InitializeReceiveVstsWebHooks(this HttpConfiguration config) + { + WebHooksConfig.Initialize(config); + } + } +} diff --git a/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Handlers/VstsWebHookHandlerBase.cs b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Handlers/VstsWebHookHandlerBase.cs new file mode 100644 index 0000000..9fbdf01 --- /dev/null +++ b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Handlers/VstsWebHookHandlerBase.cs @@ -0,0 +1,147 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Globalization; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNet.WebHooks.Payloads; +using Microsoft.AspNet.WebHooks.Properties; +using Newtonsoft.Json.Linq; + +namespace Microsoft.AspNet.WebHooks +{ + /// + /// Provides a base implementation which can be used to for handling Visual Studio Team Services WebHook + /// using strongly-typed payloads. For details about MyGet WebHooks, see https://www.visualstudio.com/en-us/get-started/integrate/service-hooks/webhooks-and-vso-vs. + /// + public abstract class VstsWebHookHandlerBase : WebHookHandler + { + /// + /// Initializes a new instance of the class. + /// + protected VstsWebHookHandlerBase() + { + this.Receiver = VstsWebHookReceiver.ReceiverName; + } + + /// + public override Task ExecuteAsync(string receiver, WebHookHandlerContext context) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + string action = context.Actions.First(); + JObject data = context.GetDataOrDefault(); + + // map eventType to corresponding payload + switch (action) + { + case "workitem.updated": return ExecuteAsync(context, data.ToObject()); + case "workitem.restored": return ExecuteAsync(context, data.ToObject()); + case "workitem.deleted": return ExecuteAsync(context, data.ToObject()); + case "workitem.created": return ExecuteAsync(context, data.ToObject()); + case "workitem.commented": return ExecuteAsync(context, data.ToObject()); + case "message.posted": return ExecuteAsync(context, data.ToObject()); + case "tfvc.checkin": return ExecuteAsync(context, data.ToObject()); + case "build.complete": return ExecuteAsync(context, data.ToObject()); + default: + string msg = string.Format(CultureInfo.CurrentCulture, VstsReceiverResources.Handler_NonMappedEventType, action); + context.RequestContext.Configuration.DependencyResolver.GetLogger().Warn(msg); + return ExecuteAsync(context, data); + } + } + + /// + /// Executes the incoming WebHook request for event 'workitem.updated'. + /// + /// Provides context for the for further processing the incoming WebHook. + /// Strong-typed WebHook payload. + public virtual Task ExecuteAsync(WebHookHandlerContext context, WorkItemUpdatedPayload payload) + { + return Task.FromResult(true); + } + + /// + /// Executes the incoming WebHook request for event 'workitem.restored'. + /// + /// Provides context for the for further processing the incoming WebHook. + /// Strong-typed WebHook payload. + public virtual Task ExecuteAsync(WebHookHandlerContext context, WorkItemRestoredPayload payload) + { + return Task.FromResult(true); + } + + /// + /// Executes the incoming WebHook request for event 'workitem.deleted'. + /// + /// Provides context for the for further processing the incoming WebHook. + /// Strong-typed WebHook payload. + public virtual Task ExecuteAsync(WebHookHandlerContext context, WorkItemDeletedPayload payload) + { + return Task.FromResult(true); + } + + /// + /// Executes the incoming WebHook request for event 'workitem.created'. + /// + /// Provides context for the for further processing the incoming WebHook. + /// Strong-typed WebHook payload. + public virtual Task ExecuteAsync(WebHookHandlerContext context, WorkItemCreatedPayload payload) + { + return Task.FromResult(true); + } + + /// + /// Executes the incoming WebHook request for event 'workitem.commented'. + /// + /// Provides context for the for further processing the incoming WebHook. + /// Strong-typed WebHook payload. + public virtual Task ExecuteAsync(WebHookHandlerContext context, WorkItemCommentedOnPayload payload) + { + return Task.FromResult(true); + } + + /// + /// Executes the incoming WebHook request for event 'message.posted'. + /// + /// Provides context for the for further processing the incoming WebHook. + /// Strong-typed WebHook payload. + public virtual Task ExecuteAsync(WebHookHandlerContext context, TeamRoomMessagePostedPayload payload) + { + return Task.FromResult(true); + } + + /// + /// Executes the incoming WebHook request for event 'tfvc.checkin'. + /// + /// Provides context for the for further processing the incoming WebHook. + /// Strong-typed WebHook payload. + public virtual Task ExecuteAsync(WebHookHandlerContext context, CodeCheckedInPayload payload) + { + return Task.FromResult(true); + } + + /// + /// Executes the incoming WebHook request for event 'build.complete'. + /// + /// Provides context for the for further processing the incoming WebHook. + /// Strong-typed WebHook payload. + public virtual Task ExecuteAsync(WebHookHandlerContext context, BuildCompletedPayload payload) + { + return Task.FromResult(true); + } + + /// + /// Executes the incoming WebHook request for unknown event. + /// + /// Provides context for the for further processing the incoming WebHook. + /// JSON payload. + public virtual Task ExecuteAsync(WebHookHandlerContext context, JObject payload) + { + return Task.FromResult(true); + } + } +} diff --git a/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Microsoft.AspNet.WebHooks.Receivers.VSTS.csproj b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Microsoft.AspNet.WebHooks.Receivers.VSTS.csproj new file mode 100644 index 0000000..5cf823a --- /dev/null +++ b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Microsoft.AspNet.WebHooks.Receivers.VSTS.csproj @@ -0,0 +1,137 @@ + + + + + Debug + AnyCPU + + + 2.0 + {6B501D55-2AA3-4757-A185-1277BB881DE8} + Library + Properties + Microsoft.AspNet.WebHooks + Microsoft.AspNet.WebHooks.Receivers.VSTS + $(OutputPath)$(AssemblyName).xml + $(CodeAnalysis) + /assemblyCompareMode:StrongNameIgnoringVersion + ..\..\FxCop.ruleset + $(DefineConstants);ASPNETWEBHOOKS + true + b718ef60 + + + + ..\..\packages\Newtonsoft.Json.6.0.4\lib\net45\Newtonsoft.Json.dll + True + + + + + ..\..\packages\Microsoft.AspNet.WebApi.Client.5.2.2\lib\net45\System.Net.Http.Formatting.dll + True + + + ..\..\packages\Microsoft.AspNet.WebApi.Core.5.2.2\lib\net45\System.Web.Http.dll + True + + + + + + + + + + + Properties\CommonAssemblyInfo.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + VstsReceiverResources.resx + True + True + + + + + + + + + + + + + + + + + + Designer + + + + + + {f7dd0935-6320-4efc-9464-d5a2d2b8c2f7} + Microsoft.AspNet.WebHooks.Common + + + {8CED31FB-32F2-4FFB-9997-452FB9728577} + Microsoft.AspNet.WebHooks.Receivers + + + + + CustomDictionary.xml + Designer + + + + + ResXFileCodeGenerator + VstsReceiverResources.Designer.cs + Designer + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + \ No newline at end of file diff --git a/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Microsoft.AspNet.WebHooks.Receivers.VSTS.nuspec b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Microsoft.AspNet.WebHooks.Receivers.VSTS.nuspec new file mode 100644 index 0000000..ad16f65 --- /dev/null +++ b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Microsoft.AspNet.WebHooks.Receivers.VSTS.nuspec @@ -0,0 +1,20 @@ + + + + $id$ + $symversion$ + Microsoft ASP.NET WebHooks Receiver for Visual Studio Team Services + Microsoft + Microsoft, aspnet + http://www.microsoft.com/web/webpi/eula/net_library_eula_ENU.htm + http://go.microsoft.com/fwlink/?LinkId=690277 + http://go.microsoft.com/fwlink/?LinkID=288859 + true + + This package provides support for receiving WebHooks from Visual Studio Team Services. For information about Visual Studio Team Services WebHooks, see "https://www.visualstudio.com/en-us/get-started/integrate/service-hooks/webhooks-and-vso-vs". + + + © Microsoft Corporation. All rights reserved. + Microsoft AspNet WebApi AspNetWebApi WebHooks TFS VSTS Visual Studio Team Services + + \ No newline at end of file diff --git a/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/BasePayload.cs b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/BasePayload.cs new file mode 100644 index 0000000..f1a0c63 --- /dev/null +++ b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/BasePayload.cs @@ -0,0 +1,81 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Newtonsoft.Json; + +namespace Microsoft.AspNet.WebHooks.Payloads +{ + /// + /// Root object of payload sent for all types of events. + /// + /// Type of resource within payload which differs depending on 'eventType' field + public abstract class BasePayload where T : BaseResource + { + /// + /// Gets the subscription identifier which triggered the event. + /// + [JsonProperty("subscriptionId")] + public string SubscriptionId { get; set; } + + /// + /// Gets the notification identifier within subscription. + /// + [JsonProperty("notificationId")] + public int NotificationId { get; set; } + + /// + /// Gets the identifier of HTTP request. + /// + [JsonProperty("id")] + public string Id { get; set; } + + /// + /// Gets the type of the event. + /// + [JsonProperty("eventType")] + public string EventType { get; set; } + + /// + /// Gets the publisher identifier. + /// + [JsonProperty("publisherId")] + public string PublisherId { get; set; } + + /// + /// Gets the message which describes the event. + /// + [JsonProperty("message")] + public PayloadMessage Message { get; set; } + + /// + /// Gets the detailed message which describes the event. + /// + [JsonProperty("detailedMessage")] + public PayloadMessage DetailedMessage { get; set; } + + /// + /// Gets the resource itself - data associated with corresponding event. + /// + [JsonProperty("resource")] + public T Resource { get; set; } + + /// + /// Gets the resource version. + /// + [JsonProperty("resourceVersion")] + public string ResourceVersion { get; set; } + + /// + /// Gets the resource containers. + /// + [JsonProperty("resourceContainers")] + public PayloadResourceContainers ResourceContainers { get; set; } + + /// + /// Gets the date when HTTP request was created. + /// + [JsonProperty("createdDate")] + public DateTime CreatedDate { get; set; } + } +} diff --git a/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/BaseResource.cs b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/BaseResource.cs new file mode 100644 index 0000000..c7e08a6 --- /dev/null +++ b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/BaseResource.cs @@ -0,0 +1,13 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.AspNet.WebHooks.Payloads +{ + /// + /// Base class for resource object which describes + /// a specific event type. + /// + public abstract class BaseResource + { + } +} diff --git a/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/BaseWorkItemResource.cs b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/BaseWorkItemResource.cs new file mode 100644 index 0000000..e083307 --- /dev/null +++ b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/BaseWorkItemResource.cs @@ -0,0 +1,45 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Newtonsoft.Json; + +namespace Microsoft.AspNet.WebHooks.Payloads +{ + /// + /// Base class for resource object which describes WorkItem event types. + /// + /// Type which describes fields associated with this kind of WorkItem change + public abstract class BaseWorkItemResource : BaseResource + { + /// + /// Gets the identifier of WorkItem. + /// + [JsonProperty("id")] + public int Id { get; set; } + + /// + /// Gets the revision number. + /// + [JsonProperty("rev")] + public int RevisionNumber { get; set; } + + /// + /// Gets fields associated with the WorkItem. + /// + [JsonProperty("fields")] + public T Fields { get; set; } + + /// + /// Gets links associated with the WorkItem. + /// + [JsonProperty("_links")] + public WorkItemLinks Links { get; set; } + + /// + /// Gets the URL of the WorkItem. + /// + [JsonProperty("url")] + public Uri Url { get; set; } + } +} diff --git a/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/BuildCompletedDefinition.cs b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/BuildCompletedDefinition.cs new file mode 100644 index 0000000..18b95af --- /dev/null +++ b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/BuildCompletedDefinition.cs @@ -0,0 +1,50 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Newtonsoft.Json; + +namespace Microsoft.AspNet.WebHooks.Payloads +{ + /// + /// Describes build definition + /// + public class BuildCompletedDefinition + { + /// + /// Gets the size of the batch. + /// + [JsonProperty("batchSize")] + public int BatchSize { get; set; } + + /// + /// Gets the trigger type. + /// + [JsonProperty("triggerType")] + public string TriggerType { get; set; } + + /// + /// Gets the trigger type. + /// + [JsonProperty("definitionType")] + public string DefinitionType { get; set; } + + /// + /// Gets the identifier of the build definition. + /// + [JsonProperty("id")] + public int Id { get; set; } + + /// + /// Gets the name of the build definition. + /// + [JsonProperty("name")] + public string Name { get; set; } + + /// + /// Gets the URL of the build definition. + /// + [JsonProperty("url")] + public Uri Url { get; set; } + } +} diff --git a/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/BuildCompletedDrop.cs b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/BuildCompletedDrop.cs new file mode 100644 index 0000000..0a934af --- /dev/null +++ b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/BuildCompletedDrop.cs @@ -0,0 +1,38 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Newtonsoft.Json; + +namespace Microsoft.AspNet.WebHooks.Payloads +{ + /// + /// Describes build drop + /// + public class BuildCompletedDrop + { + /// + /// Gets drop location. + /// + [JsonProperty("location")] + public string Location { get; set; } + + /// + /// Gets drop type. + /// + [JsonProperty("type")] + public string DropType { get; set; } + + /// + /// Gets drop location URL. + /// + [JsonProperty("url")] + public Uri Url { get; set; } + + /// + /// Gets drop location download URL. + /// + [JsonProperty("downloadUrl")] + public Uri DownloadUrl { get; set; } + } +} diff --git a/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/BuildCompletedLog.cs b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/BuildCompletedLog.cs new file mode 100644 index 0000000..dd22659 --- /dev/null +++ b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/BuildCompletedLog.cs @@ -0,0 +1,32 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Newtonsoft.Json; + +namespace Microsoft.AspNet.WebHooks.Payloads +{ + /// + /// Describes build log + /// + public class BuildCompletedLog + { + /// + /// Gets the log type. + /// + [JsonProperty("type")] + public string LogType { get; set; } + + /// + /// Gets the log URL. + /// + [JsonProperty("url")] + public Uri Url { get; set; } + + /// + /// Gets the log download URL. + /// + [JsonProperty("downloadUrl")] + public Uri DownloadUrl { get; set; } + } +} diff --git a/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/BuildCompletedPayload.cs b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/BuildCompletedPayload.cs new file mode 100644 index 0000000..64af11e --- /dev/null +++ b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/BuildCompletedPayload.cs @@ -0,0 +1,12 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.AspNet.WebHooks.Payloads +{ + /// + /// Describes the entire payload of event 'build.complete'. + /// + public class BuildCompletedPayload : BasePayload + { + } +} diff --git a/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/BuildCompletedQueueDefinition.cs b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/BuildCompletedQueueDefinition.cs new file mode 100644 index 0000000..d2c4361 --- /dev/null +++ b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/BuildCompletedQueueDefinition.cs @@ -0,0 +1,38 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Newtonsoft.Json; + +namespace Microsoft.AspNet.WebHooks.Payloads +{ + /// + /// Describes the queue of the build. + /// + public class BuildCompletedQueueDefinition + { + /// + /// Gets the type of the queue. + /// + [JsonProperty("queueType")] + public string QueueType { get; set; } + + /// + /// Gets the identifier of the queue. + /// + [JsonProperty("id")] + public int Id { get; set; } + + /// + /// Gets the name of the queue. + /// + [JsonProperty("name")] + public string Name { get; set; } + + /// + /// Gets the URL of the queue. + /// + [JsonProperty("url")] + public Uri Url { get; set; } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/BuildCompletedRequest.cs b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/BuildCompletedRequest.cs new file mode 100644 index 0000000..4d2cf79 --- /dev/null +++ b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/BuildCompletedRequest.cs @@ -0,0 +1,32 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Newtonsoft.Json; + +namespace Microsoft.AspNet.WebHooks.Payloads +{ + /// + /// Describes the request of the build. + /// + public class BuildCompletedRequest + { + /// + /// Gets the identifier of the request. + /// + [JsonProperty("id")] + public int Id { get; set; } + + /// + /// Gets the URL of the request. + /// + [JsonProperty("url")] + public Uri Url { get; set; } + + /// + /// Gets the user associated with the request. + /// + [JsonProperty("requestedFor")] + public ResourceUser RequestedFor { get; set; } + } +} diff --git a/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/BuildCompletedResource.cs b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/BuildCompletedResource.cs new file mode 100644 index 0000000..af75d9e --- /dev/null +++ b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/BuildCompletedResource.cs @@ -0,0 +1,128 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.ObjectModel; +using Newtonsoft.Json; + +namespace Microsoft.AspNet.WebHooks.Payloads +{ + /// + /// Describes the resource that associated with + /// + public class BuildCompletedResource : BaseResource + { + private readonly Collection _requests = new Collection(); + + /// + /// Gets the build URI. + /// + [JsonProperty("uri")] + public Uri Uri { get; set; } + + /// + /// Gets the build identifier. + /// + [JsonProperty("id")] + public int Id { get; set; } + + /// + /// Gets the build number. + /// + [JsonProperty("buildNumber")] + public string BuildNumber { get; set; } + + /// + /// Gets the build URL. + /// + [JsonProperty("url")] + public Uri Url { get; set; } + + /// + /// Gets the start time of the build. + /// + [JsonProperty("startTime")] + public DateTime StartTime { get; set; } + + /// + /// Gets the finish time of the build. + /// + [JsonProperty("finishTime")] + public DateTime FinishTime { get; set; } + + /// + /// Gets the reason which triggered the build. + /// + [JsonProperty("reason")] + public string Reason { get; set; } + + /// + /// Gets the outcome status of the build. + /// + [JsonProperty("status")] + public string Status { get; set; } + + /// + /// Gets the build drop location. + /// + [JsonProperty("dropLocation")] + public string DropLocation { get; set; } + + /// + /// Gets the build drop. + /// + [JsonProperty("drop")] + public BuildCompletedDrop Drop { get; set; } + + /// + /// Gets the build log. + /// + [JsonProperty("log")] + public BuildCompletedLog Log { get; set; } + + /// + /// Gets the source version for the build. + /// + [JsonProperty("sourceGetVersion")] + public string SourceGetVersion { get; set; } + + /// + /// Gets the user which last changed the source. + /// + [JsonProperty("lastChangedBy")] + public ResourceUser LastChangedBy { get; set; } + + /// + /// Gets value indicating whether this build retain indefinitely. + /// + [JsonProperty("retainIndefinitely")] + public bool RetainIndefinitely { get; set; } + + /// + /// Gets value indicating whether this build has diagnostics. + /// + [JsonProperty("hasDiagnostics")] + public bool HasDiagnostics { get; set; } + + /// + /// Gets the definition of the build. + /// + [JsonProperty("definition")] + public BuildCompletedDefinition Definition { get; set; } + + /// + /// Gets the build queue. + /// + [JsonProperty("queue")] + public BuildCompletedQueueDefinition Queue { get; set; } + + /// + /// Gets build requests. + /// + [JsonProperty("requests")] + public Collection Requests + { + get { return _requests; } + } + } +} diff --git a/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/CodeCheckedInPayload.cs b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/CodeCheckedInPayload.cs new file mode 100644 index 0000000..2d7ee2a --- /dev/null +++ b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/CodeCheckedInPayload.cs @@ -0,0 +1,12 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.AspNet.WebHooks.Payloads +{ + /// + /// Describes the entire payload of event 'tfvc.checkin'. + /// + public class CodeCheckedInPayload : BasePayload + { + } +} diff --git a/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/CodeCheckedInResource.cs b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/CodeCheckedInResource.cs new file mode 100644 index 0000000..ddcd3ba --- /dev/null +++ b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/CodeCheckedInResource.cs @@ -0,0 +1,50 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Newtonsoft.Json; + +namespace Microsoft.AspNet.WebHooks.Payloads +{ + /// + /// Describes the resource that associated with + /// + public class CodeCheckedInResource : BaseResource + { + /// + /// Gets the changeset identifier. + /// + [JsonProperty("changesetId")] + public int ChangesetId { get; set; } + + /// + /// Gets the changeset URL. + /// + [JsonProperty("url")] + public Uri Url { get; set; } + + /// + /// Gets the changeset author. + /// + [JsonProperty("author")] + public ResourceUser Author { get; set; } + + /// + /// Gets the user that checked in the changeset. + /// + [JsonProperty("checkedInBy")] + public ResourceUser CheckedInBy { get; set; } + + /// + /// Gets the changeset creation date. + /// + [JsonProperty("createdDate")] + public DateTime CreatedDate { get; set; } + + /// + /// Gets the changeset comment. + /// + [JsonProperty("comment")] + public string Comment { get; set; } + } +} diff --git a/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/PayloadMessage.cs b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/PayloadMessage.cs new file mode 100644 index 0000000..7f6d1be --- /dev/null +++ b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/PayloadMessage.cs @@ -0,0 +1,31 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Newtonsoft.Json; + +namespace Microsoft.AspNet.WebHooks.Payloads +{ + /// + /// Describes payload message. + /// + public class PayloadMessage + { + /// + /// Gets the message in plain text. + /// + [JsonProperty("text")] + public string Text { get; set; } + + /// + /// Gets the message in HTML format. + /// + [JsonProperty("html")] + public string Html { get; set; } + + /// + /// Gets the message in markdown format. + /// + [JsonProperty("markdown")] + public string Markdown { get; set; } + } +} diff --git a/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/PayloadResourceContainer.cs b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/PayloadResourceContainer.cs new file mode 100644 index 0000000..1339f93 --- /dev/null +++ b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/PayloadResourceContainer.cs @@ -0,0 +1,19 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Newtonsoft.Json; + +namespace Microsoft.AspNet.WebHooks.Payloads +{ + /// + /// Describes container + /// + public class PayloadResourceContainer + { + /// + /// Gets the identifier of container. + /// + [JsonProperty("id")] + public string Id { get; set; } + } +} diff --git a/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/PayloadResourceContainers.cs b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/PayloadResourceContainers.cs new file mode 100644 index 0000000..058cca7 --- /dev/null +++ b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/PayloadResourceContainers.cs @@ -0,0 +1,31 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Newtonsoft.Json; + +namespace Microsoft.AspNet.WebHooks.Payloads +{ + /// + /// Describes containers containing the resource + /// + public class PayloadResourceContainers + { + /// + /// Gets the collection. + /// + [JsonProperty("collection")] + public PayloadResourceContainer Collection { get; set; } + + /// + /// Gets the account. + /// + [JsonProperty("account")] + public PayloadResourceContainer Account { get; set; } + + /// + /// Gets the project. + /// + [JsonProperty("project")] + public PayloadResourceContainer Project { get; set; } + } +} diff --git a/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/ResourceUser.cs b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/ResourceUser.cs new file mode 100644 index 0000000..d9c94f5 --- /dev/null +++ b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/ResourceUser.cs @@ -0,0 +1,44 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Newtonsoft.Json; + +namespace Microsoft.AspNet.WebHooks.Payloads +{ + /// + /// Describes user entity + /// + public class ResourceUser + { + /// + /// Gets the identifier of the user. + /// + [JsonProperty("id")] + public string Id { get; set; } + + /// + /// Gets the user display name. + /// + [JsonProperty("displayName")] + public string DisplayName { get; set; } + + /// + /// Gets the user unique name. + /// + [JsonProperty("uniqueName")] + public string UniqueName { get; set; } + + /// + /// Gets the user URL. + /// + [JsonProperty("url")] + public Uri Url { get; set; } + + /// + /// Gets the user image URL. + /// + [JsonProperty("imageUrl")] + public Uri ImageUrl { get; set; } + } +} diff --git a/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/TeamRoomMessagePostedPayload.cs b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/TeamRoomMessagePostedPayload.cs new file mode 100644 index 0000000..95abcf4 --- /dev/null +++ b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/TeamRoomMessagePostedPayload.cs @@ -0,0 +1,12 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.AspNet.WebHooks.Payloads +{ + /// + /// Describes the entire payload of event 'message.posted'. + /// + public class TeamRoomMessagePostedPayload : BasePayload + { + } +} diff --git a/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/TeamRoomMessagePostedResource.cs b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/TeamRoomMessagePostedResource.cs new file mode 100644 index 0000000..a54ecd1 --- /dev/null +++ b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/TeamRoomMessagePostedResource.cs @@ -0,0 +1,50 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Newtonsoft.Json; + +namespace Microsoft.AspNet.WebHooks.Payloads +{ + /// + /// Describes the resource that associated with + /// + public class TeamRoomMessagePostedResource : BaseResource + { + /// + /// Gets the identifier. + /// + [JsonProperty("id")] + public int Id { get; set; } + + /// + /// Gets the content of the message. + /// + [JsonProperty("content")] + public string Content { get; set; } + + /// + /// Gets the type of the message. + /// + [JsonProperty("messageType")] + public string MessageType { get; set; } + + /// + /// Gets the posted time of the message. + /// + [JsonProperty("postedTime")] + public DateTime PostedTime { get; set; } + + /// + /// Gets the room identifier where message was posted. + /// + [JsonProperty("postedRoomId")] + public int PostedRoomId { get; set; } + + /// + /// Gets the user who posted the message. + /// + [JsonProperty("postedBy")] + public ResourceUser PostedBy { get; set; } + } +} diff --git a/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemCommentedOnPayload.cs b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemCommentedOnPayload.cs new file mode 100644 index 0000000..31a4d74 --- /dev/null +++ b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemCommentedOnPayload.cs @@ -0,0 +1,12 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.AspNet.WebHooks.Payloads +{ + /// + /// Describes the entire payload of event 'workitem.commented'. + /// + public class WorkItemCommentedOnPayload : BasePayload + { + } +} diff --git a/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemCommentedOnResource.cs b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemCommentedOnResource.cs new file mode 100644 index 0000000..28d0f7b --- /dev/null +++ b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemCommentedOnResource.cs @@ -0,0 +1,12 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.AspNet.WebHooks.Payloads +{ + /// + /// Describes the resource that associated with + /// + public class WorkItemCommentedOnResource : BaseWorkItemResource + { + } +} diff --git a/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemCreatedPayload.cs b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemCreatedPayload.cs new file mode 100644 index 0000000..bb54be0 --- /dev/null +++ b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemCreatedPayload.cs @@ -0,0 +1,12 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.AspNet.WebHooks.Payloads +{ + /// + /// Describes the entire payload of event 'workitem.created'. + /// + public class WorkItemCreatedPayload : BasePayload + { + } +} diff --git a/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemCreatedResource.cs b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemCreatedResource.cs new file mode 100644 index 0000000..fdecd0f --- /dev/null +++ b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemCreatedResource.cs @@ -0,0 +1,12 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.AspNet.WebHooks.Payloads +{ + /// + /// Describes the resource that associated with + /// + public class WorkItemCreatedResource : BaseWorkItemResource + { + } +} diff --git a/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemDeletedPayload.cs b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemDeletedPayload.cs new file mode 100644 index 0000000..303ac73 --- /dev/null +++ b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemDeletedPayload.cs @@ -0,0 +1,12 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.AspNet.WebHooks.Payloads +{ + /// + /// Describes the entire payload of event 'workitem.deleted'. + /// + public class WorkItemDeletedPayload : BasePayload + { + } +} diff --git a/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemDeletedResource.cs b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemDeletedResource.cs new file mode 100644 index 0000000..ec9645d --- /dev/null +++ b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemDeletedResource.cs @@ -0,0 +1,12 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.AspNet.WebHooks.Payloads +{ + /// + /// Describes the resource that associated with + /// + public class WorkItemDeletedResource : BaseWorkItemResource + { + } +} diff --git a/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemFields.cs b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemFields.cs new file mode 100644 index 0000000..57af3b0 --- /dev/null +++ b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemFields.cs @@ -0,0 +1,98 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Newtonsoft.Json; + +namespace Microsoft.AspNet.WebHooks.Payloads +{ + /// + /// Describes fields of the WorkItem + /// + public class WorkItemFields + { + /// + /// Gets the value of field System.AreaPath. + /// + [JsonProperty("System.AreaPath")] + public string SystemAreaPath { get; set; } + + /// + /// Gets the value of field System.TeamProject. + /// + [JsonProperty("System.TeamProject")] + public string SystemTeamProject { get; set; } + + /// + /// Gets the value of field System.IterationPath. + /// + [JsonProperty("System.IterationPath")] + public string SystemIterationPath { get; set; } + + /// + /// Gets the value of field System.WorkItemType. + /// + [JsonProperty("System.WorkItemType")] + public string SystemWorkItemType { get; set; } + + /// + /// Gets the value of field System.State. + /// + [JsonProperty("System.State")] + public string SystemState { get; set; } + + /// + /// Gets the value of field System.Reason. + /// + [JsonProperty("System.Reason")] + public string SystemReason { get; set; } + + /// + /// Gets the value of field System.CreatedDate. + /// + [JsonProperty("System.CreatedDate")] + public DateTime SystemCreatedDate { get; set; } + + /// + /// Gets the value of field System.CreatedBy. + /// + [JsonProperty("System.CreatedBy")] + public string SystemCreatedBy { get; set; } + + /// + /// Gets the value of field System.ChangedDate. + /// + [JsonProperty("System.ChangedDate")] + public DateTime SystemChangedDate { get; set; } + + /// + /// Gets the value of field System.ChangedBy. + /// + [JsonProperty("System.ChangedBy")] + public string SystemChangedBy { get; set; } + + /// + /// Gets the value of field System.Title. + /// + [JsonProperty("System.Title")] + public string SystemTitle { get; set; } + + /// + /// Gets the value of field Microsoft.VSTS.Common.Severity. + /// + [JsonProperty("Microsoft.VSTS.Common.Severity")] + public string MicrosoftCommonSeverity { get; set; } + + /// + /// Gets the value of field WEF_EB329F44FE5F4A94ACB1DA153FDF38BA_Kanban.Column. + /// + [JsonProperty("WEF_EB329F44FE5F4A94ACB1DA153FDF38BA_Kanban.Column")] + public string KanbanColumn { get; set; } + + /// + /// Gets the value of field System.History. + /// + [JsonProperty("System.History")] + public string SystemHistory { get; set; } + } +} diff --git a/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemLink.cs b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemLink.cs new file mode 100644 index 0000000..35c6f7b --- /dev/null +++ b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemLink.cs @@ -0,0 +1,19 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Newtonsoft.Json; + +namespace Microsoft.AspNet.WebHooks.Payloads +{ + /// + /// Describes the WorkItem's link. + /// + public class WorkItemLink + { + /// + /// Gets the URL of WorkItem's link. + /// + [JsonProperty("href")] + public string Href { get; set; } + } +} diff --git a/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemLinks.cs b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemLinks.cs new file mode 100644 index 0000000..c3633cc --- /dev/null +++ b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemLinks.cs @@ -0,0 +1,61 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Newtonsoft.Json; + +namespace Microsoft.AspNet.WebHooks.Payloads +{ + /// + /// Describes links of the WorkItem. + /// + public class WorkItemLinks + { + /// + /// Gets the link to the WorkItem itself. + /// + [JsonProperty("self")] + public WorkItemLink Self { get; set; } + + /// + /// Gets the link to the parent WorkItem if exists. + /// + [JsonProperty("parent")] + public WorkItemLink Parent { get; set; } + + /// + /// Gets the link to the WorkItem' updates. + /// + [JsonProperty("workItemUpdates")] + public WorkItemLink WorkItemUpdates { get; set; } + + /// + /// Gets the link to the WorkItem's revisions. + /// + [JsonProperty("workItemRevisions")] + public WorkItemLink WorkItemRevisions { get; set; } + + /// + /// Gets the link to the WorkItem's type. + /// + [JsonProperty("workItemType")] + public WorkItemLink WorkItemType { get; set; } + + /// + /// Gets the link to the WorkItem's fields. + /// + [JsonProperty("fields")] + public WorkItemLink Fields { get; set; } + + /// + /// Gets the link to the WorkItem's HTML. + /// + [JsonProperty("html")] + public WorkItemLink Html { get; set; } + + /// + /// Gets the link to the WorkItem's history. + /// + [JsonProperty("workItemHistory")] + public WorkItemLink WorkItemHistory { get; set; } + } +} diff --git a/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemRestoredPayload.cs b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemRestoredPayload.cs new file mode 100644 index 0000000..f2a5ab6 --- /dev/null +++ b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemRestoredPayload.cs @@ -0,0 +1,12 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.AspNet.WebHooks.Payloads +{ + /// + /// Describes the entire payload of event 'workitem.restored'. + /// + public class WorkItemRestoredPayload : BasePayload + { + } +} diff --git a/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemRestoredResource.cs b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemRestoredResource.cs new file mode 100644 index 0000000..1cfbb66 --- /dev/null +++ b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemRestoredResource.cs @@ -0,0 +1,12 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.AspNet.WebHooks.Payloads +{ + /// + /// Describes the resource that associated with + /// + public class WorkItemRestoredResource : BaseWorkItemResource + { + } +} diff --git a/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemUpdatedFieldValue.cs b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemUpdatedFieldValue.cs new file mode 100644 index 0000000..cc67bae --- /dev/null +++ b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemUpdatedFieldValue.cs @@ -0,0 +1,26 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Newtonsoft.Json; + +namespace Microsoft.AspNet.WebHooks.Payloads +{ + /// + /// Describes change of specific field + /// + /// The string-type of the field that is being changed + public class WorkItemUpdatedFieldValue + { + /// + /// Gets the value of the field before the change. + /// + [JsonProperty("oldValue")] + public T OldValue { get; set; } + + /// + /// Gets the value of the field after the change. + /// + [JsonProperty("newValue")] + public T NewValue { get; set; } + } +} diff --git a/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemUpdatedFields.cs b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemUpdatedFields.cs new file mode 100644 index 0000000..5f2a3c8 --- /dev/null +++ b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemUpdatedFields.cs @@ -0,0 +1,68 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Newtonsoft.Json; + +namespace Microsoft.AspNet.WebHooks.Payloads +{ + /// + /// Describes fields of the WorkItem that was updated + /// + public class WorkItemUpdatedFields + { + /// + /// Gets the change information for the field 'System.Rev'. + /// + [JsonProperty("System.Rev")] + public WorkItemUpdatedFieldValue SystemRev { get; set; } + + /// + /// Gets the change information for the field 'System.AuthorizedDate'. + /// + [JsonProperty("System.AuthorizedDate")] + public WorkItemUpdatedFieldValue SystemAuthorizedDate { get; set; } + + /// + /// Gets the change information for the field 'System.RevisedDate'. + /// + [JsonProperty("System.RevisedDate")] + public WorkItemUpdatedFieldValue SystemRevisedDate { get; set; } + + /// + /// Gets the change information for the field 'System.State'. + /// + [JsonProperty("System.State")] + public WorkItemUpdatedFieldValue SystemState { get; set; } + + /// + /// Gets the change information for the field 'System.Reason'. + /// + [JsonProperty("System.Reason")] + public WorkItemUpdatedFieldValue SystemReason { get; set; } + + /// + /// Gets the change information for the field 'System.AssignedTo'. + /// + [JsonProperty("System.AssignedTo")] + public WorkItemUpdatedFieldValue SystemAssignedTo { get; set; } + + /// + /// Gets the change information for the field 'System.ChangedDate'. + /// + [JsonProperty("System.ChangedDate")] + public WorkItemUpdatedFieldValue SystemChangedDate { get; set; } + + /// + /// Gets the change information for the field 'System.Watermark'. + /// + [JsonProperty("System.Watermark")] + public WorkItemUpdatedFieldValue SystemWatermark { get; set; } + + /// + /// Gets the change information for the field 'Microsoft.VSTS.Common.Severity'. + /// + [JsonProperty("Microsoft.Vsts.Common.Severity")] + public WorkItemUpdatedFieldValue MicrosoftCommonSeverity { get; set; } + } +} diff --git a/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemUpdatedPayload.cs b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemUpdatedPayload.cs new file mode 100644 index 0000000..3c1047e --- /dev/null +++ b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemUpdatedPayload.cs @@ -0,0 +1,12 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.AspNet.WebHooks.Payloads +{ + /// + /// Describes the entire payload of event 'workitem.updated'. + /// + public class WorkItemUpdatedPayload : BasePayload + { + } +} diff --git a/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemUpdatedResource.cs b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemUpdatedResource.cs new file mode 100644 index 0000000..4fa8c72 --- /dev/null +++ b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemUpdatedResource.cs @@ -0,0 +1,38 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Newtonsoft.Json; + +namespace Microsoft.AspNet.WebHooks.Payloads +{ + /// + /// Describes the resource that associated with + /// + public class WorkItemUpdatedResource : BaseWorkItemResource + { + /// + /// Gets WorkItem identifier. + /// + [JsonProperty("workItemId")] + public int WorkItemId { get; set; } + + /// + /// Gets the author of revision. + /// + [JsonProperty("revisedBy")] + public ResourceUser RevisedBy { get; set; } + + /// + /// Gets the revised date. + /// + [JsonProperty("revisedDate")] + public DateTime RevisedDate { get; set; } + + /// + /// Gets the revision. + /// + [JsonProperty("revision")] + public WorkItemUpdatedRevision Revision { get; set; } + } +} diff --git a/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemUpdatedRevision.cs b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemUpdatedRevision.cs new file mode 100644 index 0000000..8e0cb24 --- /dev/null +++ b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Payloads/WorkItemUpdatedRevision.cs @@ -0,0 +1,38 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Newtonsoft.Json; + +namespace Microsoft.AspNet.WebHooks.Payloads +{ + /// + /// Describes the revision + /// + public class WorkItemUpdatedRevision + { + /// + /// Gets the identifier of the revision. + /// + [JsonProperty("id")] + public int Id { get; set; } + + /// + /// Gets the revision number. + /// + [JsonProperty("rev")] + public int Rev { get; set; } + + /// + /// Gets the revision fields. + /// + [JsonProperty("fields")] + public WorkItemFields Fields { get; set; } + + /// + /// Gets the revision URL. + /// + [JsonProperty("url")] + public Uri Url { get; set; } + } +} diff --git a/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Properties/AssemblyInfo.cs b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..b4ca08d --- /dev/null +++ b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Properties/AssemblyInfo.cs @@ -0,0 +1,6 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Microsoft.AspNet.WebHooks.Receivers.TFS.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] diff --git a/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Properties/VstsReceiverResources.Designer.cs b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Properties/VstsReceiverResources.Designer.cs new file mode 100644 index 0000000..3487f72 --- /dev/null +++ b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Properties/VstsReceiverResources.Designer.cs @@ -0,0 +1,81 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Microsoft.AspNet.WebHooks.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class VstsReceiverResources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal VstsReceiverResources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.AspNet.WebHooks.Properties.VstsReceiverResources", typeof(VstsReceiverResources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to The property 'eventType' contains unmapped value '{0}'.. + /// + internal static string Handler_NonMappedEventType { + get { + return ResourceManager.GetString("Handler_NonMappedEventType", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No property 'eventType' was found in root of the object.. + /// + internal static string Receiver_NoEventType { + get { + return ResourceManager.GetString("Receiver_NoEventType", resourceCulture); + } + } + } +} diff --git a/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Properties/VstsReceiverResources.resx b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Properties/VstsReceiverResources.resx new file mode 100644 index 0000000..5ebe328 --- /dev/null +++ b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/Properties/VstsReceiverResources.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + The property 'eventType' contains unmapped value '{0}'. + + + No property 'eventType' was found in root of the object. + + \ No newline at end of file diff --git a/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/WebHooks/VstsWebHookReceiver.cs b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/WebHooks/VstsWebHookReceiver.cs new file mode 100644 index 0000000..9a08492 --- /dev/null +++ b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/WebHooks/VstsWebHookReceiver.cs @@ -0,0 +1,87 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Net; +using System.Net.Http; +using System.Threading.Tasks; +using System.Web.Http.Controllers; +using Microsoft.AspNet.WebHooks.Properties; +using Newtonsoft.Json.Linq; + +namespace Microsoft.AspNet.WebHooks +{ + /// + /// Provides an implementation which supports WebHooks generated by Visual Studio Team Services. + /// + /// The corresponding WebHook URI is of the form 'https://<host>/api/webhooks/incoming/vsts/{id}?code={code}'. + /// For security reasons the WebHook URI must be an https URI and contain a 'code' query parameter with the + /// same value as configured in the 'MS_WebHookReceiverSecret_Tfs' application setting, optionally using IDs + /// to differentiate between multiple WebHooks, for example 'secret0, id1=secret1, id2=secret2'. + /// The 'code' parameter must be between 32 and 128 characters long. + /// + /// For details about Visual Studio Team Services WebHooks, see https://www.visualstudio.com/en-us/get-started/integrate/service-hooks/webhooks-and-vso-vs. + /// + public class VstsWebHookReceiver : WebHookReceiver + { + internal const string RecName = "vsts"; + internal const string EventTypeTokenName = "eventType"; + + /// + /// Gets the receiver name for this receiver. + /// + public static string ReceiverName + { + get { return RecName; } + } + + /// + public override string Name + { + get { return RecName; } + } + + /// + public override async Task ReceiveAsync(string id, HttpRequestContext context, HttpRequestMessage request) + { + if (id == null) + { + throw new ArgumentNullException(nameof(id)); + } + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + if (request == null) + { + throw new ArgumentNullException(nameof(request)); + } + + if (request.Method != HttpMethod.Post) + { + return CreateBadMethodResponse(request); + } + + // Ensure that we use https and have a valid code parameter + await EnsureValidCode(request, id); + + // Read the request entity body + JObject jsonBody = await ReadAsJsonAsync(request); + + // Read the action from body + JToken action; + string actionAsString; + if (!jsonBody.TryGetValue(EventTypeTokenName, out action)) + { + request.GetConfiguration().DependencyResolver.GetLogger().Error(VstsReceiverResources.Receiver_NoEventType); + return request.CreateErrorResponse(HttpStatusCode.BadRequest, VstsReceiverResources.Receiver_NoEventType); + } + else + { + actionAsString = action.Value(); + } + + return await ExecuteWebHookAsync(id, context, request, new[] { actionAsString }, jsonBody); + } + } +} diff --git a/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/packages.config b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/packages.config new file mode 100644 index 0000000..11b3234 --- /dev/null +++ b/src/Microsoft.AspNet.WebHooks.Receivers.VSTS/packages.config @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/App.config b/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/App.config new file mode 100644 index 0000000..8daa28a --- /dev/null +++ b/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/App.config @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Common/Extensions.cs b/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Common/Extensions.cs new file mode 100644 index 0000000..4c20f77 --- /dev/null +++ b/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Common/Extensions.cs @@ -0,0 +1,16 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Globalization; + +namespace Microsoft.AspNet.WebHooks +{ + internal static class Extensions + { + public static DateTime ToDateTime(this string self) + { + return DateTime.Parse(self, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal); + } + } +} diff --git a/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Handlers/VstsWebHookHandlerBaseTests.cs b/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Handlers/VstsWebHookHandlerBaseTests.cs new file mode 100644 index 0000000..6965bca --- /dev/null +++ b/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Handlers/VstsWebHookHandlerBaseTests.cs @@ -0,0 +1,168 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.Net.Http; +using System.Threading.Tasks; +using System.Web.Http; +using System.Web.Http.Controllers; +using Microsoft.AspNet.WebHooks.Payloads; +using Moq; +using Newtonsoft.Json.Linq; +using Xunit; + +namespace Microsoft.AspNet.WebHooks.Handlers +{ + public class VstsWebHookHandlerBaseTests + { + private readonly Mock _handlerMock; + private readonly VstsWebHookHandlerBase _handler; + + private WebHookHandlerContext _context; + + public VstsWebHookHandlerBaseTests() + { + _handlerMock = new Mock { CallBase = true }; + _handler = _handlerMock.Object; + } + + [Fact] + public void VstsWebHookHandlerBase_SetsReceiverName() + { + Assert.Equal(VstsWebHookReceiver.ReceiverName, _handler.Receiver); + } + + [Fact] + public async Task ExecuteAsync_Dispatches_BuildCompleted() + { + // Arrange + _context = GetContext("Microsoft.AspNet.WebHooks.Messages.build.complete.json", "build.complete"); + + // Act + await _handler.ExecuteAsync(VstsWebHookReceiver.ReceiverName, _context); + + // Assert + _handlerMock.Verify(h => h.ExecuteAsync(_context, It.IsAny()), Times.Once()); + } + + [Fact] + public async Task ExecuteAsync_Dispatches_CodeCheckedIn() + { + // Arrange + _context = GetContext("Microsoft.AspNet.WebHooks.Messages.tfvc.checkin.json", "tfvc.checkin"); + + // Act + await _handler.ExecuteAsync(VstsWebHookReceiver.ReceiverName, _context); + + // Assert + _handlerMock.Verify(h => h.ExecuteAsync(_context, It.IsAny()), Times.Once()); + } + + [Fact] + public async Task ExecuteAsync_Dispatches_TeamRoomMessagePosted() + { + // Arrange + _context = GetContext("Microsoft.AspNet.WebHooks.Messages.message.posted.json", "message.posted"); + + // Act + await _handler.ExecuteAsync(VstsWebHookReceiver.ReceiverName, _context); + + // Assert + _handlerMock.Verify(h => h.ExecuteAsync(_context, It.IsAny()), Times.Once()); + } + + [Fact] + public async Task ExecuteAsync_Dispatches_WorkItemCommentedOn() + { + // Arrange + _context = GetContext("Microsoft.AspNet.WebHooks.Messages.workitem.commented.json", "workitem.commented"); + + // Act + await _handler.ExecuteAsync(VstsWebHookReceiver.ReceiverName, _context); + + // Assert + _handlerMock.Verify(h => h.ExecuteAsync(_context, It.IsAny()), Times.Once()); + } + + [Fact] + public async Task ExecuteAsync_Dispatches_WorkItemCreated() + { + // Arrange + _context = GetContext("Microsoft.AspNet.WebHooks.Messages.workitem.created.json", "workitem.created"); + + // Act + await _handler.ExecuteAsync(VstsWebHookReceiver.ReceiverName, _context); + + // Assert + _handlerMock.Verify(h => h.ExecuteAsync(_context, It.IsAny()), Times.Once()); + } + + [Fact] + public async Task ExecuteAsync_Dispatches_WorkItemDeleted() + { + // Arrange + _context = GetContext("Microsoft.AspNet.WebHooks.Messages.workitem.deleted.json", "workitem.deleted"); + + // Act + await _handler.ExecuteAsync(VstsWebHookReceiver.ReceiverName, _context); + + // Assert + _handlerMock.Verify(h => h.ExecuteAsync(_context, It.IsAny()), Times.Once()); + } + + [Fact] + public async Task ExecuteAsync_Dispatches_WorkItemRestored() + { + // Arrange + _context = GetContext("Microsoft.AspNet.WebHooks.Messages.workitem.restored.json", "workitem.restored"); + + // Act + await _handler.ExecuteAsync(VstsWebHookReceiver.ReceiverName, _context); + + // Assert + _handlerMock.Verify(h => h.ExecuteAsync(_context, It.IsAny()), Times.Once()); + } + + [Fact] + public async Task ExecuteAsync_Dispatches_WorkItemUpdated() + { + // Arrange + _context = GetContext("Microsoft.AspNet.WebHooks.Messages.workitem.updated.json", "workitem.updated"); + + // Act + await _handler.ExecuteAsync(VstsWebHookReceiver.ReceiverName, _context); + + // Assert + _handlerMock.Verify(h => h.ExecuteAsync(_context, It.IsAny()), Times.Once()); + } + + [Fact] + public async Task ExecuteAsync_Handles_UnknownEventType() + { + // Arrange + _context = GetContext("Microsoft.AspNet.WebHooks.Messages.bad.notMappedEventType.json", "unknown"); + + // Act + await _handler.ExecuteAsync(VstsWebHookReceiver.ReceiverName, _context); + + // Assert + _handlerMock.Verify(h => h.ExecuteAsync(_context, It.IsAny()), Times.Once()); + } + + private static WebHookHandlerContext GetContext(string payload, string action) + { + JObject data = EmbeddedResource.ReadAsJObject(payload); + HttpConfiguration httpConfig = new HttpConfiguration(); + HttpRequestContext requestContext = new HttpRequestContext { Configuration = httpConfig }; + HttpRequestMessage request = new HttpRequestMessage(); + request.SetRequestContext(requestContext); + IEnumerable actions = new[] { action }; + return new WebHookHandlerContext(actions) + { + Data = data, + Request = request, + RequestContext = requestContext + }; + } + } +} diff --git a/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Messages/bad.noEventType.json b/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Messages/bad.noEventType.json new file mode 100644 index 0000000..a7d792e --- /dev/null +++ b/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Messages/bad.noEventType.json @@ -0,0 +1,3 @@ +{ + "subscriptionId": "00000000-0000-0000-0000-000000000000" +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Messages/bad.notMappedEventType.json b/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Messages/bad.notMappedEventType.json new file mode 100644 index 0000000..99b7d7d --- /dev/null +++ b/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Messages/bad.notMappedEventType.json @@ -0,0 +1,5 @@ +{ + "subscriptionId": "00000000-0000-0000-0000-000000000000", + "notificationId": 5, + "eventType": "unknownType" +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Messages/build.complete.json b/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Messages/build.complete.json new file mode 100644 index 0000000..8fc5f97 --- /dev/null +++ b/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Messages/build.complete.json @@ -0,0 +1,83 @@ +{ + "subscriptionId": "00000000-0000-0000-0000-000000000000", + "notificationId": 1, + "id": "4a5d99d6-1c75-4e53-91b9-ee80057d4ce3", + "eventType": "build.complete", + "publisherId": "tfs", + "message": { + "text": "Build ConsumerAddressModule_20150407.2 succeeded", + "html": "Build ConsumerAddressModule_20150407.2 succeeded", + "markdown": "Build [ConsumerAddressModule_20150407.2](https://good-company.some.ssl.host/web/build.aspx?id=5023c10b-bef3-41c3-bf53-686c4e34ee9e&builduri=vstfs%3a%2f%2f%2fBuild%2fBuild%2f3) succeeded" + }, + "detailedMessage": { + "text": "Build ConsumerAddressModule_20150407.2 succeeded", + "html": "Build ConsumerAddressModule_20150407.2 succeeded", + "markdown": "Build [ConsumerAddressModule_20150407.2](https://good-company.some.ssl.host/web/build.aspx?id=5023c10b-bef3-41c3-bf53-686c4e34ee9e&builduri=vstfs%3a%2f%2f%2fBuild%2fBuild%2f3) succeeded" + }, + "resource": { + "uri": "vstfs:///Build/Build/2", + "id": 2, + "buildNumber": "ConsumerAddressModule_20150407.1", + "url": "https://good-company.some.ssl.host/DefaultCollection/71777fbc-1cf2-4bd1-9540-128c1c71f766/_apis/build/Builds/2", + "startTime": "2015-04-07T18:04:06.83Z", + "finishTime": "2015-04-07T18:06:10.69Z", + "reason": "manual", + "status": "succeeded", + "dropLocation": "#/3/drop", + "drop": { + "location": "#/3/drop", + "type": "container", + "url": "https://good-company.some.ssl.host/DefaultCollection/_apis/resources/Containers/3/drop", + "downloadUrl": "https://good-company.some.ssl.host/DefaultCollection/_apis/resources/Containers/3/drop?api-version=1.0&$format=zip&downloadFileName=ConsumerAddressModule_20150407.1_drop" + }, + "log": { + "type": "container", + "url": "https://good-company.some.ssl.host/DefaultCollection/_apis/resources/Containers/3/logs", + "downloadUrl": "https://good-company.some.ssl.host/_apis/resources/Containers/3/logs?api-version=1.0&$format=zip&downloadFileName=ConsumerAddressModule_20150407.1_logs" + }, + "sourceGetVersion": "LG:refs/heads/master:600c52d2d5b655caa111abfd863e5a9bd304bb0e", + "lastChangedBy": { + "id": "d6245f20-2af8-44f4-9451-8107cb2767db", + "displayName": "John Smith", + "uniqueName": "fabrikamfiber16@hotmail.com", + "url": "https://good-company.some.ssl.host/_apis/Identities/d6245f20-2af8-44f4-9451-8107cb2767db", + "imageUrl": "https://good-company.some.ssl.host/DefaultCollection/_api/_common/identityImage?id=d6245f20-2af8-44f4-9451-8107cb2767db" + }, + "retainIndefinitely": false, + "hasDiagnostics": true, + "definition": { + "batchSize": 1, + "triggerType": "none", + "definitionType": "xaml", + "id": 2, + "name": "ConsumerAddressModule", + "url": "https://good-company.some.ssl.host/DefaultCollection/71777fbc-1cf2-4bd1-9540-128c1c71f766/_apis/build/Definitions/2" + }, + "queue": { + "queueType": "buildController", + "id": 4, + "name": "Hosted Build Controller", + "url": "https://good-company.some.ssl.host/DefaultCollection/_apis/build/Queues/4" + }, + "requests": [ + { + "id": 1, + "url": "https://good-company.some.ssl.host/DefaultCollection/71777fbc-1cf2-4bd1-9540-128c1c71f766/_apis/build/Requests/1", + "requestedFor": { + "id": "d6245f20-2af8-44f4-9451-8107cb2767db", + "displayName": "John Smith", + "uniqueName": "fabrikamfiber16@hotmail.com", + "url": "https://good-company.some.ssl.host/_apis/Identities/d6245f20-2af8-44f4-9451-8107cb2767db", + "imageUrl": "https://good-company.some.ssl.host/DefaultCollection/_api/_common/identityImage?id=d6245f20-2af8-44f4-9451-8107cb2767db" + } + } + ] + }, + "resourceVersion": "1.0", + "resourceContainers": { + "collection": { "id": "c12d0eb8-e382-443b-9f9c-c52cba5014c2" }, + "account": { "id": "f844ec47-a9db-4511-8281-8b63f4eaf94e" }, + "project": { "id": "be9b3917-87e6-42a4-a549-2bc06a7a878f" } + }, + "createdDate": "2016-05-02T19:00:39.5893296Z" +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Messages/message.posted.json b/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Messages/message.posted.json new file mode 100644 index 0000000..117b8b5 --- /dev/null +++ b/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Messages/message.posted.json @@ -0,0 +1,36 @@ +{ + "subscriptionId": "00000000-0000-0000-0000-000000000000", + "notificationId": 3, + "id": "daae438c-296b-4512-b08e-571910874e9b", + "eventType": "message.posted", + "publisherId": "tfs", + "message": { + "text": "Jamal Hartnett posted a message to Northward-Fiber-Git Team Room\r\nHello", + "html": "Jamal Hartnett posted a message to Northward-Fiber-Git Team Room\r\nHello", + "markdown": "Jamal Hartnett posted a message to Northward-Fiber-Git Team Room\r\nHello" + }, + "detailedMessage": { + "text": "Jamal Hartnett posted a message to Northward-Fiber-Git Team Room\r\nHello", + "html": "Jamal Hartnett posted a message to Northward-Fiber-Git Team Room

Hello

", + "markdown": "Jamal Hartnett posted a message to Northward-Fiber-Git Team Room\r\nHello" + }, + "resource": { + "id": 0, + "content": "Hello", + "messageType": "normal", + "postedTime": "2014-05-02T19:17:13.3309587Z", + "postedRoomId": 1, + "postedBy": { + "id": "00067FFED5C7AF52@Live.com", + "displayName": "Jamal Hartnett", + "uniqueName": "Windows Live ID\\fabrikamfiber4@hotmail.com" + } + }, + "resourceVersion": "1.0", + "resourceContainers": { + "collection": { "id": "c12d0eb8-e382-443b-9f9c-c52cba5014c2" }, + "account": { "id": "f844ec47-a9db-4511-8281-8b63f4eaf94e" }, + "project": { "id": "be9b3917-87e6-42a4-a549-2bc06a7a878f" } + }, + "createdDate": "2016-05-02T19:13:40.8417653Z" +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Messages/tfvc.checkin.json b/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Messages/tfvc.checkin.json new file mode 100644 index 0000000..e990b4b --- /dev/null +++ b/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Messages/tfvc.checkin.json @@ -0,0 +1,40 @@ +{ + "subscriptionId": "00000000-0000-0000-0000-000000000000", + "notificationId": 2, + "id": "f9b4c23e-88dd-4516-b04d-849787304e32", + "eventType": "tfvc.checkin", + "publisherId": "tfs", + "message": { + "text": "John Smith checked in changeset 18: Dropping in new Java sample", + "html": "John Smith checked in changeset 18: Dropping in new Java sample", + "markdown": "John Smith checked in changeset [18](https://good-company.some.ssl.host/web/cs.aspx?id=d81542e4-cdfa-4333-b082-1ae2d6c3ad16&cs=18): Dropping in new Java sample" + }, + "detailedMessage": { + "text": "John Smith checked in changeset 18: Dropping in new Java sample", + "html": "John Smith checked in changeset 18: Dropping in new Java sample", + "markdown": "John Smith checked in changeset [18](https://good-company.some.ssl.host/web/cs.aspx?id=d81542e4-cdfa-4333-b082-1ae2d6c3ad16&cs=18): Dropping in new Java sample" + }, + "resource": { + "changesetId": 18, + "url": "https://good-company.some.ssl.host/DefaultCollection/_apis/tfvc/changesets/18", + "author": { + "id": "d6245f20-2af8-44f4-9451-8107cb2767db", + "displayName": "John Smith", + "uniqueName": "fabrikamfiber16@hotmail.com" + }, + "checkedInBy": { + "id": "d6245f20-2af8-44f4-9451-8107cb2767db", + "displayName": "John Smith", + "uniqueName": "fabrikamfiber16@hotmail.com" + }, + "createdDate": "2014-05-12T22:41:16Z", + "comment": "Dropping in new Java sample" + }, + "resourceVersion": "1.0", + "resourceContainers": { + "collection": { "id": "c12d0eb8-e382-443b-9f9c-c52cba5014c2" }, + "account": { "id": "f844ec47-a9db-4511-8281-8b63f4eaf94e" }, + "project": { "id": "be9b3917-87e6-42a4-a549-2bc06a7a878f" } + }, + "createdDate": "2016-05-02T19:01:11.7056821Z" +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Messages/workitem.commented.json b/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Messages/workitem.commented.json new file mode 100644 index 0000000..354d3fc --- /dev/null +++ b/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Messages/workitem.commented.json @@ -0,0 +1,52 @@ +{ + "subscriptionId": "00000000-0000-0000-0000-000000000000", + "notificationId": 4, + "id": "fb2617ed-60df-4518-81fa-749faa6c5cd6", + "eventType": "workitem.commented", + "publisherId": "tfs", + "message": { + "text": "Bug #5 (Some great new idea!) commented on by Jamal Hartnett.\r\n(http://good-company.some.ssl.host/web/wi.aspx?id=74e918bf-3376-436d-bd20-8e8c1287f465&id=5)", + "html": "Bug #5 (Some great new idea!) commented on by Jamal Hartnett.", + "markdown": "[Bug #5](http://good-company.some.ssl.host/web/wi.aspx?id=74e918bf-3376-436d-bd20-8e8c1287f465&id=5) (Some great new idea!) commented on by Jamal Hartnett." + }, + "detailedMessage": { + "text": "Bug #5 (Some great new idea!) commented on by Jamal Hartnett.\r\n(http://good-company.some.ssl.host/web/wi.aspx?id=74e918bf-3376-436d-bd20-8e8c1287f465&id=5)\r\nThis is a great new idea", + "html": "Bug #5 (Some great new idea!) commented on by Jamal Hartnett.
This is a great new idea", + "markdown": "[Bug #5](http://good-company.some.ssl.host/web/wi.aspx?id=74e918bf-3376-436d-bd20-8e8c1287f465&id=5) (Some great new idea!) commented on by Jamal Hartnett.\r\nThis is a great new idea" + }, + "resource": { + "id": 5, + "rev": 4, + "fields": { + "System.AreaPath": "GoodCompanyCloud", + "System.TeamProject": "GoodCompanyCloud", + "System.IterationPath": "GoodCompanyCloud\\Release 1\\Sprint 1", + "System.WorkItemType": "Bug", + "System.State": "New", + "System.Reason": "New defect reported", + "System.CreatedDate": "2014-07-15T17:42:44.663Z", + "System.CreatedBy": "Jamal Hartnett", + "System.ChangedDate": "2014-07-15T17:42:44.663Z", + "System.ChangedBy": "Jamal Hartnett", + "System.Title": "Some great new idea!", + "Microsoft.VSTS.Common.Severity": "3 - Medium", + "WEF_EB329F44FE5F4A94ACB1DA153FDF38BA_Kanban.Column": "New", + "System.History": "This is a great new idea" + }, + "_links": { + "self": { "href": "http://good-company.some.ssl.host/DefaultCollection/_apis/wit/workItems/5" }, + "workItemUpdates": { "href": "http://good-company.some.ssl.host/DefaultCollection/_apis/wit/workItems/5/updates" }, + "workItemRevisions": { "href": "http://good-company.some.ssl.host/DefaultCollection/_apis/wit/workItems/5/revisions" }, + "workItemType": { "href": "http://good-company.some.ssl.host/DefaultCollection/_apis/wit/ea830882-2a3c-4095-a53f-972f9a376f6e/workItemTypes/Bug" }, + "fields": { "href": "http://good-company.some.ssl.host/DefaultCollection/_apis/wit/fields" } + }, + "url": "http://good-company.some.ssl.host/DefaultCollection/_apis/wit/workItems/5" + }, + "resourceVersion": "1.0", + "resourceContainers": { + "collection": { "id": "c12d0eb8-e382-443b-9f9c-c52cba5014c2" }, + "account": { "id": "f844ec47-a9db-4511-8281-8b63f4eaf94e" }, + "project": { "id": "be9b3917-87e6-42a4-a549-2bc06a7a878f" } + }, + "createdDate": "2016-05-02T19:15:37.4638247Z" +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Messages/workitem.created.json b/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Messages/workitem.created.json new file mode 100644 index 0000000..293083b --- /dev/null +++ b/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Messages/workitem.created.json @@ -0,0 +1,51 @@ +{ + "subscriptionId": "00000000-0000-0000-0000-000000000000", + "notificationId": 5, + "id": "d2d46fb1-dba5-403c-9373-427583f19e8c", + "eventType": "workitem.created", + "publisherId": "tfs", + "message": { + "text": "Bug #5 (Some great new idea!) created by Jamal Hartnett.\r\n(http://good-company.some.ssl.host/web/wi.aspx?id=74e918bf-3376-436d-bd20-8e8c1287f465&id=5)", + "html": "Bug #5 (Some great new idea!) created by Jamal Hartnett.", + "markdown": "[Bug #5](http://good-company.some.ssl.host/web/wi.aspx?id=74e918bf-3376-436d-bd20-8e8c1287f465&id=5) (Some great new idea!) created by Jamal Hartnett." + }, + "detailedMessage": { + "text": "Bug #5 (Some great new idea!) created by Jamal Hartnett.\r\n(http://good-company.some.ssl.host/web/wi.aspx?id=74e918bf-3376-436d-bd20-8e8c1287f465&id=5)\r\n\r\n- State: New\r\n- Assigned to: \r\n- Comment: \r\n- Severity: 3 - Medium\r\n", + "html": "Bug #5 (Some great new idea!) created by Jamal Hartnett.
    \r\n
  • State: New
  • \r\n
  • Assigned to:
  • \r\n
  • Comment:
  • \r\n
  • Severity: 3 - Medium
", + "markdown": "[Bug #5](http://good-company.some.ssl.host/web/wi.aspx?id=74e918bf-3376-436d-bd20-8e8c1287f465&id=5) (Some great new idea!) created by Jamal Hartnett.\r\n\r\n* State: New\r\n* Assigned to: \r\n* Comment: \r\n* Severity: 3 - Medium\r\n" + }, + "resource": { + "id": 5, + "rev": 1, + "fields": { + "System.AreaPath": "GoodCompanyCloud", + "System.TeamProject": "GoodCompanyCloud", + "System.IterationPath": "GoodCompanyCloud\\Release 1\\Sprint 1", + "System.WorkItemType": "Bug", + "System.State": "New", + "System.Reason": "New defect reported", + "System.CreatedDate": "2014-07-15T17:42:44.663Z", + "System.CreatedBy": "Jamal Hartnett", + "System.ChangedDate": "2014-07-15T17:42:44.663Z", + "System.ChangedBy": "Jamal Hartnett", + "System.Title": "Some great new idea!", + "Microsoft.VSTS.Common.Severity": "3 - Medium", + "WEF_EB329F44FE5F4A94ACB1DA153FDF38BA_Kanban.Column": "New" + }, + "_links": { + "self": { "href": "http://good-company.some.ssl.host/DefaultCollection/_apis/wit/workItems/5" }, + "workItemUpdates": { "href": "http://good-company.some.ssl.host/DefaultCollection/_apis/wit/workItems/5/updates" }, + "workItemRevisions": { "href": "http://good-company.some.ssl.host/DefaultCollection/_apis/wit/workItems/5/revisions" }, + "workItemType": { "href": "http://good-company.some.ssl.host/DefaultCollection/_apis/wit/ea830882-2a3c-4095-a53f-972f9a376f6e/workItemTypes/Bug" }, + "fields": { "href": "http://good-company.some.ssl.host/DefaultCollection/_apis/wit/fields" } + }, + "url": "http://good-company.some.ssl.host/DefaultCollection/_apis/wit/workItems/5" + }, + "resourceVersion": "1.0", + "resourceContainers": { + "collection": { "id": "c12d0eb8-e382-443b-9f9c-c52cba5014c2" }, + "account": { "id": "f844ec47-a9db-4511-8281-8b63f4eaf94e" }, + "project": { "id": "be9b3917-87e6-42a4-a549-2bc06a7a878f" } + }, + "createdDate": "2016-05-02T19:16:25.6251162Z" +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Messages/workitem.deleted.json b/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Messages/workitem.deleted.json new file mode 100644 index 0000000..120b097 --- /dev/null +++ b/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Messages/workitem.deleted.json @@ -0,0 +1,49 @@ +{ + "subscriptionId": "00000000-0000-0000-0000-000000000000", + "notificationId": 6, + "id": "72da0ade-0709-40ee-beb7-104287bf7e84", + "eventType": "workitem.deleted", + "publisherId": "tfs", + "message": { + "text": "Bug #5 (Some great new idea!) deleted by Jamal Hartnett.", + "html": "Bug #5 (Some great new idea!) deleted by Jamal Hartnett.", + "markdown": "[Bug #5] (Some great new idea!) deleted by Jamal Hartnett." + }, + "detailedMessage": { + "text": "Bug #5 (Some great new idea!) deleted by Jamal Hartnett.\r\n\r\n- State: New\r\n", + "html": "Bug #5 (Some great new idea!) deleted by Jamal Hartnett.
    \r\n
  • State: New
", + "markdown": "[Bug #5] (Some great new idea!) deleted by Jamal Hartnett.\r\n\r\n* State: New\r\n" + }, + "resource": { + "id": 5, + "rev": 1, + "fields": { + "System.AreaPath": "GoodCompanyCloud", + "System.TeamProject": "GoodCompanyCloud", + "System.IterationPath": "GoodCompanyCloud\\Release 1\\Sprint 1", + "System.WorkItemType": "Bug", + "System.State": "New", + "System.Reason": "New defect reported", + "System.CreatedDate": "2014-07-15T17:42:44.663Z", + "System.CreatedBy": "Jamal Hartnett", + "System.ChangedDate": "2014-07-15T17:42:44.663Z", + "System.ChangedBy": "Jamal Hartnett", + "System.Title": "Some great new idea!", + "Microsoft.VSTS.Common.Severity": "3 - Medium", + "WEF_EB329F44FE5F4A94ACB1DA153FDF38BA_Kanban.Column": "New" + }, + "_links": { + "self": { "href": "http://good-company.some.ssl.host/DefaultCollection/_apis/wit/recyclebin/5" }, + "workItemType": { "href": "http://good-company.some.ssl.host/DefaultCollection/_apis/wit/ea830882-2a3c-4095-a53f-972f9a376f6e/workItemTypes/Bug" }, + "fields": { "href": "http://good-company.some.ssl.host/DefaultCollection/_apis/wit/fields" } + }, + "url": "http://good-company.some.ssl.host/DefaultCollection/_apis/wit/recyclebin/5" + }, + "resourceVersion": "1.0", + "resourceContainers": { + "collection": { "id": "c12d0eb8-e382-443b-9f9c-c52cba5014c2" }, + "account": { "id": "f844ec47-a9db-4511-8281-8b63f4eaf94e" }, + "project": { "id": "be9b3917-87e6-42a4-a549-2bc06a7a878f" } + }, + "createdDate": "2016-05-02T19:17:28.3644564Z" +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Messages/workitem.restored.json b/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Messages/workitem.restored.json new file mode 100644 index 0000000..de83dc1 --- /dev/null +++ b/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Messages/workitem.restored.json @@ -0,0 +1,53 @@ +{ + "subscriptionId": "00000000-0000-0000-0000-000000000000", + "notificationId": 7, + "id": "1ca023d6-6cff-49dd-b3d1-302b69311810", + "eventType": "workitem.restored", + "publisherId": "tfs", + "message": { + "text": "Bug #5 (Some great new idea!) restored by Jamal Hartnett.\r\n(http://good-company.some.ssl.host/web/wi.aspx?id=74e918bf-3376-436d-bd20-8e8c1287f465&id=5)", + "html": "Bug #5 (Some great new idea!) restored by Jamal Hartnett.", + "markdown": "[Bug #5](http://good-company.some.ssl.host/web/wi.aspx?id=74e918bf-3376-436d-bd20-8e8c1287f465&id=5) (Some great new idea!) restored by Jamal Hartnett." + }, + "detailedMessage": { + "text": "Bug #5 (Some great new idea!) restored by Jamal Hartnett.\r\n(http://good-company.some.ssl.host/web/wi.aspx?id=74e918bf-3376-436d-bd20-8e8c1287f465&id=5)\r\n\r\n- State: New\r\n- Severity: 3 - Medium\r\n", + "html": "Bug #5 (Some great new idea!) restored by Jamal Hartnett.
    \r\n
  • State: New
  • Severity: 3 - Medium
", + "markdown": "[Bug #5](http://good-company.some.ssl.host/web/wi.aspx?id=74e918bf-3376-436d-bd20-8e8c1287f465&id=5) (Some great new idea!) restored by Jamal Hartnett.\r\n\r\n* State: New\r\n* Severity: 3 - Medium\r\n" + }, + "resource": { + "id": 5, + "rev": 1, + "fields": { + "System.AreaPath": "GoodCompanyCloud", + "System.TeamProject": "GoodCompanyCloud", + "System.IterationPath": "GoodCompanyCloud\\Release 1\\Sprint 1", + "System.WorkItemType": "Bug", + "System.State": "New", + "System.Reason": "New defect reported", + "System.CreatedDate": "2014-07-15T17:42:44.663Z", + "System.CreatedBy": "Jamal Hartnett", + "System.ChangedDate": "2014-07-15T17:42:44.663Z", + "System.ChangedBy": "Jamal Hartnett", + "System.Title": "Some great new idea!", + "Microsoft.VSTS.Common.Severity": "3 - Medium", + "WEF_EB329F44FE5F4A94ACB1DA153FDF38BA_Kanban.Column": "New" + }, + "_links": { + "self": { "href": "http://good-company.some.ssl.host/DefaultCollection/_apis/wit/workItems/5" }, + "workItemUpdates": { "href": "http://good-company.some.ssl.host/DefaultCollection/_apis/wit/workItems/5/updates" }, + "workItemRevisions": { "href": "http://good-company.some.ssl.host/DefaultCollection/_apis/wit/workItems/5/revisions" }, + "workItemType": { "href": "http://good-company.some.ssl.host/DefaultCollection/_apis/wit/ea830882-2a3c-4095-a53f-972f9a376f6e/workItemTypes/Bug" }, + "fields": { "href": "http://good-company.some.ssl.host/DefaultCollection/_apis/wit/fields" }, + "html": { "href": "https://good-company.some.ssl.host/web/wi.aspx?id=d81542e4-cdfa-4333-b082-1ae2d6c3ad16&id=5" }, + "workItemHistory": { "href": "https://good-company.some.ssl.host/DefaultCollection/_apis/wit/workItems/5/history" } + }, + "url": "http://good-company.some.ssl.host/DefaultCollection/_apis/wit/workItems/5" + }, + "resourceVersion": "1.0", + "resourceContainers": { + "collection": { "id": "c12d0eb8-e382-443b-9f9c-c52cba5014c2" }, + "account": { "id": "f844ec47-a9db-4511-8281-8b63f4eaf94e" }, + "project": { "id": "be9b3917-87e6-42a4-a549-2bc06a7a878f" } + }, + "createdDate": "2016-05-02T19:18:15.5707279Z" +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Messages/workitem.updated.json b/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Messages/workitem.updated.json new file mode 100644 index 0000000..ec32878 --- /dev/null +++ b/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Messages/workitem.updated.json @@ -0,0 +1,92 @@ +{ + "subscriptionId": "00000000-0000-0000-0000-000000000000", + "notificationId": 8, + "id": "27646e0e-b520-4d2b-9411-bba7524947cd", + "eventType": "workitem.updated", + "publisherId": "tfs", + "message": { + "text": "Bug #5 (Some great new idea!) updated by Jamal Hartnett.\r\n(http://good-company.some.ssl.host/web/wi.aspx?id=74e918bf-3376-436d-bd20-8e8c1287f465&id=5)", + "html": "Bug #5 (Some great new idea!) updated by Jamal Hartnett.", + "markdown": "[Bug #5](http://good-company.some.ssl.host/web/wi.aspx?id=74e918bf-3376-436d-bd20-8e8c1287f465&id=5) (Some great new idea!) updated by Jamal Hartnett." + }, + "detailedMessage": { + "text": "Bug #5 (Some great new idea!) updated by Jamal Hartnett.\r\n(http://good-company.some.ssl.host/web/wi.aspx?id=74e918bf-3376-436d-bd20-8e8c1287f465&id=5)\r\n\r\n- New State: Approved\r\n", + "html": "Bug #5 (Some great new idea!) updated by Jamal Hartnett.
    \r\n
  • New State: Approved
", + "markdown": "[Bug #5](http://good-company.some.ssl.host/web/wi.aspx?id=74e918bf-3376-436d-bd20-8e8c1287f465&id=5) (Some great new idea!) updated by Jamal Hartnett.\r\n\r\n* New State: Approved\r\n" + }, + "resource": { + "id": 2, + "workItemId": 0, + "rev": 2, + "revisedBy": null, + "revisedDate": "0001-01-01T00:00:00", + "fields": { + "System.Rev": { + "oldValue": "1", + "newValue": "2" + }, + "System.AuthorizedDate": { + "oldValue": "2014-07-15T16:48:44.663Z", + "newValue": "2014-07-15T17:42:44.663Z" + }, + "System.RevisedDate": { + "oldValue": "2014-07-15T17:42:44.663Z", + "newValue": "9999-01-01T00:00:00Z" + }, + "System.State": { + "oldValue": "New", + "newValue": "Approved" + }, + "System.Reason": { + "oldValue": "New defect reported", + "newValue": "Approved by the Product Owner" + }, + "System.AssignedTo": { "newValue": "Jamal Hartnet" }, + "System.ChangedDate": { + "oldValue": "2014-07-15T16:48:44.663Z", + "newValue": "2014-07-15T17:42:44.663Z" + }, + "System.Watermark": { + "oldValue": "2", + "newValue": "5" + }, + "Microsoft.VSTS.Common.Severity": { + "oldValue": "3 - Medium", + "newValue": "2 - High" + } + }, + "_links": { + "self": { "href": "http://good-company.some.ssl.host/DefaultCollection/_apis/wit/workItems/5/updates/2" }, + "parent": { "href": "http://good-company.some.ssl.host/DefaultCollection/_apis/wit/workItems/5" }, + "workItemUpdates": { "href": "http://good-company.some.ssl.host/DefaultCollection/_apis/wit/workItems/5/updates" } + }, + "url": "http://good-company.some.ssl.host/DefaultCollection/_apis/wit/workItems/5/updates/2", + "revision": { + "id": 5, + "rev": 2, + "fields": { + "System.AreaPath": "GoodCompanyCloud", + "System.TeamProject": "GoodCompanyCloud", + "System.IterationPath": "GoodCompanyCloud\\Release 1\\Sprint 1", + "System.WorkItemType": "Bug", + "System.State": "New", + "System.Reason": "New defect reported", + "System.CreatedDate": "2014-07-15T16:48:44.663Z", + "System.CreatedBy": "Jamal Hartnett", + "System.ChangedDate": "2014-07-15T16:48:44.663Z", + "System.ChangedBy": "Jamal Hartnett", + "System.Title": "Some great new idea!", + "Microsoft.VSTS.Common.Severity": "3 - Medium", + "WEF_EB329F44FE5F4A94ACB1DA153FDF38BA_Kanban.Column": "New" + }, + "url": "http://good-company.some.ssl.host/DefaultCollection/_apis/wit/workItems/5/revisions/2" + } + }, + "resourceVersion": "1.0", + "resourceContainers": { + "collection": { "id": "c12d0eb8-e382-443b-9f9c-c52cba5014c2" }, + "account": { "id": "f844ec47-a9db-4511-8281-8b63f4eaf94e" }, + "project": { "id": "be9b3917-87e6-42a4-a549-2bc06a7a878f" } + }, + "createdDate": "2016-05-02T19:19:12.8836446Z" +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test.csproj b/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test.csproj new file mode 100644 index 0000000..d89c726 --- /dev/null +++ b/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test.csproj @@ -0,0 +1,139 @@ + + + + + + + {B27CC8B5-BF38-434B-B5CE-42ACEC40624C} + Library + Properties + Microsoft.AspNet.WebHooks + Microsoft.AspNet.WebHooks.Receivers.VSTS.Test + ..\..\bin\$(Configuration)\Test\ + $(CodeAnalysis) + /assemblyCompareMode:StrongNameIgnoringVersion + ..\..\FxCopTest.ruleset + $(DefineConstants);ASPNETWEBHOOKS + + + + + + ..\..\packages\Moq.4.2.1502.0911\lib\net40\Moq.dll + True + + + ..\..\packages\Newtonsoft.Json.6.0.4\lib\net45\Newtonsoft.Json.dll + True + + + + + + ..\..\packages\Microsoft.AspNet.WebApi.Client.5.2.2\lib\net45\System.Net.Http.Formatting.dll + True + + + ..\..\packages\Microsoft.AspNet.WebApi.Core.5.2.2\lib\net45\System.Web.Http.dll + True + + + + + + + + ..\..\packages\xunit.abstractions.2.0.0\lib\net35\xunit.abstractions.dll + True + + + ..\..\packages\xunit.assert.2.0.0\lib\portable-net45+win+wpa81+wp80+monotouch+monoandroid+Xamarin.iOS\xunit.assert.dll + True + + + ..\..\packages\xunit.extensibility.core.2.0.0\lib\portable-net45+win+wpa81+wp80+monotouch+monoandroid+Xamarin.iOS\xunit.core.dll + True + + + + + Common\EmbeddedResource.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + Designer + + + + + {f7dd0935-6320-4efc-9464-d5a2d2b8c2f7} + Microsoft.AspNet.WebHooks.Common + + + {6b501d55-2aa3-4757-a185-1277bb881de8} + Microsoft.AspNet.WebHooks.Receivers.VSTS + + + {8ced31fb-32f2-4ffb-9997-452fb9728577} + Microsoft.AspNet.WebHooks.Receivers + + + {cb965ed1-1e07-4374-a829-c636db09f567} + Microsoft.AspNet.WebHooks.Receivers.Test + + + {608b1d09-e4de-4dba-8dbf-7758003988f0} + Microsoft.TestUtilities + + + + + CustomDictionary.xml + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + + + \ No newline at end of file diff --git a/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Payloads/BuildCompletedPayloadTests.cs b/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Payloads/BuildCompletedPayloadTests.cs new file mode 100644 index 0000000..da5ee37 --- /dev/null +++ b/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Payloads/BuildCompletedPayloadTests.cs @@ -0,0 +1,124 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNet.WebHooks.Payloads; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Xunit; + +namespace Microsoft.AspNet.WebHooks +{ + public class BuildCompletedPayloadTests + { + [Fact] + public void BuildCompletedPayload_Roundtrips() + { + // Arrange + JObject data = EmbeddedResource.ReadAsJObject("Microsoft.AspNet.WebHooks.Messages.build.complete.json"); + + var expected = new BuildCompletedPayload + { + SubscriptionId = "00000000-0000-0000-0000-000000000000", + NotificationId = 1, + Id = "4a5d99d6-1c75-4e53-91b9-ee80057d4ce3", + EventType = "build.complete", + PublisherId = "tfs", + Message = new PayloadMessage + { + Text = "Build ConsumerAddressModule_20150407.2 succeeded", + Html = "Build ConsumerAddressModule_20150407.2 succeeded", + Markdown = "Build [ConsumerAddressModule_20150407.2](https://good-company.some.ssl.host/web/build.aspx?id=5023c10b-bef3-41c3-bf53-686c4e34ee9e&builduri=vstfs%3a%2f%2f%2fBuild%2fBuild%2f3) succeeded" + }, + DetailedMessage = new PayloadMessage + { + Text = "Build ConsumerAddressModule_20150407.2 succeeded", + Html = "Build ConsumerAddressModule_20150407.2 succeeded", + Markdown = "Build [ConsumerAddressModule_20150407.2](https://good-company.some.ssl.host/web/build.aspx?id=5023c10b-bef3-41c3-bf53-686c4e34ee9e&builduri=vstfs%3a%2f%2f%2fBuild%2fBuild%2f3) succeeded" + }, + + Resource = new BuildCompletedResource + { + Uri = new Uri("vstfs:///Build/Build/2"), + Id = 2, + BuildNumber = "ConsumerAddressModule_20150407.1", + Url = new Uri("https://good-company.some.ssl.host/DefaultCollection/71777fbc-1cf2-4bd1-9540-128c1c71f766/_apis/build/Builds/2"), + StartTime = "2015-04-07T18:04:06.83Z".ToDateTime(), + FinishTime = "2015-04-07T18:06:10.69Z".ToDateTime(), + Reason = "manual", + Status = "succeeded", + DropLocation = "#/3/drop", + Drop = new BuildCompletedDrop + { + Location = "#/3/drop", + DropType = "container", + Url = new Uri("https://good-company.some.ssl.host/DefaultCollection/_apis/resources/Containers/3/drop"), + DownloadUrl = new Uri("https://good-company.some.ssl.host/DefaultCollection/_apis/resources/Containers/3/drop?api-version=1.0&$format=zip&downloadFileName=ConsumerAddressModule_20150407.1_drop") + }, + Log = new BuildCompletedLog + { + LogType = "container", + Url = new Uri("https://good-company.some.ssl.host/DefaultCollection/_apis/resources/Containers/3/logs"), + DownloadUrl = new Uri("https://good-company.some.ssl.host/_apis/resources/Containers/3/logs?api-version=1.0&$format=zip&downloadFileName=ConsumerAddressModule_20150407.1_logs") + }, + SourceGetVersion = "LG:refs/heads/master:600c52d2d5b655caa111abfd863e5a9bd304bb0e", + LastChangedBy = new ResourceUser + { + Id = "d6245f20-2af8-44f4-9451-8107cb2767db", + DisplayName = "John Smith", + UniqueName = "fabrikamfiber16@hotmail.com", + Url = new Uri("https://good-company.some.ssl.host/_apis/Identities/d6245f20-2af8-44f4-9451-8107cb2767db"), + ImageUrl = new Uri("https://good-company.some.ssl.host/DefaultCollection/_api/_common/identityImage?id=d6245f20-2af8-44f4-9451-8107cb2767db") + }, + RetainIndefinitely = false, + HasDiagnostics = true, + Definition = new BuildCompletedDefinition + { + BatchSize = 1, + TriggerType = "none", + DefinitionType = "xaml", + Id = 2, + Name = "ConsumerAddressModule", + Url = new Uri("https://good-company.some.ssl.host/DefaultCollection/71777fbc-1cf2-4bd1-9540-128c1c71f766/_apis/build/Definitions/2") + }, + Queue = new BuildCompletedQueueDefinition + { + QueueType = "buildController", + Id = 4, + Name = "Hosted Build Controller", + Url = new Uri("https://good-company.some.ssl.host/DefaultCollection/_apis/build/Queues/4") + } + }, + ResourceVersion = "1.0", + ResourceContainers = new PayloadResourceContainers + { + Collection = new PayloadResourceContainer { Id = "c12d0eb8-e382-443b-9f9c-c52cba5014c2" }, + Account = new PayloadResourceContainer { Id = "f844ec47-a9db-4511-8281-8b63f4eaf94e" }, + Project = new PayloadResourceContainer { Id = "be9b3917-87e6-42a4-a549-2bc06a7a878f" } + }, + CreatedDate = "2016-05-02T19:00:39.5893296Z".ToDateTime() + }; + expected.Resource.Requests.Add(new BuildCompletedRequest + { + Id = 1, + Url = new Uri("https://good-company.some.ssl.host/DefaultCollection/71777fbc-1cf2-4bd1-9540-128c1c71f766/_apis/build/Requests/1"), + RequestedFor = new ResourceUser + { + Id = "d6245f20-2af8-44f4-9451-8107cb2767db", + DisplayName = "John Smith", + UniqueName = "fabrikamfiber16@hotmail.com", + Url = new Uri("https://good-company.some.ssl.host/_apis/Identities/d6245f20-2af8-44f4-9451-8107cb2767db"), + ImageUrl = new Uri("https://good-company.some.ssl.host/DefaultCollection/_api/_common/identityImage?id=d6245f20-2af8-44f4-9451-8107cb2767db") + } + }); + + // Act + var actual = data.ToObject(); + + // Assert + string expectedJson = JsonConvert.SerializeObject(expected); + string actualJson = JsonConvert.SerializeObject(actual); + Assert.Equal(expectedJson, actualJson); + } + } +} diff --git a/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Payloads/CodeCheckedInPayloadTests.cs b/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Payloads/CodeCheckedInPayloadTests.cs new file mode 100644 index 0000000..6350a96 --- /dev/null +++ b/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Payloads/CodeCheckedInPayloadTests.cs @@ -0,0 +1,76 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNet.WebHooks.Payloads; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Xunit; + +namespace Microsoft.AspNet.WebHooks +{ + public class CodeCheckedInPayloadTests + { + [Fact] + public void CodeCheckedInPayload_Roundtrips() + { + // Arrange + JObject data = EmbeddedResource.ReadAsJObject("Microsoft.AspNet.WebHooks.Messages.tfvc.checkin.json"); + var expected = new CodeCheckedInPayload + { + SubscriptionId = "00000000-0000-0000-0000-000000000000", + NotificationId = 2, + Id = "f9b4c23e-88dd-4516-b04d-849787304e32", + EventType = "tfvc.checkin", + PublisherId = "tfs", + Message = new PayloadMessage + { + Text = "John Smith checked in changeset 18: Dropping in new Java sample", + Html = "John Smith checked in changeset 18: Dropping in new Java sample", + Markdown = "John Smith checked in changeset [18](https://good-company.some.ssl.host/web/cs.aspx?id=d81542e4-cdfa-4333-b082-1ae2d6c3ad16&cs=18): Dropping in new Java sample" + }, + DetailedMessage = new PayloadMessage + { + Text = "John Smith checked in changeset 18: Dropping in new Java sample", + Html = "John Smith checked in changeset 18: Dropping in new Java sample", + Markdown = "John Smith checked in changeset [18](https://good-company.some.ssl.host/web/cs.aspx?id=d81542e4-cdfa-4333-b082-1ae2d6c3ad16&cs=18): Dropping in new Java sample" + }, + Resource = new CodeCheckedInResource + { + ChangesetId = 18, + Url = new Uri("https://good-company.some.ssl.host/DefaultCollection/_apis/tfvc/changesets/18"), + Author = new ResourceUser + { + Id = "d6245f20-2af8-44f4-9451-8107cb2767db", + DisplayName = "John Smith", + UniqueName = "fabrikamfiber16@hotmail.com" + }, + CheckedInBy = new ResourceUser + { + Id = "d6245f20-2af8-44f4-9451-8107cb2767db", + DisplayName = "John Smith", + UniqueName = "fabrikamfiber16@hotmail.com" + }, + CreatedDate = "2014-05-12T22:41:16Z".ToDateTime(), + Comment = "Dropping in new Java sample" + }, + ResourceVersion = "1.0", + ResourceContainers = new PayloadResourceContainers + { + Collection = new PayloadResourceContainer { Id = "c12d0eb8-e382-443b-9f9c-c52cba5014c2" }, + Account = new PayloadResourceContainer { Id = "f844ec47-a9db-4511-8281-8b63f4eaf94e" }, + Project = new PayloadResourceContainer { Id = "be9b3917-87e6-42a4-a549-2bc06a7a878f" } + }, + CreatedDate = "2016-05-02T19:01:11.7056821Z".ToDateTime() + }; + + // Act + var actual = data.ToObject(); + + // Assert + string expectedJson = JsonConvert.SerializeObject(expected); + string actualJson = JsonConvert.SerializeObject(actual); + Assert.Equal(expectedJson, actualJson); + } + } +} diff --git a/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Payloads/TeamRoomMessagePostedPayloadTests.cs b/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Payloads/TeamRoomMessagePostedPayloadTests.cs new file mode 100644 index 0000000..72973e9 --- /dev/null +++ b/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Payloads/TeamRoomMessagePostedPayloadTests.cs @@ -0,0 +1,70 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNet.WebHooks.Payloads; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Xunit; + +namespace Microsoft.AspNet.WebHooks +{ + public class TeamRoomMessagePostedPayloadTests + { + [Fact] + public void TeamRoomMessagePostedPayload_Roundtrips() + { + // Arrange + JObject data = EmbeddedResource.ReadAsJObject("Microsoft.AspNet.WebHooks.Messages.message.posted.json"); + var expected = new TeamRoomMessagePostedPayload + { + SubscriptionId = "00000000-0000-0000-0000-000000000000", + NotificationId = 3, + Id = "daae438c-296b-4512-b08e-571910874e9b", + EventType = "message.posted", + PublisherId = "tfs", + Message = new PayloadMessage + { + Text = "Jamal Hartnett posted a message to Northward-Fiber-Git Team Room\r\nHello", + Html = "Jamal Hartnett posted a message to Northward-Fiber-Git Team Room\r\nHello", + Markdown = "Jamal Hartnett posted a message to Northward-Fiber-Git Team Room\r\nHello" + }, + DetailedMessage = new PayloadMessage + { + Text = "Jamal Hartnett posted a message to Northward-Fiber-Git Team Room\r\nHello", + Html = "Jamal Hartnett posted a message to Northward-Fiber-Git Team Room

Hello

", + Markdown = "Jamal Hartnett posted a message to Northward-Fiber-Git Team Room\r\nHello" + }, + Resource = new TeamRoomMessagePostedResource + { + Id = 0, + Content = "Hello", + MessageType = "normal", + PostedTime = "2014-05-02T19:17:13.3309587Z".ToDateTime(), + PostedRoomId = 1, + PostedBy = new ResourceUser + { + Id = "00067FFED5C7AF52@Live.com", + DisplayName = "Jamal Hartnett", + UniqueName = "Windows Live ID\\fabrikamfiber4@hotmail.com" + } + }, + ResourceVersion = "1.0", + ResourceContainers = new PayloadResourceContainers + { + Collection = new PayloadResourceContainer { Id = "c12d0eb8-e382-443b-9f9c-c52cba5014c2" }, + Account = new PayloadResourceContainer { Id = "f844ec47-a9db-4511-8281-8b63f4eaf94e" }, + Project = new PayloadResourceContainer { Id = "be9b3917-87e6-42a4-a549-2bc06a7a878f" } + }, + CreatedDate = "2016-05-02T19:13:40.8417653Z".ToDateTime() + }; + + // Act + var actual = data.ToObject(); + + // Assert + string expectedJson = JsonConvert.SerializeObject(expected); + string actualJson = JsonConvert.SerializeObject(actual); + Assert.Equal(expectedJson, actualJson); + } + } +} diff --git a/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Payloads/WorkItemCommentedOnPayloadTests.cs b/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Payloads/WorkItemCommentedOnPayloadTests.cs new file mode 100644 index 0000000..a7ec1fe --- /dev/null +++ b/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Payloads/WorkItemCommentedOnPayloadTests.cs @@ -0,0 +1,88 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNet.WebHooks.Payloads; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Xunit; + +namespace Microsoft.AspNet.WebHooks +{ + public class WorkItemCommentedOnPayloadTests + { + [Fact] + public void WorkItemCommentedOnPayload_Roundtrips() + { + // Arrange + JObject data = EmbeddedResource.ReadAsJObject("Microsoft.AspNet.WebHooks.Messages.workitem.commented.json"); + var expected = new WorkItemCommentedOnPayload + { + SubscriptionId = "00000000-0000-0000-0000-000000000000", + NotificationId = 4, + Id = "fb2617ed-60df-4518-81fa-749faa6c5cd6", + EventType = "workitem.commented", + PublisherId = "tfs", + Message = new PayloadMessage + { + Text = "Bug #5 (Some great new idea!) commented on by Jamal Hartnett.\r\n(http://good-company.some.ssl.host/web/wi.aspx?id=74e918bf-3376-436d-bd20-8e8c1287f465&id=5)", + Html = "Bug #5 (Some great new idea!) commented on by Jamal Hartnett.", + Markdown = "[Bug #5](http://good-company.some.ssl.host/web/wi.aspx?id=74e918bf-3376-436d-bd20-8e8c1287f465&id=5) (Some great new idea!) commented on by Jamal Hartnett." + }, + DetailedMessage = new PayloadMessage + { + Text = "Bug #5 (Some great new idea!) commented on by Jamal Hartnett.\r\n(http://good-company.some.ssl.host/web/wi.aspx?id=74e918bf-3376-436d-bd20-8e8c1287f465&id=5)\r\nThis is a great new idea", + Html = "Bug #5 (Some great new idea!) commented on by Jamal Hartnett.
This is a great new idea", + Markdown = "[Bug #5](http://good-company.some.ssl.host/web/wi.aspx?id=74e918bf-3376-436d-bd20-8e8c1287f465&id=5) (Some great new idea!) commented on by Jamal Hartnett.\r\nThis is a great new idea" + }, + Resource = new WorkItemCommentedOnResource + { + Id = 5, + RevisionNumber = 4, + Fields = new WorkItemFields + { + SystemAreaPath = "GoodCompanyCloud", + SystemTeamProject = "GoodCompanyCloud", + SystemIterationPath = "GoodCompanyCloud\\Release 1\\Sprint 1", + SystemWorkItemType = "Bug", + SystemState = "New", + SystemReason = "New defect reported", + SystemCreatedDate = "2014-07-15T17:42:44.663Z".ToDateTime(), + SystemCreatedBy = "Jamal Hartnett", + SystemChangedDate = "2014-07-15T17:42:44.663Z".ToDateTime(), + SystemChangedBy = "Jamal Hartnett", + SystemTitle = "Some great new idea!", + MicrosoftCommonSeverity = "3 - Medium", + KanbanColumn = "New", + SystemHistory = "This is a great new idea" + }, + Links = new WorkItemLinks + { + Self = new WorkItemLink { Href = "http://good-company.some.ssl.host/DefaultCollection/_apis/wit/workItems/5" }, + WorkItemUpdates = new WorkItemLink { Href = "http://good-company.some.ssl.host/DefaultCollection/_apis/wit/workItems/5/updates" }, + WorkItemRevisions = new WorkItemLink { Href = "http://good-company.some.ssl.host/DefaultCollection/_apis/wit/workItems/5/revisions" }, + WorkItemType = new WorkItemLink { Href = "http://good-company.some.ssl.host/DefaultCollection/_apis/wit/ea830882-2a3c-4095-a53f-972f9a376f6e/workItemTypes/Bug" }, + Fields = new WorkItemLink { Href = "http://good-company.some.ssl.host/DefaultCollection/_apis/wit/fields" } + }, + Url = new Uri("http://good-company.some.ssl.host/DefaultCollection/_apis/wit/workItems/5") + }, + ResourceVersion = "1.0", + ResourceContainers = new PayloadResourceContainers + { + Collection = new PayloadResourceContainer { Id = "c12d0eb8-e382-443b-9f9c-c52cba5014c2" }, + Account = new PayloadResourceContainer { Id = "f844ec47-a9db-4511-8281-8b63f4eaf94e" }, + Project = new PayloadResourceContainer { Id = "be9b3917-87e6-42a4-a549-2bc06a7a878f" } + }, + CreatedDate = "2016-05-02T19:15:37.4638247Z".ToDateTime() + }; + + // Act + var actual = data.ToObject(); + + // Assert + string expectedJson = JsonConvert.SerializeObject(expected); + string actualJson = JsonConvert.SerializeObject(actual); + Assert.Equal(expectedJson, actualJson); + } + } +} diff --git a/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Payloads/WorkItemCreatedPayloadTests.cs b/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Payloads/WorkItemCreatedPayloadTests.cs new file mode 100644 index 0000000..8036ea8 --- /dev/null +++ b/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Payloads/WorkItemCreatedPayloadTests.cs @@ -0,0 +1,87 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNet.WebHooks.Payloads; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Xunit; + +namespace Microsoft.AspNet.WebHooks +{ + public class WorkItemCreatedPayloadTests + { + [Fact] + public void WorkItemCreatedPayload_Roundtrips() + { + // Arrange + JObject data = EmbeddedResource.ReadAsJObject("Microsoft.AspNet.WebHooks.Messages.workitem.created.json"); + var expected = new WorkItemCreatedPayload + { + SubscriptionId = "00000000-0000-0000-0000-000000000000", + NotificationId = 5, + Id = "d2d46fb1-dba5-403c-9373-427583f19e8c", + EventType = "workitem.created", + PublisherId = "tfs", + Message = new PayloadMessage + { + Text = "Bug #5 (Some great new idea!) created by Jamal Hartnett.\r\n(http://good-company.some.ssl.host/web/wi.aspx?id=74e918bf-3376-436d-bd20-8e8c1287f465&id=5)", + Html = "Bug #5 (Some great new idea!) created by Jamal Hartnett.", + Markdown = "[Bug #5](http://good-company.some.ssl.host/web/wi.aspx?id=74e918bf-3376-436d-bd20-8e8c1287f465&id=5) (Some great new idea!) created by Jamal Hartnett." + }, + DetailedMessage = new PayloadMessage + { + Text = "Bug #5 (Some great new idea!) created by Jamal Hartnett.\r\n(http://good-company.some.ssl.host/web/wi.aspx?id=74e918bf-3376-436d-bd20-8e8c1287f465&id=5)\r\n\r\n- State: New\r\n- Assigned to: \r\n- Comment: \r\n- Severity: 3 - Medium\r\n", + Html = "Bug #5 (Some great new idea!) created by Jamal Hartnett.
    \r\n
  • State: New
  • \r\n
  • Assigned to:
  • \r\n
  • Comment:
  • \r\n
  • Severity: 3 - Medium
", + Markdown = "[Bug #5](http://good-company.some.ssl.host/web/wi.aspx?id=74e918bf-3376-436d-bd20-8e8c1287f465&id=5) (Some great new idea!) created by Jamal Hartnett.\r\n\r\n* State: New\r\n* Assigned to: \r\n* Comment: \r\n* Severity: 3 - Medium\r\n" + }, + Resource = new WorkItemCreatedResource + { + Id = 5, + RevisionNumber = 1, + Fields = new WorkItemFields + { + SystemAreaPath = "GoodCompanyCloud", + SystemTeamProject = "GoodCompanyCloud", + SystemIterationPath = "GoodCompanyCloud\\Release 1\\Sprint 1", + SystemWorkItemType = "Bug", + SystemState = "New", + SystemReason = "New defect reported", + SystemCreatedDate = "2014-07-15T17:42:44.663Z".ToDateTime(), + SystemCreatedBy = "Jamal Hartnett", + SystemChangedDate = "2014-07-15T17:42:44.663Z".ToDateTime(), + SystemChangedBy = "Jamal Hartnett", + SystemTitle = "Some great new idea!", + MicrosoftCommonSeverity = "3 - Medium", + KanbanColumn = "New" + }, + Links = new WorkItemLinks + { + Self = new WorkItemLink { Href = "http://good-company.some.ssl.host/DefaultCollection/_apis/wit/workItems/5" }, + WorkItemUpdates = new WorkItemLink { Href = "http://good-company.some.ssl.host/DefaultCollection/_apis/wit/workItems/5/updates" }, + WorkItemRevisions = new WorkItemLink { Href = "http://good-company.some.ssl.host/DefaultCollection/_apis/wit/workItems/5/revisions" }, + WorkItemType = new WorkItemLink { Href = "http://good-company.some.ssl.host/DefaultCollection/_apis/wit/ea830882-2a3c-4095-a53f-972f9a376f6e/workItemTypes/Bug" }, + Fields = new WorkItemLink { Href = "http://good-company.some.ssl.host/DefaultCollection/_apis/wit/fields" } + }, + Url = new Uri("http://good-company.some.ssl.host/DefaultCollection/_apis/wit/workItems/5") + }, + ResourceVersion = "1.0", + ResourceContainers = new PayloadResourceContainers + { + Collection = new PayloadResourceContainer { Id = "c12d0eb8-e382-443b-9f9c-c52cba5014c2" }, + Account = new PayloadResourceContainer { Id = "f844ec47-a9db-4511-8281-8b63f4eaf94e" }, + Project = new PayloadResourceContainer { Id = "be9b3917-87e6-42a4-a549-2bc06a7a878f" } + }, + CreatedDate = "2016-05-02T19:16:25.6251162Z".ToDateTime() + }; + + // Act + var actual = data.ToObject(); + + // Assert + string expectedJson = JsonConvert.SerializeObject(expected); + string actualJson = JsonConvert.SerializeObject(actual); + Assert.Equal(expectedJson, actualJson); + } + } +} diff --git a/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Payloads/WorkItemDeletedPayloadTests.cs b/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Payloads/WorkItemDeletedPayloadTests.cs new file mode 100644 index 0000000..27bf1be --- /dev/null +++ b/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Payloads/WorkItemDeletedPayloadTests.cs @@ -0,0 +1,85 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNet.WebHooks.Payloads; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Xunit; + +namespace Microsoft.AspNet.WebHooks +{ + public class WorkItemDeletedPayloadTests + { + [Fact] + public void WorkItemDeletedPayload_Roundtrips() + { + // Arrange + JObject data = EmbeddedResource.ReadAsJObject("Microsoft.AspNet.WebHooks.Messages.workitem.deleted.json"); + var expected = new WorkItemDeletedPayload + { + SubscriptionId = "00000000-0000-0000-0000-000000000000", + NotificationId = 6, + Id = "72da0ade-0709-40ee-beb7-104287bf7e84", + EventType = "workitem.deleted", + PublisherId = "tfs", + Message = new PayloadMessage + { + Text = "Bug #5 (Some great new idea!) deleted by Jamal Hartnett.", + Html = "Bug #5 (Some great new idea!) deleted by Jamal Hartnett.", + Markdown = "[Bug #5] (Some great new idea!) deleted by Jamal Hartnett." + }, + DetailedMessage = new PayloadMessage + { + Text = "Bug #5 (Some great new idea!) deleted by Jamal Hartnett.\r\n\r\n- State: New\r\n", + Html = "Bug #5 (Some great new idea!) deleted by Jamal Hartnett.
    \r\n
  • State: New
", + Markdown = "[Bug #5] (Some great new idea!) deleted by Jamal Hartnett.\r\n\r\n* State: New\r\n" + }, + Resource = new WorkItemDeletedResource + { + Id = 5, + RevisionNumber = 1, + Fields = new WorkItemFields + { + SystemAreaPath = "GoodCompanyCloud", + SystemTeamProject = "GoodCompanyCloud", + SystemIterationPath = "GoodCompanyCloud\\Release 1\\Sprint 1", + SystemWorkItemType = "Bug", + SystemState = "New", + SystemReason = "New defect reported", + SystemCreatedDate = "2014-07-15T17:42:44.663Z".ToDateTime(), + SystemCreatedBy = "Jamal Hartnett", + SystemChangedDate = "2014-07-15T17:42:44.663Z".ToDateTime(), + SystemChangedBy = "Jamal Hartnett", + SystemTitle = "Some great new idea!", + MicrosoftCommonSeverity = "3 - Medium", + KanbanColumn = "New" + }, + Links = new WorkItemLinks + { + Self = new WorkItemLink { Href = "http://good-company.some.ssl.host/DefaultCollection/_apis/wit/recyclebin/5" }, + WorkItemType = new WorkItemLink { Href = "http://good-company.some.ssl.host/DefaultCollection/_apis/wit/ea830882-2a3c-4095-a53f-972f9a376f6e/workItemTypes/Bug" }, + Fields = new WorkItemLink { Href = "http://good-company.some.ssl.host/DefaultCollection/_apis/wit/fields" } + }, + Url = new Uri("http://good-company.some.ssl.host/DefaultCollection/_apis/wit/recyclebin/5") + }, + ResourceVersion = "1.0", + ResourceContainers = new PayloadResourceContainers + { + Collection = new PayloadResourceContainer { Id = "c12d0eb8-e382-443b-9f9c-c52cba5014c2" }, + Account = new PayloadResourceContainer { Id = "f844ec47-a9db-4511-8281-8b63f4eaf94e" }, + Project = new PayloadResourceContainer { Id = "be9b3917-87e6-42a4-a549-2bc06a7a878f" } + }, + CreatedDate = "2016-05-02T19:17:28.3644564Z".ToDateTime() + }; + + // Act + var actual = data.ToObject(); + + // Assert + string expectedJson = JsonConvert.SerializeObject(expected); + string actualJson = JsonConvert.SerializeObject(actual); + Assert.Equal(expectedJson, actualJson); + } + } +} diff --git a/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Payloads/WorkItemRestoredPayloadTests.cs b/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Payloads/WorkItemRestoredPayloadTests.cs new file mode 100644 index 0000000..9b534be --- /dev/null +++ b/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Payloads/WorkItemRestoredPayloadTests.cs @@ -0,0 +1,89 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNet.WebHooks.Payloads; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Xunit; + +namespace Microsoft.AspNet.WebHooks +{ + public class WorkItemRestoredPayloadTests + { + [Fact] + public void WorkItemRestoredPayload_Roundtrips() + { + // Arrange + JObject data = EmbeddedResource.ReadAsJObject("Microsoft.AspNet.WebHooks.Messages.workitem.restored.json"); + var expected = new WorkItemRestoredPayload + { + SubscriptionId = "00000000-0000-0000-0000-000000000000", + NotificationId = 7, + Id = "1ca023d6-6cff-49dd-b3d1-302b69311810", + EventType = "workitem.restored", + PublisherId = "tfs", + Message = new PayloadMessage + { + Text = "Bug #5 (Some great new idea!) restored by Jamal Hartnett.\r\n(http://good-company.some.ssl.host/web/wi.aspx?id=74e918bf-3376-436d-bd20-8e8c1287f465&id=5)", + Html = "Bug #5 (Some great new idea!) restored by Jamal Hartnett.", + Markdown = "[Bug #5](http://good-company.some.ssl.host/web/wi.aspx?id=74e918bf-3376-436d-bd20-8e8c1287f465&id=5) (Some great new idea!) restored by Jamal Hartnett." + }, + DetailedMessage = new PayloadMessage + { + Text = "Bug #5 (Some great new idea!) restored by Jamal Hartnett.\r\n(http://good-company.some.ssl.host/web/wi.aspx?id=74e918bf-3376-436d-bd20-8e8c1287f465&id=5)\r\n\r\n- State: New\r\n- Severity: 3 - Medium\r\n", + Html = "Bug #5 (Some great new idea!) restored by Jamal Hartnett.
    \r\n
  • State: New
  • Severity: 3 - Medium
", + Markdown = "[Bug #5](http://good-company.some.ssl.host/web/wi.aspx?id=74e918bf-3376-436d-bd20-8e8c1287f465&id=5) (Some great new idea!) restored by Jamal Hartnett.\r\n\r\n* State: New\r\n* Severity: 3 - Medium\r\n" + }, + Resource = new WorkItemRestoredResource + { + Id = 5, + RevisionNumber = 1, + Fields = new WorkItemFields + { + SystemAreaPath = "GoodCompanyCloud", + SystemTeamProject = "GoodCompanyCloud", + SystemIterationPath = "GoodCompanyCloud\\Release 1\\Sprint 1", + SystemWorkItemType = "Bug", + SystemState = "New", + SystemReason = "New defect reported", + SystemCreatedDate = "2014-07-15T17:42:44.663Z".ToDateTime(), + SystemCreatedBy = "Jamal Hartnett", + SystemChangedDate = "2014-07-15T17:42:44.663Z".ToDateTime(), + SystemChangedBy = "Jamal Hartnett", + SystemTitle = "Some great new idea!", + MicrosoftCommonSeverity = "3 - Medium", + KanbanColumn = "New" + }, + Links = new WorkItemLinks + { + Self = new WorkItemLink { Href = "http://good-company.some.ssl.host/DefaultCollection/_apis/wit/workItems/5" }, + WorkItemUpdates = new WorkItemLink { Href = "http://good-company.some.ssl.host/DefaultCollection/_apis/wit/workItems/5/updates" }, + WorkItemRevisions = new WorkItemLink { Href = "http://good-company.some.ssl.host/DefaultCollection/_apis/wit/workItems/5/revisions" }, + WorkItemType = new WorkItemLink { Href = "http://good-company.some.ssl.host/DefaultCollection/_apis/wit/ea830882-2a3c-4095-a53f-972f9a376f6e/workItemTypes/Bug" }, + Fields = new WorkItemLink { Href = "http://good-company.some.ssl.host/DefaultCollection/_apis/wit/fields" }, + Html = new WorkItemLink { Href = "https://good-company.some.ssl.host/web/wi.aspx?id=d81542e4-cdfa-4333-b082-1ae2d6c3ad16&id=5" }, + WorkItemHistory = new WorkItemLink { Href = "https://good-company.some.ssl.host/DefaultCollection/_apis/wit/workItems/5/history" } + }, + Url = new Uri("http://good-company.some.ssl.host/DefaultCollection/_apis/wit/workItems/5") + }, + ResourceVersion = "1.0", + ResourceContainers = new PayloadResourceContainers + { + Collection = new PayloadResourceContainer { Id = "c12d0eb8-e382-443b-9f9c-c52cba5014c2" }, + Account = new PayloadResourceContainer { Id = "f844ec47-a9db-4511-8281-8b63f4eaf94e" }, + Project = new PayloadResourceContainer { Id = "be9b3917-87e6-42a4-a549-2bc06a7a878f" } + }, + CreatedDate = "2016-05-02T19:18:15.5707279Z".ToDateTime() + }; + + // Act + var actual = data.ToObject(); + + // Assert + string expectedJson = JsonConvert.SerializeObject(expected); + string actualJson = JsonConvert.SerializeObject(actual); + Assert.Equal(expectedJson, actualJson); + } + } +} diff --git a/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Payloads/WorkItemUpdatedPayloadTests.cs b/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Payloads/WorkItemUpdatedPayloadTests.cs new file mode 100644 index 0000000..7fc037a --- /dev/null +++ b/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Payloads/WorkItemUpdatedPayloadTests.cs @@ -0,0 +1,141 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNet.WebHooks.Payloads; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Xunit; + +namespace Microsoft.AspNet.WebHooks +{ + public class WorkItemUpdatedPayloadTests + { + [Fact] + public void WorkItemUpdatedPayload_Roundtrips() + { + // Arrange + JObject data = EmbeddedResource.ReadAsJObject("Microsoft.AspNet.WebHooks.Messages.workitem.updated.json"); + var expected = new WorkItemUpdatedPayload + { + SubscriptionId = "00000000-0000-0000-0000-000000000000", + NotificationId = 8, + Id = "27646e0e-b520-4d2b-9411-bba7524947cd", + EventType = "workitem.updated", + PublisherId = "tfs", + Message = new PayloadMessage + { + Text = "Bug #5 (Some great new idea!) updated by Jamal Hartnett.\r\n(http://good-company.some.ssl.host/web/wi.aspx?id=74e918bf-3376-436d-bd20-8e8c1287f465&id=5)", + Html = "Bug #5 (Some great new idea!) updated by Jamal Hartnett.", + Markdown = "[Bug #5](http://good-company.some.ssl.host/web/wi.aspx?id=74e918bf-3376-436d-bd20-8e8c1287f465&id=5) (Some great new idea!) updated by Jamal Hartnett." + }, + DetailedMessage = new PayloadMessage + { + Text = "Bug #5 (Some great new idea!) updated by Jamal Hartnett.\r\n(http://good-company.some.ssl.host/web/wi.aspx?id=74e918bf-3376-436d-bd20-8e8c1287f465&id=5)\r\n\r\n- New State: Approved\r\n", + Html = "Bug #5 (Some great new idea!) updated by Jamal Hartnett.
    \r\n
  • New State: Approved
", + Markdown = "[Bug #5](http://good-company.some.ssl.host/web/wi.aspx?id=74e918bf-3376-436d-bd20-8e8c1287f465&id=5) (Some great new idea!) updated by Jamal Hartnett.\r\n\r\n* New State: Approved\r\n" + }, + Resource = new WorkItemUpdatedResource + { + Id = 2, + WorkItemId = 0, + RevisionNumber = 2, + RevisedBy = null, + RevisedDate = "0001-01-01T00:00:00".ToDateTime(), + Fields = new WorkItemUpdatedFields + { + SystemRev = new WorkItemUpdatedFieldValue + { + OldValue = "1", + NewValue = "2" + }, + SystemAuthorizedDate = new WorkItemUpdatedFieldValue + { + OldValue = "2014-07-15T16:48:44.663Z".ToDateTime(), + NewValue = "2014-07-15T17:42:44.663Z".ToDateTime() + }, + SystemRevisedDate = new WorkItemUpdatedFieldValue + { + OldValue = "2014-07-15T17:42:44.663Z".ToDateTime(), + NewValue = "9999-01-01T00:00:00Z".ToDateTime() + }, + SystemState = new WorkItemUpdatedFieldValue + { + OldValue = "New", + NewValue = "Approved" + }, + SystemReason = new WorkItemUpdatedFieldValue + { + OldValue = "New defect reported", + NewValue = "Approved by the Product Owner" + }, + SystemAssignedTo = new WorkItemUpdatedFieldValue + { + NewValue = "Jamal Hartnet" + }, + SystemChangedDate = new WorkItemUpdatedFieldValue + { + OldValue = "2014-07-15T16:48:44.663Z".ToDateTime(), + NewValue = "2014-07-15T17:42:44.663Z".ToDateTime() + }, + SystemWatermark = new WorkItemUpdatedFieldValue + { + OldValue = "2", + NewValue = "5" + }, + MicrosoftCommonSeverity = new WorkItemUpdatedFieldValue + { + OldValue = "3 - Medium", + NewValue = "2 - High" + } + }, + Links = new WorkItemLinks + { + Self = new WorkItemLink { Href = "http://good-company.some.ssl.host/DefaultCollection/_apis/wit/workItems/5/updates/2" }, + Parent = new WorkItemLink { Href = "http://good-company.some.ssl.host/DefaultCollection/_apis/wit/workItems/5" }, + WorkItemUpdates = new WorkItemLink { Href = "http://good-company.some.ssl.host/DefaultCollection/_apis/wit/workItems/5/updates" } + }, + Url = new Uri("http://good-company.some.ssl.host/DefaultCollection/_apis/wit/workItems/5/updates/2"), + Revision = new WorkItemUpdatedRevision + { + Id = 5, + Rev = 2, + Fields = new WorkItemFields + { + SystemAreaPath = "GoodCompanyCloud", + SystemTeamProject = "GoodCompanyCloud", + SystemIterationPath = "GoodCompanyCloud\\Release 1\\Sprint 1", + SystemWorkItemType = "Bug", + SystemState = "New", + SystemReason = "New defect reported", + SystemCreatedDate = "2014-07-15T16:48:44.663Z".ToDateTime(), + SystemCreatedBy = "Jamal Hartnett", + SystemChangedDate = "2014-07-15T16:48:44.663Z".ToDateTime(), + SystemChangedBy = "Jamal Hartnett", + SystemTitle = "Some great new idea!", + MicrosoftCommonSeverity = "3 - Medium", + KanbanColumn = "New" + }, + Url = new Uri("http://good-company.some.ssl.host/DefaultCollection/_apis/wit/workItems/5/revisions/2") + } + }, + ResourceVersion = "1.0", + ResourceContainers = new PayloadResourceContainers + { + Collection = new PayloadResourceContainer { Id = "c12d0eb8-e382-443b-9f9c-c52cba5014c2" }, + Account = new PayloadResourceContainer { Id = "f844ec47-a9db-4511-8281-8b63f4eaf94e" }, + Project = new PayloadResourceContainer { Id = "be9b3917-87e6-42a4-a549-2bc06a7a878f" } + }, + CreatedDate = "2016-05-02T19:19:12.8836446Z".ToDateTime() + }; + + // Act + var actual = data.ToObject(); + + // Assert + string expectedJson = JsonConvert.SerializeObject(expected); + string actualJson = JsonConvert.SerializeObject(actual); + Assert.Equal(expectedJson, actualJson); + } + } +} diff --git a/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Properties/AssemblyInfo.cs b/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..f63ea89 --- /dev/null +++ b/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/Properties/AssemblyInfo.cs @@ -0,0 +1,8 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Runtime.InteropServices; + +[assembly: CLSCompliant(false)] +[assembly: ComVisible(false)] diff --git a/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/WebHooks/VstsWebHookReceiverTests.cs b/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/WebHooks/VstsWebHookReceiverTests.cs new file mode 100644 index 0000000..705eccf --- /dev/null +++ b/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/WebHooks/VstsWebHookReceiverTests.cs @@ -0,0 +1,203 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using System.Web.Http; +using Moq; +using Moq.Protected; +using Xunit; + +namespace Microsoft.AspNet.WebHooks +{ + public class VstsWebHookReceiverTests : WebHookReceiverTestsBase + { + private const string TestAction = "message.posted"; + private const string TestId = ""; + private const string TestSecret = "12345678901234567890123456789012"; + private const string TestAddress = "https://some.ssl.host?code=" + TestSecret; + private HttpRequestMessage _postRequest; + + public static TheoryData InvalidCodeQueries + { + get + { + return new TheoryData + { + string.Empty, + "=", + "==", + "invalid", + "code", + "code=", + "k1=v1;k2=v2", + }; + } + } + + [Fact] + public void ReceiverName_IsConsistent() + { + // Arrange + IWebHookReceiver rec = new VstsWebHookReceiver(); + string expected = "vsts"; + + // Act + string actual1 = rec.Name; + string actual2 = VstsWebHookReceiver.ReceiverName; + + // Assert + Assert.Equal(expected, actual1); + Assert.Equal(actual1, actual2); + } + + [Fact] + public async Task ReceiveAsync_Throws_IfPostIsNotUsingHttps() + { + // Arrange + Initialize(TestSecret); + _postRequest.RequestUri = new Uri("http://some.no.ssl.host"); + + // Act + HttpResponseException ex = await Assert.ThrowsAsync(() => ReceiverMock.Object.ReceiveAsync(TestId, RequestContext, _postRequest)); + + // Assert + HttpError error = await ex.Response.Content.ReadAsAsync(); + Assert.Equal("The WebHook receiver 'VstsWebHookReceiverProxy' requires HTTPS in order to be secure. Please register a WebHook URI of type 'https'.", error.Message); + ReceiverMock.Protected() + .Verify>("ExecuteWebHookAsync", Times.Never(), TestId, RequestContext, _postRequest, ItExpr.IsAny>(), ItExpr.IsAny()); + } + + [Theory] + [MemberData("InvalidCodeQueries")] + public async Task ReceiveAsync_Throws_IfPostHasNoCodeParameter(string query) + { + // Arrange + Initialize(TestSecret); + _postRequest.RequestUri = new Uri("https://some.no.ssl.host?" + query); + + // Act + HttpResponseException ex = await Assert.ThrowsAsync(() => ReceiverMock.Object.ReceiveAsync(TestId, RequestContext, _postRequest)); + + // Assert + HttpError error = await ex.Response.Content.ReadAsAsync(); + Assert.Equal("The WebHook verification request must contain a 'code' query parameter.", error.Message); + ReceiverMock.Protected() + .Verify>("ExecuteWebHookAsync", Times.Never(), TestId, RequestContext, _postRequest, ItExpr.IsAny>(), ItExpr.IsAny()); + } + + [Fact] + public async Task ReceiveAsync_Throws_IfPostHasWrongCodeParameter() + { + // Arrange + Initialize(TestSecret); + _postRequest.RequestUri = new Uri("https://some.no.ssl.host?code=invalid"); + + // Act + HttpResponseException ex = await Assert.ThrowsAsync(() => ReceiverMock.Object.ReceiveAsync(TestId, RequestContext, _postRequest)); + + // Assert + HttpError error = await ex.Response.Content.ReadAsAsync(); + Assert.Equal("The 'code' query parameter provided in the HTTP request did not match the expected value.", error.Message); + ReceiverMock.Protected() + .Verify>("ExecuteWebHookAsync", Times.Never(), TestId, RequestContext, _postRequest, ItExpr.IsAny>(), ItExpr.IsAny()); + } + + [Theory] + [MemberData("ValidIdData")] + public async Task ReceiveAsync_Throws_IfPostIsNotJson(string id) + { + // Arrange + Initialize(GetConfigValue(id, TestSecret)); + _postRequest.Content = new StringContent("Hello World!", Encoding.UTF8, "text/plain"); + + // Act + HttpResponseException ex = await Assert.ThrowsAsync(() => ReceiverMock.Object.ReceiveAsync(id, RequestContext, _postRequest)); + + // Assert + HttpError error = await ex.Response.Content.ReadAsAsync(); + Assert.Equal("The WebHook request must contain an entity body formatted as JSON.", error.Message); + ReceiverMock.Protected() + .Verify>("ExecuteWebHookAsync", Times.Never(), id, RequestContext, _postRequest, ItExpr.IsAny>(), ItExpr.IsAny()); + } + + [Theory] + [MemberData("ValidIdData")] + public async Task ReceiveAsync_ReturnsError_IfPostHasNoEventTypeProperty(string id) + { + // Arrange + Initialize(GetConfigValue(id, TestSecret)); + _postRequest.Content = CreateRequestContent("Microsoft.AspNet.WebHooks.Messages.bad.noEventType.json"); + + // Act + HttpResponseMessage actual = await ReceiverMock.Object.ReceiveAsync(id, RequestContext, _postRequest); + + // Assert + HttpError error = await actual.Content.ReadAsAsync(); + Assert.Equal("No property 'eventType' was found in root of the object.", error.Message); + ReceiverMock.Protected() + .Verify>("ExecuteWebHookAsync", Times.Never(), id, RequestContext, _postRequest, ItExpr.IsAny>(), ItExpr.IsAny()); + } + + [Theory] + [MemberData("ValidIdData")] + public async Task ReceiveAsync_Succeeds_IfValidPostRequest(string id) + { + // Arrange + Initialize(GetConfigValue(id, TestSecret)); + List actions = new List { TestAction }; + ReceiverMock.Protected() + .Setup>("ExecuteWebHookAsync", id, RequestContext, _postRequest, actions, ItExpr.IsAny()) + .ReturnsAsync(new HttpResponseMessage()) + .Verifiable(); + + // Act + await ReceiverMock.Object.ReceiveAsync(id, RequestContext, _postRequest); + + // Assert + ReceiverMock.Verify(); + } + + [Theory] + [InlineData("GET")] + [InlineData("HEAD")] + [InlineData("PATCH")] + [InlineData("PUT")] + [InlineData("OPTIONS")] + public async Task ReceiveAsync_ReturnsError_IfInvalidMethod(string method) + { + // Arrange + Initialize(TestSecret); + HttpRequestMessage req = new HttpRequestMessage { Method = new HttpMethod(method) }; + req.SetRequestContext(RequestContext); + + // Act + HttpResponseMessage actual = await ReceiverMock.Object.ReceiveAsync(TestId, RequestContext, req); + + // Assert + Assert.Equal(HttpStatusCode.MethodNotAllowed, actual.StatusCode); + ReceiverMock.Protected() + .Verify>("ExecuteWebHookAsync", Times.Never(), TestId, RequestContext, req, ItExpr.IsAny>(), ItExpr.IsAny()); + } + + public override void Initialize(string config) + { + base.Initialize(config); + HttpConfig.InitializeReceiveVstsWebHooks(); + + _postRequest = new HttpRequestMessage(HttpMethod.Post, TestAddress); + _postRequest.SetRequestContext(RequestContext); + _postRequest.Content = CreateRequestContent("Microsoft.AspNet.WebHooks.Messages.message.posted.json"); + } + + private HttpContent CreateRequestContent(string payload) + { + string content = EmbeddedResource.ReadAsString(payload); + return new StringContent(content, Encoding.UTF8, "application/json"); + } + } +} diff --git a/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/packages.config b/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/packages.config new file mode 100644 index 0000000..3e60551 --- /dev/null +++ b/test/Microsoft.AspNet.WebHooks.Receivers.VSTS.Test/packages.config @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tools/SkipStrongNames.xml b/tools/SkipStrongNames.xml index a255609..9d95bd4 100644 --- a/tools/SkipStrongNames.xml +++ b/tools/SkipStrongNames.xml @@ -24,6 +24,7 @@ + @@ -51,6 +52,7 @@ +