Skip to content

Commit 6bf66b0

Browse files
Merge pull request #444 from martincostello/Drop-Newtonsoft-And-Old-TFMs
Drop Newtonsoft.Json and unsupported TFMs
2 parents f21a9cf + f0fb798 commit 6bf66b0

18 files changed

+216
-173
lines changed

Directory.Build.props

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@
3737
<RepositoryUrl>$(PackageProjectUrl).git</RepositoryUrl>
3838
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
3939
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
40-
<AssemblyVersion>3.0.0.0</AssemblyVersion>
41-
<VersionPrefix>3.2.0</VersionPrefix>
40+
<AssemblyVersion>4.0.0.0</AssemblyVersion>
41+
<VersionPrefix>4.0.0</VersionPrefix>
4242
<VersionSuffix Condition=" '$(VersionSuffix)' == '' AND '$(GITHUB_ACTIONS)' != '' AND '$(GITHUB_HEAD_REF)' != '' ">beta.$(GITHUB_RUN_NUMBER)</VersionSuffix>
4343
<VersionPrefix Condition=" $(GITHUB_REF.StartsWith(`refs/tags/v`)) ">$(GITHUB_REF.Replace('refs/tags/v', ''))</VersionPrefix>
4444
<VersionSuffix Condition=" $(GITHUB_REF.StartsWith(`refs/tags/v`)) "></VersionSuffix>

Directory.Packages.props

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,34 +6,29 @@
66
<PackageVersion Include="MarkdownSnippets.MsBuild" Version="24.5.0" />
77
<PackageVersion Include="MartinCostello.Logging.XUnit" Version="0.3.0" />
88
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="6.0.10" />
9-
<PackageVersion Include="Microsoft.AspNetCore.WebUtilities" Version="1.0.0" />
9+
<PackageVersion Include="Microsoft.AspNetCore.WebUtilities" Version="2.2.0" />
1010
<PackageVersion Include="Microsoft.CodeAnalysis.PublicApiAnalyzers" Version="3.3.3" />
1111
<PackageVersion Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" />
1212
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.4.0" />
1313
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="1.1.1" />
1414
<PackageVersion Include="Moq" Version="4.18.2" />
15-
<PackageVersion Include="Newtonsoft.Json" Version="9.0.1" />
1615
<PackageVersion Include="Newtonsoft.Json.Schema" Version="3.0.14" />
1716
<PackageVersion Include="Polly" Version="7.2.3" />
1817
<PackageVersion Include="Refit" Version="6.3.2" />
1918
<PackageVersion Include="ReportGenerator" Version="5.1.11" />
2019
<PackageVersion Include="Shouldly" Version="4.1.0" />
2120
<PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.435" />
22-
<PackageVersion Include="System.Text.Json" Version="4.6.0" />
21+
<PackageVersion Include="System.Text.Json" Version="4.7.2" />
2322
<PackageVersion Include="xunit" Version="2.4.2" />
2423
<PackageVersion Include="xunit.runner.visualstudio" Version="2.4.5" />
2524
</ItemGroup>
2625
<ItemGroup Condition=" '$(AssemblyName)' == 'JustEat.HttpClientInterception' ">
27-
<PackageVersion Include="System.Net.Http" Version="4.0.0" />
26+
<PackageVersion Include="System.Net.Http" Version="4.3.4" />
2827
</ItemGroup>
29-
<ItemGroup Condition=" '$(AssemblyName)' == 'JustEat.HttpClientInterception' and '$(TargetFramework)' == 'net5.0' ">
30-
<PackageVersion Update="Microsoft.AspNetCore.WebUtilities" Version="2.2.0" />
31-
<PackageVersion Update="Newtonsoft.Json" Version="12.0.3" />
32-
<PackageVersion Update="System.Text.Json" Version="5.0.0" />
28+
<ItemGroup Condition=" '$(AssemblyName)' == 'JustEat.HttpClientInterception' and '$(TargetFramework)' == 'net6.0' ">
29+
<PackageVersion Update="System.Text.Json" Version="6.0.0" />
3330
</ItemGroup>
3431
<ItemGroup Condition=" '$(AssemblyName)' != 'JustEat.HttpClientInterception' ">
35-
<PackageVersion Update="Microsoft.AspNetCore.WebUtilities" Version="2.2.0" />
36-
<PackageVersion Update="Newtonsoft.Json" Version="13.0.1" />
3732
<PackageVersion Update="System.Text.Json" Version="6.0.6" />
3833
</ItemGroup>
3934
<ItemGroup>

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ A .NET Standard library for intercepting server-side HTTP dependencies.
1010

1111
## Introduction
1212

