Skip to content

Commit 910d3cc

Browse files
committed
�Avoid tight coupling with Ocelot type versions.
* Remove Ocelot pack reference * Still use NewtonsoftJson pack * Add ref to System.Text.Json
1 parent db063b0 commit 910d3cc

File tree

6 files changed

+278
-85
lines changed

6 files changed

+278
-85
lines changed

src/AcceptanceSteps.cs

Lines changed: 82 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,23 @@
66
using Microsoft.Extensions.DependencyInjection;
77
using Microsoft.Extensions.Hosting;
88
using Microsoft.Extensions.Logging;
9-
using Newtonsoft.Json;
10-
using Ocelot.Configuration.File;
11-
using Ocelot.DependencyInjection;
12-
using Ocelot.Middleware;
9+
using Microsoft.Extensions.Options;
10+
11+
//using Newtonsoft.Json;
12+
//using Ocelot.Configuration.File;
13+
//using Ocelot.DependencyInjection;
14+
//using Ocelot.Middleware;
1315
using Shouldly;
16+
using System.Collections;
1417
using System.Diagnostics;
1518
using System.Net;
1619
using System.Net.Http.Headers;
20+
using System.Reflection;
1721
using System.Runtime.CompilerServices;
22+
using System.Text.Encodings.Web;
23+
using System.Text.Json;
24+
using System.Text.Unicode;
25+
using System.Xml;
1826
using CookieHeaderValue = Microsoft.Net.Http.Headers.CookieHeaderValue;
1927
using MediaTypeHeaderValue = System.Net.Http.Headers.MediaTypeHeaderValue;
2028

@@ -36,7 +44,7 @@ public AcceptanceSteps()
3644
{
3745
_testId = Guid.NewGuid();
3846
random = new Random();
39-
ocelotConfigFileName = $"{_testId:N}-{ConfigurationBuilderExtensions.PrimaryConfigFile}";
47+
ocelotConfigFileName = $"{_testId:N}-ocelot.json"; // {ConfigurationBuilderExtensions.PrimaryConfigFile}";
4048
Files = [ocelotConfigFileName];
4149
Folders = [];
4250
handler = new();
@@ -46,56 +54,100 @@ public AcceptanceSteps()
4654
protected List<string> Folders { get; }
4755
protected string TestID { get => _testId.ToString("N"); }
4856

49-
protected static FileHostAndPort Localhost(int port) => new("localhost", port);
57+
protected virtual /*FileHostAndPort*/object? Localhost(int port)
58+
{
59+
// return new("localhost", port);
60+
return Ocelot.CreateFileHostAndPort("localhost", port, out Type type);
61+
}
62+
5063
protected static string DownstreamUrl(int port) => DownstreamUrl(port, Uri.UriSchemeHttp);
5164
protected static string DownstreamUrl(int port, string scheme) => $"{scheme ?? Uri.UriSchemeHttp}://localhost:{port}";
5265
protected static string LoopbackLocalhostUrl(int port, int loopbackIndex = 0) => $"{Uri.UriSchemeHttp}://127.0.0.{++loopbackIndex}:{port}";
5366

54-
protected virtual FileConfiguration GivenConfiguration(params FileRoute[] routes) => new()
67+
protected virtual /*FileConfiguration*/object? GivenConfiguration(params /*FileRoute*/object[] routes)
5568
{
56-
Routes = [.. routes],
57-
};
69+
object? c = Ocelot.CreateFileConfiguration(out Type type);
70+
PropertyInfo? property = type.GetProperty("Routes");
71+
if (property?.GetValue(c) is IList list)
72+
foreach (var route in routes)
73+
list.Add(route);
74+
return c;
75+
}
5876

