Skip to content

5x #389

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 97 commits into from
May 28, 2025
Merged

5x #389

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
97 commits
Select commit Hold shift + click to select a range
7caec91
Update docker-compose.yml
zapadi Apr 25, 2025
361cfa9
Set sdk version to 9.0.203
zapadi May 11, 2025
afbd230
Bump up packages version
zapadi May 11, 2025
aa13c27
Disable nullable and enable implicit usings
zapadi May 11, 2025
f51610c
[New] Directory.Packages.props
zapadi May 11, 2025
f950278
AType
zapadi Mar 30, 2025
614be25
Add System.Memory
zapadi May 14, 2025
1139f34
[RedmineConstants] Add AUTHORIZATION_HEADER_KEY
zapadi Jan 6, 2025
b44c5fc
[RedmineConstants] Add more keys
zapadi May 14, 2025
4a1866a
RedmineSerializationException
zapadi Apr 25, 2025
c63a1b5
WebClientExtensions
zapadi Apr 25, 2025
57ea27e
[New] IdentifiableNameExtensions
zapadi May 11, 2025
ab78f62
[New] EnumExtensions
zapadi May 11, 2025
e091d37
[DebuggerDisplay] Replace + with interpolation
zapadi May 11, 2025
6036e6e
[IRedmineSerializer] Add ContentType
zapadi May 11, 2025
3dd6859
Split ICollectionExtensions into List & Enumerable extensions
zapadi May 11, 2025
3c3dd3a
Split IRedmineApiClient into IAsync|SyncRedmineApiClient
zapadi May 11, 2025
678b1ff
Split InternalRedmineApiWebClient into sync|async
zapadi May 11, 2025
3a8b4af
[RedmineManager] Small ctor refactor
zapadi May 11, 2025
3c073ab
[RequestOptions] Add Include static method
zapadi May 11, 2025
921177f
[RequestOptions] Headers
zapadi May 11, 2025
7a2f857
[New] RandomHelper
zapadi May 14, 2025
d100e1e
[IntegrationTests] More tests
zapadi May 11, 2025
3ec413c
[Serialization] Add more Json tests
zapadi May 11, 2025
6fa7643
[IRedmineApiClientOptions] Remove redundant properties
zapadi May 11, 2025
4dc7b8d
[IdentifiableName] Add string operator
zapadi May 11, 2025
e95156a
Enums ToLowerInvariant instead of ToString().ToLowerInv()
zapadi May 11, 2025
8ad3418
Add icons
zapadi May 14, 2025
07f9c70
Add ArgumentVerifier
zapadi May 14, 2025
1ba7053
Add Include options (Group, Issue, Project, User & Wiki)
zapadi May 14, 2025
29d0780
Add Create(id, name) overload
zapadi May 11, 2025
08e906d
Add ctor with parameter
zapadi May 11, 2025
7d16b78
Add ToIssueStatusIdentifier
zapadi May 14, 2025
4fd2fc1
Add GeneratePassword & SendInformation props
zapadi May 14, 2025
6da61fc
Add static methods to create single/multiple custom fields
zapadi May 14, 2025
a070e4b
Serialize UserId only at create
zapadi May 14, 2025
63edbb2
Remove ProjectId from serialization
zapadi May 14, 2025
fa72680
Serialize default assigned/version
zapadi May 14, 2025
a8894e1
Add DocumentCategory
zapadi May 14, 2025
96a9cd7
Code arrange
zapadi May 11, 2025
9e0cf15
Update summary
zapadi May 11, 2025
85fb8ae
Version inherits from IdentifiableName instead of Identifiable
zapadi May 11, 2025
865d686
Improvements
zapadi May 11, 2025
f964a14
Remove unused directives
zapadi May 11, 2025
3b96df6
Replace magic strings with constants
zapadi May 11, 2025
06f4466
Rename extension
zapadi May 14, 2025
9512e28
Replace ToString(CultureInfo.InvariantCulture) with ToInvariantString()
zapadi May 14, 2025
f277624
Add appsettings-local
zapadi May 14, 2025
e892ae5
Move to common folder
zapadi May 14, 2025
e3c9b2b
Cleanup
zapadi May 14, 2025
f476b5c
Refactor
zapadi May 14, 2025
efc14a7
Fix clone error
zapadi May 14, 2025
366ae23
Fix wiki create
zapadi May 14, 2025
81d1913
Fix Search
zapadi May 14, 2025
b33c9d6
Add EnsureDeserializationInputIsNotNullOrWhiteSpace
zapadi May 14, 2025
52b5fc1
Throw RedmineSerializationException instead of ArgumentNullException
zapadi May 14, 2025
962a0ef
Add StatusCode to ApiResponseMessage
zapadi May 14, 2025
7a1550a
Add RedmineAuthenticationType enum
zapadi May 14, 2025
21cfb0c
Dispose register cancellation token
zapadi May 14, 2025
205f0e9
Add async download file progress
zapadi May 14, 2025
8e651b9
Suppress warning
zapadi May 14, 2025
837ee00
Enable nullale
zapadi May 14, 2025
3272a92
ApiUrl
zapadi May 14, 2025
7ad05e9
[WebClientExtensions] Fix namespace
zapadi May 14, 2025
fa7f60c
Rearrange
zapadi May 14, 2025
7620b7f
IProgress
zapadi May 14, 2025
cb95b26
Refactor exception handling
zapadi May 15, 2025
a2c61a8
Replace magic strings with constants
zapadi May 15, 2025
2c8f8e1
Fix fixture namespace
zapadi May 15, 2025
0a7c022
Fix test type -> xml
zapadi May 15, 2025
c53118f
[New] GetIssue_With_Watchers_And_Relations_Should_Succeed test
zapadi May 15, 2025
91b4649
Rename ThreadSafeRandom to RandomHelper
zapadi May 15, 2025
31ecb99
Integration tests
zapadi May 16, 2025
ec07a5c
Fix web exception handling
zapadi May 16, 2025
6e9ff65
Add AType to common
zapadi May 16, 2025
77ac5e0
[RedmineKeys] Add CUSTOM_FIELD_VALUES
zapadi May 16, 2025
defe527
Integration tests
zapadi May 16, 2025
bf60ae3
Fix project membership json serialization
zapadi May 16, 2025
196c3fe
Fix news and wiki urls
zapadi May 16, 2025
370942a
Add issue to extension
zapadi May 16, 2025
d8984fa
[New] Logger
zapadi May 22, 2025
db9e0e3
[New] Integration ProgressTests
zapadi May 22, 2025
7e48c46
Add GetMyAccountAssync
zapadi May 23, 2025
f2b2e07
Bump up redmine and postgres version
zapadi May 28, 2025
f3908ee
Remove obsolete files
zapadi May 28, 2025
1a77e2e
Change folder structure & some web client improvements
zapadi May 28, 2025
759352d
Extract host validation logic to HostHelper
zapadi May 28, 2025
0f474ef
Enums ToLowerName
zapadi May 28, 2025
3369cdd
Remove logger extensions
zapadi May 28, 2025
86e3ced
Remove escape uri
zapadi May 28, 2025
afe72d2
Project CustomFieldsValues & IssueCustomFields
zapadi May 28, 2025
87e02b7
HttpClient
zapadi May 28, 2025
a63a267
Add more Wiki deserialization tests
zapadi May 28, 2025
5e89c05
Integration tests
zapadi May 28, 2025
1435d2c
...
zapadi May 28, 2025
23065cd
Change collection type from IList to List
zapadi May 28, 2025
a77d976
Fix tests
zapadi May 28, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ jobs:
- name: Restore
run: dotnet restore "${{ env.PROJECT_PATH }}"

