Merge branch 'release/v2.0.0' into 'dev'

Resolve "Prepare release v2.0.0"

Closes #23

See merge request leon.hoppe/HopFrame!12
This commit is contained in:
2024-11-23 15:16:16 +00:00
24 changed files with 92 additions and 268 deletions

34
.gitlab-ci.yml Normal file
View File

@@ -0,0 +1,34 @@
image: mcr.microsoft.com/dotnet/sdk:8.0
stages:
- build
- test
- publish
before_script:
- echo "Setting up environment"
- 'dotnet --version'
build:
stage: build
script:
- dotnet restore
- dotnet build --configuration Release --no-restore
artifacts:
paths:
- "**/bin/Release"
test:
stage: test
script:
- dotnet test --no-restore --verbosity normal
publish:
stage: publish
script:
- dotnet pack -c Release -o .
- for nupkg in *.nupkg; do dotnet nuget push $nupkg -k $NUGET_API_KEY -s https://api.nuget.org/v3/index.json; done
only:
- main
variables:
NUGET_API_KEY: $NUGET_API_KEY

View File

@@ -5,21 +5,15 @@ A simple backend management api for ASP.NET Core Web APIs
- [x] Database management - [x] Database management
- [x] User authentication - [x] User authentication
- [x] Permission management - [x] Permission management
- [x] Frontend dashboards - [x] Generated frontend administration boards
## 2.0 Todo list
- [x] 1.0 bug fixes
- [x] Code cleanup
- [x] Relations in database
- [x] Generated Admin pages
- [x] Pretty Login page for administration
- [ ] Clean documentation
# Usage # Usage
There are two different versions of HopFrame, either the Web API version or the full Blazor web version. There are two different versions of HopFrame, either the Web API version or the full Blazor web version.
## Ho to use the Web API version ## Ho to use the Web API version
> **Hint:** For more information about the HopFrame installation and usage go to the [docs](./docs).
1. Add the HopFrame.Api library to your project: 1. Add the HopFrame.Api library to your project:
``` ```

View File

@@ -7,7 +7,7 @@
<Nullable>disable</Nullable> <Nullable>disable</Nullable>
<PackageId>HopFrame.Api</PackageId> <PackageId>HopFrame.Api</PackageId>
<Version>1.1.0</Version> <Version>2.0.0</Version>
<PackageReadmeFile>README.md</PackageReadmeFile> <PackageReadmeFile>README.md</PackageReadmeFile>
<PackageLicenseExpression>MIT</PackageLicenseExpression> <PackageLicenseExpression>MIT</PackageLicenseExpression>
<IsPackable>true</IsPackable> <IsPackable>true</IsPackable>

View File

@@ -1,100 +1,4 @@
# HopFrame API module # HopFrame API module
This module contains some useful endpoints for user login / register management. This module contains some useful endpoints for user login / register management.
## Ho to use the Web API version For more information about the HopFrame visit the [docs](https://git.leon-hoppe.de/leon.hoppe/HopFrame).
1. Add the HopFrame.Api library to your project:
```
dotnet add package HopFrame.Api
```
2. Create a DbContext that inherits the ``HopDbContext`` and add a data source
```csharp
public class DatabaseContext : HopDbContextBase {
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
base.OnConfiguring(optionsBuilder);
optionsBuilder.UseSqlite("...");
}
}
```
3. Add the DbContext and HopFrame to your services
```csharp
builder.Services.AddDbContext<DatabaseContext>();
builder.Services.AddHopFrame<DatabaseContext>();
```
# Endpoints
By default, the module provides a controller for handling authentication based requests by the user.
You can explore the contoller by the build in swagger site from ASP .NET.
## Disable the Endpoints
```csharp
builder.Services.AddDbContext<DatabaseContext>();
//builder.Services.AddHopFrame<DatabaseContext>();
services.AddHopFrameNoEndpoints<TDbContext>();
```
# Services added in this module
You can use these services by specifying them as a dependency. All of them are scoped dependencies.
## LogicResult
Logic result is an extension of the ActionResult for an ApiController. It provides simple Http status results with either a message or data by specifying the generic type.
```csharp
public class LogicResult : ILogicResult {
public static LogicResult Ok();
public static LogicResult BadRequest();
public static LogicResult BadRequest(string message);
public static LogicResult Forbidden();
public static LogicResult Forbidden(string message);
public static LogicResult NotFound();
public static LogicResult NotFound(string message);
public static LogicResult Conflict();
public static LogicResult Conflict(string message);
public static LogicResult Forward(LogicResult result);
public static LogicResult Forward<T>(ILogicResult<T> result);
public static implicit operator ActionResult(LogicResult v);
}
public class LogicResult<T> : ILogicResult<T> {
public static LogicResult<T> Ok();
public static LogicResult<T> Ok(T result);
...
}
```
## IAuthLogic
This service handles all logic needed to provide the authentication endpoints by using the LogicResults.
```csharp
public interface IAuthLogic {
Task<LogicResult<SingleValueResult<string>>> Login(UserLogin login);
Task<LogicResult<SingleValueResult<string>>> Register(UserRegister register);
Task<LogicResult<SingleValueResult<string>>> Authenticate();
Task<LogicResult> Logout();
Task<LogicResult> Delete(UserPasswordValidation validation);
}
```