59-
protected static FileRoute GivenDefaultRoute(int port) => GivenRoute(port);
60-
protected static FileRoute GivenCatchAllRoute(int port) => GivenRoute(port, "/{everything}", "/{everything}");
61-
protected static FileRoute GivenRoute(int port, string? upstream = null, string? downstream = null)
77+
protected virtual /*FileRoute*/object? GivenDefaultRoute(int port) => GivenRoute(port);
78+
protected virtual /*FileRoute*/object? GivenCatchAllRoute(int port) => GivenRoute(port, "/{everything}", "/{everything}");
79+
protected virtual /*FileRoute*/object? GivenRoute(int port, string? upstream = null, string? downstream = null)
6280
{
63-
var r = Activator.CreateInstance<FileRoute>();
64-
r.DownstreamHostAndPorts.Add(Localhost(port));
65-
r.DownstreamPathTemplate = downstream ?? "/";
66-
r.DownstreamScheme = Uri.UriSchemeHttp;
67-
r.UpstreamPathTemplate = upstream ?? "/";
68-
IList<string> uhmList = r.UpstreamHttpMethod;
69-
uhmList.Add(HttpMethods.Get);
81+
object? r = Ocelot.CreateFileRoute(out Type type);
82+
83+
//r.DownstreamHostAndPorts.Add(Localhost(port));
84+
PropertyInfo? property = type.GetProperty("DownstreamHostAndPorts");
85+
if (property?.GetValue(r) is IList downstreamHostAndPorts)
86+
downstreamHostAndPorts.Add(Localhost(port));
87+
88+
//r.DownstreamPathTemplate = downstream ?? "/";
89+
property = type.GetProperty("DownstreamPathTemplate");
90+
property?.SetValue(r, downstream ?? "/");
91+
92+
//r.DownstreamScheme = Uri.UriSchemeHttp;
93+
property = type.GetProperty("DownstreamScheme");
94+
property?.SetValue(r, Uri.UriSchemeHttp);
95+
96+
//r.UpstreamPathTemplate = upstream ?? "/";
97+
property = type.GetProperty("UpstreamPathTemplate");
98+
property?.SetValue(r, upstream ?? "/");
99+
100+
//r.UpstreamHttpMethod.Add(HttpMethods.Get);
101+
property = type.GetProperty("UpstreamHttpMethod");
102+
if (property?.GetValue(r) is IList upstreamHttpMethod)
103+
upstreamHttpMethod.Add(HttpMethods.Get);
104+
70105
return r;
71106
}
72107

73-
public void GivenThereIsAConfiguration(FileConfiguration fileConfiguration)
108+
public virtual void GivenThereIsAConfiguration(/*FileConfiguration*/object fileConfiguration)
74109
=> GivenThereIsAConfiguration(fileConfiguration, ocelotConfigFileName);
75-
public void GivenThereIsAConfiguration(FileConfiguration from, string toFile)
110+
public virtual void GivenThereIsAConfiguration(/*FileConfiguration*/object from, string toFile)
76111
{
77112
var json = SerializeJson(from, ref toFile);
78113
File.WriteAllText(toFile, json);
79114
}
80-
public Task GivenThereIsAConfigurationAsync(FileConfiguration from, string toFile)
115+
public virtual Task GivenThereIsAConfigurationAsync(/*FileConfiguration*/object from, string toFile)
81116
{
82117
var json = SerializeJson(from, ref toFile);
83118
return File.WriteAllTextAsync(toFile, json);
84119
}
85-
protected string SerializeJson(FileConfiguration from, ref string toFile)
120+
protected virtual string SerializeJson(/*FileConfiguration*/object from, ref string toFile)
86121
{
87122
toFile ??= ocelotConfigFileName;
88123
Files.Add(toFile); // register for disposing
89-
return JsonConvert.SerializeObject(from, Formatting.Indented);
124+
125+
// return JsonConvert.SerializeObject(from, Formatting.Indented);
126+
string json = JsonSerializer.Serialize(from, JsonWebIndented);
127+
return json;
90128
}
91129

