sozsoft-platform/api/src/Sozsoft.Platform.Domain/Data/CustomComponentDataSourceResolver.cs
2026-09-05 21:20:18 +03:00

120 lines
4.3 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
namespace Sozsoft.Platform.Data.Seeds;
/// <summary>
/// Component'in designer dokumanindaki (Props) Data sekmesi endpoint'lerini cozup
/// CRUD endpoint katalogu ile eslestiren ortak mantik.
/// <para>
/// Cozum kaydetme sirasinda (CustomComponentAppService) katalogu veritabanindan okur;
/// boylece <c>DataSources</c> sutunu tasarimci dokumanindan tutarli sekilde uretilir.
/// </para>
/// </summary>
public static class CustomComponentDataSourceResolver
{
private static readonly JsonSerializerOptions JsonReadOptions = new()
{
PropertyNameCaseInsensitive = true
};
/// <summary>Eslestirmede kullanilan CRUD endpoint tanimi.</summary>
public sealed class CrudEndpointReference
{
public string EntityName { get; set; } = string.Empty;
public string Method { get; set; } = string.Empty;
public string Path { get; set; } = string.Empty;
public string OperationType { get; set; } = string.Empty;
}
/// <summary>
/// Props icindeki <c>visualDesigner.dataSources</c> listesini okur ve her kaydi method + path
/// ile <paramref name="endpoints"/> icinde arar. Eslesmeyen (elle yazilmis ya da custom)
/// endpoint'ler de listede kalir; yalnizca entity/seed dosyasi alanlari bos gelir.
/// </summary>
public static List<CustomComponentSeedDataSourceDto> Resolve(
string? props,
IReadOnlyCollection<CrudEndpointReference> endpoints)
{
var documentSources = ParseDocumentDataSources(props);
if (documentSources.Count == 0)
{
return [];
}
var resolved = new List<CustomComponentSeedDataSourceDto>();
foreach (var source in documentSources)
{
var method = (source.Method ?? string.Empty).Trim().ToUpperInvariant();
var url = (source.Url ?? string.Empty).Trim();
if (url.Length == 0)
{
continue;
}
// Ayni method + path birden fazla bilesene baglanmis olabilir; tekrarlari eleriz.
if (resolved.Exists(x => x.Method == method && string.Equals(x.Url, url, StringComparison.OrdinalIgnoreCase)))
{
continue;
}
var endpoint = endpoints.FirstOrDefault(x =>
string.Equals(x.Method?.Trim(), method, StringComparison.OrdinalIgnoreCase) &&
string.Equals(x.Path?.Trim(), url, StringComparison.OrdinalIgnoreCase));
resolved.Add(new CustomComponentSeedDataSourceDto
{
Name = (source.Name ?? string.Empty).Trim(),
Method = method,
Url = url,
ResponsePath = string.IsNullOrWhiteSpace(source.ResponsePath) ? null : source.ResponsePath.Trim(),
EntityName = endpoint?.EntityName,
OperationType = endpoint?.OperationType,
SeedFile = endpoint == null
? null
: $"{SeedPathResolver.CrudFolder}/{endpoint.EntityName}.json"
});
}
return resolved;
}
/// <summary>Props JSON'undaki designer dokumanindan yalnizca dataSources dizisini ayiklar.</summary>
private static List<DesignerDataSourceDto> ParseDocumentDataSources(string? props)
{
if (string.IsNullOrWhiteSpace(props))
{
return [];
}
try
{
using var document = JsonDocument.Parse(props);
if (!document.RootElement.TryGetProperty("visualDesigner", out var designer) ||
designer.ValueKind != JsonValueKind.Object ||
!designer.TryGetProperty("dataSources", out var dataSources) ||
dataSources.ValueKind != JsonValueKind.Array)
{
return [];
}
return JsonSerializer.Deserialize<List<DesignerDataSourceDto>>(dataSources.GetRawText(), JsonReadOptions) ?? [];
}
catch (JsonException)
{
return [];
}
}
/// <summary>Designer dokumanindaki tek bir data source kaydi.</summary>
private sealed class DesignerDataSourceDto
{
public string? Name { get; set; }
public string? Method { get; set; }
public string? Url { get; set; }
public string? ResponsePath { get; set; }
}
}