13-
This library provides functionality for intercepting HTTP requests made using the `HttpClient` class in code targeting .NET Standard 2.0 (and later), and .NET Framework 4.6.1 and 4.7.2.
13+
This library provides functionality for intercepting HTTP requests made using the `HttpClient` class in code targeting .NET Standard 2.0 (and later), and .NET Framework 4.7.2.
1414

1515
The primary use-case is for providing stub responses for use in tests for applications, such as an ASP.NET Core application, to drive your functional test scenarios.
1616

src/HttpClientInterception/BundleExtensions.cs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,58 @@ public static HttpClientInterceptorOptions RegisterBundle(
6666

6767
var bundle = BundleFactory.Create(path);
6868

69+
return options.RegisterBundle(bundle, templateValues);
70+
}
71+
72+
/// <summary>
73+
/// Registers a bundle of HTTP request interceptions from a specified JSON file.
74+
/// </summary>
75+
/// <param name="options">The <see cref="HttpClientInterceptorOptions"/> to register the bundle with.</param>
76+
/// <param name="path">The path of the JSON file containing the serialized bundle.</param>
77+
/// <param name="templateValues">The optional template values to specify.</param>
78+
/// <param name="cancellationToken">The optional <see cref="CancellationToken"/> to use.</param>
79+
/// <returns>
80+
/// The value specified by <paramref name="options"/>.
81+
/// </returns>
82+
/// <exception cref="ArgumentNullException">
83+
/// <paramref name="options"/>, <paramref name="path"/> or <paramref name="templateValues"/> is <see langword="null"/>.
84+
/// </exception>
85+
/// <exception cref="NotSupportedException">
86+
/// The version of the serialized bundle is not supported.
87+
/// </exception>
88+
public static async Task<HttpClientInterceptorOptions> RegisterBundleAsync(
89+
this HttpClientInterceptorOptions options,
90+
string path,
91+
IEnumerable<KeyValuePair<string, string>>? templateValues = default,
92+
CancellationToken cancellationToken = default)
93+
{
94+
if (options is null)
95+
{
96+
throw new ArgumentNullException(nameof(options));
97+
}
98+
99+
if (path is null)
100+
{
101+
throw new ArgumentNullException(nameof(path));
102+
}
103+
104+
templateValues ??= Array.Empty<KeyValuePair<string, string>>();
105+
106+
var bundle = await BundleFactory.CreateAsync(path, cancellationToken).ConfigureAwait(false);
107+
108+
return options.RegisterBundle(bundle!, templateValues);
109+
}
110+
111+
private static HttpClientInterceptorOptions RegisterBundle(
112+
this HttpClientInterceptorOptions options,
113+
Bundle? bundle,
114+
IEnumerable<KeyValuePair<string, string>> templateValues)
115+
{
116+
if (bundle is null)
117+
{
118+
throw new InvalidOperationException("No HTTP request interception bundle was deserialized.");
119+
}
120+
69121
if (bundle.Version != 1)
70122
{
71123
throw new NotSupportedException($"HTTP request interception bundles of version {bundle.Version} are not supported.");

src/HttpClientInterception/Bundles/Bundle.cs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copyright (c) Just Eat, 2017. All rights reserved.
22
// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information.
33

4-
using Newtonsoft.Json;
4+
using System.Text.Json.Serialization;
55

66
namespace JustEat.HttpClientInterception.Bundles;
77

@@ -15,24 +15,30 @@ internal sealed class Bundle
1515
/// <summary>
1616
/// Gets or sets the bundle's Id.
1717
/// </summary>
18-
[JsonProperty("id", NullValueHandling = NullValueHandling.Ignore)]
18+
[JsonPropertyName("id")]
19+
#if NET6_0_OR_GREATER
20+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
21+
#endif
1922
public string? Id { get; set; }
2023

2124
/// <summary>
2225
/// Gets or sets the bundle's comment.
2326
/// </summary>
24-
[JsonProperty("comment", NullValueHandling = NullValueHandling.Ignore)]
27+
[JsonPropertyName("comment")]
28+
#if NET6_0_OR_GREATER
29+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
30+
#endif
2531
public string? Comment { get; set; }
2632

2733
/// <summary>
2834
/// Gets or sets the bundle version.
2935
/// </summary>
30-
[JsonProperty("version")]
36+
[JsonPropertyName("version")]
3137
public int Version { get; set; } = 1;
3238

3339
/// <summary>
3440
/// Gets or sets the items in the bundle.
3541
/// </summary>
36-
[JsonProperty("items")]
42+
[JsonPropertyName("items")]
3743
public IList<BundleItem>? Items { get; set; }
3844
}

src/HttpClientInterception/Bundles/BundleFactory.cs

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copyright (c) Just Eat, 2017. All rights reserved.
22
// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information.
33

4-
using Newtonsoft.Json;
4+
using System.Text.Json;
55

66
namespace JustEat.HttpClientInterception.Bundles;
77

@@ -13,7 +13,12 @@ internal static class BundleFactory
1313
/// <summary>
1414
/// Gets the JSON serializer settings to use.
1515
/// </summary>
16-
private static JsonSerializerSettings Settings { get; } = new JsonSerializerSettings();
16+
private static JsonSerializerOptions Settings { get; } =
17+
#if NET7_0_OR_GREATER
18+
JsonSerializerOptions.Default;
19+
#else
20+
new();
21+
#endif
1722

1823
/// <summary>
1924
/// Creates a <see cref="Bundle"/> from the specified JSON file.
@@ -22,9 +27,24 @@ internal static class BundleFactory
2227
/// <returns>
2328
/// The <see cref="Bundle"/> deserialized from the file specified by <paramref name="path"/>.
2429
/// </returns>
25-
public static Bundle Create(string path)
30+
public static Bundle? Create(string path)
2631
{
2732
string json = File.ReadAllText(path);
28-
return JsonConvert.DeserializeObject<Bundle>(json, Settings)!;
33+
return JsonSerializer.Deserialize<Bundle>(json, Settings);
34+
}
35+
36+
/// <summary>
37+
/// Creates a <see cref="Bundle"/> from the specified JSON file as an asynchronous operation.
38+
/// </summary>
39+
/// <param name="path">The path of the JSON file containing the bundle.</param>
40+
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to use.</param>
41+
/// <returns>
42+
/// A <see cref="ValueTask{Bundle}"/> representing the asynchronous operation which
43+
/// returns the bundle deserialized from the file specified by <paramref name="path"/>.
44+
/// </returns>
45+
public static async ValueTask<Bundle?> CreateAsync(string path, CancellationToken cancellationToken)
46+
{
47+
using var stream = File.OpenRead(path);
48+
return await JsonSerializer.DeserializeAsync<Bundle>(stream, Settings, cancellationToken).ConfigureAwait(false);
2949
}
3050
}
Lines changed: 47 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
// Copyright (c) Just Eat, 2017. All rights reserved.
22
// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information.
33