130+
public readonly static JsonSerializerOptions JsonWebIndented = new()
131+
{
132+
Encoder = JavaScriptEncoder.Create(UnicodeRanges.All), // Avoid escaping non-ASCII
133+
PropertyNamingPolicy = JsonNamingPolicy.CamelCase, // Use camelCase for web
134+
WriteIndented = true, // Not compact output for better readability
135+
PropertyNameCaseInsensitive = true // Optional: for deserialization
136+
};
137+
92138
#region GivenOcelotIsRunning
93-
public void WithBasicConfiguration(WebHostBuilderContext hosting, IConfigurationBuilder config) => config
94-
.SetBasePath(hosting.HostingEnvironment.ContentRootPath)
95-
.AddOcelot(ocelotConfigFileName, false, false);
96-
public static void WithAddOcelot(IServiceCollection services) => services.AddOcelot();
97-
public static void WithUseOcelot(IApplicationBuilder app) => app.UseOcelot().Wait();
98-
public static Task<IApplicationBuilder> WithUseOcelotAsync(IApplicationBuilder app) => app.UseOcelot();
139+
public void WithBasicConfiguration(WebHostBuilderContext hosting, IConfigurationBuilder config)
140+
{
141+
config.SetBasePath(hosting.HostingEnvironment.ContentRootPath);
142+
//config.AddOcelot(ocelotConfigFileName, false, false);
143+
config.AddJsonFile(ocelotConfigFileName, false, false);
144+
}
145+
146+
//public static void WithAddOcelot(IServiceCollection services) => services.AddOcelot();
147+
public static void WithAddOcelot(IServiceCollection services) => Ocelot.AddOcelot(services); // services.AddOcelot();
148+
149+
public static void WithUseOcelot(IApplicationBuilder app) => Ocelot.UseOcelot(app).Wait(); // app.UseOcelot().Wait();
150+
public static Task<IApplicationBuilder> WithUseOcelotAsync(IApplicationBuilder app) => Ocelot.UseOcelot(app); // app.UseOcelot();
99151

100152
public int GivenOcelotIsRunning()
101153
=> GivenOcelotIsRunning(null, null, null, null, null, null);

