Skip to content

Commit dee35c7

Browse files
committed
feat: add .NET 10 target framework
1 parent 3ccebc1 commit dee35c7

11 files changed

Lines changed: 196 additions & 80 deletions

File tree

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ jobs:
2727
- uses: actions/setup-dotnet@v5.2.0
2828
with:
2929
dotnet-version: |
30-
6.0.x
3130
8.0.x
3231
9.0.x
32+
10.0.x
3333
- run: dotnet restore
3434
- run: dotnet build --configuration Release --no-restore /warnAsError /nologo /clp:NoSummary
3535

Directory.Build.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project>
22

33
<PropertyGroup>
4-
<TargetFrameworks>netstandard2.0;net8.0</TargetFrameworks>
4+
<TargetFrameworks>netstandard2.0;net8.0;net10.0</TargetFrameworks>
55
<LangVersion>latest</LangVersion>
66
<Nullable>enable</Nullable>
77
<GenerateDocumentationFile>true</GenerateDocumentationFile>

Directory.Packages.props

Lines changed: 26 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,40 +2,38 @@
22
<ItemGroup>
33
<PackageVersion Include="Ardalis.SmartEnum" Version="2.1.0"/>
44
<PackageVersion Include="Ardalis.SmartEnum.SystemTextJson" Version="2.1.0"/>
5-
<PackageVersion Include="coverlet.msbuild" Version="6.0.4"/>
6-
<PackageVersion Include="DotNet.ReproducibleBuilds" Version="1.2.25"/>
7-
<PackageVersion Include="DotNet.ReproducibleBuilds.Isolated" Version="1.2.25"/>
5+
<PackageVersion Include="coverlet.msbuild" Version="8.0.1"/>
6+
<PackageVersion Include="DotNet.ReproducibleBuilds" Version="2.0.2"/>
7+
<PackageVersion Include="DotNet.ReproducibleBuilds.Isolated" Version="2.0.2"/>
88
<PackageVersion Include="FluentAssertions" Version="7.2.0"/>
99
<PackageVersion Include="IsExternalInit" Version="1.0.3"/>
10-
<PackageVersion Include="Jetbrains.Annotations" Version="2025.2.2"/>
11-
<PackageVersion Include="JunitXml.TestLogger" Version="6.1.0"/>
12-
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="9.0.9"/>
13-
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="9.0.9"/>
14-
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="9.0.9"/>
15-
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.9"/>
16-
<PackageVersion Include="Microsoft.Extensions.Http" Version="9.0.9"/>
17-
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="9.0.9"/>
18-
<PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="9.0.9"/>
19-
<PackageVersion Include="Microsoft.Extensions.Options.DataAnnotations" Version="9.0.9"/>
20-
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.13.0"/>
10+
<PackageVersion Include="Jetbrains.Annotations" Version="2025.2.4"/>
11+
<PackageVersion Include="JunitXml.TestLogger" Version="8.0.0"/>
12+
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="10.0.5"/>
13+
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="10.0.5"/>
14+
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="10.0.5"/>
15+
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.5"/>
16+
<PackageVersion Include="Microsoft.Extensions.Http" Version="10.0.5"/>
17+
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="10.0.5"/>
18+
<PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="10.0.5"/>
19+
<PackageVersion Include="Microsoft.Extensions.Options.DataAnnotations" Version="10.0.5"/>
20+
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.3.0"/>
2121
<PackageVersion Include="Newtonsoft.Json" Version="13.0.4"/>
22-
<PackageVersion Include="NodaTime" Version="3.2.2"/>
23-
<PackageVersion Include="NodaTime.Serialization.SystemTextJson" Version="1.3.0"/>
24-
<PackageVersion Include="NodaTime.Testing" Version="3.2.2"/>
22+
<PackageVersion Include="NodaTime" Version="3.3.1"/>
23+
<PackageVersion Include="NodaTime.Serialization.SystemTextJson" Version="1.3.1"/>
24+
<PackageVersion Include="NodaTime.Testing" Version="3.3.1"/>
2525
<PackageVersion Include="Nullable" Version="1.3.1"/>
26-
<PackageVersion Include="NUnit" Version="3.14.0"/>
27-
<PackageVersion Include="NUnit.Analyzers" Version="4.10.0"/>
28-
<PackageVersion Include="NUnit3TestAdapter" Version="5.1.0"/>
26+
<PackageVersion Include="NUnit" Version="4.5.1"/>
27+
<PackageVersion Include="NUnit.Analyzers" Version="4.12.0"/>
28+
<PackageVersion Include="NUnit3TestAdapter" Version="6.2.0"/>
2929
<PackageVersion Include="System.ComponentModel.Annotations" Version="5.0.0"/>
30-
<PackageVersion Include="System.Linq.Async" Version="6.0.3"/>
31-
<PackageVersion Include="System.Net.Http" Version="4.3.4"/>
32-
<PackageVersion Include="System.Net.Http.Json" Version="9.0.9"/>
33-
<PackageVersion Include="System.Text.Json" Version="9.0.9"/>
34-
<PackageVersion Include="System.Text.RegularExpressions" Version="4.3.1"/>
30+
<PackageVersion Include="System.Linq.Async" Version="7.0.0"/>
31+
<PackageVersion Include="System.Net.Http.Json" Version="10.0.5"/>
32+
<PackageVersion Include="System.Text.Json" Version="10.0.5"/>
3533
<PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.556"/>
36-
<PackageVersion Include="Testcontainers.Redis" Version="3.4.0"/>
37-
<PackageVersion Include="VMelnalksnis.Testcontainers.Paperless" Version="0.2.0"/>
34+
<PackageVersion Include="Testcontainers.Redis" Version="4.6.0"/>
35+
<PackageVersion Include="VMelnalksnis.Testcontainers.Paperless" Version="0.3.0"/>
3836
<PackageVersion Include="xunit" Version="2.9.3"/>
39-
<PackageVersion Include="xunit.runner.visualstudio" Version="2.4.5"/>
37+
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5"/>
4038
</ItemGroup>
4139
</Project>

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ and [IServiceCollection](https://docs.microsoft.com/en-us/dotnet/api/microsoft.e
1717
For use outside of ASP.NET Core, see the
1818
[example in tests](tests/VMelnalksnis.PaperlessDotNet.Tests.Integration/MinimalExampleTests.cs).
1919

20-
1. Add configuration (see [options](source/VMelnalksnis.PaperlessDotNet.DependencyInjection/PaperlessOptions.cs))
20+
1. Add configuration (see [options](source/VMelnalksnis.PaperlessDotNet/PaperlessOptions.cs))
2121
```yaml
2222
"Paperless": {
2323
"BaseAddress": "",
@@ -38,7 +38,7 @@ For use outside of ASP.NET Core, see the
3838
## Filtering
3939
Some objects, such as documents, support filtering on various fields.
4040
The filter format slightly differs from the object itself, and can be seen in a respective `Filter` object;
41-
for example [DocumentFilter](source/VMelnalksnis.PaperlessDotNet/Filters/DocumentFilter.cs) for documents.
41+
for example [DocumentFilter](source/VMelnalksnis.PaperlessDotNet/Documents/DocumentFilter.cs) for documents.
4242
Filters can be written inline as expressions:
4343
```csharp
4444
var filteredDocuments = await Client.Documents.Get(

global.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"sdk": {
3-
"version": "9.0.203",
3+
"version": "10.0.201",
44
"rollForward": "latestFeature",
55
"allowPrerelease": false
66
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Copyright 2022 Valters Melnalksnis
2+
// Licensed under the Apache License 2.0.
3+
// See LICENSE file in the project root for full license information.
4+
5+
using Microsoft.Extensions.Options;
6+
7+
namespace VMelnalksnis.PaperlessDotNet.DependencyInjection;
8+
9+
/// <inheritdoc />
10+
[OptionsValidator]
11+
public sealed partial class PaperlessOptionsValidator : IValidateOptions<PaperlessOptions>;

source/VMelnalksnis.PaperlessDotNet.DependencyInjection/ServiceCollectionExtensions.cs

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
// See LICENSE file in the project root for full license information.
44

55
using System;
6-
using System.Diagnostics.CodeAnalysis;
76
using System.Linq;
87
using System.Net.Http;
98
using System.Net.Http.Headers;
@@ -39,19 +38,14 @@ static ServiceCollectionExtensions()
3938
/// <param name="serviceCollection">The service collection in which to register the services.</param>
4039
/// <param name="config">A delegate that is used to configure <see cref="PaperlessJsonSerializerOptions"/>.</param>
4140
/// <returns>The <see cref="IHttpClientBuilder"/> for the <see cref="HttpClient"/> used by <see cref="IPaperlessClient"/>.</returns>
42-
#if NETSTANDARD2_0
43-
[SuppressMessage("Trimming", "IL2026", Justification = $"{nameof(PaperlessOptions)} contains only system types.")]
44-
#else
45-
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026", Justification = $"{nameof(PaperlessOptions)} contains only system types.")]
46-
#endif
4741
public static IHttpClientBuilder AddPaperlessDotNet(
4842
this IServiceCollection serviceCollection,
4943
Action<PaperlessJsonSerializerOptions>? config = null)
5044
{
5145
serviceCollection
46+
.AddSingleton<IValidateOptions<PaperlessOptions>, PaperlessOptionsValidator>()
5247
.AddOptions<PaperlessOptions>()
53-
.BindConfiguration(PaperlessOptions.Name)
54-
.ValidateDataAnnotations();
48+
.BindConfiguration(PaperlessOptions.Name);
5549

5650
return serviceCollection.AddClient(config);
5751
}
@@ -61,20 +55,15 @@ public static IHttpClientBuilder AddPaperlessDotNet(
6155
/// <param name="configuration">The configuration to which to bind options models.</param>
6256
/// <param name="config">A delegate that is used to configure <see cref="PaperlessJsonSerializerOptions"/>.</param>
6357
/// <returns>The <see cref="IHttpClientBuilder"/> for the <see cref="HttpClient"/> used by <see cref="IPaperlessClient"/>.</returns>
64-
#if NETSTANDARD2_0
65-
[SuppressMessage("Trimming", "IL2026", Justification = $"{nameof(PaperlessOptions)} contains only system types.")]
66-
#else
67-
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026", Justification = $"{nameof(PaperlessOptions)} contains only system types.")]
68-
#endif
6958
public static IHttpClientBuilder AddPaperlessDotNet(
7059
this IServiceCollection serviceCollection,
7160
IConfiguration configuration,
7261
Action<PaperlessJsonSerializerOptions>? config = null)
7362
{
7463
serviceCollection
64+
.AddSingleton<IValidateOptions<PaperlessOptions>, PaperlessOptionsValidator>()
7565
.AddOptions<PaperlessOptions>()
76-
.Bind(configuration.GetSection(PaperlessOptions.Name))
77-
.ValidateDataAnnotations();
66+
.Bind(configuration.GetSection(PaperlessOptions.Name));
7867

7968
return serviceCollection.AddClient(config);
8069
}

source/VMelnalksnis.PaperlessDotNet/Filters/ExpressionExtensions.cs

Lines changed: 143 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
using System.Text.Encodings.Web;
1313
using System.Text.Json.Serialization;
1414

15+
using NodaTime;
16+
1517
using VMelnalksnis.PaperlessDotNet.Documents;
1618
using VMelnalksnis.PaperlessDotNet.DocumentTypes;
1719

@@ -94,40 +96,43 @@ private static KeyValuePair<string, string> ToKeyValuePair(this Expression expre
9496
{
9597
if (expression is BinaryExpression binaryExpression)
9698
{
97-
var suffix = binaryExpression.GetSuffix();
98-
9999
if (binaryExpression.Left is MemberExpression memberExpression)
100100
{
101-
var value = binaryExpression.Right.Evaluate();
102-
if (value is bool boolValue)
101+
var memberName = memberExpression.GetFilterMemberName();
102+
103+
var suffix = binaryExpression.GetSuffix();
104+
if (suffix is not null)
103105
{
104-
value = binaryExpression.NodeType is NotEqual
105-
? !boolValue
106-
: boolValue;
106+
memberName += $"__{suffix}";
107107
}
108-
else if (value is null)
108+
109+
if (binaryExpression.Right.Type == typeof(bool))
109110
{
110-
value = binaryExpression.NodeType is Equal;
111+
var booleanValue = binaryExpression.Right.Evaluate<bool>();
112+
booleanValue = binaryExpression.NodeType is NotEqual
113+
? !booleanValue
114+
: booleanValue;
115+
116+
return new(memberName, booleanValue ? "true" : "false");
111117
}
112118

113-
var memberName = memberExpression.GetFilterMemberName();
119+
if (binaryExpression.Right.Type == typeof(DateTime))
120+
{
121+
var dateValue = binaryExpression.Right.Evaluate<DateTime>();
122+
var dateString = memberName.Contains("__date")
123+
? dateValue.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture)
124+
: dateValue.ToString("yyyy-MM-ddTHH:mm:ss", CultureInfo.InvariantCulture);
114125

115-
if (suffix is not null)
126+
return new(memberName, dateString);
127+
}
128+
129+
var value = binaryExpression.Right.Evaluate();
130+
if (value is null)
116131
{
117-
memberName += $"__{suffix}";
132+
return new(memberName, binaryExpression.NodeType is Equal ? "true" : "false");
118133
}
119134

120-
return new(
121-
memberName,
122-
value switch
123-
{
124-
DateTime dateTime when memberName.Contains("__date") => dateTime.ToString(
125-
"yyyy-MM-dd",
126-
CultureInfo.InvariantCulture),
127-
DateTime dateTime => dateTime.ToString("yyyy-MM-ddTHH:mm:ss", CultureInfo.InvariantCulture),
128-
bool boolean => boolean.ToString().ToLowerInvariant(),
129-
_ => value.ToString() ?? string.Empty,
130-
});
135+
return new(memberName, value.ToString() ?? string.Empty);
131136
}
132137
}
133138

@@ -144,7 +149,8 @@ DateTime dateTime when memberName.Contains("__date") => dateTime.ToString(
144149
_ => throw new NotImplementedException(),
145150
};
146151

147-
var value = valueExpression.Evaluate() ?? throw new NotSupportedException("Method calls with null arguments are not supported");
152+
var value = valueExpression.Evaluate() ??
153+
throw new NotSupportedException("Method calls with null arguments are not supported");
148154
if (value is IEnumerable<int> values)
149155
{
150156
suffix = "in";
@@ -158,7 +164,8 @@ DateTime dateTime when memberName.Contains("__date") => dateTime.ToString(
158164
}
159165
else
160166
{
161-
var value = methodCallExpression.Arguments[0].Evaluate() ?? throw new InvalidOperationException("Extension method calls on null instances are not supported");
167+
var value = methodCallExpression.Arguments[0].Evaluate() ??
168+
throw new InvalidOperationException("Extension method calls on null instances are not supported");
162169
if (value is IEnumerable<int> values)
163170
{
164171
suffix = "in";
@@ -232,6 +239,116 @@ private static string GetOrderMemberName(this MemberExpression expression)
232239

233240
private static object? Evaluate(this Expression expression)
234241
{
235-
return Expression.Lambda(expression).Compile().DynamicInvoke();
242+
if (expression.Type == typeof(string))
243+
{
244+
return expression.Evaluate<string?>();
245+
}
246+
247+
if (expression.Type == typeof(Uri))
248+
{
249+
return expression.Evaluate<Uri?>();
250+
}
251+
252+
if (expression.Type == typeof(int))
253+
{
254+
return expression.Evaluate<int>();
255+
}
256+
257+
if (expression.Type == typeof(int?))
258+
{
259+
return expression.Evaluate<int?>();
260+
}
261+
262+
if (expression.Type == typeof(uint))
263+
{
264+
return expression.Evaluate<uint>();
265+
}
266+
267+
if (expression.Type == typeof(uint?))
268+
{
269+
return expression.Evaluate<uint?>();
270+
}
271+
272+
if (expression.Type == typeof(float))
273+
{
274+
return expression.Evaluate<float>();
275+
}
276+
277+
if (expression.Type == typeof(float?))
278+
{
279+
return expression.Evaluate<float?>();
280+
}
281+
282+
if (expression.Type == typeof(double))
283+
{
284+
return expression.Evaluate<double>();
285+
}
286+
287+
if (expression.Type == typeof(double?))
288+
{
289+
return expression.Evaluate<double?>();
290+
}
291+
292+
if (expression.Type == typeof(decimal))
293+
{
294+
return expression.Evaluate<decimal>();
295+
}
296+
297+
if (expression.Type == typeof(decimal?))
298+
{
299+
return expression.Evaluate<decimal?>();
300+
}
301+
302+
if (expression.Type == typeof(bool))
303+
{
304+
return expression.Evaluate<bool>();
305+
}
306+
307+
if (expression.Type == typeof(bool?))
308+
{
309+
return expression.Evaluate<bool?>();
310+
}
311+
312+
if (expression.Type == typeof(DateTime))
313+
{
314+
return expression.Evaluate<DateTime>();
315+
}
316+
317+
if (expression.Type == typeof(DateTime?))
318+
{
319+
return expression.Evaluate<DateTime?>();
320+
}
321+
322+
if (expression.Type == typeof(OffsetDate))
323+
{
324+
return expression.Evaluate<OffsetDate>();
325+
}
326+
327+
if (expression.Type == typeof(OffsetDate?))
328+
{
329+
return expression.Evaluate<OffsetDate?>();
330+
}
331+
332+
if (expression.Type == typeof(LocalDate))
333+
{
334+
return expression.Evaluate<LocalDate>();
335+
}
336+
337+
if (expression.Type == typeof(LocalDate?))
338+
{
339+
return expression.Evaluate<LocalDate?>();
340+
}
341+
342+
if (typeof(IEnumerable<int>).IsAssignableFrom(expression.Type))
343+
{
344+
return expression.Evaluate<IEnumerable<int>>();
345+
}
346+
347+
throw new ArgumentOutOfRangeException(nameof(expression.Type), expression.Type, "Unsupported expression type");
348+
}
349+
350+
private static TValue Evaluate<TValue>(this Expression expression)
351+
{
352+
return Expression.Lambda<Func<TValue>>(expression).Compile().Invoke();
236353
}
237354
}

0 commit comments

Comments
 (0)