sozsoft-platform/api/src/Sozsoft.Platform.Application/DeveloperKit/CrudEndpointAppService.cs
2026-08-19 15:13:32 +03:00

102 lines
3.7 KiB
C#

using Sozsoft.Platform.Domain.DeveloperKit;
using Sozsoft.Platform.Entities;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Repositories;
namespace Sozsoft.Platform.Application;
[RemoteService]
[Route("api/app/crudendpoint")]
public class CrudEndpointAppService : ApplicationService
{
private readonly IRepository<CrudEndpoint, Guid> _endpointRepository;
private readonly IDynamicEntityManager _dynamicManager;
public CrudEndpointAppService(
IRepository<CrudEndpoint, Guid> endpointRepository,
IDynamicEntityManager dynamicManager)
{
_endpointRepository = endpointRepository;
_dynamicManager = dynamicManager;
}
/// <summary>
/// Every query string entry that is not a paging parameter is a column filter:
/// <c>?RoleId=…</c> compares for equality, <c>?Name.contains=…</c> applies the
/// named operator. The column names are validated against the table schema in
/// the manager, so an unknown column fails loudly instead of returning the
/// unfiltered collection.
/// </summary>
[HttpGet("{entityName}")]
public async Task<List<object>?> GetEntityListAsync(string entityName)
{
await EnsureEndpointAsync(entityName, "GET", "GetList");
return await _dynamicManager.GetEntityListAsync(entityName, ReadQueryFilters());
}
/// <summary>
/// Route values are not filters, so only the query string is read. A repeated
/// parameter keeps its comma joined form, which is what the <c>in</c> operator
/// expects.
/// </summary>
private Dictionary<string, string?> ReadQueryFilters()
{
var filters = new Dictionary<string, string?>(StringComparer.OrdinalIgnoreCase);
var query = LazyServiceProvider.LazyGetService<IHttpContextAccessor>()?.HttpContext?.Request?.Query;
if (query == null)
return filters;
foreach (var entry in query)
filters[entry.Key] = string.Join(",", entry.Value.ToArray());
return filters;
}
[HttpGet("{entityName}/{id}")]
public async Task<object?> GetEntityByIdAsync(string entityName, Guid id)
{
await EnsureEndpointAsync(entityName, "GET", "GetById");
return await _dynamicManager.GetEntityByIdAsync(entityName, id);
}
[HttpPost("{entityName}")]
public async Task<object?> CreateEntityAsync(string entityName, [FromBody] JsonElement data)
{
await EnsureEndpointAsync(entityName, "POST", "Create");
return await _dynamicManager.CreateEntityAsync(entityName, data);
}
[HttpPut("{entityName}/{id}")]
public async Task<object?> UpdateEntityAsync(string entityName, Guid id, [FromBody] JsonElement data)
{
await EnsureEndpointAsync(entityName, "PUT", "Update");
return await _dynamicManager.UpdateEntityAsync(entityName, id, data);
}
[HttpDelete("{entityName}/{id}")]
public async Task<bool> DeleteEntityAsync(string entityName, Guid id)
{
await EnsureEndpointAsync(entityName, "DELETE", "Delete");
return await _dynamicManager.DeleteEntityAsync(entityName, id);
}
private async Task EnsureEndpointAsync(string entityName, string method, string operation)
{
var exists = await _endpointRepository.AnyAsync(x =>
x.EntityName.ToLower() == entityName.ToLower() &&
x.Method == method &&
x.OperationType == operation &&
x.IsActive);
if (!exists)
throw new UserFriendlyException($"No active endpoint defined for {entityName} {operation}");
}
}