src/Box.cs

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
using System.Collections;
2+
3+
namespace Ocelot.Testing;
4+
5+
public static class Box
6+
{
7+
public static Box<T> In<T>(T instance) => new(instance);
8+
public static Box<T> With<T>(T instance) => In(instance);
9+
}
10+
11+
public class Box<T>
12+
{
13+
private readonly T _route;
14+
private readonly Type _type;
15+
public Box(T route)
16+
{
17+
if (route?.GetType().FullName != "Ocelot.Configuration.File.FileRoute")
18+
throw new ArgumentException("Is not type of Ocelot.Configuration.File.FileRoute", nameof(route));
19+
_route = route;
20+
_type = typeof(T); //route.GetType();
21+
}
22+
23+
public T Out() => _route;
24+
public T Unbox() => Out();
25+
26+
public Box<T> Hosts(params object[] hosts)
27+
{
28+
//route.DownstreamHostAndPorts.AddRange(hosts);
29+
var property = _type.GetProperty("DownstreamHostAndPorts");
30+
IList? downstreamHostAndPorts = property?.GetValue(_route) as IList;
31+
ArgumentNullException.ThrowIfNull(downstreamHostAndPorts);
32+
for (int i = 0; i < hosts.Length; i++)
33+
{
34+
var host = hosts[i];
35+
if (host?.GetType().FullName != "Ocelot.Configuration.File.FileHostAndPort")
36+
throw new ArgumentException($"Argument at index {i} is not type of Ocelot.Configuration.File.FileHostAndPort", nameof(hosts));
37+
downstreamHostAndPorts.Add(host);
38+
}
39+
return this;
40+
}
41+
42+
public Box<T> Priority(int priority)
43+
{
44+
//route.Priority = priority;
45+
var property = _type.GetProperty("Priority");
46+
property?.SetValue(_route, priority);
47+
return this;
48+
}
49+
50+
public Box<T> Methods(params string[] methods)
51+
{
52+
//route.UpstreamHttpMethod = [.. methods];
53+
var property = _type.GetProperty("UpstreamHttpMethod");
54+
IList upstreamHttpMethod = property?.GetValue(_route) as IList
55+
?? throw new ArgumentNullException(nameof(upstreamHttpMethod));
56+
for (int i = 0; i < methods.Length; i++)
57+
{
58+
string method = methods[i];
59+
upstreamHttpMethod.Add(method);
60+
}
61+
return this;
62+
}
63+
64+
public Box<T> UpstreamHeaderTransform(params KeyValuePair<string, string>[] pairs)
65+
{
66+
//route.UpstreamHeaderTransform = new Dictionary<string, string>(pairs);
67+
var property = _type.GetProperty("UpstreamHeaderTransform");
68+
IDictionary<string, string> upstreamHeaderTransform = property?.GetValue(_route) as IDictionary<string, string>
69+
?? throw new ArgumentNullException(nameof(upstreamHeaderTransform));
70+
for (int i = 0; i < pairs.Length; i++)
71+
{
72+
var kv = pairs[i];
73+
upstreamHeaderTransform.Add(kv.Key, kv.Value);
74+
}
75+
return this;
76+
}
77+
78+
public Box<T> UpstreamHeaderTransform(string key, string value)
79+
{
80+
//route.UpstreamHeaderTransform.TryAdd(key, value);
81+
var property = _type.GetProperty("UpstreamHeaderTransform");
82+
IDictionary<string, string> upstreamHeaderTransform = property?.GetValue(_route) as IDictionary<string, string>
83+
?? throw new ArgumentNullException(nameof(upstreamHeaderTransform));
84+
upstreamHeaderTransform.TryAdd(key, value);
85+
return this;
86+
}
87+
88+
public Box<T> HttpHandlerOptions(/*FileHttpHandlerOptions*/object options)
89+
{
90+
//route.HttpHandlerOptions = options;
91+
if (options?.GetType().FullName != "Ocelot.Configuration.File.FileHttpHandlerOptions")
92+
throw new ArgumentException($"Is not type of Ocelot.Configuration.File.FileHttpHandlerOptions", nameof(options));
93+
var property = _type.GetProperty("HttpHandlerOptions");
94+
property?.SetValue(_route, options);
95+
return this;
96+
}
97+
98+
public Box<T> Key(string? key)
99+
{
100+
//route.Key = key;
101+
var property = _type.GetProperty("Key");
102+
property?.SetValue(_route, key);
103+
return this;
104+
}
105+
106+
public Box<T> UpstreamHost(string? upstreamHost)
107+
{
108+
//route.UpstreamHost = upstreamHost;
109+
var property = _type.GetProperty("UpstreamHost");
110+
property?.SetValue(_route, upstreamHost);
111+
return this;
112+
}
113+
}

src/FileRouteExtensions.cs

Lines changed: 24 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,29 @@
1-
using Ocelot.Configuration.File;
2-
1+
//using Ocelot.Configuration.File;
32
namespace Ocelot.Testing;
43

