Skip to content

Commit 2c0d002

Browse files
author
Dries Verbeke
committed
Enabling multiple data providers
- added an interface with factory - included ef core provider - included json provider - did some code reorganisation/move/rename
1 parent be6d708 commit 2c0d002

34 files changed

+890
-681
lines changed

Fritz.InstantAPIs/ApiMethodsToGenerate.cs

+1-7
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,9 @@ public enum ApiMethodsToGenerate
1111
All = 31
1212
}
1313

14-
public record TableApiMapping(
15-
string TableName,
16-
ApiMethodsToGenerate MethodsToGenerate = ApiMethodsToGenerate.All,
17-
string BaseUrl = ""
18-
);
19-
2014
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
2115
public class ApiMethodAttribute : Attribute
22-
{
16+
{
2317
public ApiMethodsToGenerate MethodsToGenerate { get; set; }
2418
public ApiMethodAttribute(ApiMethodsToGenerate apiMethodsToGenerate)
2519
{
+113
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
using Fritz.InstantAPIs.Repositories;
2+
using System.Linq.Expressions;
3+
4+
namespace Microsoft.AspNetCore.Builder;
5+
6+
public class InstantAPIsBuilder<TContext>
7+
where TContext : class
8+
{
9+
10+
private Type _ContextType = typeof(TContext);
11+
12+
private readonly InstantAPIsOptions _instantApiOptions;
13+
private readonly IContextHelper<TContext> _contextFactory;
14+
private readonly HashSet<InstantAPIsOptions.ITable> _tables = new HashSet<InstantAPIsOptions.ITable>();
15+
private readonly IList<string> _excludedTables = new List<string>();
16+
17+
public InstantAPIsBuilder(InstantAPIsOptions instantApiOptions, IContextHelper<TContext> contextFactory)
18+
{
19+
_instantApiOptions = instantApiOptions;
20+
_contextFactory = contextFactory;
21+
}
22+
23+
private IEnumerable<InstantAPIsOptions.ITable> DiscoverTables()
24+
{
25+
return _contextFactory != null
26+
? _contextFactory.DiscoverFromContext(_instantApiOptions.DefaultUri)
27+
: Array.Empty<InstantAPIsOptions.ITable>();
28+
}
29+
30+
#region Table Inclusion/Exclusion
31+
32+
/// <summary>
33+
/// Specify individual tables to include in the API generation with the methods requested
34+
/// </summary>
35+
/// <param name="setSelector">Select the EntityFramework DbSet to include - Required</param>
36+
/// <param name="methodsToGenerate">A flags enumerable indicating the methods to generate. By default ALL are generated</param>
37+
/// <returns>Configuration builder with this configuration applied</returns>
38+
public InstantAPIsBuilder<TContext> IncludeTable<TSet, TEntity, TKey>(Expression<Func<TContext, TSet>> setSelector,
39+
InstantAPIsOptions.TableOptions<TEntity, TKey> config, ApiMethodsToGenerate methodsToGenerate = ApiMethodsToGenerate.All,
40+
string baseUrl = "")
41+
where TSet : class
42+
where TEntity : class
43+
{
44+
var propertyName = _contextFactory.NameTable(setSelector);
45+
46+
if (!string.IsNullOrEmpty(baseUrl))
47+
{
48+
try
49+
{
50+
var testUri = new Uri(baseUrl, UriKind.RelativeOrAbsolute);
51+
baseUrl = testUri.IsAbsoluteUri ? testUri.LocalPath : baseUrl;
52+
}
53+
catch
54+
{
55+
throw new ArgumentException(nameof(baseUrl), "Not a valid Uri");
56+
}
57+
}
58+
else
59+
{
60+
baseUrl = string.Concat(_instantApiOptions.DefaultUri.ToString(), "/", propertyName);
61+
}
62+
63+
var tableApiMapping = new InstantAPIsOptions.Table<TContext, TSet, TEntity, TKey>(propertyName, new Uri(baseUrl, UriKind.Relative), setSelector, config)
64+
{
65+
ApiMethodsToGenerate = methodsToGenerate
66+
};
67+
68+
_tables.RemoveWhere(x => x.Name == tableApiMapping.Name);
69+
_tables.Add(tableApiMapping);
70+
71+
return this;
72+
73+
}
74+
75+
/// <summary>
76+
/// Exclude individual tables from the API generation. Exclusion takes priority over inclusion
77+
/// </summary>
78+
/// <param name="setSelector">Select the entity to exclude from generation</param>
79+
/// <returns>Configuration builder with this configuraiton applied</returns>
80+
public InstantAPIsBuilder<TContext> ExcludeTable<TSet>(Expression<Func<TContext, TSet>> setSelector) where TSet : class
81+
{
82+
var propertyName = _contextFactory.NameTable(setSelector);
83+
_excludedTables.Add(propertyName);
84+
85+
return this;
86+
}
87+
88+
private void BuildTables()
89+
{
90+
if (!_tables.Any())
91+
{
92+
var discoveredTables = DiscoverTables();
93+
foreach (var discoveredTable in discoveredTables)
94+
{
95+
_tables.Add(discoveredTable);
96+
}
97+
}
98+
99+
_tables.RemoveWhere(t => _excludedTables.Any(e => t.Name.Equals(e, StringComparison.InvariantCultureIgnoreCase)));
100+
101+
if (!_tables.Any()) throw new ArgumentException("All tables were excluded from this configuration");
102+
}
103+
104+
#endregion
105+
106+
internal IEnumerable<InstantAPIsOptions.ITable> Build()
107+
{
108+
BuildTables();
109+
110+
return _tables;
111+
}
112+
113+
}

Fritz.InstantAPIs/InstantAPIsConfig.cs

-137
This file was deleted.
+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
using Swashbuckle.AspNetCore.SwaggerGen;
2+
using System.Linq.Expressions;
3+
4+
namespace Fritz.InstantAPIs;
5+
6+
public enum EnableSwagger
7+
{
8+
None,
9+
DevelopmentOnly,
10+
Always
11+
}
12+
13+
public class InstantAPIsOptions
14+
{
15+
public Uri DefaultUri = new Uri("/api", UriKind.Relative);
16+
17+
public EnableSwagger? EnableSwagger { get; set; }
18+
public Action<SwaggerGenOptions>? Swagger { get; set; }
19+
20+
public IEnumerable<ITable> Tables { get; internal set; } = new HashSet<ITable>();
21+
22+
internal class Table<TContext, TSet, TEntity, TKey>
23+
: ITable
24+
where TContext : class
25+
where TSet : class
26+
where TEntity : class
27+
{
28+
public Table(string name, Uri baseUrl, Expression<Func<TContext, TSet>> entitySelector, TableOptions<TEntity, TKey> config)
29+
{
30+
Name = name;
31+
BaseUrl = baseUrl;
32+
EntitySelector = entitySelector;
33+
Config = config;
34+
35+
RepoType = typeof(TContext);
36+
InstanceType = typeof(TEntity);
37+
}
38+
39+
public string Name { get; }
40+
public Type RepoType { get; }
41+
public Type InstanceType { get; }
42+
public Uri BaseUrl { get; set; }
43+
public ApiMethodsToGenerate ApiMethodsToGenerate { get; set; } = ApiMethodsToGenerate.All;
44+
45+
public Expression<Func<TContext, TSet>> EntitySelector { get; }
46+
public TableOptions<TEntity, TKey> Config { get; }
47+
48+
public object EntitySelectorObject => EntitySelector;
49+
public object ConfigObject => Config;
50+
}
51+
52+
public interface ITable
53+
{
54+
public string Name { get; }
55+
public Type RepoType { get; }
56+
public Type InstanceType { get; }
57+
public Uri BaseUrl { get; set; }
58+
public ApiMethodsToGenerate ApiMethodsToGenerate { get; set; }
59+
60+
public object EntitySelectorObject { get; }
61+
public object ConfigObject { get; }
62+
63+
}
64+
65+
66+
public record TableOptions<TEntity, TKey>()
67+
{
68+
public Expression<Func<TEntity, TKey>>? KeySelector { get; set; }
69+
70+
public Expression<Func<TEntity, TKey>>? OrderBy { get; set; }
71+
}
72+
}

Fritz.InstantAPIs/InstantAPIsServiceCollectionExtensions.cs

+19-5
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1-
namespace Microsoft.Extensions.DependencyInjection;
1+
using Fritz.InstantAPIs.Repositories;
2+
using Fritz.InstantAPIs.Repositories.Json;
3+
4+
namespace Microsoft.Extensions.DependencyInjection;
25

36
public static class InstantAPIsServiceCollectionExtensions
47
{
5-
public static IServiceCollection AddInstantAPIs(this IServiceCollection services, Action<InstantAPIsServiceOptions>? setupAction = null)
8+
public static IServiceCollection AddInstantAPIs(this IServiceCollection services, Action<InstantAPIsOptions>? setupAction = null)
69
{
7-
var options = new InstantAPIsServiceOptions();
10+
var options = new InstantAPIsOptions();
811

912
// Get the service options
1013
setupAction?.Invoke(options);
@@ -22,11 +25,22 @@ public static IServiceCollection AddInstantAPIs(this IServiceCollection services
2225
}
2326

2427
// Register the required options so that it can be accessed by InstantAPIs middleware
25-
services.Configure<InstantAPIsServiceOptions>(config =>
28+
services.Configure<InstantAPIsOptions>(config =>
2629
{
2730
config.EnableSwagger = options.EnableSwagger;
2831
});
2932

30-
return services;
33+
services.AddSingleton(typeof(IRepositoryHelperFactory<,,,>), typeof(RepositoryHelperFactory<,,,>));
34+
services.AddSingleton(typeof(IContextHelper<>), typeof(ContextHelper<>));
35+
36+
// ef core specific
37+
services.AddSingleton<IRepositoryHelperFactory, EfCoreRepositoryHelperFactory>();
38+
services.AddSingleton<IContextHelper, EfCoreContextHelper>();
39+
40+
// json specific
41+
services.AddSingleton<IRepositoryHelperFactory, JsonRepositoryHelperFactory>();
42+
services.AddSingleton<IContextHelper, JsonContextHelper>();
43+
44+
return services;
3145
}
3246
}

0 commit comments

Comments
 (0)