sozsoft-platform/api/src/Sozsoft.Platform.DbMigrator/Migrations/CrudDataSeeder.cs

147 lines
5.7 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Sozsoft.Platform.Entities;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Repositories;
namespace Sozsoft.Platform.Data.Seeds;
/// <summary>
/// SqlQueryManager uzerinden uretilen CRUD endpoint'lerini
/// <c>{host|tenants/{tenantId}}/crud/*.json</c> dosyalarindan okuyarak veritabanina uygular.
/// <para>
/// <see cref="SqlDataSeeder"/> tablolari olusturduktan hemen sonra calisir; boylece endpoint'ler
/// her zaman dayandiklari tablolar hazir olduktan sonra yazilir. ABP'nin contributor sirasi
/// garanti edilmedigi icin bilincli olarak <see cref="IDataSeedContributor"/> degildir,
/// SqlDataSeeder tarafindan dogrudan cagrilir.
/// </para>
/// Her dosya bir entity'ye aittir ve o entity'nin endpoint listesinin tamamini icerir:
/// dosyadaki liste kaynak kabul edilir, veritabanindaki eski kayitlar dosyaya gore guncellenir.
/// </summary>
public class CrudDataSeeder : ITransientDependency
{
private static readonly JsonSerializerOptions JsonReadOptions = new()
{
PropertyNameCaseInsensitive = true
};
private readonly IRepository<CrudEndpoint, Guid> _repository;
private readonly IConfiguration _configuration;
private readonly ILogger<CrudDataSeeder> _logger;
public CrudDataSeeder(
IRepository<CrudEndpoint, Guid> repository,
IConfiguration configuration,
ILogger<CrudDataSeeder> logger)
{
_repository = repository;
_configuration = configuration;
_logger = logger;
}
public async Task SeedAsync(DataSeedContext context)
{
var scopeFolderName = SeedPathResolver.GetScopeFolderName(context.TenantId);
var crudDataPath = SeedPathResolver.GetScopePath(_configuration, context.TenantId, SeedPathResolver.CrudFolder);
if (!Directory.Exists(crudDataPath))
{
_logger.LogInformation("Seeds/{Scope}/crud directory not found, skipping CrudDataSeeder.", scopeFolderName);
return;
}
var jsonFiles = Directory.GetFiles(crudDataPath, "*.json").OrderBy(Path.GetFileName).ToArray();
if (jsonFiles.Length == 0)
{
_logger.LogInformation("No JSON files found in Seeds/{Scope}/crud directory, skipping CrudDataSeeder.", scopeFolderName);
return;
}
_logger.LogInformation("CrudDataSeeder started. {Count} file(s) to be processed.", jsonFiles.Length);
foreach (var filePath in jsonFiles)
{
var fileName = Path.GetFileName(filePath);
try
{
var json = await File.ReadAllTextAsync(filePath);
var seedFile = JsonSerializer.Deserialize<CrudEndpointSeedFileDto>(json, JsonReadOptions);
var entityName = string.IsNullOrWhiteSpace(seedFile?.EntityName)
? Path.GetFileNameWithoutExtension(filePath)
: seedFile!.EntityName.Trim();
if (seedFile?.Endpoints == null || seedFile.Endpoints.Count == 0)
{
_logger.LogWarning("[{File}] No endpoint found, skipped.", fileName);
continue;
}
await ApplyAsync(entityName, seedFile.Endpoints);
_logger.LogInformation("[{File}] '{EntityName}' applied with {Count} endpoint(s).", fileName, entityName, seedFile.Endpoints.Count);
}
catch (Exception ex)
{
// Tek bir dosyanin hatasi diger endpoint'lerin seed edilmesini engellemez.
_logger.LogError(ex, "Failed to process CRUD seed file: {FileName}", fileName);
}
}
_logger.LogInformation("CrudDataSeeder completed. {Count} file(s) processed.", jsonFiles.Length);
}
private async Task ApplyAsync(string entityName, List<CrudEndpointSeedItemDto> endpoints)
{
var existing = await _repository.GetListAsync(x => x.EntityName == entityName);
foreach (var item in endpoints)
{
if (string.IsNullOrWhiteSpace(item.OperationType))
{
continue;
}
var current = existing.FirstOrDefault(x =>
string.Equals(x.OperationType, item.OperationType, StringComparison.OrdinalIgnoreCase));
if (current == null)
{
// CsharpCode seed dosyasinda tutulmaz; endpoint'in calismasi icin gerekli degildir,
// gerektiginde SqlQueryManager'daki "Yeniden Olustur" ile uretilir.
await _repository.InsertAsync(new CrudEndpoint
{
EntityName = entityName,
Method = item.Method,
Path = item.Path,
OperationType = item.OperationType,
IsActive = item.IsActive
}, autoSave: true);
continue;
}
current.Method = item.Method;
current.Path = item.Path;
current.IsActive = item.IsActive;
await _repository.UpdateAsync(current, autoSave: true);
}
// Dosyada artik yer almayan operasyonlar kaldirilir; dosya tek kaynaktir.
var seededOperations = endpoints
.Select(x => x.OperationType)
.Where(x => !string.IsNullOrWhiteSpace(x))
.ToHashSet(StringComparer.OrdinalIgnoreCase);
var obsolete = existing.Where(x => !seededOperations.Contains(x.OperationType)).ToList();
if (obsolete.Count > 0)
{
await _repository.DeleteManyAsync(obsolete, autoSave: true);
}
}
}