54
public static class FileRouteExtensions
65
{
7-
public static FileRoute WithHosts(this FileRoute route, params FileHostAndPort[] hosts)
8-
{
9-
route.DownstreamHostAndPorts.AddRange(hosts);
10-
return route;
11-
}
12-
13-
public static FileRoute WithPriority(this FileRoute route, int priority)
14-
{
15-
route.Priority = priority;
16-
return route;
17-
}
18-
19-
public static FileRoute WithMethods(this FileRoute route, params string[] methods)
20-
{
21-
route.UpstreamHttpMethod = [.. methods];
22-
return route;
23-
}
24-
25-
public static FileRoute WithUpstreamHeaderTransform(this FileRoute route, params KeyValuePair<string, string>[] pairs)
26-
{
27-
route.UpstreamHeaderTransform = new Dictionary<string, string>(pairs);
28-
return route;
29-
}
30-
public static FileRoute WithUpstreamHeaderTransform(this FileRoute route, string key, string value)
31-
{
32-
route.UpstreamHeaderTransform.TryAdd(key, value);
33-
return route;
34-
}
35-
36-
public static FileRoute WithHttpHandlerOptions(this FileRoute route, FileHttpHandlerOptions options)
37-
{
38-
route.HttpHandlerOptions = options;
39-
return route;
40-
}
41-
42-
public static FileRoute WithKey(this FileRoute route, string? key)
43-
{
44-
route.Key = key;
45-
return route;
46-
}
47-
48-
public static FileRoute WithUpstreamHost(this FileRoute route, string? upstreamHost)
49-
{
50-
route.UpstreamHost = upstreamHost;
51-
return route;
52-
}
6+
public static /*FileRoute*/T WithHosts<T>(this /*FileRoute*/T route, params /*FileHostAndPort*/object[] hosts)
7+
=> Box.In(route).Hosts(hosts).Out(); //route.DownstreamHostAndPorts.AddRange(hosts);
8+
9+
public static /*FileRoute*/T WithPriority<T>(this /*FileRoute*/T route, int priority)
10+
=> Box.In(route).Priority(priority).Out(); //route.Priority = priority;
11+
12+
public static /*FileRoute*/T WithMethods<T>(this /*FileRoute*/T route, params string[] methods)
13+
=> Box.In(route).Methods(methods).Out(); //route.UpstreamHttpMethod = [.. methods];
14+
15+
public static /*FileRoute*/T WithUpstreamHeaderTransform<T>(this /*FileRoute*/T route, params KeyValuePair<string, string>[] pairs)
16+
=> Box.In(route).UpstreamHeaderTransform(pairs).Out(); //route.UpstreamHeaderTransform = new Dictionary<string, string>(pairs);
17+
18+
public static /*FileRoute*/T WithUpstreamHeaderTransform<T>(this /*FileRoute*/T route, string key, string value)
19+
=> Box.In(route).UpstreamHeaderTransform(key, value).Out(); //route.UpstreamHeaderTransform.TryAdd(key, value);
20+
21+
public static /*FileRoute*/T WithHttpHandlerOptions<T>(this /*FileRoute*/T route, /*FileHttpHandlerOptions*/object options)
22+
=> Box.In(route).HttpHandlerOptions(options).Out(); //route.HttpHandlerOptions = options;
23+
24+
public static /*FileRoute*/T WithKey<T>(this /*FileRoute*/T route, string? key)
25+
=> Box.In(route).Key(key).Out(); //route.Key = key;
26+
27+
public static /*FileRoute*/T WithUpstreamHost<T>(this /*FileRoute*/T route, string? upstreamHost)
28+
=> Box.In(route).UpstreamHost(upstreamHost).Out(); //route.UpstreamHost = upstreamHost;
5329
}

src/FileUnitTest.cs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
using Ocelot.DependencyInjection;
2-
1+
//using Ocelot.DependencyInjection;
32
namespace Ocelot.Testing;
43

54
public class FileUnitTest : UnitTest, IDisposable
@@ -18,9 +17,9 @@ protected FileUnitTest(string? folder)
1817
Directory.CreateDirectory(folder);
1918
folders = new() { folder };
2019

21-
primaryConfigFileName = Path.Combine(folder, ConfigurationBuilderExtensions.PrimaryConfigFile);
22-
globalConfigFileName = Path.Combine(folder, ConfigurationBuilderExtensions.GlobalConfigFile);
23-
environmentConfigFileName = Path.Combine(folder, string.Format(ConfigurationBuilderExtensions.EnvironmentConfigFile, EnvironmentName()));
20+
primaryConfigFileName = Path.Combine(folder, /*ConfigurationBuilderExtensions.PrimaryConfigFile*/ "ocelot.json");
21+
globalConfigFileName = Path.Combine(folder, /*ConfigurationBuilderExtensions.GlobalConfigFile*/ "ocelot.global.json");
22+
environmentConfigFileName = Path.Combine(folder, string.Format(/*ConfigurationBuilderExtensions.EnvironmentConfigFile*/"ocelot.{0}.json", EnvironmentName()));
2423
files = new()
2524
{
2625
primaryConfigFileName,

0 commit comments

Comments
 (0)