- name: Build
- name: 🔨 Build
run: >-
dotnet build "${{ env.PROJECT_PATH }}"
--configuration "${{ env.CONFIGURATION }}"
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ jobs:
- name: Install dependencies
run: dotnet restore "${{ env.PROJECT_PATH }}"

- name: Create the package
- name: 📦 Create the package
run: >-
dotnet pack "${{ env.PROJECT_PATH }}"
--output ./artifacts
Expand All @@ -140,7 +140,7 @@ jobs:
-p:IncludeSymbols=true
-p:SymbolPackageFormat=snupkg

- name: Create the package - Signed
- name: 📦 Create the package - Signed
run: >-
dotnet pack "${{ env.PROJECT_PATH }}"
--output ./artifacts
Expand Down
12 changes: 12 additions & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,17 @@
<Features>strict</Features>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
<Deterministic>true</Deterministic>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
</PropertyGroup>

<!-- <ItemGroup>-->
<!-- <PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" PrivateAssets="all" />-->
<!-- &lt;!&ndash; <PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" />&ndash;&gt;-->
<!-- </ItemGroup>-->

</Project>
29 changes: 29 additions & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<Project>
<PropertyGroup>
<Net20_Net46>|net20|net40|net45|net451|net452|net46|</Net20_Net46>
<Net20_Net461>|net20|net40|net45|net451|net452|net46|net461|</Net20_Net461>
<Net45_Net46>|net45|net451|net452|net46|</Net45_Net46>
<Net45_Net461>|net45|net451|net452|net46|net461|</Net45_Net461>
</PropertyGroup>
<ItemGroup Label="Build">
<PackageVersion Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" PrivateAssets="all" IncludeAssets="runtime; build; native; contentfiles; analyzers; buildtransitive" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net40' " Label=".NET 4.0 Package References">
<PackageVersion Include="System.ValueTuple" Version="4.5.0" />
<PackageVersion Include="Microsoft.Bcl.Async" Version="1.0.168" />
<PackageVersion Include="Microsoft.Net.Http" Version="2.2.29" />
</ItemGroup>
<ItemGroup Condition=" $(Net45_Net461.Contains('|$(TargetFramework)|'))" >
<PackageVersion Include="System.Net.Http" Version="4.3.4" />
<PackageVersion Include="System.Buffers" Version="4.5.1" />
<PackageVersion Include="System.Memory" Version="4.5.5" />
</ItemGroup>
<ItemGroup Condition=" $(Net20_Net461.Contains('|$(TargetFramework)|')) == FALSE " >
<PackageVersion Include="System.ValueTuple" Version="4.6.1" />
<PackageVersion Include="System.Buffers" Version="4.6.1" />
<PackageVersion Include="System.Memory" Version="4.6.3" />
<PackageVersion Include="Microsoft.Extensions.Http" Version="9.0.5" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.5" />
</ItemGroup>
</Project>
8 changes: 4 additions & 4 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ services:
redmine:
ports:
- '8089:3000'
image: 'redmine:5.1.1-alpine'
container_name: 'redmine-web'
image: 'redmine:6.0.5-alpine'
container_name: 'redmine-web605'
depends_on:
- db-postgres
# healthcheck:
Expand All @@ -32,8 +32,8 @@ services:
POSTGRES_DB: redmine
POSTGRES_USER: redmine-usr
POSTGRES_PASSWORD: redmine-pswd
container_name: 'redmine-db'
image: 'postgres:16-alpine'
container_name: 'redmine-db175'
image: 'postgres:17.5-alpine'
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 20s
Expand Down
2 changes: 1 addition & 1 deletion global.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"sdk": {
"version": "9.0.101",
"version": "9.0.203",
"allowPrerelease": false,
"rollForward": "latestMajor"
}
Expand Down
1 change: 1 addition & 0 deletions redmine-net-api.sln
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{707B6A3F
releasenotes.props = releasenotes.props
signing.props = signing.props
version.props = version.props
Directory.Packages.props = Directory.Packages.props
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Docker", "Docker", "{1D340EEB-C535-45D4-80D7-ADD4434D7B77}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ limitations under the License.
*/

using System.Net;
using Redmine.Net.Api.Extensions;

namespace Redmine.Net.Api.Authentication;

Expand All @@ -24,7 +25,7 @@ namespace Redmine.Net.Api.Authentication;
public sealed class RedmineApiKeyAuthentication: IRedmineAuthentication
{
/// <inheritdoc />
public string AuthenticationType => "X-Redmine-API-Key";
public string AuthenticationType { get; } = RedmineAuthenticationType.ApiKey.ToText();

/// <inheritdoc />
public string Token { get; init; }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Redmine.Net.Api.Authentication;

internal enum RedmineAuthenticationType
{
NoAuthentication,
Basic,
ApiKey
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ limitations under the License.
using System.Net;
using System.Text;
using Redmine.Net.Api.Exceptions;
using Redmine.Net.Api.Extensions;

namespace Redmine.Net.Api.Authentication
{
Expand All @@ -27,7 +28,7 @@ namespace Redmine.Net.Api.Authentication
public sealed class RedmineBasicAuthentication: IRedmineAuthentication
{
/// <inheritdoc />
public string AuthenticationType => "Basic";
public string AuthenticationType { get; } = RedmineAuthenticationType.Basic.ToText();

/// <inheritdoc />
public string Token { get; init; }
Expand All @@ -45,7 +46,7 @@ public RedmineBasicAuthentication(string username, string password)
if (username == null) throw new RedmineException(nameof(username));
if (password == null) throw new RedmineException(nameof(password));

Token = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{username}:{password}"));
Token = $"Basic {Convert.ToBase64String(Encoding.UTF8.GetBytes($"{username}:{password}"))}";
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ limitations under the License.
*/

using System.Net;
using Redmine.Net.Api.Extensions;

namespace Redmine.Net.Api.Authentication;

Expand All @@ -24,7 +25,7 @@ namespace Redmine.Net.Api.Authentication;
public sealed class RedmineNoAuthentication: IRedmineAuthentication
{
/// <inheritdoc />
public string AuthenticationType => "NoAuth";
public string AuthenticationType { get; } = RedmineAuthenticationType.NoAuthentication.ToText();

/// <inheritdoc />
public string Token { get; init; }
Expand Down
12 changes: 12 additions & 0 deletions src/redmine-net-api/Common/AType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System;

namespace Redmine.Net.Api.Common;

internal readonly struct A<T>{
public static A<T> Is => default;
#pragma warning disable CS0184 // 'is' expression's given expression is never of the provided type
public static bool IsEqual<U>() => Is is A<U>;
#pragma warning restore CS0184 // 'is' expression's given expression is never of the provided type
public static Type Value => typeof(T);

}
43 changes: 43 additions & 0 deletions src/redmine-net-api/Common/ArgumentVerifier.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using System;
using System.Diagnostics.CodeAnalysis;

namespace Redmine.Net.Api.Common;

/// <summary>
/// A utility class to perform argument validations.
/// </summary>
internal static class ArgumentVerifier
{
/// <summary>
/// Throws ArgumentNullException if the argument is null.
/// </summary>
/// <param name="value">Argument value to check.</param>
/// <param name="name">Name of Argument.</param>
public static void ThrowIfNull([NotNull] object value, string name)
{
if (value == null)
{
throw new ArgumentNullException(name);
}
}

/// <summary>
/// Validates string and throws:
/// ArgumentNullException if the argument is null.
/// ArgumentException if the argument is empty.
/// </summary>
/// <param name="value">Argument value to check.</param>
/// <param name="name">Name of Argument.</param>
public static void ThrowIfNullOrEmpty([NotNull] string value, string name)
{
if (value == null)
{
throw new ArgumentNullException(name);
}

if (string.IsNullOrEmpty(value))
{
throw new ArgumentException("The value cannot be null or empty", name);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ You may obtain a copy of the License at
limitations under the License.
*/

namespace Redmine.Net.Api.Types
namespace Redmine.Net.Api.Common
{
/// <summary>
///
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ limitations under the License.

using System.Collections.Generic;

namespace Redmine.Net.Api.Serialization
namespace Redmine.Net.Api.Common
{
/// <summary>
///
Expand All @@ -30,7 +30,7 @@ public sealed class PagedResults<TOut> where TOut: class
/// <param name="total"></param>
/// <param name="offset"></param>
/// <param name="pageSize"></param>
public PagedResults(IEnumerable<TOut> items, int total, int offset, int pageSize)
public PagedResults(List<TOut> items, int total, int offset, int pageSize)
{
Items = items;
TotalItems = total;
Expand Down Expand Up @@ -75,6 +75,6 @@ public PagedResults(IEnumerable<TOut> items, int total, int offset, int pageSize
/// <summary>
///
/// </summary>
public IEnumerable<TOut> Items { get; }
public List<TOut> Items { get; }
}
}
4 changes: 4 additions & 0 deletions src/redmine-net-api/Exceptions/RedmineException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ limitations under the License.
*/

using System;
using System.Diagnostics;
using System.Globalization;
using System.Runtime.Serialization;

Expand All @@ -24,6 +25,7 @@ namespace Redmine.Net.Api.Exceptions
/// Thrown in case something went wrong in Redmine
/// </summary>
/// <seealso cref="System.Exception" />
[DebuggerDisplay($"{{{nameof(DebuggerDisplay)},nq}}")]
[Serializable]
public class RedmineException : Exception
{
Expand Down Expand Up @@ -85,5 +87,7 @@ protected RedmineException(SerializationInfo serializationInfo, StreamingContext

}
#endif

private string DebuggerDisplay => $"[{Message}]";
}
}
48 changes: 48 additions & 0 deletions src/redmine-net-api/Exceptions/RedmineSerializationException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using System;

namespace Redmine.Net.Api.Exceptions;

/// <summary>
/// Represents an error that occurs during JSON serialization or deserialization.
/// </summary>
public class RedmineSerializationException : RedmineException
{
/// <summary>
/// Initializes a new instance of the <see cref="RedmineSerializationException"/> class.
/// </summary>
public RedmineSerializationException()
{
}

/// <summary>
/// Initializes a new instance of the <see cref="RedmineSerializationException"/> class with a specified error message.
/// </summary>
/// <param name="message">The error message that explains the reason for the exception.</param>
public RedmineSerializationException(string message) : base(message)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="RedmineSerializationException"/> class with a specified error message.
/// </summary>
/// <param name="message">The error message that explains the reason for the exception.</param>
/// /// <param name="paramName">The name of the parameter that caused the exception.</param>
public RedmineSerializationException(string message, string paramName) : base(message)
{
ParamName = paramName;
}

/// <summary>
/// Initializes a new instance of the <see cref="RedmineSerializationException"/> class with a specified error message and a reference to the inner exception that is the cause of this exception.
/// </summary>
/// <param name="message">The error message that explains the reason for the exception.</param>
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference if no inner exception is specified.</param>
public RedmineSerializationException(string message, Exception innerException) : base(message, innerException)
{
}

/// <summary>
/// Gets the name of the parameter that caused the current exception.
/// </summary>
public string ParamName { get; }
}
Loading
Loading