4-
using Newtonsoft.Json;
5-
using Newtonsoft.Json.Linq;
4+
using System.Text.Json;
5+
using System.Text.Json.Serialization;
66

77
namespace JustEat.HttpClientInterception.Bundles;
88

@@ -16,102 +16,129 @@ internal sealed class BundleItem
1616
/// <summary>
1717
/// Gets or sets the optional Id of the item.
1818
/// </summary>
19-
[JsonProperty("id", NullValueHandling = NullValueHandling.Ignore)]
19+
[JsonPropertyName("id")]
20+
#if NET6_0_OR_GREATER
21+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
22+
#endif
2023
public string? Id { get; set; }
2124

2225
/// <summary>
2326
/// Gets or sets the optional comment for the item.
2427
/// </summary>
25-
[JsonProperty("comment", NullValueHandling = NullValueHandling.Ignore)]
28+
[JsonPropertyName("comment")]
29+
#if NET6_0_OR_GREATER
30+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
31+
#endif
2632
public string? Comment { get; set; }
2733

2834
/// <summary>
2935
/// Gets or sets the optional HTTP version for the item.
3036
/// </summary>
31-
[JsonProperty("version", NullValueHandling = NullValueHandling.Ignore)]
37+
[JsonPropertyName("version")]
38+
#if NET6_0_OR_GREATER
39+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
40+
#endif
3241
public string? Version { get; set; }
3342

3443
/// <summary>
3544
/// Gets or sets the default HTTP method for the item.
3645
/// </summary>
37-
[JsonProperty("method")]
46+
[JsonPropertyName("method")]
3847
public string? Method { get; set; }
3948

4049
/// <summary>
4150
/// Gets or sets the request URI for the item.
4251
/// </summary>
43-
[JsonProperty("uri")]
52+
[JsonPropertyName("uri")]
4453
public string? Uri { get; set; }
4554

4655
/// <summary>
4756
/// Gets or sets the optional priority for the item.
4857
/// </summary>
49-
[JsonProperty("priority", NullValueHandling = NullValueHandling.Ignore)]
58+
[JsonPropertyName("priority")]
59+
#if NET6_0_OR_GREATER
60+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
61+
#endif
5062
public int? Priority { get; set; }
5163

5264
/// <summary>
5365
/// Gets or sets the HTTP status code for the response for item.
5466
/// </summary>
55-
[JsonProperty("status", NullValueHandling = NullValueHandling.Ignore)]
67+
[JsonPropertyName("status")]
68+
#if NET6_0_OR_GREATER
69+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
70+
#endif
5671
public string? Status { get; set; }
5772

