From c1913691e50b932ca72b49c1e206d26800f5f416 Mon Sep 17 00:00:00 2001 From: slewis74 Date: Wed, 6 Apr 2022 11:24:52 +1000 Subject: [PATCH 1/2] fixed code format rules and ran a reformat --- .github/workflows/codeql-analysis.yml | 50 ++-- NuGet.Config | 10 +- build/Build.cs | 7 +- build/Configuration.cs | 13 +- ...erver.Extensibility.JiraIntegration.nuspec | 46 ++-- build/_build.csproj | 30 +-- source/Client/Client.csproj | 36 +-- source/Client/JiraConfigurationResource.cs | 7 +- source/JiraIntegration.sln.DotSettings | 252 +++++++++++++++++- .../ConnectivityCheckActionsBaseFixture.cs | 35 +-- ...onnectAppConnectivityCheckActionFixture.cs | 39 +-- ...edentialsConnectivityCheckActionFixture.cs | 9 +- .../Server.E2E.Tests/Server.E2E.Tests.csproj | 18 +- ...rkItemLinkMapperErrorHandlingTestScript.cs | 19 +- .../WorkItemLinkMapperTestScript.cs | 69 ++--- .../WorkItemMapperBaseFixture.cs | 7 +- source/Server.Tests/CommentParserScenarios.cs | 12 +- .../JiraDeploymentDataSerializesCorrectly.cs | 12 +- .../Server.Tests/OnlyExposeWhatIsNecessary.cs | 4 +- .../PublicSurfaceAreaScenario.cs | 5 +- source/Server.Tests/Server.Tests.csproj | 34 +-- .../WorkItemLinkMapperScenarios.cs | 56 ++-- .../Actions/JiraServiceDeskActionHandler.cs | 24 +- .../Configuration/DatabaseInitializer.cs | 5 +- .../IJiraConfigurationSettings.cs | 8 +- .../Configuration/IJiraConfigurationStore.cs | 13 +- .../Server/Configuration/JiraConfiguration.cs | 9 +- .../Configuration/JiraConfigurationMapping.cs | 7 +- .../JiraConfigurationResource.cs | 5 +- .../JiraConfigurationSettings.cs | 24 +- .../Configuration/JiraConfigurationStore.cs | 3 +- .../Configuration/JiraConfigureCommands.cs | 44 +-- .../Server/Deployments/DeploymentObserver.cs | 20 +- .../Server/Deployments/IJiraApiDeployment.cs | 3 +- .../Deployments/JiraAssociationConstants.cs | 9 +- source/Server/Deployments/JiraDeployment.cs | 83 +++--- .../Deployments/JiraDeploymentException.cs | 2 +- .../JiraIssueTrackerApiDeployment.cs | 19 +- source/Server/Deployments/JiraPayloadData.cs | 27 +- .../JiraServiceDeskApiDeployment.cs | 11 +- .../Deployments/OctopusJiraPayloadData.cs | 8 +- ...mentEnvironmentSettingsMetadataProvider.cs | 7 +- source/Server/Integration/IJiraRestClient.cs | 3 +- .../Integration/JiraConnectAppClient.cs | 16 +- source/Server/Integration/JiraRestClient.cs | 47 ++-- source/Server/JiraIntegration.cs | 3 +- source/Server/JiraIntegrationApi.cs | 19 +- source/Server/JiraIntegrationExtension.cs | 52 ++-- .../JiraIntegrationHomeLinksContributor.cs | 12 +- source/Server/Properties/AssemblyInfo.cs | 4 +- source/Server/Server.csproj | 44 +-- .../JiraConnectAppConnectivityCheckAction.cs | 34 +-- .../JiraCredentialsConnectivityCheckAction.cs | 14 +- source/Server/WorkItems/CommentParser.cs | 7 +- source/Server/WorkItems/WorkItemLinkMapper.cs | 57 ++-- 55 files changed, 889 insertions(+), 524 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index ae038aa..54b2710 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -37,34 +37,34 @@ jobs: # Learn more about CodeQL language support at https://git.io/codeql-language-support steps: - - name: Checkout repository - uses: actions/checkout@v2 + - name: Checkout repository + uses: actions/checkout@v2 - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v1 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - # queries: ./path/to/local/query, your-org/your-repo/queries@main + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + # queries: ./path/to/local/query, your-org/your-repo/queries@main - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v1 + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v1 - # ℹ️ Command-line programs to run using the OS shell. - # 📚 https://git.io/JvXDl + # ℹ️ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl - # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines - # and modify them (or add more) to build your code if your project - # uses a compiled language + # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language - #- run: | - # make bootstrap - # make release + #- run: | + # make bootstrap + # make release - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 diff --git a/NuGet.Config b/NuGet.Config index 61e4beb..c2e950f 100644 --- a/NuGet.Config +++ b/NuGet.Config @@ -1,8 +1,8 @@  - - - - - + + + + + \ No newline at end of file diff --git a/build/Build.cs b/build/Build.cs index c6db7f4..ef2a272 100644 --- a/build/Build.cs +++ b/build/Build.cs @@ -29,12 +29,13 @@ class Build : NukeBuild, IExtensionBuild [Parameter("Configuration to build - Default is 'Debug' (local) or 'Release' (server)")] public Enumeration Config => Configuration.Release; + + public string NuspecFilePath => "../../build/Octopus.Server.Extensibility.JiraIntegration.nuspec"; + /// Support plugins are available for: /// - JetBrains ReSharper https://nuke.build/resharper /// - JetBrains Rider https://nuke.build/rider /// - Microsoft VisualStudio https://nuke.build/visualstudio /// - Microsoft VSCode https://nuke.build/vscode public static int Main() => Execute(x => ((IExtensionBuild)x).Default); - - public string NuspecFilePath => "../../build/Octopus.Server.Extensibility.JiraIntegration.nuspec"; -} \ No newline at end of file +} diff --git a/build/Configuration.cs b/build/Configuration.cs index 9b22a3b..f25ad2e 100644 --- a/build/Configuration.cs +++ b/build/Configuration.cs @@ -1,14 +1,15 @@ +using System; using System.ComponentModel; using Nuke.Common.Tooling; [TypeConverter(typeof(TypeConverter))] public class Configuration : Enumeration { - public static Configuration Debug = new Configuration { Value = nameof(Debug) }; - public static Configuration Release = new Configuration { Value = nameof(Release) }; + public static Configuration Debug = new() + { Value = nameof(Debug) }; - public static implicit operator string(Configuration configuration) - { - return configuration.Value; - } + public static Configuration Release = new() + { Value = nameof(Release) }; + + public static implicit operator string(Configuration configuration) => configuration.Value; } diff --git a/build/Octopus.Server.Extensibility.JiraIntegration.nuspec b/build/Octopus.Server.Extensibility.JiraIntegration.nuspec index 1bc27e5..408e9d1 100644 --- a/build/Octopus.Server.Extensibility.JiraIntegration.nuspec +++ b/build/Octopus.Server.Extensibility.JiraIntegration.nuspec @@ -1,25 +1,25 @@  - - - Octopus.Server.Extensibility.JiraIntegration - Octopus Server Extensibility Issue Tracker - Jira Integration - $version$ - Octopus Deploy - Octopus Deploy Pty. Ltd. - Octopus Deploy Jira Integration issue tracker. - - Implements the Jira Integration issue tracker. - - en-US - false - https://github.com/OctopusDeploy/JiraIntegration/blob/main/LICENSE.txt - https://github.com/OctopusDeploy/JiraIntegration - - https://i.octopusdeploy.com/resources/Avatar3_360.png - automation deployment - true - - - - + + + Octopus.Server.Extensibility.JiraIntegration + Octopus Server Extensibility Issue Tracker - Jira Integration + $version$ + Octopus Deploy + Octopus Deploy Pty. Ltd. + Octopus Deploy Jira Integration issue tracker. + + Implements the Jira Integration issue tracker. + + en-US + false + https://github.com/OctopusDeploy/JiraIntegration/blob/main/LICENSE.txt + https://github.com/OctopusDeploy/JiraIntegration + + https://i.octopusdeploy.com/resources/Avatar3_360.png + automation deployment + true + + + + \ No newline at end of file diff --git a/build/_build.csproj b/build/_build.csproj index 7fbc8f1..1c682c6 100644 --- a/build/_build.csproj +++ b/build/_build.csproj @@ -1,20 +1,20 @@ - - Exe - net5.0 - - CS0649;CS0169 - .. - .. - 1 - + + Exe + net5.0 + + CS0649;CS0169 + .. + .. + 1 + - - - - - - + + + + + + diff --git a/source/Client/Client.csproj b/source/Client/Client.csproj index 80c3979..edeff12 100644 --- a/source/Client/Client.csproj +++ b/source/Client/Client.csproj @@ -1,20 +1,20 @@  - - net452;netstandard2.0 - Octopus.Client.Extensibility.JiraIntegration - Octopus.Client.Extensibility.JiraIntegration - en-US - Octopus Deploy - Octopus Deploy Pty. Ltd. - Octopus Deploy Client Extensibility - Jira - Octopus Deploy Client Extensibility for the Jira Integration and Service Desk - Octopus.Client.Extensibility.JiraIntegration - https://i.octopusdeploy.com/resources/Avatar3_360.png - https://github.com/OctopusDeploy/JiraIntegration/blob/master/LICENSE.txt - https://github.com/OctopusDeploy/JiraIntegration - https://github.com/OctopusDeploy/JiraIntegration - - - - + + net452;netstandard2.0 + Octopus.Client.Extensibility.JiraIntegration + Octopus.Client.Extensibility.JiraIntegration + en-US + Octopus Deploy + Octopus Deploy Pty. Ltd. + Octopus Deploy Client Extensibility - Jira + Octopus Deploy Client Extensibility for the Jira Integration and Service Desk + Octopus.Client.Extensibility.JiraIntegration + https://i.octopusdeploy.com/resources/Avatar3_360.png + https://github.com/OctopusDeploy/JiraIntegration/blob/master/LICENSE.txt + https://github.com/OctopusDeploy/JiraIntegration + https://github.com/OctopusDeploy/JiraIntegration + + + + \ No newline at end of file diff --git a/source/Client/JiraConfigurationResource.cs b/source/Client/JiraConfigurationResource.cs index 3e2d422..92ec953 100644 --- a/source/Client/JiraConfigurationResource.cs +++ b/source/Client/JiraConfigurationResource.cs @@ -1,4 +1,5 @@ -using System.ComponentModel; +using System; +using System.ComponentModel; using Octopus.Client.Extensibility.Attributes; using Octopus.Client.Extensibility.Extensions.Infrastructure.Configuration; using Octopus.Client.Model; @@ -43,7 +44,7 @@ public class ReleaseNoteOptionsResource [Description(UsernameDescription)] [Writeable] public string Username { get; set; } - + [DisplayName("Jira Password")] [Description(PasswordDescription)] [Writeable] @@ -54,4 +55,4 @@ public class ReleaseNoteOptionsResource [Writeable] public string ReleaseNotePrefix { get; set; } } -} \ No newline at end of file +} diff --git a/source/JiraIntegration.sln.DotSettings b/source/JiraIntegration.sln.DotSettings index 542dd4c..a00c346 100644 --- a/source/JiraIntegration.sln.DotSettings +++ b/source/JiraIntegration.sln.DotSettings @@ -1,3 +1,253 @@  + True + True + True + + + + + + True + ExplicitlyExcluded + + True + ExplicitlyExcluded + ExplicitlyExcluded + DO_NOT_SHOW + DO_NOT_SHOW + DO_NOT_SHOW + DO_NOT_SHOW + DO_NOT_SHOW + DO_NOT_SHOW + <?xml version="1.0" encoding="utf-16"?><Profile name="Octopus Deploy"><CSArrangeThisQualifier>True</CSArrangeThisQualifier><CSRemoveCodeRedundancies>True</CSRemoveCodeRedundancies><CSUseAutoProperty>True</CSUseAutoProperty><CSMakeFieldReadonly>True</CSMakeFieldReadonly><CSUseVar><BehavourStyle>CAN_CHANGE_TO_IMPLICIT</BehavourStyle><LocalVariableStyle>ALWAYS_IMPLICIT</LocalVariableStyle><ForeachVariableStyle>ALWAYS_IMPLICIT</ForeachVariableStyle></CSUseVar><CSOptimizeUsings><OptimizeUsings>True</OptimizeUsings><EmbraceInRegion>False</EmbraceInRegion><RegionName></RegionName></CSOptimizeUsings><CSReformatCode>True</CSReformatCode><CSharpFormatDocComments>True</CSharpFormatDocComments><CssAlphabetizeProperties>True</CssAlphabetizeProperties><HtmlReformatCode>True</HtmlReformatCode><RemoveCodeRedundancies>True</RemoveCodeRedundancies><CSArrangeQualifiers>True</CSArrangeQualifiers><CSEnforceVarKeywordUsageSettings>True</CSEnforceVarKeywordUsageSettings><CSShortenReferences>True</CSShortenReferences><CSReorderTypeMembers>True</CSReorderTypeMembers><CSMakeAutoPropertyGetOnly>True</CSMakeAutoPropertyGetOnly></Profile> + Octopus Deploy + RequiredForMultilineStatement + RequiredForMultilineStatement + NotRequiredForBoth + Implicit + Implicit + public private protected internal abstract virtual sealed override static new readonly extern unsafe volatile async + False + + NEXT_LINE + False + False + SEPARATE + ALWAYS_ADD + ALWAYS_ADD + ALWAYS_ADD + ALWAYS_ADD + ALWAYS_ADD + ALWAYS_ADD + False + + NEXT_LINE + 1 + 1 + True + True + True + True + 5 + 1 + 5 + 5 + 5 + 5 + NEVER + True + False + NEVER + False + False + False + True + True + CHOP_IF_LONG + True + True + CHOP_IF_LONG + CHOP_IF_LONG + CHOP_IF_LONG + 200 + False + CHOP_ALWAYS + CHOP_IF_LONG + DoNotTouch + False + 1 + ByFirstAttr + 300 + 300 + <?xml version="1.0" encoding="utf-16"?> +<Patterns xmlns="urn:schemas-jetbrains-com:member-reordering-patterns"> + <TypePattern DisplayName="COM interfaces or structs"> + <TypePattern.Match> + <Or> + <And> + <Kind Is="Interface" /> + <Or> + <HasAttribute Name="System.Runtime.InteropServices.InterfaceTypeAttribute" /> + <HasAttribute Name="System.Runtime.InteropServices.ComImport" /> + </Or> + </And> + <Kind Is="Struct" /> + </Or> + </TypePattern.Match> + </TypePattern> + <TypePattern DisplayName="NUnit Test Fixtures" RemoveRegions="All"> + <TypePattern.Match> + <And> + <Kind Is="Class" /> + <HasAttribute Name="NUnit.Framework.TestFixtureAttribute" Inherited="True" /> + <HasAttribute Name="NUnit.Framework.TestCaseFixtureAttribute" Inherited="True" /> + </And> + </TypePattern.Match> + <Entry DisplayName="Setup/Teardown Methods"> + <Entry.Match> + <And> + <Kind Is="Method" /> + <Or> + <HasAttribute Name="NUnit.Framework.SetUpAttribute" Inherited="True" /> + <HasAttribute Name="NUnit.Framework.TearDownAttribute" Inherited="True" /> + <HasAttribute Name="NUnit.Framework.FixtureSetUpAttribute" Inherited="True" /> + <HasAttribute Name="NUnit.Framework.FixtureTearDownAttribute" Inherited="True" /> + </Or> + </And> + </Entry.Match> + </Entry> + <Entry DisplayName="All other members" /> + <Entry Priority="100" DisplayName="Test Methods"> + <Entry.Match> + <And> + <Kind Is="Method" /> + <HasAttribute Name="NUnit.Framework.TestAttribute" /> + </And> + </Entry.Match> + <Entry.SortBy> + <Name /> + </Entry.SortBy> + </Entry> + </TypePattern> + <TypePattern DisplayName="Default Pattern"> + <Entry Priority="100" DisplayName="Public Delegates"> + <Entry.Match> + <And> + <Access Is="Public" /> + <Kind Is="Delegate" /> + </And> + </Entry.Match> + <Entry.SortBy> + <Name /> + </Entry.SortBy> + </Entry> + <Entry Priority="100" DisplayName="Public Enums"> + <Entry.Match> + <And> + <Access Is="Public" /> + <Kind Is="Enum" /> + </And> + </Entry.Match> + <Entry.SortBy> + <Name /> + </Entry.SortBy> + </Entry> + <Entry DisplayName="Static Fields and Constants"> + <Entry.Match> + <Or> + <Kind Is="Constant" /> + <And> + <Kind Is="Field" /> + <Static /> + </And> + </Or> + </Entry.Match> + <Entry.SortBy> + <Kind Order="Constant Field" /> + </Entry.SortBy> + </Entry> + <Entry DisplayName="Fields"> + <Entry.Match> + <And> + <Kind Is="Field" /> + <Not> + <Static /> + </Not> + </And> + </Entry.Match> + <Entry.SortBy> + <Readonly /> + </Entry.SortBy> + </Entry> + <Entry DisplayName="Constructors"> + <Entry.Match> + <Kind Is="Constructor" /> + </Entry.Match> + <Entry.SortBy> + <Static /> + </Entry.SortBy> + </Entry> + <Entry DisplayName="Properties, Indexers"> + <Entry.Match> + <Or> + <Kind Is="Property" /> + <Kind Is="Indexer" /> + </Or> + </Entry.Match> + </Entry> + <Entry DisplayName="All other members" /> + <Entry DisplayName="Nested Types"> + <Entry.Match> + <Kind Is="Type" /> + </Entry.Match> + </Entry> + </TypePattern> +</Patterns> + System + System + AD + CSS + ID + SSL + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"><ExtraRule Prefix="" Suffix="" Style="AaBb_AaBb" /></Policy> <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> \ No newline at end of file + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb"><ExtraRule Prefix="" Suffix="" Style="AaBb" /></Policy> + + + + + + True + True + True + True + True + True + True + True + True + True + True + True + False + False + None + True + True + Document map + True + 0 + True + True + Everywhere + map + True + public class $Target$Map : DocumentMap<$Target$> +{ + public $Target$Map() + { + Column(m => m.Foo); + } +} + diff --git a/source/Server.E2E.Tests/ConnectivityCheckActionsBaseFixture.cs b/source/Server.E2E.Tests/ConnectivityCheckActionsBaseFixture.cs index 8f6f8ae..9530d30 100644 --- a/source/Server.E2E.Tests/ConnectivityCheckActionsBaseFixture.cs +++ b/source/Server.E2E.Tests/ConnectivityCheckActionsBaseFixture.cs @@ -14,12 +14,12 @@ namespace Octopus.Server.Extensibility.JiraIntegration.E2E.Tests { abstract class ConnectivityCheckActionsBaseFixture { + const string JiraBaseUrlEnvironmentVariable = "JiraIntegration_E2E_BaseUrl"; + const string JiraUsernameEnvironmentVariable = "JiraIntegration_E2E_Username"; + const string JiraAuthTokenEnvironmentVariable = "JiraIntegration_E2E_AuthToken"; protected IJiraConfigurationStore store; protected ISystemLog log; protected IOctopusHttpClientFactory httpClientFactory; - private const string JiraBaseUrlEnvironmentVariable = "JiraIntegration_E2E_BaseUrl"; - private const string JiraUsernameEnvironmentVariable = "JiraIntegration_E2E_Username"; - private const string JiraAuthTokenEnvironmentVariable = "JiraIntegration_E2E_AuthToken"; [OneTimeSetUp] public void Setup() @@ -38,17 +38,20 @@ static IOctopusHttpClientFactory BuildOctopusHttpClientFactory(string baseUrl, s { var httpClientFactory = Substitute.For(); - httpClientFactory.CreateClient().Returns(_ => - { - var httpClient = new HttpClient - { - BaseAddress = new Uri(baseUrl) - }; - httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", - Convert.ToBase64String(Encoding.UTF8.GetBytes($"{username}:{authToken}"))); + httpClientFactory.CreateClient() + .Returns( + _ => + { + var httpClient = new HttpClient + { + BaseAddress = new Uri(baseUrl) + }; + httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue( + "Basic", + Convert.ToBase64String(Encoding.UTF8.GetBytes($"{username}:{authToken}"))); - return httpClient; - }); + return httpClient; + }); return httpClientFactory; } @@ -70,9 +73,7 @@ static bool TryGetJiraSettings(out string jiraBaseUrl, out string jiraUsername, jiraUsername = Environment.GetEnvironmentVariable(JiraUsernameEnvironmentVariable); jiraAuthToken = Environment.GetEnvironmentVariable(JiraAuthTokenEnvironmentVariable); - return !jiraBaseUrl.IsNullOrEmpty() && - !jiraUsername.IsNullOrEmpty() && - !jiraAuthToken.IsNullOrEmpty(); + return !jiraBaseUrl.IsNullOrEmpty() && !jiraUsername.IsNullOrEmpty() && !jiraAuthToken.IsNullOrEmpty(); } } -} \ No newline at end of file +} diff --git a/source/Server.E2E.Tests/JiraConnectAppConnectivityCheckActionFixture.cs b/source/Server.E2E.Tests/JiraConnectAppConnectivityCheckActionFixture.cs index b02224d..0d8367a 100644 --- a/source/Server.E2E.Tests/JiraConnectAppConnectivityCheckActionFixture.cs +++ b/source/Server.E2E.Tests/JiraConnectAppConnectivityCheckActionFixture.cs @@ -33,8 +33,9 @@ public async Task WhenDnsCannotBeResolved() var action = new JiraConnectAppConnectivityCheckAction(log, store, installationIdProvider, new JiraConnectAppClient(installationIdProvider, store, httpClientFactory), httpClientFactory); var octoRequest = Substitute.For(); octoRequest.GetBody(Arg.Any>()) - .Returns(new JiraConnectAppConnectionCheckData - { BaseUrl = baseUrl, Password = "Does not matter" }); + .Returns( + new JiraConnectAppConnectionCheckData + { BaseUrl = baseUrl, Password = "Does not matter" }); var response = await action.ExecuteAsync(octoRequest); @@ -53,23 +54,26 @@ public async Task WhenProxyAuthenticationIsRequired() var explicitEndPoint = new ExplicitProxyEndPoint(IPAddress.Any, port, false); proxyServer.AddEndPoint(explicitEndPoint); // Fake authentication failure for proxy - proxyServer.ProxySchemeAuthenticateFunc = (d, s, arg3) => Task.FromResult(new ProxyAuthenticationContext { Result = ProxyAuthenticationResult.Failure}); - proxyServer.ProxyAuthenticationSchemes = new[] {"basic"}; + proxyServer.ProxySchemeAuthenticateFunc = (d, s, arg3) => Task.FromResult(new ProxyAuthenticationContext { Result = ProxyAuthenticationResult.Failure }); + proxyServer.ProxyAuthenticationSchemes = new[] { "basic" }; proxyServer.Start(); var baseUrl = "http://notexistingdomain.dddd.ttt"; - httpClientFactory.CreateClient().Returns(_ => - { - var httpClient = new HttpClient(new HttpClientHandler {Proxy = new WebProxy("127.0.0.1", port)}) - { - BaseAddress = new Uri(baseUrl), - }; + httpClientFactory.CreateClient() + .Returns( + _ => + { + var httpClient = new HttpClient(new HttpClientHandler { Proxy = new WebProxy("127.0.0.1", port) }) + { + BaseAddress = new Uri(baseUrl) + }; - httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", - Convert.ToBase64String(Encoding.UTF8.GetBytes("username:authToken"))); + httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue( + "Basic", + Convert.ToBase64String(Encoding.UTF8.GetBytes("username:authToken"))); - return httpClient; - }); + return httpClient; + }); var installationIdProvider = Substitute.For(); installationIdProvider.GetInstallationId().Returns(Guid.NewGuid()); @@ -79,8 +83,9 @@ public async Task WhenProxyAuthenticationIsRequired() var action = new JiraConnectAppConnectivityCheckAction(log, store, installationIdProvider, new JiraConnectAppClient(installationIdProvider, store, httpClientFactory), httpClientFactory); var octoRequest = Substitute.For(); octoRequest.GetBody(Arg.Any>()) - .Returns(new JiraConnectAppConnectionCheckData - { BaseUrl = baseUrl, Password = "Does not matter" }); + .Returns( + new JiraConnectAppConnectionCheckData + { BaseUrl = baseUrl, Password = "Does not matter" }); var response = await action.ExecuteAsync(octoRequest); @@ -90,4 +95,4 @@ public async Task WhenProxyAuthenticationIsRequired() connectivityCheckResponse.Messages.First().Message.ShouldBe("Failed to get authentication token from Jira Connect App."); } } -} \ No newline at end of file +} diff --git a/source/Server.E2E.Tests/JiraCredentialsConnectivityCheckActionFixture.cs b/source/Server.E2E.Tests/JiraCredentialsConnectivityCheckActionFixture.cs index 9d0c8da..474b6c8 100644 --- a/source/Server.E2E.Tests/JiraCredentialsConnectivityCheckActionFixture.cs +++ b/source/Server.E2E.Tests/JiraCredentialsConnectivityCheckActionFixture.cs @@ -1,3 +1,4 @@ +using System; using System.Linq; using System.Threading.Tasks; using NSubstitute; @@ -10,7 +11,7 @@ namespace Octopus.Server.Extensibility.JiraIntegration.E2E.Tests { [TestFixture] - class JiraCredentialsConnectivityCheckActionFixture: ConnectivityCheckActionsBaseFixture + class JiraCredentialsConnectivityCheckActionFixture : ConnectivityCheckActionsBaseFixture { [Test] public async Task WhenDnsCannotBeResolved() @@ -19,7 +20,7 @@ public async Task WhenDnsCannotBeResolved() var octoRequest = Substitute.For(); var baseUrl = "http://notexistingdomain.dddd.ttt"; octoRequest.GetBody(Arg.Any>()) - .Returns(new JiraCredentialsConnectionCheckData {BaseUrl = baseUrl, Username = "Does not matter", Password = "Does not matter"}); + .Returns(new JiraCredentialsConnectionCheckData { BaseUrl = baseUrl, Username = "Does not matter", Password = "Does not matter" }); var response = await action.ExecuteAsync(octoRequest); @@ -36,7 +37,7 @@ public async Task WhenUsernameIsNotRight() var octoRequest = Substitute.For(); var baseUrl = store.GetBaseUrl(); octoRequest.GetBody(Arg.Any>()) - .Returns(new JiraCredentialsConnectionCheckData {BaseUrl = baseUrl, Username = "Does not matter", Password = "Does not matter"}); + .Returns(new JiraCredentialsConnectionCheckData { BaseUrl = baseUrl, Username = "Does not matter", Password = "Does not matter" }); var response = await action.ExecuteAsync(octoRequest); @@ -46,4 +47,4 @@ public async Task WhenUsernameIsNotRight() connectivityCheckResponse.Messages.First().Message.ShouldStartWith($"Failed to connect to {baseUrl}. Response code: Unauthorized"); } } -} \ No newline at end of file +} diff --git a/source/Server.E2E.Tests/Server.E2E.Tests.csproj b/source/Server.E2E.Tests/Server.E2E.Tests.csproj index 8105d07..da66d63 100644 --- a/source/Server.E2E.Tests/Server.E2E.Tests.csproj +++ b/source/Server.E2E.Tests/Server.E2E.Tests.csproj @@ -6,17 +6,17 @@ - - - - - - - - + + + + + + + + - + diff --git a/source/Server.E2E.Tests/WorkItemLinkMapperErrorHandlingTestScript.cs b/source/Server.E2E.Tests/WorkItemLinkMapperErrorHandlingTestScript.cs index 5e51007..0a7a418 100644 --- a/source/Server.E2E.Tests/WorkItemLinkMapperErrorHandlingTestScript.cs +++ b/source/Server.E2E.Tests/WorkItemLinkMapperErrorHandlingTestScript.cs @@ -17,22 +17,25 @@ public class WorkItemLinkMapperErrorHandlingTestScript : WorkItemMapperBaseFixtu [Test] public void HttpAuthExceptionShouldLogMeaningfulMessage() { - if(!TryGetJiraSettings(out var baseUrl, out var username, out var _)) + if (!TryGetJiraSettings(out var baseUrl, out var username, out var _)) + { Assert.Ignore($"Configure the following environment variables '{JiraBaseUrlEnvironmentVariable}', '{JiraUsernameEnvironmentVariable}', '{JiraAuthTokenEnvironmentVariable}' to run these tests."); + } var log = Substitute.For(); var store = BuildJiraConfigurationStore(baseUrl, username, "invalidtoken"); var jira = BuildJiraRestClient(baseUrl, username, "invalidtoken", log); - var buildInformation = CreateBuildInformation(new[] - { - new Commit + var buildInformation = CreateBuildInformation( + new[] { - Id = "234", - Comment = "OATP-9999" - } - }); + new Commit + { + Id = "234", + Comment = "OATP-9999" + } + }); var workItemLinkMapper = new WorkItemLinkMapper( store, diff --git a/source/Server.E2E.Tests/WorkItemLinkMapperTestScript.cs b/source/Server.E2E.Tests/WorkItemLinkMapperTestScript.cs index 8112503..db7ba3b 100644 --- a/source/Server.E2E.Tests/WorkItemLinkMapperTestScript.cs +++ b/source/Server.E2E.Tests/WorkItemLinkMapperTestScript.cs @@ -21,7 +21,7 @@ public class WorkItemLinkMapperTestScript : WorkItemMapperBaseFixture [OneTimeSetUp] public void Setup() { - if(!TryGetJiraSettings(out var baseUrl, out var username, out var authToken)) + if (!TryGetJiraSettings(out var baseUrl, out var username, out var authToken)) Assert.Ignore($"Configure the following environment variables '{JiraBaseUrlEnvironmentVariable}', '{JiraUsernameEnvironmentVariable}', '{JiraAuthTokenEnvironmentVariable}' to run these tests."); var log = Substitute.For(); @@ -39,19 +39,20 @@ public void Setup() [Test] public void WeCanDeserializeJiraIssuesWithComments() { - var buildInformation = CreateBuildInformation(new[] - { - new Commit + var buildInformation = CreateBuildInformation( + new[] { - Id = "123", - Comment = "OATP-1", - }, - new Commit - { - Id = "234", - Comment = "OATP-9", - } - }); + new Commit + { + Id = "123", + Comment = "OATP-1" + }, + new Commit + { + Id = "234", + Comment = "OATP-9" + } + }); var result = (ResultFromExtension)workItemLinkMapper.Map(buildInformation); @@ -65,19 +66,20 @@ public void WeCanDeserializeJiraIssuesWithComments() [Test] public void WeCanDeserializeJiraIssuesWhenOnlySomeIssuesAreFound() { - var buildInformation = CreateBuildInformation(new[] - { - new Commit - { - Id = "123", - Comment = "OATP-1", - }, - new Commit + var buildInformation = CreateBuildInformation( + new[] { - Id = "234", - Comment = "OATP-9999", // non-existent - } - }); + new Commit + { + Id = "123", + Comment = "OATP-1" + }, + new Commit + { + Id = "234", + Comment = "OATP-9999" // non-existent + } + }); var result = (ResultFromExtension)workItemLinkMapper.Map(buildInformation); @@ -90,14 +92,15 @@ public void WeCanDeserializeJiraIssuesWhenOnlySomeIssuesAreFound() [Test] public void WeCanDeserializeJiraIssuesAsEmptyCollectionWhenNoIssuesAreFound() { - var buildInformation = CreateBuildInformation(new[] - { - new Commit + var buildInformation = CreateBuildInformation( + new[] { - Id = "234", - Comment = "OATP-9999", // non-existent - } - }); + new Commit + { + Id = "234", + Comment = "OATP-9999" // non-existent + } + }); var result = (ResultFromExtension)workItemLinkMapper.Map(buildInformation); @@ -113,4 +116,4 @@ static void AssertIssueWasReturnedAndHasCorrectDetails(string issueId, string ex Assert.AreEqual(expectedDescription, issue.Description); } } -} \ No newline at end of file +} diff --git a/source/Server.E2E.Tests/WorkItemMapperBaseFixture.cs b/source/Server.E2E.Tests/WorkItemMapperBaseFixture.cs index 096c437..7c5f5de 100644 --- a/source/Server.E2E.Tests/WorkItemMapperBaseFixture.cs +++ b/source/Server.E2E.Tests/WorkItemMapperBaseFixture.cs @@ -37,7 +37,8 @@ static IOctopusHttpClientFactory BuildOctopusHttpClientFactory(string baseUrl, s { BaseAddress = new Uri(baseUrl) }; - httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", + httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue( + "Basic", Convert.ToBase64String(Encoding.UTF8.GetBytes($"{username}:{authToken}"))); var httpClientFactory = Substitute.For(); @@ -63,9 +64,7 @@ protected static bool TryGetJiraSettings(out string jiraBaseUrl, out string jira jiraUsername = Environment.GetEnvironmentVariable(JiraUsernameEnvironmentVariable); jiraAuthToken = Environment.GetEnvironmentVariable(JiraAuthTokenEnvironmentVariable); - return !jiraBaseUrl.IsNullOrEmpty() && - !jiraUsername.IsNullOrEmpty() && - !jiraAuthToken.IsNullOrEmpty(); + return !jiraBaseUrl.IsNullOrEmpty() && !jiraUsername.IsNullOrEmpty() && !jiraAuthToken.IsNullOrEmpty(); } internal static OctopusBuildInformation CreateBuildInformation(Commit[] commits) diff --git a/source/Server.Tests/CommentParserScenarios.cs b/source/Server.Tests/CommentParserScenarios.cs index a14bb09..1de3c8f 100644 --- a/source/Server.Tests/CommentParserScenarios.cs +++ b/source/Server.Tests/CommentParserScenarios.cs @@ -1,3 +1,4 @@ +using System; using System.Linq; using NUnit.Framework; using Octopus.Server.Extensibility.JiraIntegration.WorkItems; @@ -26,7 +27,6 @@ public void JiraIssueInBranchNameGetsParsedCorrectly(string comment, string expe var reference = workItemReferences.First(); Assert.AreEqual(expectedReference, reference); - } [TestCase("Fixes [JRE-1234]", "JRE-1234")] @@ -76,8 +76,8 @@ public void MultipleIssueNumberReferencesGetsParsedCorrectly() Assert.AreEqual("JRE-2345", reference); } - [TestCase("Fixes [JRE-1234],[JRE-2345]", "JRE-1234","JRE-2345")] - [TestCase("Fixes (JRE-1234),(JRE-2345)", "JRE-1234","JRE-2345")] + [TestCase("Fixes [JRE-1234],[JRE-2345]", "JRE-1234", "JRE-2345")] + [TestCase("Fixes (JRE-1234),(JRE-2345)", "JRE-1234", "JRE-2345")] public void MultipleIssueNumberReferencesEnclosedInBracketsGetsParsedCorrectly(string comment, params string[] expectedReferences) { var workItemReferences = new CommentParser().ParseWorkItemIds(Create(comment)); @@ -152,12 +152,12 @@ public void CommentsWithStringThatLookCloseToReferencesGetParsedCorrectly(string Assert.IsEmpty(workItemReferences); } - private OctopusBuildInformation Create(string comment) + OctopusBuildInformation Create(string comment) { return new OctopusBuildInformation { - Commits = new[] { new Commit{ Id = "a", Comment = comment }} + Commits = new[] { new Commit { Id = "a", Comment = comment } } }; } } -} \ No newline at end of file +} diff --git a/source/Server.Tests/JiraDeploymentData/JiraDeploymentDataSerializesCorrectly.cs b/source/Server.Tests/JiraDeploymentData/JiraDeploymentDataSerializesCorrectly.cs index 9c84b02..5abee42 100644 --- a/source/Server.Tests/JiraDeploymentData/JiraDeploymentDataSerializesCorrectly.cs +++ b/source/Server.Tests/JiraDeploymentData/JiraDeploymentDataSerializesCorrectly.cs @@ -21,18 +21,18 @@ public void DataSerializesCorrectly() { Deployments = new[] { - new Extensibility.JiraIntegration.Deployments.JiraDeploymentData + new Deployments.JiraDeploymentData { DeploymentSequenceNumber = 11, UpdateSequenceNumber = 3, DisplayName = "Task Name", - Associations = new [] + Associations = new[] { - new JiraAssociation() + new JiraAssociation { AssociationType = JiraAssociationConstants.JiraAssociationTypeIssueKeys, - Values = new [] { "JIR-1", "JIR-2"} - }, + Values = new[] { "JIR-1", "JIR-2" } + } }, Url = "https://octopussample.com/app#/Spaces-1/projects/foo/releases/1.0.0/deployments/deployments-123", @@ -60,4 +60,4 @@ public void DataSerializesCorrectly() this.Assent(json); } } -} \ No newline at end of file +} diff --git a/source/Server.Tests/OnlyExposeWhatIsNecessary.cs b/source/Server.Tests/OnlyExposeWhatIsNecessary.cs index 2595b2f..f497e5e 100644 --- a/source/Server.Tests/OnlyExposeWhatIsNecessary.cs +++ b/source/Server.Tests/OnlyExposeWhatIsNecessary.cs @@ -15,8 +15,8 @@ public void ServerExtensionsShouldMinimiseWhatIsExposed() var publicThings = assembly.GetExportedTypes() .Select(t => t.FullName); - + this.Assent(string.Join(Environment.NewLine, publicThings)); } } -} \ No newline at end of file +} diff --git a/source/Server.Tests/PublicSurfaceArea/PublicSurfaceAreaScenario.cs b/source/Server.Tests/PublicSurfaceArea/PublicSurfaceAreaScenario.cs index 35189e3..9a2dc7e 100644 --- a/source/Server.Tests/PublicSurfaceArea/PublicSurfaceAreaScenario.cs +++ b/source/Server.Tests/PublicSurfaceArea/PublicSurfaceAreaScenario.cs @@ -1,3 +1,4 @@ +using System; using System.Linq; using Assent; using NUnit.Framework; @@ -11,8 +12,8 @@ public class PublicSurfaceAreaScenario public void ExtensionsShouldKeepThingsPrivate() { var types = typeof(JiraIntegrationExtension).Assembly.GetExportedTypes().Select(t => t.FullName); - + this.Assent(string.Join('\n', types)); } } -} \ No newline at end of file +} diff --git a/source/Server.Tests/Server.Tests.csproj b/source/Server.Tests/Server.Tests.csproj index 346cbd0..f7ca6ae 100644 --- a/source/Server.Tests/Server.Tests.csproj +++ b/source/Server.Tests/Server.Tests.csproj @@ -1,20 +1,20 @@  - - net5.0 - Octopus.Server.Extensibility.JiraIntegration.Tests - Octopus.Server.Extensibility.JiraIntegration.Tests - - - - - - - - - - - - - + + net5.0 + Octopus.Server.Extensibility.JiraIntegration.Tests + Octopus.Server.Extensibility.JiraIntegration.Tests + + + + + + + + + + + + + diff --git a/source/Server.Tests/WorkItemLinkMapperScenarios.cs b/source/Server.Tests/WorkItemLinkMapperScenarios.cs index e614236..ad67eee 100644 --- a/source/Server.Tests/WorkItemLinkMapperScenarios.cs +++ b/source/Server.Tests/WorkItemLinkMapperScenarios.cs @@ -43,7 +43,7 @@ public string GetWorkItemDescription(string linkData, string releaseNotePrefix, Comments = new JiraIssueComments { Total = 1, - Comments = new [] + Comments = new[] { new JiraIssueComment { @@ -53,7 +53,7 @@ public string GetWorkItemDescription(string linkData, string releaseNotePrefix, } } }; - jiraClient.GetIssues(Arg.Any()).Returns(ResultFromExtension.Success(new [] { jiraIssue })); + jiraClient.GetIssues(Arg.Any()).Returns(ResultFromExtension.Success(new[] { jiraIssue })); return new WorkItemLinkMapper(store, new CommentParser(), jiraClientLazy, Substitute.For()).GetReleaseNote(jiraIssue, releaseNotePrefix); } @@ -76,18 +76,20 @@ public void DuplicatesGetIgnored() Comments = new JiraIssueComments() } }; - jiraClient.GetIssues(Arg.Any()).Returns(ResultFromExtension.Success(new [] {jiraIssue})); + jiraClient.GetIssues(Arg.Any()).Returns(ResultFromExtension.Success(new[] { jiraIssue })); var mapper = new WorkItemLinkMapper(store, new CommentParser(), jiraClientLazy, Substitute.For()); - var workItems = mapper.Map(new OctopusBuildInformation - { - Commits = new Commit[] + var workItems = mapper.Map( + new OctopusBuildInformation { - new Commit { Id = "abcd", Comment = "This is a test commit message. Fixes JRE-1234"}, - new Commit { Id = "defg", Comment = "This is a test commit message with duplicates. Fixes JRE-1234"} - } - }); + Commits = new[] + { + new() + { Id = "abcd", Comment = "This is a test commit message. Fixes JRE-1234" }, + new Commit { Id = "defg", Comment = "This is a test commit message with duplicates. Fixes JRE-1234" } + } + }); Assert.AreEqual("JRE-1234", ((ISuccessResult)workItems).Value.Single().Id, "Single work item should be returned"); } @@ -110,17 +112,19 @@ public void SourceGetsSet() Comments = new JiraIssueComments() } }; - jiraClient.GetIssues(Arg.Any()).Returns(ResultFromExtension.Success(new [] { jiraIssue })); + jiraClient.GetIssues(Arg.Any()).Returns(ResultFromExtension.Success(new[] { jiraIssue })); var mapper = new WorkItemLinkMapper(store, new CommentParser(), jiraClientLazy, Substitute.For()); - var workItems = mapper.Map(new OctopusBuildInformation - { - Commits = new Commit[] + var workItems = mapper.Map( + new OctopusBuildInformation { - new Commit { Id = "abcd", Comment = "This is a test commit message. Fixes JRE-1234"} - } - }); + Commits = new[] + { + new() + { Id = "abcd", Comment = "This is a test commit message. Fixes JRE-1234" } + } + }); Assert.AreEqual("Jira", ((ISuccessResult)workItems).Value.Single().Source, "Source should be set"); } @@ -143,19 +147,21 @@ public void KeyGetsUpperCased() Comments = new JiraIssueComments() } }; - jiraClient.GetIssues(Arg.Any()).Returns(ResultFromExtension.Success(new [] { jiraIssue })); + jiraClient.GetIssues(Arg.Any()).Returns(ResultFromExtension.Success(new[] { jiraIssue })); var mapper = new WorkItemLinkMapper(store, new CommentParser(), jiraClientLazy, Substitute.For()); - var workItems = mapper.Map(new OctopusBuildInformation - { - Commits = new Commit[] + var workItems = mapper.Map( + new OctopusBuildInformation { - new Commit { Id = "abcd", Comment = "This is a test commit message. Fixes jre-1234"} - } - }); + Commits = new[] + { + new() + { Id = "abcd", Comment = "This is a test commit message. Fixes jre-1234" } + } + }); Assert.AreEqual("JRE-1234", ((ISuccessResult)workItems).Value.Single().Id); } } -} \ No newline at end of file +} diff --git a/source/Server/Actions/JiraServiceDeskActionHandler.cs b/source/Server/Actions/JiraServiceDeskActionHandler.cs index 34f4e7a..72b641e 100644 --- a/source/Server/Actions/JiraServiceDeskActionHandler.cs +++ b/source/Server/Actions/JiraServiceDeskActionHandler.cs @@ -1,4 +1,5 @@ #nullable enable +using System; using System.Threading; using Octopus.Server.Extensibility.HostServices.Diagnostics; using Octopus.Server.Extensibility.JiraIntegration.Deployments; @@ -11,16 +12,7 @@ namespace Octopus.Server.Extensibility.JiraIntegration.Actions { class JiraServiceDeskActionHandler : IActionHandler { - public string Id => "Octopus.JiraIntegration.ServiceDeskAction"; - public string Name => "Jira Service Desk Change Request"; - public string Description => "Initiate a Change Request in Jira Service Desk"; - public string? Keywords => null; - public bool ShowInStepTemplatePickerUI => true; - public bool WhenInAChildStepRunInTheContextOfTheTargetMachine => false; - public bool CanRunOnDeploymentTarget => false; - public ActionHandlerCategory[] Categories => new[] { ActionHandlerCategory.BuiltInStep, ActionHandlerCategory.Atlassian }; - - private readonly IMediator mediator; + readonly IMediator mediator; readonly JiraDeployment jiraDeployment; public JiraServiceDeskActionHandler( @@ -31,6 +23,15 @@ public JiraServiceDeskActionHandler( this.jiraDeployment = jiraDeployment; } + public string Id => "Octopus.JiraIntegration.ServiceDeskAction"; + public string Name => "Jira Service Desk Change Request"; + public string Description => "Initiate a Change Request in Jira Service Desk"; + public string? Keywords => null; + public bool ShowInStepTemplatePickerUI => true; + public bool WhenInAChildStepRunInTheContextOfTheTargetMachine => false; + public bool CanRunOnDeploymentTarget => false; + public ActionHandlerCategory[] Categories => new[] { ActionHandlerCategory.BuiltInStep, ActionHandlerCategory.Atlassian }; + public IActionHandlerResult Execute(IActionHandlerContext context, ITaskLog taskLog) { var deploymentId = context.Variables.Get(KnownVariables.Deployment.Id, ""); @@ -51,6 +52,5 @@ public IActionHandlerResult Execute(IActionHandlerContext context, ITaskLog task return ActionHandlerResult.FromSuccess(); } - } -} \ No newline at end of file +} diff --git a/source/Server/Configuration/DatabaseInitializer.cs b/source/Server/Configuration/DatabaseInitializer.cs index fe4090e..93565ef 100644 --- a/source/Server/Configuration/DatabaseInitializer.cs +++ b/source/Server/Configuration/DatabaseInitializer.cs @@ -1,4 +1,5 @@ -using Octopus.Data.Storage.Configuration; +using System; +using Octopus.Data.Storage.Configuration; using Octopus.Diagnostics; using Octopus.Server.Extensibility.Extensions.Infrastructure; @@ -40,4 +41,4 @@ class JiraConfigurationWithSettableId : JiraConfiguration public new string Id { get; set; } = string.Empty; } } -} \ No newline at end of file +} diff --git a/source/Server/Configuration/IJiraConfigurationSettings.cs b/source/Server/Configuration/IJiraConfigurationSettings.cs index dad268d..427c847 100644 --- a/source/Server/Configuration/IJiraConfigurationSettings.cs +++ b/source/Server/Configuration/IJiraConfigurationSettings.cs @@ -1,7 +1,9 @@ -using Octopus.Server.Extensibility.Extensions.Infrastructure.Configuration; +using System; +using Octopus.Server.Extensibility.Extensions.Infrastructure.Configuration; namespace Octopus.Server.Extensibility.JiraIntegration.Configuration { interface IJiraConfigurationSettings : IHasConfigurationSettings - { } -} \ No newline at end of file + { + } +} diff --git a/source/Server/Configuration/IJiraConfigurationStore.cs b/source/Server/Configuration/IJiraConfigurationStore.cs index f83bc38..33d7c3d 100644 --- a/source/Server/Configuration/IJiraConfigurationStore.cs +++ b/source/Server/Configuration/IJiraConfigurationStore.cs @@ -1,4 +1,5 @@ -using Octopus.Data.Model; +using System; +using Octopus.Data.Model; using Octopus.Server.Extensibility.Extensions.Infrastructure.Configuration; namespace Octopus.Server.Extensibility.JiraIntegration.Configuration @@ -9,19 +10,19 @@ interface IJiraConfigurationStore : IExtensionConfigurationStore typeof(JiraConfiguration); + public Type GetTypeToMap() + { + return typeof(JiraConfiguration); + } } -} \ No newline at end of file +} diff --git a/source/Server/Configuration/JiraConfigurationResource.cs b/source/Server/Configuration/JiraConfigurationResource.cs index e0d7259..777e0c0 100644 --- a/source/Server/Configuration/JiraConfigurationResource.cs +++ b/source/Server/Configuration/JiraConfigurationResource.cs @@ -1,4 +1,5 @@ -using System.ComponentModel; +using System; +using System.ComponentModel; using Octopus.Server.Extensibility.Extensions.Infrastructure.Configuration; using Octopus.Server.MessageContracts; using Octopus.Server.MessageContracts.Attributes; @@ -42,7 +43,7 @@ class JiraConfigurationResource : ExtensionConfigurationResource public string? OctopusServerUrl { get; set; } [DisplayName("Release Note Options")] - public ReleaseNoteOptionsResource ReleaseNoteOptions { get; set; } = new ReleaseNoteOptionsResource(); + public ReleaseNoteOptionsResource ReleaseNoteOptions { get; set; } = new(); } class ReleaseNoteOptionsResource diff --git a/source/Server/Configuration/JiraConfigurationSettings.cs b/source/Server/Configuration/JiraConfigurationSettings.cs index caa0cb8..1d72608 100644 --- a/source/Server/Configuration/JiraConfigurationSettings.cs +++ b/source/Server/Configuration/JiraConfigurationSettings.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using Octopus.Data.Model; using Octopus.Server.Extensibility.Extensions.Infrastructure.Configuration; using Octopus.Server.Extensibility.HostServices.Configuration; @@ -9,10 +10,10 @@ namespace Octopus.Server.Extensibility.JiraIntegration.Configuration { class JiraConfigurationSettings : ExtensionConfigurationSettings, IJiraConfigurationSettings { - private readonly IInstallationIdProvider installationIdProvider; - private readonly IServerConfigurationStore serverConfigurationStore; + readonly IInstallationIdProvider installationIdProvider; + readonly IServerConfigurationStore serverConfigurationStore; - public JiraConfigurationSettings(IJiraConfigurationStore configurationDocumentStore, + public JiraConfigurationSettings(IJiraConfigurationStore configurationDocumentStore, IInstallationIdProvider installationIdProvider, IServerConfigurationStore serverConfigurationStore) : base(configurationDocumentStore) { @@ -30,7 +31,7 @@ public override IEnumerable GetConfigurationValues() { var isEnabled = ConfigurationDocumentStore.GetIsEnabled(); - yield return new ConfigurationValue( "Octopus.JiraIntegration.IsEnabled", isEnabled, isEnabled, "Is Enabled"); + yield return new ConfigurationValue("Octopus.JiraIntegration.IsEnabled", isEnabled, isEnabled, "Is Enabled"); yield return new ConfigurationValue("Octopus.JiraIntegration.BaseUrl", ConfigurationDocumentStore.GetBaseUrl(), isEnabled && !string.IsNullOrWhiteSpace(ConfigurationDocumentStore.GetBaseUrl()), "Jira Base Url"); yield return new ConfigurationValue("Octopus.JiraIntegration.ConnectAppPassword", ConfigurationDocumentStore.GetConnectAppPassword(), isEnabled && !string.IsNullOrWhiteSpace(ConfigurationDocumentStore.GetConnectAppPassword()?.Value), "Jira Connect App Password"); yield return new ConfigurationValue("Octopus.JiraIntegration.Username", ConfigurationDocumentStore.GetJiraUsername(), isEnabled && !string.IsNullOrWhiteSpace(ConfigurationDocumentStore.GetJiraUsername()), "Jira Username"); @@ -41,12 +42,13 @@ public override IEnumerable GetConfigurationValues() public override void BuildMappings(IResourceMappingsBuilder builder) { builder.Map() - .EnrichResource((model, resource) => - { - resource.OctopusInstallationId = installationIdProvider.GetInstallationId().ToString(); - resource.OctopusServerUrl = serverConfigurationStore.GetServerUri(); - }); + .EnrichResource( + (model, resource) => + { + resource.OctopusInstallationId = installationIdProvider.GetInstallationId().ToString(); + resource.OctopusServerUrl = serverConfigurationStore.GetServerUri(); + }); builder.Map(); } } -} \ No newline at end of file +} diff --git a/source/Server/Configuration/JiraConfigurationStore.cs b/source/Server/Configuration/JiraConfigurationStore.cs index 9b86e54..513307f 100644 --- a/source/Server/Configuration/JiraConfigurationStore.cs +++ b/source/Server/Configuration/JiraConfigurationStore.cs @@ -1,4 +1,5 @@ -using Octopus.Data.Model; +using System; +using Octopus.Data.Model; using Octopus.Data.Storage.Configuration; using Octopus.Server.Extensibility.Extensions.Infrastructure.Configuration; diff --git a/source/Server/Configuration/JiraConfigureCommands.cs b/source/Server/Configuration/JiraConfigureCommands.cs index cb773a6..9860b62 100644 --- a/source/Server/Configuration/JiraConfigureCommands.cs +++ b/source/Server/Configuration/JiraConfigureCommands.cs @@ -20,22 +20,32 @@ public JiraConfigureCommands( public IEnumerable GetOptions() { - yield return new ConfigureCommandOption("jiraIsEnabled=", "Set whether Jira Integration is enabled.", v => - { - var isEnabled = bool.Parse(v); - jiraConfiguration.Value.SetIsEnabled(isEnabled); - systemLog.Info($"Jira Integration IsEnabled set to: {isEnabled}"); - }); - yield return new ConfigureCommandOption("jiraBaseUrl=", JiraConfigurationResource.JiraBaseUrlDescription, v => - { - jiraConfiguration.Value.SetBaseUrl(v); - systemLog.Info($"Jira Integration base Url set to: {v}"); - }); - yield return new ConfigureCommandOption("jiraConnectAppUrl=", "Set the URL for the Jira Connect App", v => - { - jiraConfiguration.Value.SetConnectAppUrl(v); - systemLog.Info($"Jira Integration ConnectAppUrl set to: {v}"); - }, hide: true); + yield return new ConfigureCommandOption( + "jiraIsEnabled=", + "Set whether Jira Integration is enabled.", + v => + { + var isEnabled = bool.Parse(v); + jiraConfiguration.Value.SetIsEnabled(isEnabled); + systemLog.Info($"Jira Integration IsEnabled set to: {isEnabled}"); + }); + yield return new ConfigureCommandOption( + "jiraBaseUrl=", + JiraConfigurationResource.JiraBaseUrlDescription, + v => + { + jiraConfiguration.Value.SetBaseUrl(v); + systemLog.Info($"Jira Integration base Url set to: {v}"); + }); + yield return new ConfigureCommandOption( + "jiraConnectAppUrl=", + "Set the URL for the Jira Connect App", + v => + { + jiraConfiguration.Value.SetConnectAppUrl(v); + systemLog.Info($"Jira Integration ConnectAppUrl set to: {v}"); + }, + true); } } -} \ No newline at end of file +} diff --git a/source/Server/Deployments/DeploymentObserver.cs b/source/Server/Deployments/DeploymentObserver.cs index b7f3bb9..433c713 100644 --- a/source/Server/Deployments/DeploymentObserver.cs +++ b/source/Server/Deployments/DeploymentObserver.cs @@ -1,4 +1,5 @@ -using System.Linq; +using System; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Octopus.Server.Extensibility.HostServices.Diagnostics; @@ -10,9 +11,9 @@ namespace Octopus.Server.Extensibility.JiraIntegration.Deployments { class DeploymentObserver : IHandleEvent { - private JiraDeployment jiraDeployment; - private readonly IMediator mediator; - private readonly ITaskLogFactory taskLogFactory; + readonly IMediator mediator; + readonly ITaskLogFactory taskLogFactory; + readonly JiraDeployment jiraDeployment; public DeploymentObserver(JiraDeployment jiraDeployment, IMediator mediator, @@ -26,9 +27,11 @@ public DeploymentObserver(JiraDeployment jiraDeployment, public async Task Handle(DeploymentEvent domainEvent, CancellationToken cancellationToken) { var deployment = (await mediator.Request(new GetDeploymentRequest(domainEvent.DeploymentId), cancellationToken)).Deployment; - if (deployment.Changes.All(drn => - drn.BuildInformation.All(pm => - pm.WorkItems.All(wi => wi.Source != JiraConfigurationStore.CommentParser)))) + if (deployment.Changes.All( + drn => + drn.BuildInformation.All( + pm => + pm.WorkItems.All(wi => wi.Source != JiraConfigurationStore.CommentParser)))) return; var taskLog = taskLogFactory.Get(domainEvent.TaskLogCorrelationId); @@ -50,6 +53,5 @@ string StateFromEventType(DeploymentEventType eventType) return "unknown"; } } - } -} \ No newline at end of file +} diff --git a/source/Server/Deployments/IJiraApiDeployment.cs b/source/Server/Deployments/IJiraApiDeployment.cs index c6d4237..dfa2aac 100644 --- a/source/Server/Deployments/IJiraApiDeployment.cs +++ b/source/Server/Deployments/IJiraApiDeployment.cs @@ -1,3 +1,4 @@ +using System; using Octopus.Server.MessageContracts.Features.Projects.Releases.Deployments; namespace Octopus.Server.Extensibility.JiraIntegration.Deployments @@ -8,4 +9,4 @@ interface IJiraApiDeployment public string[] DeploymentValues(DeploymentResource deployment); public void HandleJiraIntegrationIsUnavailable(); } -} \ No newline at end of file +} diff --git a/source/Server/Deployments/JiraAssociationConstants.cs b/source/Server/Deployments/JiraAssociationConstants.cs index 18bfffe..412f820 100644 --- a/source/Server/Deployments/JiraAssociationConstants.cs +++ b/source/Server/Deployments/JiraAssociationConstants.cs @@ -1,13 +1,16 @@ -namespace Octopus.Server.Extensibility.JiraIntegration.Deployments +using System; + +namespace Octopus.Server.Extensibility.JiraIntegration.Deployments { static class JiraAssociationConstants { public static string JiraAssociationTypeIssueKeys = "issueKeys"; public static string JiraAssociationTypeServiceIdOrKeys = "serviceIdOrKeys"; - public static readonly string[] ValidJiraAssociationTypes = { + public static readonly string[] ValidJiraAssociationTypes = + { JiraAssociationTypeIssueKeys, JiraAssociationTypeServiceIdOrKeys }; } -} \ No newline at end of file +} diff --git a/source/Server/Deployments/JiraDeployment.cs b/source/Server/Deployments/JiraDeployment.cs index 46bd5c4..63d2c22 100644 --- a/source/Server/Deployments/JiraDeployment.cs +++ b/source/Server/Deployments/JiraDeployment.cs @@ -26,16 +26,15 @@ namespace Octopus.Server.Extensibility.JiraIntegration.Deployments { class JiraDeployment { - private readonly IMediator mediator; - private readonly ITaskLogFactory taskLogFactory; - private readonly IJiraConfigurationStore store; - private readonly JiraConnectAppClient connectAppClient; - private readonly IInstallationIdProvider installationIdProvider; - private readonly IClock clock; - private readonly IProvideDeploymentEnvironmentSettingsValues deploymentEnvironmentSettingsProvider; - private readonly IServerConfigurationStore serverConfigurationStore; - private readonly IOctopusHttpClientFactory octopusHttpClientFactory; - + readonly IMediator mediator; + readonly ITaskLogFactory taskLogFactory; + readonly IJiraConfigurationStore store; + readonly JiraConnectAppClient connectAppClient; + readonly IInstallationIdProvider installationIdProvider; + readonly IClock clock; + readonly IProvideDeploymentEnvironmentSettingsValues deploymentEnvironmentSettingsProvider; + readonly IServerConfigurationStore serverConfigurationStore; + readonly IOctopusHttpClientFactory octopusHttpClientFactory; public JiraDeployment( IMediator mediator, @@ -62,12 +61,14 @@ IOctopusHttpClientFactory octopusHttpClientFactory bool JiraIntegrationUnavailable(DeploymentResource deployment) { - return !store.GetIsEnabled() || - store.GetJiraInstanceType() == JiraInstanceType.Server; + return !store.GetIsEnabled() || store.GetJiraInstanceType() == JiraInstanceType.Server; } - public async Task PublishToJira(string eventType, DeploymentResource deployment, IJiraApiDeployment jiraApiDeployment, - ITaskLog taskLog, CancellationToken cancellationToken) + public async Task PublishToJira(string eventType, + DeploymentResource deployment, + IJiraApiDeployment jiraApiDeployment, + ITaskLog taskLog, + CancellationToken cancellationToken) { if (JiraIntegrationUnavailable(deployment)) { @@ -83,8 +84,7 @@ public async Task PublishToJira(string eventType, DeploymentResource deployment, return; } - if (string.IsNullOrWhiteSpace(store.GetConnectAppUrl()) || - string.IsNullOrWhiteSpace(store.GetConnectAppPassword()?.Value)) + if (string.IsNullOrWhiteSpace(store.GetConnectAppUrl()) || string.IsNullOrWhiteSpace(store.GetConnectAppPassword()?.Value)) { taskLog.Warn("Jira integration is enabled but settings are incomplete, ignoring deployment events"); return; @@ -105,18 +105,36 @@ public async Task PublishToJira(string eventType, DeploymentResource deployment, var environmentSettings = deploymentEnvironmentSettingsProvider .GetSettings( - JiraConfigurationStore.SingletonId, deployment.EnvironmentId.Value) ?? new DeploymentEnvironmentSettingsMetadataProvider.JiraDeploymentEnvironmentSettings(); - - var data = await PrepareOctopusJiraPayload(eventType, serverUri, deployment, jiraApiDeployment, cancellationToken, deploymentEnvironment, environmentSettings); + JiraConfigurationStore.SingletonId, + deployment.EnvironmentId.Value) + ?? new DeploymentEnvironmentSettingsMetadataProvider.JiraDeploymentEnvironmentSettings(); + + var data = await PrepareOctopusJiraPayload( + eventType, + serverUri, + deployment, + jiraApiDeployment, + cancellationToken, + deploymentEnvironment, + environmentSettings); // Push data to Jira - await SendToJira(token, data, deployment, taskLogBlock, deploymentEnvironment, environmentSettings); + await SendToJira( + token, + data, + deployment, + taskLogBlock, + deploymentEnvironment, + environmentSettings); taskLogFactory.Finish(taskLogBlock); } - async Task PrepareOctopusJiraPayload(string eventType, string serverUri, - DeploymentResource deployment, IJiraApiDeployment jiraApiDeployment, CancellationToken cancellationToken, + async Task PrepareOctopusJiraPayload(string eventType, + string serverUri, + DeploymentResource deployment, + IJiraApiDeployment jiraApiDeployment, + CancellationToken cancellationToken, EnvironmentResource deploymentEnvironment, DeploymentEnvironmentSettingsMetadataProvider.JiraDeploymentEnvironmentSettings environmentSettings) @@ -125,13 +143,11 @@ async Task PrepareOctopusJiraPayload(string eventType, s var release = (await mediator.Request(new GetReleaseRequest(deployment.ReleaseId), cancellationToken)).Release; var serverTask = (await mediator.Request(new GetServerTaskRequest(deployment.TaskId), cancellationToken)).Task; - + TenantResource? tenant = null; if (deployment.TenantId?.Value is not null) - { tenant = (await mediator.Request(new GetTenantByIdOrNameRequest(deployment.TenantId.Value.ToTenantIdOrName()), cancellationToken)).Resource; - } - + return new OctopusJiraPayloadData { InstallationId = installationIdProvider.GetInstallationId().ToString(), @@ -145,9 +161,9 @@ async Task PrepareOctopusJiraPayload(string eventType, s DeploymentSequenceNumber = int.Parse(deployment.Id!.ToString().Split('-')[1]), UpdateSequenceNumber = DateTime.UtcNow.Ticks, DisplayName = serverTask.Description, - Associations = new [] + Associations = new[] { - new JiraAssociation() + new JiraAssociation { AssociationType = jiraApiDeployment.DeploymentType, Values = jiraApiDeployment.DeploymentValues(deployment) @@ -167,7 +183,7 @@ async Task PrepareOctopusJiraPayload(string eventType, s Environment = new JiraDeploymentEnvironment { Id = $"{deployment.EnvironmentId}{(deployment.TenantId is null ? "" : $"-{deployment.TenantId}")}", - DisplayName = $"{deploymentEnvironment.Name}{(tenant?.Name is null ? "" : $" ({tenant.Name})")}", + DisplayName = $"{deploymentEnvironment.Name}{(tenant?.Name is null ? "" : $" ({tenant.Name})")}", Type = environmentSettings?.JiraEnvironmentType.ToString() ?? JiraEnvironmentType.unmapped.ToString() }, SchemeVersion = "1.0" @@ -177,8 +193,11 @@ async Task PrepareOctopusJiraPayload(string eventType, s }; } - async Task SendToJira(string token, OctopusJiraPayloadData data, DeploymentResource deployment, - ITaskLog taskLogBlock, EnvironmentResource deploymentEnvironment, + async Task SendToJira(string token, + OctopusJiraPayloadData data, + DeploymentResource deployment, + ITaskLog taskLogBlock, + EnvironmentResource deploymentEnvironment, DeploymentEnvironmentSettingsMetadataProvider.JiraDeploymentEnvironmentSettings environmentSettings) { @@ -202,4 +221,4 @@ async Task SendToJira(string token, OctopusJiraPayloadData data, DeploymentResou } } } -} \ No newline at end of file +} diff --git a/source/Server/Deployments/JiraDeploymentException.cs b/source/Server/Deployments/JiraDeploymentException.cs index 09b2f06..0fd6cec 100644 --- a/source/Server/Deployments/JiraDeploymentException.cs +++ b/source/Server/Deployments/JiraDeploymentException.cs @@ -14,4 +14,4 @@ public JiraDeploymentException(string message, Exception innerException) { } } -} \ No newline at end of file +} diff --git a/source/Server/Deployments/JiraIssueTrackerApiDeployment.cs b/source/Server/Deployments/JiraIssueTrackerApiDeployment.cs index 2cdfd95..c22d75f 100644 --- a/source/Server/Deployments/JiraIssueTrackerApiDeployment.cs +++ b/source/Server/Deployments/JiraIssueTrackerApiDeployment.cs @@ -1,3 +1,4 @@ +using System; using System.Linq; using Octopus.Server.Extensibility.JiraIntegration.Configuration; using Octopus.Server.MessageContracts.Features.Projects.Releases.Deployments; @@ -10,13 +11,17 @@ class JiraIssueTrackerApiDeployment : IJiraApiDeployment public string[] DeploymentValues(DeploymentResource deployment) { - return deployment.Changes.SelectMany(drn => drn.BuildInformation - .SelectMany(pm => pm.WorkItems) - .Where(wi => wi.Source == JiraConfigurationStore.CommentParser) - .Select(wi => wi.Id) - .Distinct()).ToArray(); + return deployment.Changes.SelectMany( + drn => drn.BuildInformation + .SelectMany(pm => pm.WorkItems) + .Where(wi => wi.Source == JiraConfigurationStore.CommentParser) + .Select(wi => wi.Id) + .Distinct()) + .ToArray(); } - public void HandleJiraIntegrationIsUnavailable() { } + public void HandleJiraIntegrationIsUnavailable() + { + } } -} \ No newline at end of file +} diff --git a/source/Server/Deployments/JiraPayloadData.cs b/source/Server/Deployments/JiraPayloadData.cs index 61e4b8c..90b8f3a 100644 --- a/source/Server/Deployments/JiraPayloadData.cs +++ b/source/Server/Deployments/JiraPayloadData.cs @@ -14,27 +14,33 @@ class JiraDeploymentData { [JsonProperty("deploymentSequenceNumber")] public int DeploymentSequenceNumber { get; set; } + [JsonProperty("updateSequenceNumber")] public long UpdateSequenceNumber { get; set; } + [JsonProperty("displayName")] public string DisplayName { get; set; } = string.Empty; [JsonProperty("associations")] public JiraAssociation[] Associations { get; set; } = Array.Empty(); + [JsonProperty("url")] public string Url { get; set; } = string.Empty; + [JsonProperty("description")] public string? Description { get; set; } + [JsonProperty("lastUpdated")] public DateTimeOffset LastUpdated { get; set; } + [JsonProperty("state")] public string State { get; set; } = string.Empty; [JsonProperty("pipeline")] - public JiraDeploymentPipeline Pipeline { get; set; } = new JiraDeploymentPipeline(); + public JiraDeploymentPipeline Pipeline { get; set; } = new(); [JsonProperty("environment")] - public JiraDeploymentEnvironment Environment { get; set; } = new JiraDeploymentEnvironment(); + public JiraDeploymentEnvironment Environment { get; set; } = new(); [JsonProperty("id")] public string SchemeVersion { get; set; } = string.Empty; @@ -44,8 +50,10 @@ class JiraDeploymentPipeline { [JsonProperty("id")] public string Id { get; set; } = string.Empty; + [JsonProperty("displayName")] public string DisplayName { get; set; } = string.Empty; + [JsonProperty("url")] public string Url { get; set; } = string.Empty; } @@ -54,31 +62,32 @@ class JiraDeploymentEnvironment { [JsonProperty("id")] public string Id { get; set; } = string.Empty; + [JsonProperty("displayName")] public string DisplayName { get; set; } = string.Empty; + [JsonProperty("type")] public string Type { get; set; } = string.Empty; } class JiraAssociation { - private string associationType = string.Empty; + string associationType = string.Empty; + [JsonProperty("associationType")] - public string AssociationType { + public string AssociationType + { get => associationType; set { if (!JiraAssociationConstants.ValidJiraAssociationTypes.Contains(value)) - { throw new Exception($"Association Type {value} is not a valid type."); - } associationType = value; - } + } } [JsonProperty("values")] public string[] Values { get; set; } = Array.Empty(); } - -} \ No newline at end of file +} diff --git a/source/Server/Deployments/JiraServiceDeskApiDeployment.cs b/source/Server/Deployments/JiraServiceDeskApiDeployment.cs index 15e1943..7be2175 100644 --- a/source/Server/Deployments/JiraServiceDeskApiDeployment.cs +++ b/source/Server/Deployments/JiraServiceDeskApiDeployment.cs @@ -16,18 +16,15 @@ public JiraServiceDeskApiDeployment(string jiraServiceId) public string[] DeploymentValues(DeploymentResource deployment) { - if (String.IsNullOrEmpty(jiraServiceId)) - { + if (string.IsNullOrEmpty(jiraServiceId)) throw new JiraDeploymentException("Service ID is empty. Please supply a Jira Service Desk Service ID and try again"); - } + return new[] { jiraServiceId }; } public void HandleJiraIntegrationIsUnavailable() { - throw new JiraDeploymentException($"Trying to use Jira Service Desk Change Request step without having " + - $"Jira Integration enabled. Please enable Jira Integration or disable the Jira " + - $"Service Desk Change Request step"); + throw new JiraDeploymentException("Trying to use Jira Service Desk Change Request step without having " + "Jira Integration enabled. Please enable Jira Integration or disable the Jira " + "Service Desk Change Request step"); } } -} \ No newline at end of file +} diff --git a/source/Server/Deployments/OctopusJiraPayloadData.cs b/source/Server/Deployments/OctopusJiraPayloadData.cs index 0154e91..75cacf0 100644 --- a/source/Server/Deployments/OctopusJiraPayloadData.cs +++ b/source/Server/Deployments/OctopusJiraPayloadData.cs @@ -1,9 +1,11 @@ -namespace Octopus.Server.Extensibility.JiraIntegration.Deployments +using System; + +namespace Octopus.Server.Extensibility.JiraIntegration.Deployments { class OctopusJiraPayloadData { public string InstallationId { get; set; } = string.Empty; public string BaseHostUrl { get; set; } = string.Empty; - public JiraPayloadData DeploymentsInfo { get; set; } = new JiraPayloadData(); + public JiraPayloadData DeploymentsInfo { get; set; } = new(); } -} \ No newline at end of file +} diff --git a/source/Server/Environments/DeploymentEnvironmentSettingsMetadataProvider.cs b/source/Server/Environments/DeploymentEnvironmentSettingsMetadataProvider.cs index c924a7e..2a848f8 100644 --- a/source/Server/Environments/DeploymentEnvironmentSettingsMetadataProvider.cs +++ b/source/Server/Environments/DeploymentEnvironmentSettingsMetadataProvider.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.ComponentModel; using System.Linq; using Octopus.Server.Extensibility.Extensions.Model.Environments; @@ -10,7 +11,7 @@ namespace Octopus.Server.Extensibility.JiraIntegration.Environments { class DeploymentEnvironmentSettingsMetadataProvider : IContributeDeploymentEnvironmentSettingsMetadata { - private readonly IJiraConfigurationStore store; + readonly IJiraConfigurationStore store; public DeploymentEnvironmentSettingsMetadataProvider(IJiraConfigurationStore store) { @@ -42,4 +43,4 @@ enum JiraEnvironmentType staging, production } -} \ No newline at end of file +} diff --git a/source/Server/Integration/IJiraRestClient.cs b/source/Server/Integration/IJiraRestClient.cs index 5255ae4..0739ec9 100644 --- a/source/Server/Integration/IJiraRestClient.cs +++ b/source/Server/Integration/IJiraRestClient.cs @@ -1,3 +1,4 @@ +using System; using System.Threading.Tasks; using Octopus.Server.Extensibility.Resources.Configuration; using Octopus.Server.Extensibility.Results; @@ -9,4 +10,4 @@ interface IJiraRestClient Task ConnectivityCheck(); Task> GetIssues(string[] workItemId); } -} \ No newline at end of file +} diff --git a/source/Server/Integration/JiraConnectAppClient.cs b/source/Server/Integration/JiraConnectAppClient.cs index f1d58c0..16be006 100644 --- a/source/Server/Integration/JiraConnectAppClient.cs +++ b/source/Server/Integration/JiraConnectAppClient.cs @@ -13,9 +13,9 @@ namespace Octopus.Server.Extensibility.JiraIntegration.Integration { class JiraConnectAppClient { - private readonly IInstallationIdProvider installationIdProvider; - private readonly IJiraConfigurationStore configurationStore; - private readonly IOctopusHttpClientFactory octopusHttpClientFactory; + readonly IInstallationIdProvider installationIdProvider; + readonly IJiraConfigurationStore configurationStore; + readonly IOctopusHttpClientFactory octopusHttpClientFactory; public JiraConnectAppClient( IInstallationIdProvider installationIdProvider, @@ -46,10 +46,13 @@ public JiraConnectAppClient( if (result.IsSuccessStatusCode) { var authTokenFromConnectApp = - JsonConvert.DeserializeObject(result.Content.ReadAsStringAsync().GetAwaiter() - .GetResult()); + JsonConvert.DeserializeObject( + result.Content.ReadAsStringAsync() + .GetAwaiter() + .GetResult()); return authTokenFromConnectApp.Token; } + log.ErrorFormat("Unable to get authentication token for Jira Connect App. Response code: {0}", result.StatusCode); return null; @@ -71,6 +74,5 @@ class JsonTokenData [JsonProperty("token")] public string Token { get; set; } = string.Empty; } - } -} \ No newline at end of file +} diff --git a/source/Server/Integration/JiraRestClient.cs b/source/Server/Integration/JiraRestClient.cs index 9271ee7..a3ecb40 100644 --- a/source/Server/Integration/JiraRestClient.cs +++ b/source/Server/Integration/JiraRestClient.cs @@ -19,19 +19,23 @@ class JiraRestClient : IJiraRestClient, IDisposable { const string BrowseProjectsKey = "BROWSE_PROJECTS"; - private readonly AuthenticationHeaderValue authorizationHeader; - private readonly HttpClient httpClient; + readonly AuthenticationHeaderValue authorizationHeader; + readonly HttpClient httpClient; - private readonly string baseUrl; - private readonly ISystemLog systemLog; - private readonly string baseApiUri = "rest/api/2"; + readonly string baseUrl; + readonly ISystemLog systemLog; + readonly string baseApiUri = "rest/api/2"; - public JiraRestClient(string baseUrl, string username, string? password, ISystemLog systemLog, + public JiraRestClient(string baseUrl, + string username, + string? password, + ISystemLog systemLog, IOctopusHttpClientFactory octopusHttpClientFactory) { this.baseUrl = baseUrl; this.systemLog = systemLog; - authorizationHeader = new AuthenticationHeaderValue("Basic", + authorizationHeader = new AuthenticationHeaderValue( + "Basic", Convert.ToBase64String(Encoding.UTF8.GetBytes($"{username}:{password}"))); httpClient = CreateHttpClient(octopusHttpClientFactory); } @@ -54,13 +58,14 @@ public async Task ConnectivityCheck() if (permissionsContainer == null) { - connectivityCheckResponse.AddMessage(ConnectivityCheckMessageCategory.Error,$"Unable to read permissions from response body"); + connectivityCheckResponse.AddMessage(ConnectivityCheckMessageCategory.Error, "Unable to read permissions from response body"); return connectivityCheckResponse; } if (!permissionsContainer.Permissions.ContainsKey(BrowseProjectsKey)) { - connectivityCheckResponse.AddMessage(ConnectivityCheckMessageCategory.Error, + connectivityCheckResponse.AddMessage( + ConnectivityCheckMessageCategory.Error, $"Permissions returned from Jira does not contain the {BrowseProjectsKey} permission details."); return connectivityCheckResponse; } @@ -68,7 +73,8 @@ public async Task ConnectivityCheck() var setting = permissionsContainer.Permissions[BrowseProjectsKey]; if (!setting.HavePermission) { - connectivityCheckResponse.AddMessage(ConnectivityCheckMessageCategory.Error, + connectivityCheckResponse.AddMessage( + ConnectivityCheckMessageCategory.Error, $"User does not have the '{setting.Name}' permission in Jira"); return connectivityCheckResponse; } @@ -77,20 +83,23 @@ public async Task ConnectivityCheck() } } - connectivityCheckResponse.AddMessage(ConnectivityCheckMessageCategory.Error, + connectivityCheckResponse.AddMessage( + ConnectivityCheckMessageCategory.Error, $"Failed to connect to {baseUrl}. Response code: {response.StatusCode}{(!string.IsNullOrEmpty(response.ReasonPhrase) ? $" Reason: {response.ReasonPhrase}" : "")}"); return connectivityCheckResponse; } catch (HttpRequestException e) { - connectivityCheckResponse.AddMessage(ConnectivityCheckMessageCategory.Error, + connectivityCheckResponse.AddMessage( + ConnectivityCheckMessageCategory.Error, $"Failed to connect to {baseUrl}. Reason: {e.Message}"); return connectivityCheckResponse; } catch (TaskCanceledException e) { - connectivityCheckResponse.AddMessage(ConnectivityCheckMessageCategory.Error, + connectivityCheckResponse.AddMessage( + ConnectivityCheckMessageCategory.Error, $"Failed to connect to {baseUrl}. Reason: {e.Message}"); return connectivityCheckResponse; } @@ -102,8 +111,9 @@ public async Task> GetIssues(string[] workItem // WARNING: while the Jira API documentation says that validateQuery values of true/false are deprecated, // that is only valid for Jira Cloud. Jira Server only supports true/false - var content = JsonConvert.SerializeObject(new - { jql = workItemQuery, fields = new[] { "summary", "comment" }, maxResults = 10000, validateQuery = "false" }); + var content = JsonConvert.SerializeObject( + new + { jql = workItemQuery, fields = new[] { "summary", "comment" }, maxResults = 10000, validateQuery = "false" }); string errorMessage; try @@ -117,6 +127,7 @@ public async Task> GetIssues(string[] workItem systemLog.Info("Jira Work Item data not found in response body"); return ResultFromExtension.Failed("Jira Work Item data not found in response body"); } + systemLog.Info($"Retrieved Jira Work Item data for work item ids {string.Join(", ", result.Issues.Select(wi => wi.Key))}"); return ResultFromExtension.Success(result.Issues); } @@ -139,6 +150,7 @@ public async Task> GetIssues(string[] workItem { errorMessage = $"Failed to retrieve Jira issues '{string.Join(", ", workItemIds)}' from {baseUrl}. (Reason: {e.Message})"; } + systemLog.Warn(errorMessage); return ResultFromExtension.Failed(errorMessage); @@ -192,6 +204,7 @@ class JiraIssue { [JsonProperty("key")] public string Key { get; set; } = string.Empty; + [JsonProperty("fields")] public JiraIssueFields Fields { get; set; } = new(); } @@ -200,6 +213,7 @@ class JiraIssueFields { [JsonProperty("summary")] public string Summary { get; set; } = string.Empty; + [JsonProperty("comment")] public JiraIssueComments Comments { get; set; } = new(); } @@ -228,7 +242,8 @@ class PermissionSettingsContainer class PermissionSettings { public string Name { get; set; } = string.Empty; + [JsonProperty("havePermission")] public bool HavePermission { get; set; } } -} \ No newline at end of file +} diff --git a/source/Server/JiraIntegration.cs b/source/Server/JiraIntegration.cs index 191efa4..38b288b 100644 --- a/source/Server/JiraIntegration.cs +++ b/source/Server/JiraIntegration.cs @@ -1,4 +1,5 @@ -using Octopus.Server.Extensibility.Extensions.WorkItems; +using System; +using Octopus.Server.Extensibility.Extensions.WorkItems; using Octopus.Server.Extensibility.JiraIntegration.Configuration; namespace Octopus.Server.Extensibility.JiraIntegration diff --git a/source/Server/JiraIntegrationApi.cs b/source/Server/JiraIntegrationApi.cs index f36d1f2..0eccfbb 100644 --- a/source/Server/JiraIntegrationApi.cs +++ b/source/Server/JiraIntegrationApi.cs @@ -1,3 +1,4 @@ +using System; using Octopus.Server.Extensibility.Extensions.Infrastructure.Web.Api; using Octopus.Server.Extensibility.JiraIntegration.Web; @@ -10,8 +11,20 @@ class JiraIntegrationApi : RegistersEndpoints public JiraIntegrationApi() { - Add("POST", ApiJiraCredentialsTest, RouteCategory.Raw, new SecuredEndpointInvocation(), null, "JiraIntegration"); - Add("POST", ApiConnectAppCredentialsTest, RouteCategory.Raw, new SecuredEndpointInvocation(), null, "JiraIntegration"); + Add( + "POST", + ApiJiraCredentialsTest, + RouteCategory.Raw, + new SecuredEndpointInvocation(), + null, + "JiraIntegration"); + Add( + "POST", + ApiConnectAppCredentialsTest, + RouteCategory.Raw, + new SecuredEndpointInvocation(), + null, + "JiraIntegration"); } } -} \ No newline at end of file +} diff --git a/source/Server/JiraIntegrationExtension.cs b/source/Server/JiraIntegrationExtension.cs index cf2cfac..21fa4e8 100644 --- a/source/Server/JiraIntegrationExtension.cs +++ b/source/Server/JiraIntegrationExtension.cs @@ -1,4 +1,5 @@ -using Autofac; +using System; +using Autofac; using Octopus.Diagnostics; using Octopus.Server.Extensibility.Extensions; using Octopus.Server.Extensibility.Extensions.Infrastructure; @@ -66,29 +67,32 @@ public void Load(ContainerBuilder builder) .AsSelf() .InstancePerDependency(); - builder.Register(c => - { - var store = c.Resolve(); - if (!store.GetIsEnabled()) - return null; - - var baseUrl = store.GetBaseUrl(); - var username = store.GetJiraUsername(); - if (baseUrl == null || username == null) - return null; - - var password = store.GetJiraPassword(); - return new JiraRestClient( - baseUrl, - username, - password?.Value, - c.Resolve(), - c.Resolve() - ); - }).As() - .InstancePerDependency(); - - builder.RegisterType().As() + builder.Register( + c => + { + var store = c.Resolve(); + if (!store.GetIsEnabled()) + return null; + + var baseUrl = store.GetBaseUrl(); + var username = store.GetJiraUsername(); + if (baseUrl == null || username == null) + return null; + + var password = store.GetJiraPassword(); + return new JiraRestClient( + baseUrl, + username, + password?.Value, + c.Resolve(), + c.Resolve() + ); + }) + .As() + .InstancePerDependency(); + + builder.RegisterType() + .As() .InstancePerDependency(); } } diff --git a/source/Server/JiraIntegrationHomeLinksContributor.cs b/source/Server/JiraIntegrationHomeLinksContributor.cs index 23ab8f9..2022314 100644 --- a/source/Server/JiraIntegrationHomeLinksContributor.cs +++ b/source/Server/JiraIntegrationHomeLinksContributor.cs @@ -1,5 +1,5 @@ +using System; using System.Collections.Generic; -using System.Diagnostics; using Octopus.Server.Extensibility.HostServices.Web; using Octopus.Server.Extensibility.JiraIntegration.Configuration; @@ -7,24 +7,24 @@ namespace Octopus.Server.Extensibility.JiraIntegration { class JiraIntegrationHomeLinksContributor : IHomeLinksContributor { - private readonly IJiraConfigurationStore configurationStore; public const string ApiConnectAppCredentialsTestLinkName = "JiraConnectAppCredentialsTest"; public const string ApiJiraCredentialsTestLinkName = "JiraCredentialsTest"; + readonly IJiraConfigurationStore configurationStore; public JiraIntegrationHomeLinksContributor(IJiraConfigurationStore configurationStore) { this.configurationStore = configurationStore; } - + public IDictionary GetLinksToContribute() { var linksToContribute = new Dictionary { - {ApiConnectAppCredentialsTestLinkName, $"~{JiraIntegrationApi.ApiConnectAppCredentialsTest}"}, - {ApiJiraCredentialsTestLinkName, $"~{JiraIntegrationApi.ApiJiraCredentialsTest}"} + { ApiConnectAppCredentialsTestLinkName, $"~{JiraIntegrationApi.ApiConnectAppCredentialsTest}" }, + { ApiJiraCredentialsTestLinkName, $"~{JiraIntegrationApi.ApiJiraCredentialsTest}" } }; return linksToContribute; } } -} \ No newline at end of file +} diff --git a/source/Server/Properties/AssemblyInfo.cs b/source/Server/Properties/AssemblyInfo.cs index f07a156..6b32110 100644 --- a/source/Server/Properties/AssemblyInfo.cs +++ b/source/Server/Properties/AssemblyInfo.cs @@ -1,6 +1,6 @@ -using System.Reflection; +using System; using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("Octopus.Server.Extensibility.JiraIntegration.Tests")] [assembly: InternalsVisibleTo("Octopus.Server.Extensibility.JiraIntegration.E2E.Tests")] -[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] \ No newline at end of file +[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] diff --git a/source/Server/Server.csproj b/source/Server/Server.csproj index be4e0b3..423771d 100644 --- a/source/Server/Server.csproj +++ b/source/Server/Server.csproj @@ -1,24 +1,24 @@  - - net5.0 - Octopus.Server.Extensibility.JiraIntegration - Octopus.Server.Extensibility.JiraIntegration - Implements the Jira Integration and Jira Service Desk. - en-US - Octopus Deploy - Octopus Deploy Pty. Ltd. - https://i.octopusdeploy.com/resources/Avatar3_360.png - https://github.com/OctopusDeploy/JiraIntegration/blob/main/LICENSE.txt - https://github.com/OctopusDeploy/JiraIntegration - https://github.com/OctopusDeploy/JiraIntegration - enable - - - - - - - - - + + net5.0 + Octopus.Server.Extensibility.JiraIntegration + Octopus.Server.Extensibility.JiraIntegration + Implements the Jira Integration and Jira Service Desk. + en-US + Octopus Deploy + Octopus Deploy Pty. Ltd. + https://i.octopusdeploy.com/resources/Avatar3_360.png + https://github.com/OctopusDeploy/JiraIntegration/blob/main/LICENSE.txt + https://github.com/OctopusDeploy/JiraIntegration + https://github.com/OctopusDeploy/JiraIntegration + enable + + + + + + + + + \ No newline at end of file diff --git a/source/Server/Web/JiraConnectAppConnectivityCheckAction.cs b/source/Server/Web/JiraConnectAppConnectivityCheckAction.cs index 1f5d5ac..0e20341 100644 --- a/source/Server/Web/JiraConnectAppConnectivityCheckAction.cs +++ b/source/Server/Web/JiraConnectAppConnectivityCheckAction.cs @@ -1,3 +1,4 @@ +using System; using System.Net; using System.Net.Http; using System.Net.Http.Headers; @@ -15,14 +16,14 @@ namespace Octopus.Server.Extensibility.JiraIntegration.Web { class JiraConnectAppConnectivityCheckAction : IAsyncApiAction { - static readonly RequestBodyRegistration Data = new RequestBodyRegistration(); - static readonly OctopusJsonRegistration Result = new OctopusJsonRegistration(); + static readonly RequestBodyRegistration Data = new(); + static readonly OctopusJsonRegistration Result = new(); - private readonly ISystemLog systemLog; - private readonly IJiraConfigurationStore configurationStore; - private readonly IInstallationIdProvider installationIdProvider; - private readonly JiraConnectAppClient connectAppClient; - private readonly IOctopusHttpClientFactory octopusHttpClientFactory; + readonly ISystemLog systemLog; + readonly IJiraConfigurationStore configurationStore; + readonly IInstallationIdProvider installationIdProvider; + readonly JiraConnectAppClient connectAppClient; + readonly IOctopusHttpClientFactory octopusHttpClientFactory; public JiraConnectAppConnectivityCheckAction( ISystemLog systemLog, @@ -69,16 +70,18 @@ public async Task ExecuteAsync(IOctoRequest request) client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token); var connectivityCheckPayload = - JsonConvert.SerializeObject(new JiraConnectAppConnectivityCheckRequest {BaseHostUrl = baseUrl, OctopusInstallationId = username}); + JsonConvert.SerializeObject(new JiraConnectAppConnectivityCheckRequest { BaseHostUrl = baseUrl, OctopusInstallationId = username }); var result = await client.PostAsync( - $"{configurationStore.GetConnectAppUrl()}/relay/connectivitycheck", - new StringContent(connectivityCheckPayload, Encoding.UTF8, "application/json")); + $"{configurationStore.GetConnectAppUrl()}/relay/connectivitycheck", + new StringContent(connectivityCheckPayload, Encoding.UTF8, "application/json")); if (!result.IsSuccessStatusCode) { - connectivityCheckResponse.AddMessage(ConnectivityCheckMessageCategory.Error, result.StatusCode == HttpStatusCode.NotFound - ? $"Failed to find an installation for Jira host {configurationStore.GetBaseUrl()}. Please ensure you have installed the Octopus Deploy for Jira plugin from the [Atlassian Marketplace](https://marketplace.atlassian.com/apps/1220376/octopus-deploy-for-jira). [Learn more](https://g.octopushq.com/JiraIntegration)." - : $"Failed to check connectivity to Jira. Response code: {result.StatusCode}, Message: {result.Content.ReadAsStringAsync().GetAwaiter().GetResult()}"); + connectivityCheckResponse.AddMessage( + ConnectivityCheckMessageCategory.Error, + result.StatusCode == HttpStatusCode.NotFound + ? $"Failed to find an installation for Jira host {configurationStore.GetBaseUrl()}. Please ensure you have installed the Octopus Deploy for Jira plugin from the [Atlassian Marketplace](https://marketplace.atlassian.com/apps/1220376/octopus-deploy-for-jira). [Learn more](https://g.octopushq.com/JiraIntegration)." + : $"Failed to check connectivity to Jira. Response code: {result.StatusCode}, Message: {result.Content.ReadAsStringAsync().GetAwaiter().GetResult()}"); return Result.Response(connectivityCheckResponse); } } @@ -86,15 +89,12 @@ public async Task ExecuteAsync(IOctoRequest request) connectivityCheckResponse.AddMessage(ConnectivityCheckMessageCategory.Info, "The Jira Connect App connection was tested successfully"); if (!configurationStore.GetIsEnabled()) - { connectivityCheckResponse.AddMessage(ConnectivityCheckMessageCategory.Warning, "The Jira Integration is not enabled, so its functionality will not currently be available"); - } return Result.Response(connectivityCheckResponse); } - #nullable disable - private class JiraConnectAppConnectivityCheckRequest + class JiraConnectAppConnectivityCheckRequest { public string BaseHostUrl { get; set; } = string.Empty; public string OctopusInstallationId { get; set; } = string.Empty; diff --git a/source/Server/Web/JiraCredentialsConnectivityCheckAction.cs b/source/Server/Web/JiraCredentialsConnectivityCheckAction.cs index c2f7ea1..1f81d48 100644 --- a/source/Server/Web/JiraCredentialsConnectivityCheckAction.cs +++ b/source/Server/Web/JiraCredentialsConnectivityCheckAction.cs @@ -1,3 +1,4 @@ +using System; using System.Linq; using System.Threading.Tasks; using Octopus.Diagnostics; @@ -10,12 +11,12 @@ namespace Octopus.Server.Extensibility.JiraIntegration.Web { class JiraCredentialsConnectivityCheckAction : IAsyncApiAction { - static readonly RequestBodyRegistration Data = new RequestBodyRegistration(); - static readonly OctopusJsonRegistration Result = new OctopusJsonRegistration(); + static readonly RequestBodyRegistration Data = new(); + static readonly OctopusJsonRegistration Result = new(); - private readonly IJiraConfigurationStore configurationStore; - private readonly IOctopusHttpClientFactory octopusHttpClientFactory; - private readonly ISystemLog systemLog; + readonly IJiraConfigurationStore configurationStore; + readonly IOctopusHttpClientFactory octopusHttpClientFactory; + readonly ISystemLog systemLog; public JiraCredentialsConnectivityCheckAction(IJiraConfigurationStore configurationStore, IOctopusHttpClientFactory octopusHttpClientFactory, ISystemLog systemLog) { @@ -52,9 +53,7 @@ public async Task ExecuteAsync(IOctoRequest request) connectivityCheckResponse.AddMessage(ConnectivityCheckMessageCategory.Info, "The Jira Connect App connection was tested successfully"); if (!configurationStore.GetIsEnabled()) - { connectivityCheckResponse.AddMessage(ConnectivityCheckMessageCategory.Warning, "The Jira Integration is not enabled, so its functionality will not currently be available"); - } } return Result.Response(connectivityCheckResponse); @@ -68,5 +67,4 @@ class JiraCredentialsConnectionCheckData public string Username { get; set; } public string Password { get; set; } } - } diff --git a/source/Server/WorkItems/CommentParser.cs b/source/Server/WorkItems/CommentParser.cs index 6c9477a..cef726f 100644 --- a/source/Server/WorkItems/CommentParser.cs +++ b/source/Server/WorkItems/CommentParser.cs @@ -1,3 +1,4 @@ +using System; using System.Linq; using System.Text.RegularExpressions; using Octopus.Server.MessageContracts.Features.BuildInformation; @@ -8,7 +9,7 @@ class CommentParser { // Expression based on example found here https://confluence.atlassian.com/stashkb/integrating-with-custom-jira-issue-key-313460921.html?_ga=2.163394108.1696841245.1556699049-1954949426.1532303954 with modified negative lookbehind // with added '$' and '.' to exclude strings that look similar to Jira issues, e.g. `text that may cause confusion: $foo-1 or test.TST-01.com` - private static readonly Regex Expression = new Regex("(?[A-Z0-9]+-\\d+)(?![A-Z])", RegexOptions.Compiled | RegexOptions.IgnoreCase); + static readonly Regex Expression = new("(?[A-Z0-9]+-\\d+)(?![A-Z])", RegexOptions.Compiled | RegexOptions.IgnoreCase); public string[] ParseWorkItemIds(OctopusBuildInformation buildInformation) { @@ -17,11 +18,11 @@ public string[] ParseWorkItemIds(OctopusBuildInformation buildInformation) .ToArray(); } - private string[] WorkItemIds(string comment) + string[] WorkItemIds(string comment) { return Expression.Matches(comment) .Select(m => m.Groups[0].Value) .ToArray(); } } -} \ No newline at end of file +} diff --git a/source/Server/WorkItems/WorkItemLinkMapper.cs b/source/Server/WorkItems/WorkItemLinkMapper.cs index ac47b9c..72e2916 100644 --- a/source/Server/WorkItems/WorkItemLinkMapper.cs +++ b/source/Server/WorkItems/WorkItemLinkMapper.cs @@ -14,10 +14,10 @@ namespace Octopus.Server.Extensibility.JiraIntegration.WorkItems { class WorkItemLinkMapper : IWorkItemLinkMapper { - private readonly IJiraConfigurationStore store; - private readonly CommentParser commentParser; - private readonly Lazy jira; - private readonly ISystemLog systemLog; + readonly IJiraConfigurationStore store; + readonly CommentParser commentParser; + readonly Lazy jira; + readonly ISystemLog systemLog; public WorkItemLinkMapper(IJiraConfigurationStore store, CommentParser commentParser, @@ -37,6 +37,7 @@ public IResultFromExtension Map(OctopusBuildInformation buildInf { if (!IsEnabled) return ResultFromExtension.ExtensionDisabled(); + if (string.IsNullOrEmpty(store.GetJiraUsername()) || string.IsNullOrEmpty(store.GetJiraPassword()?.Value)) return FailureWithLog("Username/password not configured"); @@ -48,22 +49,18 @@ public IResultFromExtension Map(OctopusBuildInformation buildInf var releaseNotePrefix = store.GetReleaseNotePrefix(); var workItemIds = commentParser.ParseWorkItemIds(buildInformation).Distinct().ToArray(); if (workItemIds.Length == 0) - { return ResultFromExtension.Success(Array.Empty()); - } return TryConvertWorkItemLinks(workItemIds, releaseNotePrefix, baseUrl); } - private IResultFromExtension TryConvertWorkItemLinks(string[] workItemIds, string? releaseNotePrefix, string baseUrl) + IResultFromExtension TryConvertWorkItemLinks(string[] workItemIds, string? releaseNotePrefix, string baseUrl) { systemLog.InfoFormat("Getting work items {0} from Jira", string.Join(", ", workItemIds)); var response = jira.Value.GetIssues(workItemIds.ToArray()).GetAwaiter().GetResult(); if (response is IFailureResult failureResult) - { return FailureWithLog(failureResult.Errors); - } var issues = ((ISuccessResult)response).Value; if (issues.Length == 0) @@ -76,27 +73,27 @@ private IResultFromExtension TryConvertWorkItemLinks(string[] wo var workItemsNotFound = workItemIds.Where(x => !issueMap.ContainsKey(x)).ToArray(); if (workItemsNotFound.Length > 0) - { systemLog.Warn($"Parsed work item ids {string.Join(", ", workItemsNotFound)} from commit messages but could not locate them in Jira"); - } - return ResultFromExtension.Success(workItemIds - .Where(workItemId => issueMap.ContainsKey(workItemId)) - .Select(workItemId => - { - var issue = issueMap[workItemId]; - return new WorkItemLink - { - Id = issue.Key, - Description = GetReleaseNote(issueMap[workItemId], releaseNotePrefix), - LinkUrl = baseUrl + "/browse/" + workItemId, - Source = JiraConfigurationStore.CommentParser - }; - }) - .Where(i => i != null) - // ReSharper disable once RedundantEnumerableCastCall - .Cast() // cast back from `WorkItemLink?` type to keep the compiler happy - .ToArray()); + return ResultFromExtension.Success( + workItemIds + .Where(workItemId => issueMap.ContainsKey(workItemId)) + .Select( + workItemId => + { + var issue = issueMap[workItemId]; + return new WorkItemLink + { + Id = issue.Key, + Description = GetReleaseNote(issueMap[workItemId], releaseNotePrefix), + LinkUrl = baseUrl + "/browse/" + workItemId, + Source = JiraConfigurationStore.CommentParser + }; + }) + .Where(i => i != null) + // ReSharper disable once RedundantEnumerableCastCall + .Cast() // cast back from `WorkItemLink?` type to keep the compiler happy + .ToArray()); } public string GetReleaseNote(JiraIssue issue, string? releaseNotePrefix) @@ -108,7 +105,7 @@ public string GetReleaseNote(JiraIssue issue, string? releaseNotePrefix) var releaseNote = issue.Fields.Comments.Comments.Select(x => x.Body).Where(x => x is not null).LastOrDefault(c => releaseNoteRegex.IsMatch(c)); return !string.IsNullOrWhiteSpace(releaseNote) - ? releaseNoteRegex.Replace(releaseNote, String.Empty)?.Trim() ?? string.Empty + ? releaseNoteRegex.Replace(releaseNote, string.Empty)?.Trim() ?? string.Empty : issue.Fields.Summary ?? issue.Key; } @@ -118,4 +115,4 @@ IResultFromExtension FailureWithLog(params string[] errors) return ResultFromExtension.Failed(errors); } } -} \ No newline at end of file +} From d6942caf5149fd7c2a79d7273bf526f614ab5fab Mon Sep 17 00:00:00 2001 From: slewis74 Date: Wed, 6 Apr 2022 11:40:27 +1000 Subject: [PATCH 2/2] last couple of fixes --- .../WorkItemLinkMapperErrorHandlingTestScript.cs | 2 -- source/Server.Tests/WorkItemLinkMapperScenarios.cs | 6 ++---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/source/Server.E2E.Tests/WorkItemLinkMapperErrorHandlingTestScript.cs b/source/Server.E2E.Tests/WorkItemLinkMapperErrorHandlingTestScript.cs index 0a7a418..1ff1c41 100644 --- a/source/Server.E2E.Tests/WorkItemLinkMapperErrorHandlingTestScript.cs +++ b/source/Server.E2E.Tests/WorkItemLinkMapperErrorHandlingTestScript.cs @@ -18,9 +18,7 @@ public class WorkItemLinkMapperErrorHandlingTestScript : WorkItemMapperBaseFixtu public void HttpAuthExceptionShouldLogMeaningfulMessage() { if (!TryGetJiraSettings(out var baseUrl, out var username, out var _)) - { Assert.Ignore($"Configure the following environment variables '{JiraBaseUrlEnvironmentVariable}', '{JiraUsernameEnvironmentVariable}', '{JiraAuthTokenEnvironmentVariable}' to run these tests."); - } var log = Substitute.For(); diff --git a/source/Server.Tests/WorkItemLinkMapperScenarios.cs b/source/Server.Tests/WorkItemLinkMapperScenarios.cs index ad67eee..3cb65d3 100644 --- a/source/Server.Tests/WorkItemLinkMapperScenarios.cs +++ b/source/Server.Tests/WorkItemLinkMapperScenarios.cs @@ -121,8 +121,7 @@ public void SourceGetsSet() { Commits = new[] { - new() - { Id = "abcd", Comment = "This is a test commit message. Fixes JRE-1234" } + new Commit { Id = "abcd", Comment = "This is a test commit message. Fixes JRE-1234" } } }); @@ -156,8 +155,7 @@ public void KeyGetsUpperCased() { Commits = new[] { - new() - { Id = "abcd", Comment = "This is a test commit message. Fixes jre-1234" } + new Commit { Id = "abcd", Comment = "This is a test commit message. Fixes jre-1234" } } });