Skip to content

Commit aad380b

Browse files
committed
Added configuration to specify the Url for the API.
1 parent f9b0587 commit aad380b

File tree

6 files changed

+142
-28
lines changed

6 files changed

+142
-28
lines changed

Fritz.InstantAPIs/ApiMethodsToGenerate.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@ public enum ApiMethodsToGenerate
1111
All = 31
1212
}
1313

14-
public record TableApiMapping(string TableName, ApiMethodsToGenerate MethodsToGenerate = ApiMethodsToGenerate.All);
14+
public record TableApiMapping(
15+
string TableName,
16+
ApiMethodsToGenerate MethodsToGenerate = ApiMethodsToGenerate.All,
17+
string BaseUrl = ""
18+
);
1519

1620
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
1721
public class ApiMethodAttribute : Attribute

Fritz.InstantAPIs/InstantAPIsConfig.cs

Lines changed: 39 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ public class InstantAPIsConfigBuilder<D> where D : DbContext
1616
private D _TheContext;
1717
private readonly HashSet<TableApiMapping> _IncludedTables = new();
1818
private readonly List<string> _ExcludedTables = new();
19+
private const string DEFAULT_URI = "/api/";
1920

2021
public InstantAPIsConfigBuilder(D theContext)
2122
{
@@ -30,13 +31,30 @@ public InstantAPIsConfigBuilder(D theContext)
3031
/// <param name="entitySelector">Select the EntityFramework DbSet to include - Required</param>
3132
/// <param name="methodsToGenerate">A flags enumerable indicating the methods to generate. By default ALL are generated</param>
3233
/// <returns>Configuration builder with this configuration applied</returns>
33-
public InstantAPIsConfigBuilder<D> IncludeTable<T>(Func<D, DbSet<T>> entitySelector, ApiMethodsToGenerate methodsToGenerate = ApiMethodsToGenerate.All) where T : class
34+
public InstantAPIsConfigBuilder<D> IncludeTable<T>(Func<D, DbSet<T>> entitySelector, ApiMethodsToGenerate methodsToGenerate = ApiMethodsToGenerate.All, string baseUrl = "") where T : class
3435
{
3536

3637
var theSetType = entitySelector(_TheContext).GetType().BaseType;
3738
var property = _ContextType.GetProperties().First(p => p.PropertyType == theSetType);
3839

39-
var tableApiMapping = new TableApiMapping(property.Name, methodsToGenerate);
40+
if (!string.IsNullOrEmpty(baseUrl))
41+
{
42+
try
43+
{
44+
var testUri = new Uri(baseUrl, UriKind.RelativeOrAbsolute);
45+
baseUrl = testUri.IsAbsoluteUri ? testUri.LocalPath : baseUrl;
46+
}
47+
catch
48+
{
49+
throw new ArgumentException(nameof(baseUrl), "Not a valid Uri");
50+
}
51+
}
52+
else
53+
{
54+
baseUrl = String.Concat(DEFAULT_URI, property.Name);
55+
}
56+
57+
var tableApiMapping = new TableApiMapping(property.Name, methodsToGenerate, baseUrl);
4058
_IncludedTables.Add(tableApiMapping);
4159

4260
if (_ExcludedTables.Contains(tableApiMapping.TableName)) _ExcludedTables.Remove(tableApiMapping.TableName);
@@ -68,35 +86,33 @@ private void BuildTables()
6886
{
6987

7088
var tables = WebApplicationExtensions.GetDbTablesForContext<D>().ToArray();
71-
72-
if (!_IncludedTables.Any() && !_ExcludedTables.Any())
73-
{
74-
_Config.Tables.UnionWith(tables.Select(t => new WebApplicationExtensions.TypeTable
75-
{
76-
Name = t.Name,
77-
InstanceType = t.InstanceType,
78-
ApiMethodsToGenerate = ApiMethodsToGenerate.All
79-
}));
80-
return;
81-
}
89+
WebApplicationExtensions.TypeTable[]? outTables;
8290

8391
// Add the Included tables
84-
var outTables = tables.Where(t => _IncludedTables.Any(i => i.TableName.Equals(t.Name, StringComparison.InvariantCultureIgnoreCase)))
85-
.Select(t => new WebApplicationExtensions.TypeTable
92+
if (_IncludedTables.Any())
93+
{
94+
outTables = tables.Where(t => _IncludedTables.Any(i => i.TableName.Equals(t.Name, StringComparison.InvariantCultureIgnoreCase)))
95+
.Select(t => new WebApplicationExtensions.TypeTable
96+
{
97+
Name = t.Name,
98+
InstanceType = t.InstanceType,
99+
ApiMethodsToGenerate = _IncludedTables.First(i => i.TableName.Equals(t.Name, StringComparison.InvariantCultureIgnoreCase)).MethodsToGenerate,
100+
BaseUrl = new Uri(_IncludedTables.First(i => i.TableName.Equals(t.Name, StringComparison.InvariantCultureIgnoreCase)).BaseUrl, UriKind.Relative)
101+
}).ToArray();
102+
} else {
103+
outTables = tables.Select(t => new WebApplicationExtensions.TypeTable
86104
{
87105
Name = t.Name,
88106
InstanceType = t.InstanceType,
89-
ApiMethodsToGenerate = _IncludedTables.First(i => i.TableName.Equals(t.Name, StringComparison.InvariantCultureIgnoreCase)).MethodsToGenerate
107+
BaseUrl = new Uri(DEFAULT_URI + t.Name, uriKind: UriKind.Relative)
90108
}).ToArray();
109+
}
91110

92-
// If no tables were added, added them all
93-
if (outTables.Length == 0)
111+
// Exit now if no tables were excluded
112+
if (!_ExcludedTables.Any())
94113
{
95-
outTables = tables.Select(t => new WebApplicationExtensions.TypeTable
96-
{
97-
Name = t.Name,
98-
InstanceType = t.InstanceType
99-
}).ToArray();
114+
_Config.Tables.UnionWith(outTables);
115+
return;
100116
}
101117

102118
// Remove the Excluded tables

Fritz.InstantAPIs/WebApplicationExtensions.cs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ private static void MapInstantAPIsUsingReflection<D>(IEndpointRouteBuilder app,
5050
{
5151

5252
// The default URL for an InstantAPI is /api/TABLENAME
53-
var url = $"/api/{table.Name}";
53+
//var url = $"/api/{table.Name}";
5454

5555
initialize.MakeGenericMethod(typeof(D), table.InstanceType).Invoke(null, new[] { logger });
5656

@@ -64,7 +64,7 @@ private static void MapInstantAPIsUsingReflection<D>(IEndpointRouteBuilder app,
6464
if ((table.ApiMethodsToGenerate & methodType) != methodType) continue;
6565

6666
var genericMethod = method.MakeGenericMethod(typeof(D), table.InstanceType);
67-
genericMethod.Invoke(null, new object[] { app, url });
67+
genericMethod.Invoke(null, new object[] { app, table.BaseUrl.ToString() });
6868
}
6969

7070
}
@@ -100,7 +100,11 @@ internal static IEnumerable<TypeTable> GetDbTablesForContext<D>() where D : DbCo
100100
{
101101
return typeof(D).GetProperties(BindingFlags.Instance | BindingFlags.Public)
102102
.Where(x => x.PropertyType.FullName.StartsWith("Microsoft.EntityFrameworkCore.DbSet"))
103-
.Select(x => new TypeTable { Name = x.Name, InstanceType = x.PropertyType.GenericTypeArguments.First() })
103+
.Select(x => new TypeTable {
104+
Name = x.Name,
105+
InstanceType = x.PropertyType.GenericTypeArguments.First(),
106+
BaseUrl = new Uri($"/api/{x.Name}", uriKind: UriKind.RelativeOrAbsolute)
107+
})
104108
.ToArray();
105109
}
106110

@@ -109,6 +113,7 @@ internal class TypeTable
109113
public string Name { get; set; }
110114
public Type InstanceType { get; set; }
111115
public ApiMethodsToGenerate ApiMethodsToGenerate { get; set; } = ApiMethodsToGenerate.All;
116+
public Uri BaseUrl { get; set; }
112117
}
113118

114119
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
using Microsoft.AspNetCore.Builder;
2+
using Microsoft.EntityFrameworkCore;
3+
using Xunit;
4+
5+
namespace Test.Configuration;
6+
7+
public class WhenIncludeDoesNotSpecifyBaseUrl : BaseFixture
8+
{
9+
10+
InstantAPIsConfigBuilder<MyContext> _Builder;
11+
12+
public WhenIncludeDoesNotSpecifyBaseUrl()
13+
{
14+
15+
var _ContextOptions = new DbContextOptionsBuilder<MyContext>()
16+
.UseInMemoryDatabase("TestDb")
17+
.Options;
18+
_Builder = new(new(_ContextOptions));
19+
20+
}
21+
22+
[Fact]
23+
public void ShouldSpecifyDefaultUrl()
24+
{
25+
26+
// arrange
27+
28+
// act
29+
_Builder.IncludeTable(db => db.Contacts);
30+
var config = _Builder.Build();
31+
32+
// assert
33+
Assert.Single(config.Tables);
34+
Assert.Equal(new Uri("/api/Contacts", uriKind: UriKind.Relative), config.Tables.First().BaseUrl);
35+
36+
}
37+
38+
39+
}
40+
41+
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
using Microsoft.AspNetCore.Builder;
2+
using Microsoft.EntityFrameworkCore;
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Linq;
6+
using System.Text;
7+
using System.Threading.Tasks;
8+
using Xunit;
9+
10+
namespace Test.Configuration;
11+
12+
13+
public class WhenIncludeSpecifiesBaseUrl : BaseFixture
14+
{
15+
16+
InstantAPIsConfigBuilder<MyContext> _Builder;
17+
18+
public WhenIncludeSpecifiesBaseUrl()
19+
{
20+
21+
var _ContextOptions = new DbContextOptionsBuilder<MyContext>()
22+
.UseInMemoryDatabase("TestDb")
23+
.Options;
24+
_Builder = new(new(_ContextOptions));
25+
26+
}
27+
28+
[Fact]
29+
public void ShouldSpecifyThatUrl()
30+
{
31+
32+
// arrange
33+
34+
// act
35+
var BaseUrl = new Uri("/testapi", UriKind.Relative);
36+
_Builder.IncludeTable(db => db.Contacts, baseUrl: BaseUrl.ToString());
37+
var config = _Builder.Build();
38+
39+
// assert
40+
Assert.Single(config.Tables);
41+
Assert.Equal(BaseUrl, config.Tables.First().BaseUrl);
42+
43+
}
44+
45+
46+
}
47+
48+

WorkingApi/Program.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
app.MapInstantAPIs<MyContext>(config =>
1717
{
18-
config.IncludeTable(db => db.Contacts, ApiMethodsToGenerate.All);
18+
config.IncludeTable(db => db.Contacts, ApiMethodsToGenerate.All, "addressBook");
1919
});
2020

2121
app.Run();

0 commit comments

Comments
 (0)