5873
/// <summary>
5974
/// Gets or sets the optional request headers for the item.
6075
/// </summary>
61-
[JsonProperty("requestHeaders", NullValueHandling = NullValueHandling.Ignore)]
76+
[JsonPropertyName("requestHeaders")]
77+
#if NET6_0_OR_GREATER
78+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
79+
#endif
6280
public IDictionary<string, ICollection<string>>? RequestHeaders { get; set; }
6381

6482
/// <summary>
6583
/// Gets or sets the optional response headers for the item.
6684
/// </summary>
67-
[JsonProperty("responseHeaders", NullValueHandling = NullValueHandling.Ignore)]
85+
[JsonPropertyName("responseHeaders")]
86+
#if NET6_0_OR_GREATER
87+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
88+
#endif
6889
public IDictionary<string, ICollection<string>>? ResponseHeaders { get; set; }
6990

7091
/// <summary>
7192
/// Gets or sets the optional content headers for the item.
7293
/// </summary>
73-
[JsonProperty("contentHeaders", NullValueHandling = NullValueHandling.Ignore)]
94+
[JsonPropertyName("contentHeaders")]
95+
#if NET6_0_OR_GREATER
96+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
97+
#endif
7498
public IDictionary<string, ICollection<string>>? ContentHeaders { get; set; }
7599

76100
/// <summary>
77101
/// Gets or sets the optional content format of the item.
78102
/// </summary>
79-
[JsonProperty("contentFormat")]
103+
[JsonPropertyName("contentFormat")]
80104
public string? ContentFormat { get; set; }
81105

82106
/// <summary>
83107
/// Gets or sets the content of the item as JSON.
84108
/// </summary>
85-
[JsonProperty("contentJson")]
86-
public JToken? ContentJson { get; set; }
109+
[JsonPropertyName("contentJson")]
110+
public JsonElement ContentJson { get; set; }
87111

88112
/// <summary>
89113
/// Gets or sets the content of the item as a string.
90114
/// </summary>
91-
[JsonProperty("contentString")]
115+
[JsonPropertyName("contentString")]
92116
public string? ContentString { get; set; }
93117

94118
/// <summary>
95119
/// Gets or sets the optional templating values.
96120
/// </summary>
97-
[JsonProperty("templateValues", NullValueHandling = NullValueHandling.Ignore)]
121+
[JsonPropertyName("templateValues")]
122+
#if NET6_0_OR_GREATER
123+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
124+
#endif
98125
public IDictionary<string, string>? TemplateValues { get; set; }
99126

100127
/// <summary>
101128
/// Gets or sets a value indicating whether to ignore the URI's path.
102129
/// </summary>
103-
[JsonProperty("ignorePath", NullValueHandling = NullValueHandling.Ignore)]
130+
[JsonPropertyName("ignorePath")]
104131
public bool IgnorePath { get; set; }
105132

106133
/// <summary>
107134
/// Gets or sets a value indicating whether to ignore the URI's query string.
108135
/// </summary>
109-
[JsonProperty("ignoreQuery", NullValueHandling = NullValueHandling.Ignore)]
136+
[JsonPropertyName("ignoreQuery")]
110137
public bool IgnoreQuery { get; set; }
111138

112139
/// <summary>
113140
/// Gets or sets a value indicating whether to skip the item.
114141
/// </summary>
115-
[JsonProperty("skip", NullValueHandling = NullValueHandling.Ignore)]
142+
[JsonPropertyName("skip")]
116143
public bool Skip { get; set; }
117144
}

src/HttpClientInterception/Bundles/BundleItemConverter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ private static HttpRequestInterceptionBuilder SetContent(this HttpRequestInterce
100100
return Encoding.UTF8.GetString(decoded);
101101

102102
case "JSON":
103-
return item.ContentJson!.ToString();
103+
return item.ContentJson.ToString();
104104

105105
case null:
106106
case "":

src/HttpClientInterception/HttpClientInterceptorOptionsExtensions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
using System.ComponentModel;
55
using System.Net;
66
using System.Text;
7-
using Newtonsoft.Json;
7+
using System.Text.Json;
88

99
namespace JustEat.HttpClientInterception;
1010

@@ -202,7 +202,7 @@ public static HttpClientInterceptorOptions RegisterGetJson(
202202

203203
byte[] ContentFactory()
204204
{
205-
string json = JsonConvert.SerializeObject(content);
205+
string json = JsonSerializer.Serialize(content);
206206
return Encoding.UTF8.GetBytes(json);
207207
}
208208

0 commit comments

Comments
 (0)