View File

@@ -7,7 +7,7 @@
<Nullable>disable</Nullable> <Nullable>disable</Nullable>
<PackageId>HopFrame.Database</PackageId> <PackageId>HopFrame.Database</PackageId>
<Version>1.1.0</Version> <Version>2.0.0</Version>
<PackageReadmeFile>README.md</PackageReadmeFile> <PackageReadmeFile>README.md</PackageReadmeFile>
<PackageLicenseExpression>MIT</PackageLicenseExpression> <PackageLicenseExpression>MIT</PackageLicenseExpression>
<IsPackable>true</IsPackable> <IsPackable>true</IsPackable>

View File

@@ -1,2 +1,4 @@
# HopFrame Database module # HopFrame Database module
This module contains all the logic for the database communication This module contains all the logic for the database communication.
For more information about the HopFrame visit the [docs](https://git.leon-hoppe.de/leon.hoppe/HopFrame).

View File

@@ -8,7 +8,7 @@
<RootNamespace>HopFrame.Security</RootNamespace> <RootNamespace>HopFrame.Security</RootNamespace>
<PackageId>HopFrame.Security</PackageId> <PackageId>HopFrame.Security</PackageId>
<Version>1.1.0</Version> <Version>2.0.0</Version>
<PackageReadmeFile>README.md</PackageReadmeFile> <PackageReadmeFile>README.md</PackageReadmeFile>
<PackageLicenseExpression>MIT</PackageLicenseExpression> <PackageLicenseExpression>MIT</PackageLicenseExpression>
<IsPackable>true</IsPackable> <IsPackable>true</IsPackable>

View File

@@ -1,74 +1,4 @@
# HopFrame Security module # HopFrame Security module
this module contains all handlers for the login and register validation. It also checks the user permissions. this module contains all handlers for the login and register validation. It also checks the user permissions.
# Services added in this module For more information about the HopFrame visit the [docs](https://git.leon-hoppe.de/leon.hoppe/HopFrame).
You can use these services by specifying them as a dependency. All of them are scoped dependencies.
## ITokenContext
This service provides the information given by the current request
```csharp
public interface ITokenContext {
bool IsAuthenticated { get; }
User User { get; }
Guid AccessToken { get; }
}
```
## IUserService
This service simplifies the data access of the user table in the database.
```csharp
public interface IUserService {
Task<IList<User>> GetUsers();
Task<User> GetUser(Guid userId);
Task<User> GetUserByEmail(string email);
Task<User> GetUserByUsername(string username);
Task<User> AddUser(UserRegister user);
Task UpdateUser(User user);
Task DeleteUser(User user);
Task<bool> CheckUserPassword(User user, string password);
Task ChangePassword(User user, string password);
}
```
## IPermissionService
This service handles all permission and group interactions with the data source.
```csharp
public interface IPermissionService {
Task<bool> HasPermission(string permission, Guid user);
Task<IList<PermissionGroup>> GetPermissionGroups();
Task<PermissionGroup> GetPermissionGroup(string name);
Task EditPermissionGroup(PermissionGroup group);
Task<IList<PermissionGroup>> GetUserPermissionGroups(User user);
Task RemoveGroupFromUser(User user, PermissionGroup group);
Task<PermissionGroup> CreatePermissionGroup(string name, bool isDefault = false, string description = null);
Task DeletePermissionGroup(PermissionGroup group);
Task<Permission> GetPermission(string name, IPermissionOwner owner);
Task AddPermission(IPermissionOwner owner, string permission);
Task RemovePermission(Permission permission);
Task<string[]> GetFullPermissions(string user);
}
```

View File

@@ -78,11 +78,11 @@ public interface IAdminPageGenerator<TModel> {
IAdminPageGenerator<TModel> DefaultSort<TProperty>(Expression<Func<TModel, TProperty>> propertyExpression, ListSortDirection direction); IAdminPageGenerator<TModel> DefaultSort<TProperty>(Expression<Func<TModel, TProperty>> propertyExpression, ListSortDirection direction);
/// <summary> /// <summary>
/// Specifies the repository for the page /// Specifies the repository provider for the page
/// </summary> /// </summary>
/// <typeparam name="TRepository">The specified repository</typeparam> /// <typeparam name="TRepository">The specified provider</typeparam>
/// <returns></returns> /// <returns></returns>
IAdminPageGenerator<TModel> ConfigureRepository<TRepository>() where TRepository : ModelRepository<TModel>; IAdminPageGenerator<TModel> ConfigureProvider<TRepository>() where TRepository : ModelProvider<TModel>;
/// <summary> /// <summary>

View File

@@ -91,7 +91,7 @@ internal sealed class AdminPageGenerator<TModel> : IAdminPageGenerator<TModel>,
return this; return this;
} }
public IAdminPageGenerator<TModel> ConfigureRepository<TRepository>() where TRepository : ModelRepository<TModel> { public IAdminPageGenerator<TModel> ConfigureProvider<TRepository>() where TRepository : ModelProvider<TModel> {
Page.RepositoryProvider = typeof(TRepository); Page.RepositoryProvider = typeof(TRepository);
return this; return this;
} }

View File

@@ -4,10 +4,20 @@
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>disable</Nullable> <Nullable>disable</Nullable>
<PackageId>HopFrame.Web.Admin</PackageId>
<Version>2.0.0</Version>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<IsPackable>true</IsPackable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Include="README.md" Pack="true" PackagePath="\"/>
</ItemGroup>
</Project> </Project>

View File

@@ -1,6 +1,6 @@
namespace HopFrame.Web.Admin; namespace HopFrame.Web.Admin;
public abstract class ModelRepository<TModel> : IModelRepository { public abstract class ModelProvider<TModel> : IModelProvider {
public abstract Task<IEnumerable<TModel>> ReadAll(); public abstract Task<IEnumerable<TModel>> ReadAll();
public abstract Task<TModel> Create(TModel model); public abstract Task<TModel> Create(TModel model);
public abstract Task<TModel> Update(TModel model); public abstract Task<TModel> Update(TModel model);
@@ -25,7 +25,7 @@ public abstract class ModelRepository<TModel> : IModelRepository {
} }
} }
public interface IModelRepository { public interface IModelProvider {
Task<IEnumerable<object>> ReadAllO(); Task<IEnumerable<object>> ReadAllO();
Task<object> CreateO(object model); Task<object> CreateO(object model);
Task<object> UpdateO(object model); Task<object> UpdateO(object model);

View File

@@ -24,10 +24,12 @@ public class AdminPage {
public bool ShowDeleteButton { get; set; } = true; public bool ShowDeleteButton { get; set; } = true;
public bool ShowUpdateButton { get; set; } = true; public bool ShowUpdateButton { get; set; } = true;
public IModelRepository LoadModelRepository(IServiceProvider provider) { public IModelProvider LoadModelProvider(IServiceProvider provider) {
if (RepositoryProvider is null) return null; if (RepositoryProvider is null) return null;
var repoProvider = provider.GetService(RepositoryProvider);
if (repoProvider != null) return repoProvider as IModelProvider;
var dependencies = AdminContextGenerator.ResolveDependencies(RepositoryProvider, provider); var dependencies = AdminContextGenerator.ResolveDependencies(RepositoryProvider, provider);
return Activator.CreateInstance(RepositoryProvider, dependencies) as IModelRepository; return Activator.CreateInstance(RepositoryProvider, dependencies) as IModelProvider;
} }
} }

View File

@@ -0,0 +1,4 @@
# HopFrame admin pages module
This module contains all necessary information to create and compile the generated admin pages.
For more information about the HopFrame visit the [docs](https://git.leon-hoppe.de/leon.hoppe/HopFrame).

View File

@@ -128,7 +128,7 @@
private Dictionary<AdminPageProperty, FieldIdentifier> _validationIdentifiers; private Dictionary<AdminPageProperty, FieldIdentifier> _validationIdentifiers;
private IDictionary<AdminPageProperty, object> _values; private IDictionary<AdminPageProperty, object> _values;
private Dictionary<AdminPageProperty, object[]> _selectorValues; private Dictionary<AdminPageProperty, object[]> _selectorValues;
private IModelRepository _repository; private IModelProvider _provider;
private AdminPage _currentPage; private AdminPage _currentPage;
private object _entry; private object _entry;
@@ -143,7 +143,7 @@
_currentPage = page; _currentPage = page;
_entry = entryToEdit; _entry = entryToEdit;
_isEdit = entryToEdit is not null; _isEdit = entryToEdit is not null;
_repository = _currentPage.LoadModelRepository(Provider); _provider = _currentPage.LoadModelProvider(Provider);
_entry ??= Activator.CreateInstance(_currentPage.ModelType); _entry ??= Activator.CreateInstance(_currentPage.ModelType);
_context = new EditContext(_entry); _context = new EditContext(_entry);
@@ -246,7 +246,7 @@
foreach (var value in _values) { foreach (var value in _values) {
if (value.Key.Unique) { if (value.Key.Unique) {
if (value.Value == value.Key.GetValue(_entry)) continue; if (value.Value == value.Key.GetValue(_entry)) continue;
var data = _repository!.ReadAllO().GetAwaiter().GetResult(); var data = _provider!.ReadAllO().GetAwaiter().GetResult();
foreach (var entry in data) { foreach (var entry in data) {
var other = value.Key.GetValue(entry); var other = value.Key.GetValue(entry);
if (!other.Equals(value.Value)) continue; if (!other.Equals(value.Value)) continue;
@@ -293,7 +293,7 @@
throw new ArgumentException($"'{property.Name}' cannot be a selector because a admin page for '{type.Name}' does not exist!"); throw new ArgumentException($"'{property.Name}' cannot be a selector because a admin page for '{type.Name}' does not exist!");
} }
var repo = page.LoadModelRepository(Provider); var repo = page.LoadModelProvider(Provider);
var objects = (await repo!.ReadAllO()).ToArray(); var objects = (await repo!.ReadAllO()).ToArray();
_selectorValues[property] = objects; _selectorValues[property] = objects;
@@ -346,7 +346,7 @@
} }
if (!_isEdit) { if (!_isEdit) {
await _repository.CreateO(_entry); await _provider.CreateO(_entry);
Alerts.FireAsync(new SweetAlertOptions { Alerts.FireAsync(new SweetAlertOptions {
Title = "New entry added!", Title = "New entry added!",
@@ -356,7 +356,7 @@
}); });
} }
else { else {
await _repository.UpdateO(_entry); await _provider.UpdateO(_entry);
Alerts.FireAsync(new SweetAlertOptions { Alerts.FireAsync(new SweetAlertOptions {
Title = "Entry updated!", Title = "Entry updated!",

View File

@@ -5,7 +5,7 @@ using HopFrame.Web.Admin;
using HopFrame.Web.Admin.Attributes; using HopFrame.Web.Admin.Attributes;
using HopFrame.Web.Admin.Generators; using HopFrame.Web.Admin.Generators;
using HopFrame.Web.Admin.Models; using HopFrame.Web.Admin.Models;
using HopFrame.Web.Repositories; using HopFrame.Web.Provider;
namespace HopFrame.Web; namespace HopFrame.Web;
@@ -20,7 +20,7 @@ internal class HopAdminContext : AdminPagesContext {
public override void OnModelCreating(IAdminContextGenerator generator) { public override void OnModelCreating(IAdminContextGenerator generator) {
generator.Page<User>() generator.Page<User>()
.Description("On this page you can manage all user accounts.") .Description("On this page you can manage all user accounts.")
.ConfigureRepository<UserProvider>() .ConfigureProvider<UserProvider>()
.ViewPermission(AdminPermissions.ViewUsers) .ViewPermission(AdminPermissions.ViewUsers)
.CreatePermission(AdminPermissions.AddUser) .CreatePermission(AdminPermissions.AddUser)
.UpdatePermission(AdminPermissions.EditUser) .UpdatePermission(AdminPermissions.EditUser)
@@ -63,7 +63,7 @@ internal class HopAdminContext : AdminPagesContext {
generator.Page<PermissionGroup>() generator.Page<PermissionGroup>()
.Description("On this page you can view, create, edit and delete permission groups.") .Description("On this page you can view, create, edit and delete permission groups.")
.ConfigureRepository<GroupProvider>() .ConfigureProvider<GroupProvider>()
.ViewPermission(AdminPermissions.ViewGroups) .ViewPermission(AdminPermissions.ViewGroups)
.CreatePermission(AdminPermissions.AddGroup) .CreatePermission(AdminPermissions.AddGroup)
.UpdatePermission(AdminPermissions.EditGroup) .UpdatePermission(AdminPermissions.EditGroup)

View File

@@ -7,7 +7,7 @@
<AddRazorSupportForMvc>true</AddRazorSupportForMvc> <AddRazorSupportForMvc>true</AddRazorSupportForMvc>
<PackageId>HopFrame.Web</PackageId> <PackageId>HopFrame.Web</PackageId>
<Version>1.1.0</Version> <Version>2.0.0</Version>
<PackageReadmeFile>README.md</PackageReadmeFile> <PackageReadmeFile>README.md</PackageReadmeFile>
<PackageLicenseExpression>MIT</PackageLicenseExpression> <PackageLicenseExpression>MIT</PackageLicenseExpression>
<IsPackable>true</IsPackable> <IsPackable>true</IsPackable>

View File

@@ -113,7 +113,7 @@
public string Url { get; set; } public string Url { get; set; }
private AdminPage _pageData; private AdminPage _pageData;
private IModelRepository _modelRepository; private IModelProvider _modelProvider;
private IEnumerable<object> _modelBuffer; private IEnumerable<object> _modelBuffer;
private AdminPageModal _modal; private AdminPageModal _modal;
@@ -138,7 +138,7 @@
if (_pageData.RepositoryProvider is null) if (_pageData.RepositoryProvider is null)
throw new ArgumentException($"AdminPage '{_pageData.Title}' does not specify a model repository!'"); throw new ArgumentException($"AdminPage '{_pageData.Title}' does not specify a model repository!'");
_modelRepository = _pageData.LoadModelRepository(Provider); _modelProvider = _pageData.LoadModelProvider(Provider);
_hasEditPermission = _pageData.Permissions.Update is null || await Permissions.HasPermission(Auth.User, _pageData.Permissions.Update); _hasEditPermission = _pageData.Permissions.Update is null || await Permissions.HasPermission(Auth.User, _pageData.Permissions.Update);
_hasDeletePermission = _pageData.Permissions.Delete is null || await Permissions.HasPermission(Auth.User, _pageData.Permissions.Delete); _hasDeletePermission = _pageData.Permissions.Delete is null || await Permissions.HasPermission(Auth.User, _pageData.Permissions.Delete);
@@ -154,7 +154,7 @@
} }
private async Task Reload() { private async Task Reload() {
_modelBuffer = await _modelRepository.ReadAllO(); _modelBuffer = await _modelProvider.ReadAllO();
_displayedModels = _modelBuffer.ToList(); _displayedModels = _modelBuffer.ToList();
_currentSortDirection = _pageData.DefaultSortDirection; _currentSortDirection = _pageData.DefaultSortDirection;
@@ -238,7 +238,7 @@
}); });
if (result.IsConfirmed) { if (result.IsConfirmed) {
await _modelRepository.DeleteO(entry); await _modelProvider.DeleteO(entry);
await Reload(); await Reload();
await Alerts.FireAsync(new SweetAlertOptions { await Alerts.FireAsync(new SweetAlertOptions {

View File

@@ -2,9 +2,9 @@ using HopFrame.Database.Models;
using HopFrame.Database.Repositories; using HopFrame.Database.Repositories;
using HopFrame.Web.Admin; using HopFrame.Web.Admin;
namespace HopFrame.Web.Repositories; namespace HopFrame.Web.Provider;
internal sealed class GroupProvider(IGroupRepository repo) : ModelRepository<PermissionGroup> { internal sealed class GroupProvider(IGroupRepository repo) : ModelProvider<PermissionGroup> {
public override async Task<IEnumerable<PermissionGroup>> ReadAll() { public override async Task<IEnumerable<PermissionGroup>> ReadAll() {
return await repo.GetPermissionGroups(); return await repo.GetPermissionGroups();
} }

View File

@@ -2,9 +2,9 @@ using HopFrame.Database.Models;
using HopFrame.Database.Repositories; using HopFrame.Database.Repositories;
using HopFrame.Web.Admin; using HopFrame.Web.Admin;
namespace HopFrame.Web.Repositories; namespace HopFrame.Web.Provider;
internal sealed class UserProvider(IUserRepository repo) : ModelRepository<User> { internal sealed class UserProvider(IUserRepository repo) : ModelProvider<User> {
public override async Task<IEnumerable<User>> ReadAll() { public override async Task<IEnumerable<User>> ReadAll() {
return await repo.GetUsers(); return await repo.GetUsers();
} }

View File

@@ -1,60 +1,4 @@
# HopFrame Web module # HopFrame Web module
This module contains useful helpers for Blazor Apps and an Admin Dashboard. This module contains useful helpers for Blazor Apps and an Admin Dashboard.
## How to use the Blazor API For more information about the HopFrame visit the [docs](https://git.leon-hoppe.de/leon.hoppe/HopFrame).
1. Add the HopFrame.Web library to your project
```
dotnet add package HopFrame.Web
```
2. Create a DbContext that inherits the ``HopDbContext`` and add a data source
```csharp
public class DatabaseContext : HopDbContextBase {
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
base.OnConfiguring(optionsBuilder);
optionsBuilder.UseSqlite("...");
}
}
```
3. Add the DbContext and HopFrame to your services
```csharp
builder.Services.AddDbContext<DatabaseContext>();
builder.Services.AddHopFrame<DatabaseContext>();
```
4. Add the authentication middleware to your app
```csharp
app.UseMiddleware<AuthMiddleware>();
```
5. Add the HopFrame pages to your Razor components
```csharp
app.MapRazorComponents<App>()
.AddHopFrameAdminPages()
.AddInteractiveServerRenderMode();
```
# Services added in this module
You can use these services by specifying them as a dependency. All of them are scoped dependencies.
## IAuthService
This service handles all the authentication like login or register. It properly creates all tokens so the user can be identified
```csharp
public interface IAuthService {
Task Register(UserRegister register);
Task<bool> Login(UserLogin login);
Task Logout();
Task<TokenEntry> RefreshLogin();
Task<bool> IsLoggedIn();
}
```

View File

@@ -28,11 +28,11 @@ public class AdminContext : AdminPagesContext {
.Parser<Employee>((model, e) => model.AddressId = e.EmployeeId); .Parser<Employee>((model, e) => model.AddressId = e.EmployeeId);
generator.Page<Employee>() generator.Page<Employee>()
.ConfigureRepository<EmployeeProvider>() .ConfigureProvider<EmployeeProvider>()
.ListingProperty(e => e.Name); .ListingProperty(e => e.Name);
generator.Page<Address>() generator.Page<Address>()
.ConfigureRepository<AddressProvider>() .ConfigureProvider<AddressProvider>()
.ListingProperty(a => a.City); .ListingProperty(a => a.City);
} }
} }

View File

@@ -4,7 +4,7 @@ using RestApiTest.Models;
namespace FrontendTest.Providers; namespace FrontendTest.Providers;
public class AddressProvider(DatabaseContext context) : ModelRepository<Address> { public class AddressProvider(DatabaseContext context) : ModelProvider<Address> {
public override async Task<IEnumerable<Address>> ReadAll() { public override async Task<IEnumerable<Address>> ReadAll() {
return await context.Addresses.ToArrayAsync(); return await context.Addresses.ToArrayAsync();

View File

@@ -4,7 +4,7 @@ using RestApiTest.Models;
namespace FrontendTest.Providers; namespace FrontendTest.Providers;
public class EmployeeProvider(DatabaseContext context) : ModelRepository<Employee> { public class EmployeeProvider(DatabaseContext context) : ModelProvider<Employee> {
public override async Task<IEnumerable<Employee>> ReadAll() { public override async Task<IEnumerable<Employee>> ReadAll() {
return await context.Employees return await context.Employees