From 2b96619a1dd221f85cd1a1efc0cf805d800f137f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sedat=20=C3=96zt=C3=BCrk?= Date: Tue, 3 Mar 2026 00:34:19 +0300 Subject: [PATCH] =?UTF-8?q?CrudEndpoint=20ve=20CrudEndpointGenerate=20d?= =?UTF-8?q?=C3=BCzenlemesi?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DeveloperKit/CrudEndpointDto.cs | 2 - .../DeveloperKit/ICrudEndpointAppService.cs | 5 +- .../DeveloperKit/ISqlTableAppService.cs | 18 - .../DeveloperKit/SqlTableDto.cs | 62 --- .../CrudEndpointGenerateAppService.cs | 173 ++++---- .../DeveloperKitAutoMapperProfile.cs | 11 +- .../DeveloperKit/SqlTableAppService.cs | 240 ----------- .../Enums/TableNameEnum.cs | 2 - .../PlatformConsts.cs | 2 + .../TableNameResolver.cs | 2 - .../DeveloperKit/CrudEndpoint.cs | 3 - .../Administration/DeveloperKit/SqlTable.cs | 49 --- .../PlatformDynamicEntityManager.cs | 176 ++++---- .../EntityFrameworkCore/PlatformDbContext.cs | 38 -- ....cs => 20260302191746_Initial.Designer.cs} | 191 +-------- ...7_Initial.cs => 20260302191746_Initial.cs} | 134 ++---- .../PlatformDbContextModelSnapshot.cs | 189 -------- ui/src/contexts/EntityContext.tsx | 212 --------- ui/src/proxy/developerKit/models.ts | 80 +--- ui/src/services/developerKit.service.ts | 118 +---- .../developerKit/CrudEndpointManager.tsx | 88 +--- .../developerKit/SqlTableDesignerDialog.tsx | 403 +++++------------- 22 files changed, 350 insertions(+), 1848 deletions(-) delete mode 100644 api/src/Sozsoft.Platform.Application.Contracts/DeveloperKit/ISqlTableAppService.cs delete mode 100644 api/src/Sozsoft.Platform.Application.Contracts/DeveloperKit/SqlTableDto.cs delete mode 100644 api/src/Sozsoft.Platform.Application/DeveloperKit/SqlTableAppService.cs delete mode 100644 api/src/Sozsoft.Platform.Domain/Entities/Tenant/Administration/DeveloperKit/SqlTable.cs rename api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/{20260302174517_Initial.Designer.cs => 20260302191746_Initial.Designer.cs} (97%) rename api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/{20260302174517_Initial.cs => 20260302191746_Initial.cs} (97%) delete mode 100644 ui/src/contexts/EntityContext.tsx diff --git a/api/src/Sozsoft.Platform.Application.Contracts/DeveloperKit/CrudEndpointDto.cs b/api/src/Sozsoft.Platform.Application.Contracts/DeveloperKit/CrudEndpointDto.cs index ce7d8c7..d59ae8a 100644 --- a/api/src/Sozsoft.Platform.Application.Contracts/DeveloperKit/CrudEndpointDto.cs +++ b/api/src/Sozsoft.Platform.Application.Contracts/DeveloperKit/CrudEndpointDto.cs @@ -11,7 +11,6 @@ public class CrudEndpointDto : FullAuditedEntityDto public string OperationType { get; set; } = string.Empty; public string CsharpCode { get; set; } = string.Empty; public bool IsActive { get; set; } = true; - public Guid EntityId { get; set; } // Navigation property for display purposes public string? EntityDisplayName { get; set; } @@ -25,6 +24,5 @@ public class CreateUpdateCrudEndpointDto public string OperationType { get; set; } = string.Empty; public string CsharpCode { get; set; } = string.Empty; public bool IsActive { get; set; } = true; - public Guid EntityId { get; set; } } diff --git a/api/src/Sozsoft.Platform.Application.Contracts/DeveloperKit/ICrudEndpointAppService.cs b/api/src/Sozsoft.Platform.Application.Contracts/DeveloperKit/ICrudEndpointAppService.cs index 8ab7a09..5c34888 100644 --- a/api/src/Sozsoft.Platform.Application.Contracts/DeveloperKit/ICrudEndpointAppService.cs +++ b/api/src/Sozsoft.Platform.Application.Contracts/DeveloperKit/ICrudEndpointAppService.cs @@ -13,7 +13,8 @@ public interface ICrudEndpointAppService : ICrudAppService< CreateUpdateCrudEndpointDto> { Task> GetActiveEndpointsAsync(); - Task> GetEndpointsByEntityAsync(Guid entityId); - Task> GenerateCrudEndpointsAsync(Guid entityId); + Task> GetEndpointsByEntityAsync(string entityName); + Task ToggleAsync(Guid id); + Task> GenerateCrudEndpointsAsync(string entityName); } diff --git a/api/src/Sozsoft.Platform.Application.Contracts/DeveloperKit/ISqlTableAppService.cs b/api/src/Sozsoft.Platform.Application.Contracts/DeveloperKit/ISqlTableAppService.cs deleted file mode 100644 index 75f0701..0000000 --- a/api/src/Sozsoft.Platform.Application.Contracts/DeveloperKit/ISqlTableAppService.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using Volo.Abp.Application.Dtos; -using Volo.Abp.Application.Services; - -namespace Sozsoft.Platform.DeveloperKit; - -public interface ISqlTableAppService : ICrudAppService< - SqlTableDto, - Guid, - PagedAndSortedResultRequestDto, - CreateUpdateSqlTableDto> -{ - Task> GetActiveEntitiesAsync(); - Task ToggleActiveStatusAsync(Guid id); -} - diff --git a/api/src/Sozsoft.Platform.Application.Contracts/DeveloperKit/SqlTableDto.cs b/api/src/Sozsoft.Platform.Application.Contracts/DeveloperKit/SqlTableDto.cs deleted file mode 100644 index 14bd7ab..0000000 --- a/api/src/Sozsoft.Platform.Application.Contracts/DeveloperKit/SqlTableDto.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System; -using System.Collections.Generic; -using Volo.Abp.Application.Dtos; - -namespace Sozsoft.Platform.DeveloperKit; - -public class SqlTableDto : FullAuditedEntityDto -{ - public string Menu { get; set; } = string.Empty; - public string Name { get; set; } = string.Empty; - public string DisplayName { get; set; } = string.Empty; - public string TableName { get; set; } = string.Empty; - public string? Description { get; set; } - public bool IsActive { get; set; } = true; - public bool IsFullAuditedEntity { get; set; } = true; - public bool IsMultiTenant { get; set; } = false; - public string EndpointStatus { get; set; } = "pending"; - - public List Fields { get; set; } = []; -} - -public class CreateUpdateSqlTableDto -{ - public string Menu { get; set; } = string.Empty; - public string Name { get; set; } = string.Empty; - public string DisplayName { get; set; } = string.Empty; - public string TableName { get; set; } = string.Empty; - public string? Description { get; set; } - public bool IsActive { get; set; } = true; - public bool IsFullAuditedEntity { get; set; } = true; - public bool IsMultiTenant { get; set; } = false; - - public List Fields { get; set; } = []; -} - -public class SqlTableFieldDto : FullAuditedEntityDto -{ - public Guid EntityId { get; set; } - public string Name { get; set; } = string.Empty; - public string Type { get; set; } = string.Empty; - public bool IsRequired { get; set; } - public int? MaxLength { get; set; } - public bool IsUnique { get; set; } - public string? DefaultValue { get; set; } - public string? Description { get; set; } - public int DisplayOrder { get; set; } = 0; -} - -public class CreateUpdateSqlTableFieldDto -{ - public Guid? Id { get; set; } - - public string Name { get; set; } = string.Empty; - public string Type { get; set; } = string.Empty; - public bool IsRequired { get; set; } - public int? MaxLength { get; set; } - public bool IsUnique { get; set; } - public string? DefaultValue { get; set; } - public string? Description { get; set; } - public int DisplayOrder { get; set; } = 0; -} - diff --git a/api/src/Sozsoft.Platform.Application/DeveloperKit/CrudEndpointGenerateAppService.cs b/api/src/Sozsoft.Platform.Application/DeveloperKit/CrudEndpointGenerateAppService.cs index 95f8af9..598db8e 100644 --- a/api/src/Sozsoft.Platform.Application/DeveloperKit/CrudEndpointGenerateAppService.cs +++ b/api/src/Sozsoft.Platform.Application/DeveloperKit/CrudEndpointGenerateAppService.cs @@ -3,15 +3,15 @@ using System.Collections.Generic; using System.Threading.Tasks; using Sozsoft.Platform.DeveloperKit; using Sozsoft.Platform.Entities; -using Microsoft.EntityFrameworkCore; using Volo.Abp.Application.Dtos; using Volo.Abp.Application.Services; using Volo.Abp.Domain.Repositories; using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Sozsoft.Platform; namespace Platform.Api.Application; -[Authorize] public class CrudEndpointGenerateAppService : CrudAppService< CrudEndpoint, CrudEndpointDto, @@ -19,116 +19,95 @@ public class CrudEndpointGenerateAppService : CrudAppService< PagedAndSortedResultRequestDto, CreateUpdateCrudEndpointDto>, ICrudEndpointAppService { - private readonly IRepository _entityRepository; - private readonly IRepository _endpointRepository; - public CrudEndpointGenerateAppService( - IRepository repository, - IRepository entityRepository, - IRepository endpointRepository) + IRepository repository) : base(repository) { - _entityRepository = entityRepository; - _endpointRepository = endpointRepository; } + [HttpGet("api/app/crud-endpoint-generate/active-endpoints")] + [Authorize(PlatformConsts.AppCodes.DeveloperKits.CrudEndpoints)] public virtual async Task> GetActiveEndpointsAsync() { var endpoints = await Repository.GetListAsync(x => x.IsActive); return await MapToGetListOutputDtosAsync(endpoints); } - public virtual async Task> GetEndpointsByEntityAsync(Guid entityId) + [HttpGet("api/app/crud-endpoint-generate/endpoints-by-entity/{entityName}")] + [Authorize(PlatformConsts.AppCodes.DeveloperKits.CrudEndpoints)] + public virtual async Task> GetEndpointsByEntityAsync(string entityName) { - var endpoints = await _endpointRepository.GetListAsync(x => x.EntityId == entityId); + var endpoints = await Repository.GetListAsync(x => x.EntityName == entityName); return ObjectMapper.Map, List>(endpoints); } - public virtual async Task> GenerateCrudEndpointsAsync(Guid entityId) + [HttpPost("api/app/crud-endpoint-generate/{id}/toggle")] + [Authorize(PlatformConsts.AppCodes.DeveloperKits.CrudEndpoints)] + public virtual async Task ToggleAsync(Guid id) { - // Entity + Fields - var entityQueryable = await _entityRepository.GetQueryableAsync(); - var entity = await entityQueryable - .Include(x => x.Fields) - .FirstOrDefaultAsync(x => x.Id == entityId); - - if (entity == null) - { - throw new Exception($"Entity with ID {entityId} not found"); - } + var endpoint = await Repository.GetAsync(id); + endpoint.IsActive = !endpoint.IsActive; + await Repository.UpdateAsync(endpoint, autoSave: true); + return ObjectMapper.Map(endpoint); + } + [HttpPost("api/app/crud-endpoint-generate/generate-crud-endpoints/{entityName}")] + [Authorize(PlatformConsts.AppCodes.DeveloperKits.CrudEndpoints)] + public virtual async Task> GenerateCrudEndpointsAsync(string entityName) + { // CRUD endpointleri oluştur - var endpoints = new List(); - var entityName = entity.Name; - var entityDisplayName = entity.DisplayName; - - endpoints.Add(new CrudEndpoint + var endpoints = new List { - EntityId = entityId, - EntityName = entityName, - Method = "GET", - Path = $"/api/app/crudendpoint/{entityName.ToLower()}", - OperationType = "GetList", - IsActive = true, - CsharpCode = GenerateGetAllCode(entityName, entityDisplayName) - }); - - endpoints.Add(new CrudEndpoint - { - EntityId = entityId, - EntityName = entityName, - Method = "GET", - Path = $"/api/app/crudendpoint/{entityName.ToLower()}/{{id}}", - OperationType = "GetById", - IsActive = true, - CsharpCode = GenerateGetByIdCode(entityName, entityDisplayName) - }); - - endpoints.Add(new CrudEndpoint - { - EntityId = entityId, - EntityName = entityName, - Method = "POST", - Path = $"/api/app/crudendpoint/{entityName.ToLower()}", - OperationType = "Create", - IsActive = true, - CsharpCode = GenerateCreateCode(entityName, entityDisplayName) - }); - - endpoints.Add(new CrudEndpoint - { - EntityId = entityId, - EntityName = entityName, - Method = "PUT", - Path = $"/api/app/crudendpoint/{entityName.ToLower()}/{{id}}", - OperationType = "Update", - IsActive = true, - CsharpCode = GenerateUpdateCode(entityName, entityDisplayName) - }); - - endpoints.Add(new CrudEndpoint - { - EntityId = entityId, - EntityName = entityName, - Method = "DELETE", - Path = $"/api/app/crudendpoint/{entityName.ToLower()}/{{id}}", - OperationType = "Delete", - IsActive = true, - CsharpCode = GenerateDeleteCode(entityName, entityDisplayName) - }); + new() { + EntityName = entityName, + Method = "GET", + Path = $"/api/app/crudendpoint/{entityName}", + OperationType = "GetList", + IsActive = true, + CsharpCode = GenerateGetAllCode(entityName) + }, + new() { + EntityName = entityName, + Method = "GET", + Path = $"/api/app/crudendpoint/{entityName}/{{id}}", + OperationType = "GetById", + IsActive = true, + CsharpCode = GenerateGetByIdCode(entityName) + }, + new() { + EntityName = entityName, + Method = "POST", + Path = $"/api/app/crudendpoint/{entityName}", + OperationType = "Create", + IsActive = true, + CsharpCode = GenerateCreateCode(entityName) + }, + new() { + EntityName = entityName, + Method = "PUT", + Path = $"/api/app/crudendpoint/{entityName}/{{id}}", + OperationType = "Update", + IsActive = true, + CsharpCode = GenerateUpdateCode(entityName) + }, + new() { + EntityName = entityName, + Method = "DELETE", + Path = $"/api/app/crudendpoint/{entityName}/{{id}}", + OperationType = "Delete", + IsActive = true, + CsharpCode = GenerateDeleteCode(entityName) + } + }; // Var olanları sil - var existingEndpoints = await _endpointRepository - .GetListAsync(x => x.EntityId == entityId); + var existingEndpoints = await Repository + .GetListAsync(x => x.EntityName == entityName); - await _endpointRepository.DeleteManyAsync(existingEndpoints); + await Repository.DeleteManyAsync(existingEndpoints); // Yeni endpointleri ekle - await _endpointRepository.InsertManyAsync(endpoints, autoSave: true); - - // Entity endpoint durumu güncelle - entity.EndpointStatus = "Uygulandı"; - await _entityRepository.UpdateAsync(entity, autoSave: true); + await Repository.InsertManyAsync(endpoints, autoSave: true); var result = ObjectMapper.Map, List>(endpoints); @@ -139,7 +118,7 @@ public class CrudEndpointGenerateAppService : CrudAppService< }; } - private string GenerateGetAllCode(string entityName, string displayName) + private string GenerateGetAllCode(string entityName) { return $@"[HttpGet] public async Task>> GetAll{entityName}sAsync() @@ -149,7 +128,7 @@ public async Task>> GetAll{entityName}sAsync() }}"; } - private string GenerateGetByIdCode(string entityName, string displayName) + private string GenerateGetByIdCode(string entityName) { return $@"[HttpGet(""{{id}}"")] public async Task> Get{entityName}Async(Guid id) @@ -157,13 +136,13 @@ public async Task> Get{entityName}Async(Guid id) var entity = await _context.{entityName}s.FindAsync(id); if (entity == null) {{ - return NotFound($""{displayName} with ID {{id}} not found""); + return NotFound($""{entityName} with ID {{id}} not found""); }} return Ok(entity); }}"; } - private string GenerateCreateCode(string entityName, string displayName) + private string GenerateCreateCode(string entityName) { return $@"[HttpPost] public async Task> Create{entityName}Async({entityName} {entityName.ToLower()}) @@ -174,7 +153,7 @@ public async Task> Create{entityName}Async({entityNam }}"; } - private string GenerateUpdateCode(string entityName, string displayName) + private string GenerateUpdateCode(string entityName) { return $@"[HttpPut(""{{id}}"")] public async Task Update{entityName}Async(Guid id, {entityName} {entityName.ToLower()}) @@ -194,7 +173,7 @@ public async Task Update{entityName}Async(Guid id, {entityName} { {{ if (!await {entityName}ExistsAsync(id)) {{ - return NotFound($""{displayName} with ID {{id}} not found""); + return NotFound($""{entityName} with ID {{id}} not found""); }} throw; }} @@ -203,7 +182,7 @@ public async Task Update{entityName}Async(Guid id, {entityName} { }}"; } - private string GenerateDeleteCode(string entityName, string displayName) + private string GenerateDeleteCode(string entityName) { return $@"[HttpDelete(""{{id}}"")] public async Task Delete{entityName}Async(Guid id) @@ -211,7 +190,7 @@ public async Task Delete{entityName}Async(Guid id) var {entityName.ToLower()} = await _context.{entityName}s.FindAsync(id); if ({entityName.ToLower()} == null) {{ - return NotFound($""{displayName} with ID {{id}} not found""); + return NotFound($""{entityName} with ID {{id}} not found""); }} _context.{entityName}s.Remove({entityName.ToLower()}); @@ -226,5 +205,3 @@ private async Task {entityName}ExistsAsync(Guid id) }}"; } } - - diff --git a/api/src/Sozsoft.Platform.Application/DeveloperKit/DeveloperKitAutoMapperProfile.cs b/api/src/Sozsoft.Platform.Application/DeveloperKit/DeveloperKitAutoMapperProfile.cs index 29e38da..b9722fe 100644 --- a/api/src/Sozsoft.Platform.Application/DeveloperKit/DeveloperKitAutoMapperProfile.cs +++ b/api/src/Sozsoft.Platform.Application/DeveloperKit/DeveloperKitAutoMapperProfile.cs @@ -7,21 +7,12 @@ public class DeveloperKitAutoMapperProfile : Profile { public DeveloperKitAutoMapperProfile() { - // SqlTable mappings - CreateMap(); - CreateMap(); - - // EntityField mappings - CreateMap(); - CreateMap(); - // CustomComponent mappings CreateMap(); CreateMap(); // GeneratedEndpoint mappings - CreateMap() - .ForMember(dest => dest.EntityDisplayName, opt => opt.MapFrom(src => src.Entity != null ? src.Entity.DisplayName : null)); + CreateMap(); CreateMap(); CreateMap() diff --git a/api/src/Sozsoft.Platform.Application/DeveloperKit/SqlTableAppService.cs b/api/src/Sozsoft.Platform.Application/DeveloperKit/SqlTableAppService.cs deleted file mode 100644 index 1d46125..0000000 --- a/api/src/Sozsoft.Platform.Application/DeveloperKit/SqlTableAppService.cs +++ /dev/null @@ -1,240 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Volo.Abp.Application.Dtos; -using Volo.Abp.Application.Services; -using Volo.Abp.Domain.Repositories; -using Volo.Abp.Domain.Entities; -using Sozsoft.Platform.Entities; -using Sozsoft.Platform.DeveloperKit; -using System; -using System.Threading.Tasks; -using System.Collections.Generic; -using System.Linq; -using Microsoft.AspNetCore.Authorization; - -namespace Platform.Api.Application; - -[Authorize] -public class SqlTableAppService : CrudAppService< - SqlTable, - SqlTableDto, - Guid, - PagedAndSortedResultRequestDto, - CreateUpdateSqlTableDto>, ISqlTableAppService -{ - private readonly IRepository _repository; - private readonly IRepository _fieldRepository; - private readonly IRepository _endpointRepository; - - public SqlTableAppService( - IRepository repository, - IRepository fieldRepository, - IRepository endpointRepository) : base(repository) - { - _repository = repository; - _fieldRepository = fieldRepository; - _endpointRepository = endpointRepository; - } - - public override async Task> GetListAsync(PagedAndSortedResultRequestDto input) - { - var query = await _repository.GetQueryableAsync(); - var fullQuery = query.Include(x => x.Fields.OrderBy(f => f.DisplayOrder)); - - var totalCount = await fullQuery.CountAsync(); - - var entities = await fullQuery - .OrderBy(x => x.CreationTime) - .Skip(input.SkipCount) - .Take(input.MaxResultCount) - .ToListAsync(); - - var dtos = ObjectMapper.Map, List>(entities); - - return new PagedResultDto(totalCount, dtos); - } - - public override async Task GetAsync(Guid id) - { - var query = await _repository.GetQueryableAsync(); - var entity = await query - .Include(x => x.Fields.OrderBy(f => f.DisplayOrder)) - .FirstOrDefaultAsync(x => x.Id == id); - - if (entity == null) - throw new EntityNotFoundException($"Sql Table with id {id} not found"); - - return ObjectMapper.Map(entity); - } - - public async Task> GetActiveEntitiesAsync() - { - var query = await _repository.GetQueryableAsync(); - var entities = await query - .Include(x => x.Fields.OrderBy(f => f.DisplayOrder)) - .Where(x => x.IsActive) - .ToListAsync(); - - return ObjectMapper.Map, List>(entities); - } - - public async Task ToggleActiveStatusAsync(Guid id) - { - var query = await _repository.GetQueryableAsync(); - var entity = await query - .Include(x => x.Fields.OrderBy(f => f.DisplayOrder)) - .FirstOrDefaultAsync(x => x.Id == id); - - if (entity == null) - throw new EntityNotFoundException($"Sql Table with id {id} not found"); - - entity.IsActive = !entity.IsActive; - await _repository.UpdateAsync(entity, autoSave: true); - - return ObjectMapper.Map(entity); - } - - public override async Task UpdateAsync(Guid id, CreateUpdateSqlTableDto input) - { - var query = await _repository.GetQueryableAsync(); - var entity = await query - .Include(x => x.Fields) - .FirstOrDefaultAsync(e => e.Id == id); - - if (entity == null) - throw new EntityNotFoundException(typeof(SqlTable), id); - - entity.Menu = input.Menu; - entity.Name = input.Name; - entity.DisplayName = input.DisplayName; - entity.TableName = input.TableName; - entity.Description = input.Description; - entity.IsActive = input.IsActive; - entity.IsFullAuditedEntity = input.IsFullAuditedEntity; - entity.IsMultiTenant = input.IsMultiTenant; - - var updatedFields = new List(); - - for (int i = 0; i < input.Fields.Count; i++) - { - var dtoField = input.Fields[i]; - SqlTableField? existingField = null; - - if (dtoField.Id.HasValue) - { - existingField = entity.Fields?.FirstOrDefault(f => f.Id == dtoField.Id.Value); - } - - if (existingField != null) - { - existingField.Name = dtoField.Name; - existingField.Type = dtoField.Type; - existingField.IsRequired = dtoField.IsRequired; - existingField.MaxLength = dtoField.MaxLength; - existingField.IsUnique = dtoField.IsUnique; - existingField.DefaultValue = dtoField.DefaultValue; - existingField.Description = dtoField.Description; - existingField.DisplayOrder = dtoField.DisplayOrder; - - updatedFields.Add(existingField); - } - else - { - var newField = new SqlTableField - { - EntityId = entity.Id, - Name = dtoField.Name, - Type = dtoField.Type, - IsRequired = dtoField.IsRequired, - MaxLength = dtoField.MaxLength, - IsUnique = dtoField.IsUnique, - DefaultValue = dtoField.DefaultValue, - Description = dtoField.Description, - DisplayOrder = dtoField.DisplayOrder - }; - - await _fieldRepository.InsertAsync(newField); - updatedFields.Add(newField); - } - } - - // Silinecek alanlar - var toRemove = entity.Fields? - .Where(existing => updatedFields.All(f => f.Id != existing.Id)) - .ToList() ?? []; - - if (toRemove.Any()) - { - await _fieldRepository.DeleteManyAsync(toRemove); - } - - await _repository.UpdateAsync(entity, autoSave: true); - - return ObjectMapper.Map(entity); - } - - public override async Task CreateAsync(CreateUpdateSqlTableDto input) - { - // Entity oluştur - var entity = new SqlTable - { - Menu = input.Menu, - Name = input.Name, - DisplayName = input.DisplayName, - TableName = input.TableName, - Description = input.Description, - IsActive = input.IsActive, - IsFullAuditedEntity = input.IsFullAuditedEntity, - IsMultiTenant = input.IsMultiTenant, - }; - - // Fields ekle - sıralama ile - for (int i = 0; i < input.Fields.Count; i++) - { - var fieldDto = input.Fields[i]; - var field = new SqlTableField - { - EntityId = entity.Id, - Name = fieldDto.Name, - Type = fieldDto.Type, - IsRequired = fieldDto.IsRequired, - MaxLength = fieldDto.MaxLength, - IsUnique = fieldDto.IsUnique, - DefaultValue = fieldDto.DefaultValue, - Description = fieldDto.Description, - DisplayOrder = fieldDto.DisplayOrder - }; - entity.Fields.Add(field); - } - - entity = await _repository.InsertAsync(entity, autoSave: true); - - return ObjectMapper.Map(entity); - } - - private bool FieldsHaveChanged(ICollection existingFields, List inputFields) - { - if (existingFields.Count != inputFields.Count) - return true; - - var existingFieldsList = existingFields.OrderBy(f => f.DisplayOrder).ToList(); - var inputFieldsList = inputFields.OrderBy(f => f.DisplayOrder).ToList(); - - for (int i = 0; i < existingFieldsList.Count; i++) - { - var existing = existingFieldsList[i]; - var input = inputFieldsList[i]; - - if (existing.Name != input.Name || - existing.Type != input.Type || - existing.IsRequired != input.IsRequired || - existing.MaxLength != input.MaxLength || - existing.IsUnique != input.IsUnique) - { - return true; - } - } - - return false; - } -} - diff --git a/api/src/Sozsoft.Platform.Domain.Shared/Enums/TableNameEnum.cs b/api/src/Sozsoft.Platform.Domain.Shared/Enums/TableNameEnum.cs index 84832f9..516fb4a 100644 --- a/api/src/Sozsoft.Platform.Domain.Shared/Enums/TableNameEnum.cs +++ b/api/src/Sozsoft.Platform.Domain.Shared/Enums/TableNameEnum.cs @@ -29,8 +29,6 @@ public enum TableNameEnum ForumCategory, ForumTopic, ForumPost, - SqlTable, - SqlTableField, CrudEndpoint, CustomEndpoint, CustomComponent, diff --git a/api/src/Sozsoft.Platform.Domain.Shared/PlatformConsts.cs b/api/src/Sozsoft.Platform.Domain.Shared/PlatformConsts.cs index 6cdcf58..2eb3a89 100644 --- a/api/src/Sozsoft.Platform.Domain.Shared/PlatformConsts.cs +++ b/api/src/Sozsoft.Platform.Domain.Shared/PlatformConsts.cs @@ -332,6 +332,8 @@ public static class PlatformConsts public const string Get = CustomEndpoints + ".Get"; public const string Post = CustomEndpoints + ".Post"; + public const string CrudEndpoints = Default + ".CrudEndpoints"; + public static class DynamicServices { public const string DynamicService = Default + ".DynamicServices"; diff --git a/api/src/Sozsoft.Platform.Domain.Shared/TableNameResolver.cs b/api/src/Sozsoft.Platform.Domain.Shared/TableNameResolver.cs index 651f070..e69ab78 100644 --- a/api/src/Sozsoft.Platform.Domain.Shared/TableNameResolver.cs +++ b/api/src/Sozsoft.Platform.Domain.Shared/TableNameResolver.cs @@ -68,8 +68,6 @@ public static class TableNameResolver { nameof(TableNameEnum.Note), (TablePrefix.TenantByName, MenuPrefix.Administration) }, { nameof(TableNameEnum.ReportCategory), (TablePrefix.TenantByName, MenuPrefix.Administration) }, { nameof(TableNameEnum.ReportTemplate), (TablePrefix.TenantByName, MenuPrefix.Administration) }, - { nameof(TableNameEnum.SqlTable), (TablePrefix.TenantByName, MenuPrefix.Administration) }, - { nameof(TableNameEnum.SqlTableField), (TablePrefix.TenantByName, MenuPrefix.Administration) }, { nameof(TableNameEnum.CrudEndpoint), (TablePrefix.TenantByName, MenuPrefix.Administration) }, { nameof(TableNameEnum.CustomEndpoint), (TablePrefix.TenantByName, MenuPrefix.Administration) }, { nameof(TableNameEnum.CustomComponent), (TablePrefix.TenantByName, MenuPrefix.Administration) }, diff --git a/api/src/Sozsoft.Platform.Domain/Entities/Tenant/Administration/DeveloperKit/CrudEndpoint.cs b/api/src/Sozsoft.Platform.Domain/Entities/Tenant/Administration/DeveloperKit/CrudEndpoint.cs index b0f5dec..54c7d20 100644 --- a/api/src/Sozsoft.Platform.Domain/Entities/Tenant/Administration/DeveloperKit/CrudEndpoint.cs +++ b/api/src/Sozsoft.Platform.Domain/Entities/Tenant/Administration/DeveloperKit/CrudEndpoint.cs @@ -15,9 +15,6 @@ public class CrudEndpoint : FullAuditedEntity, IMultiTenant public string CsharpCode { get; set; } = string.Empty; public bool IsActive { get; set; } = true; - // Foreign key to SqlTable - public Guid EntityId { get; set; } - public virtual SqlTable Entity { get; set; } = null!; public CrudEndpoint() { Id = Guid.NewGuid(); diff --git a/api/src/Sozsoft.Platform.Domain/Entities/Tenant/Administration/DeveloperKit/SqlTable.cs b/api/src/Sozsoft.Platform.Domain/Entities/Tenant/Administration/DeveloperKit/SqlTable.cs deleted file mode 100644 index 1748eee..0000000 --- a/api/src/Sozsoft.Platform.Domain/Entities/Tenant/Administration/DeveloperKit/SqlTable.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System; -using System.Collections.Generic; -using Volo.Abp.Domain.Entities.Auditing; -using Volo.Abp.MultiTenancy; - -namespace Sozsoft.Platform.Entities; - -public class SqlTable : FullAuditedEntity, IMultiTenant -{ - public virtual Guid? TenantId { get; protected set; } - - public string Menu { get; set; } = string.Empty; - public string Name { get; set; } = string.Empty; - public string DisplayName { get; set; } = string.Empty; - public string TableName { get; set; } = string.Empty; - public string? Description { get; set; } - public bool IsActive { get; set; } = true; - public bool IsFullAuditedEntity { get; set; } = true; - public bool IsMultiTenant { get; set; } = false; - public string EndpointStatus { get; set; } = "pending"; // "pending" | "applied" | "failed" - - public virtual ICollection Fields { get; set; } = []; - - public SqlTable() - { - Id = Guid.NewGuid(); // Burada erişim mümkün çünkü sınıfın içi - } -} - -public class SqlTableField : FullAuditedEntity -{ - public Guid EntityId { get; set; } = Guid.NewGuid(); - public string Name { get; set; } = string.Empty; - public string Type { get; set; } = string.Empty; - public bool IsRequired { get; set; } - public int? MaxLength { get; set; } - public bool IsUnique { get; set; } - public string? DefaultValue { get; set; } - public string? Description { get; set; } - public int DisplayOrder { get; set; } = 0; - - public virtual SqlTable Entity { get; set; } = null!; - - public SqlTableField() - { - Id = Guid.NewGuid(); // Burada erişim mümkün çünkü sınıfın içi - } -} - diff --git a/api/src/Sozsoft.Platform.EntityFrameworkCore/DeveloperKit/PlatformDynamicEntityManager.cs b/api/src/Sozsoft.Platform.EntityFrameworkCore/DeveloperKit/PlatformDynamicEntityManager.cs index 49bc8c6..ec89fb2 100644 --- a/api/src/Sozsoft.Platform.EntityFrameworkCore/DeveloperKit/PlatformDynamicEntityManager.cs +++ b/api/src/Sozsoft.Platform.EntityFrameworkCore/DeveloperKit/PlatformDynamicEntityManager.cs @@ -1,5 +1,4 @@ using Sozsoft.Platform.Domain.DeveloperKit; -using Sozsoft.Platform.Entities; using Sozsoft.Platform.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Storage; @@ -8,65 +7,59 @@ using System.Collections.Generic; using System.Linq; using System.Text.Json; using System.Threading.Tasks; -using Volo.Abp; -using Volo.Abp.Domain.Repositories; using Volo.Abp.EntityFrameworkCore; public class DynamicEntityManager : IDynamicEntityManager { private readonly IDbContextProvider _dbContextProvider; - private readonly IRepository _sqlTableRepository; public DynamicEntityManager( - IDbContextProvider dbContextProvider, - IRepository sqlTableRepository) + IDbContextProvider dbContextProvider) { _dbContextProvider = dbContextProvider; - _sqlTableRepository = sqlTableRepository; } public async Task?> GetEntityListAsync(string entityName) { - return await ExecuteDynamicQuery(entityName, "GetList", null); + var hasIsDeleted = await ColumnExistsAsync(entityName, "IsDeleted"); + var query = hasIsDeleted + ? $"SELECT * FROM [{entityName}] WHERE IsDeleted = 0 OR IsDeleted IS NULL" + : $"SELECT * FROM [{entityName}]"; + return await ExecuteRawQueryAsync(query); } public async Task GetEntityByIdAsync(string entityName, Guid id) { - var result = await ExecuteDynamicQuery(entityName, "GetById", id); + var hasIsDeleted = await ColumnExistsAsync(entityName, "IsDeleted"); + var query = hasIsDeleted + ? $"SELECT * FROM [{entityName}] WHERE Id = '{id}' AND (IsDeleted = 0 OR IsDeleted IS NULL)" + : $"SELECT * FROM [{entityName}] WHERE Id = '{id}'"; + var result = await ExecuteRawQueryAsync(query); return result?.FirstOrDefault(); } public async Task CreateEntityAsync(string entityName, JsonElement data) { - var entity = await GetEntityDefinitionAsync(entityName); - var tableName = entity.TableName; var newId = Guid.NewGuid(); + var hasIsDeleted = await ColumnExistsAsync(entityName, "IsDeleted"); + var hasCreationTime = await ColumnExistsAsync(entityName, "CreationTime"); - var columns = new List { "Id" }; + var columns = new List { "[Id]" }; var values = new List { $"'{newId}'" }; - foreach (var field in entity.Fields) + if (hasCreationTime) { columns.Add("[CreationTime]"); values.Add("SYSUTCDATETIME()"); } + if (hasIsDeleted) { columns.Add("[IsDeleted]"); values.Add("0"); } + + foreach (var prop in data.EnumerateObject()) { - if (data.TryGetProperty(field.Name, out var fieldValue)) - { - columns.Add($"[{field.Name}]"); - values.Add(FormatValueForSql(fieldValue, field.Type)); - } + if (prop.NameEquals("id") || prop.NameEquals("Id")) + continue; + + columns.Add($"[{prop.Name}]"); + values.Add(FormatValueForSql(prop.Value)); } - if (entity.IsMultiTenant && data.TryGetProperty("TenantId", out var tenantIdValue)) - { - columns.Add("[TenantId]"); - values.Add($"'{tenantIdValue.GetGuid()}'"); - } - - if (entity.IsFullAuditedEntity) - { - columns.AddRange(new[] { "[CreationTime]", "[LastModificationTime]", "[IsDeleted]" }); - values.AddRange(new[] { "SYSUTCDATETIME()", "NULL", "0" }); - } - - var insertQuery = $"INSERT INTO [{tableName}] ({string.Join(", ", columns)}) VALUES ({string.Join(", ", values)})"; + var insertQuery = $"INSERT INTO [{entityName}] ({string.Join(", ", columns)}) VALUES ({string.Join(", ", values)})"; var dbContext = await _dbContextProvider.GetDbContextAsync(); await dbContext.Database.ExecuteSqlRawAsync(insertQuery); @@ -76,26 +69,25 @@ public class DynamicEntityManager : IDynamicEntityManager public async Task UpdateEntityAsync(string entityName, Guid id, JsonElement data) { - var entity = await GetEntityDefinitionAsync(entityName); - var tableName = entity.TableName; - var existing = await GetEntityByIdAsync(entityName, id); if (existing == null) return null; var setParts = new List(); - foreach (var field in entity.Fields) - { - if (data.TryGetProperty(field.Name, out var fieldValue)) - { - setParts.Add($"[{field.Name}] = {FormatValueForSql(fieldValue, field.Type)}"); - } - } - if (entity.IsFullAuditedEntity) + var hasLastModification = await ColumnExistsAsync(entityName, "LastModificationTime"); + if (hasLastModification) setParts.Add("[LastModificationTime] = SYSUTCDATETIME()"); - var updateQuery = $"UPDATE [{tableName}] SET {string.Join(", ", setParts)} WHERE Id = '{id}'"; + foreach (var prop in data.EnumerateObject()) + { + if (prop.NameEquals("id") || prop.NameEquals("Id")) + continue; + + setParts.Add($"[{prop.Name}] = {FormatValueForSql(prop.Value)}"); + } + + var updateQuery = $"UPDATE [{entityName}] SET {string.Join(", ", setParts)} WHERE Id = '{id}'"; var dbContext = await _dbContextProvider.GetDbContextAsync(); await dbContext.Database.ExecuteSqlRawAsync(updateQuery); @@ -105,37 +97,61 @@ public class DynamicEntityManager : IDynamicEntityManager public async Task DeleteEntityAsync(string entityName, Guid id) { - var entity = await GetEntityDefinitionAsync(entityName); - var tableName = entity.TableName; - var existing = await GetEntityByIdAsync(entityName, id); if (existing == null) return false; - string deleteQuery; - if (entity.IsFullAuditedEntity) - { - // Full audited entities always have soft delete - deleteQuery = $"UPDATE [{tableName}] SET [IsDeleted] = 1, [DeletionTime] = SYSUTCDATETIME() WHERE Id = '{id}'"; - } - else - { - deleteQuery = $"DELETE FROM [{tableName}] WHERE Id = '{id}'"; - } - var dbContext = await _dbContextProvider.GetDbContextAsync(); - var affected = await dbContext.Database.ExecuteSqlRawAsync(deleteQuery); - return affected > 0; + + var hasIsDeleted = await ColumnExistsAsync(entityName, "IsDeleted"); + try + { + if (hasIsDeleted) + { + var hasDeletionTime = await ColumnExistsAsync(entityName, "DeletionTime"); + var softDeleteQuery = hasDeletionTime + ? $"UPDATE [{entityName}] SET [IsDeleted] = 1, [DeletionTime] = SYSUTCDATETIME() WHERE Id = '{id}'" + : $"UPDATE [{entityName}] SET [IsDeleted] = 1 WHERE Id = '{id}'"; + var affected = await dbContext.Database.ExecuteSqlRawAsync(softDeleteQuery); + return affected > 0; + } + else + { + var hardDeleteQuery = $"DELETE FROM [{entityName}] WHERE Id = '{id}'"; + var affected = await dbContext.Database.ExecuteSqlRawAsync(hardDeleteQuery); + return affected > 0; + } + } + catch + { + var hardDeleteQuery = $"DELETE FROM [{entityName}] WHERE Id = '{id}'"; + var affected = await dbContext.Database.ExecuteSqlRawAsync(hardDeleteQuery); + return affected > 0; + } } - private async Task> ExecuteDynamicQuery(string entityName, string operation, Guid? id) + private async Task ColumnExistsAsync(string tableName, string columnName) { - var entity = await GetEntityDefinitionAsync(entityName); - var tableName = entity.TableName; - var query = operation == "GetList" - ? $"SELECT * FROM [{tableName}]" - : $"SELECT * FROM [{tableName}] WHERE Id = '{id}'"; + var query = $"SELECT COUNT(1) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '{tableName}' AND COLUMN_NAME = '{columnName}'"; + var dbContext = await _dbContextProvider.GetDbContextAsync(); + var connection = dbContext.Database.GetDbConnection(); + await dbContext.Database.OpenConnectionAsync(); + try + { + using var command = connection.CreateCommand(); + command.CommandText = query; + command.Transaction = dbContext.Database.CurrentTransaction?.GetDbTransaction(); + var result = await command.ExecuteScalarAsync(); + return Convert.ToInt32(result) > 0; + } + finally + { + await dbContext.Database.CloseConnectionAsync(); + } + } + private async Task> ExecuteRawQueryAsync(string query) + { var dbContext = await _dbContextProvider.GetDbContextAsync(); var connection = dbContext.Database.GetDbConnection(); await dbContext.Database.OpenConnectionAsync(); @@ -144,8 +160,6 @@ public class DynamicEntityManager : IDynamicEntityManager { using var command = connection.CreateCommand(); command.CommandText = query; - - // ⭐️⭐️⭐️ Kritik satır: command.Transaction = dbContext.Database.CurrentTransaction?.GetDbTransaction(); using var reader = await command.ExecuteReaderAsync(); @@ -167,25 +181,19 @@ public class DynamicEntityManager : IDynamicEntityManager } } - private async Task GetEntityDefinitionAsync(string entityName) + private static string FormatValueForSql(JsonElement value) { - var queryable = await _sqlTableRepository.WithDetailsAsync(x => x.Fields); - var entity = await queryable.FirstOrDefaultAsync(x => x.Name.ToLower() == entityName.ToLower()); - - return entity ?? throw new UserFriendlyException($"Entity '{entityName}' not found."); - } - - private static string FormatValueForSql(JsonElement value, string type) - { - return type.ToLower() switch + return value.ValueKind switch { - "string" => $"'{value.GetString()?.Replace("'", "''")}'", - "number" => value.GetInt32().ToString(), - "decimal" => value.GetDecimal().ToString(System.Globalization.CultureInfo.InvariantCulture), - "boolean" => value.GetBoolean() ? "1" : "0", - "date" => $"'{value.GetDateTime():yyyy-MM-dd HH:mm:ss}'", - "guid" => $"'{value.GetGuid()}'", - _ => $"'{value.GetString()?.Replace("'", "''")}'", + JsonValueKind.Number when value.TryGetInt64(out var l) => l.ToString(), + JsonValueKind.Number when value.TryGetDecimal(out var d) => d.ToString(System.Globalization.CultureInfo.InvariantCulture), + JsonValueKind.True => "1", + JsonValueKind.False => "0", + JsonValueKind.Null => "NULL", + JsonValueKind.String when value.TryGetGuid(out var g) => $"'{g}'", + JsonValueKind.String when value.TryGetDateTime(out var dt) => $"'{dt:yyyy-MM-dd HH:mm:ss}'", + JsonValueKind.String => $"'{value.GetString()?.Replace("'", "''")}'" , + _ => "NULL", }; } } diff --git a/api/src/Sozsoft.Platform.EntityFrameworkCore/EntityFrameworkCore/PlatformDbContext.cs b/api/src/Sozsoft.Platform.EntityFrameworkCore/EntityFrameworkCore/PlatformDbContext.cs index 795ba3d..5315c93 100644 --- a/api/src/Sozsoft.Platform.EntityFrameworkCore/EntityFrameworkCore/PlatformDbContext.cs +++ b/api/src/Sozsoft.Platform.EntityFrameworkCore/EntityFrameworkCore/PlatformDbContext.cs @@ -58,8 +58,6 @@ public class PlatformDbContext : public DbSet ForumCategories { get; set; } public DbSet ForumTopics { get; set; } public DbSet ForumPosts { get; set; } - public DbSet CustomEntities { get; set; } - public DbSet EntityFields { get; set; } public DbSet GeneratedEndpoints { get; set; } public DbSet CustomEndpoints { get; set; } public DbSet CustomComponents { get; set; } @@ -515,37 +513,6 @@ public class PlatformDbContext : .OnDelete(DeleteBehavior.Restrict); }); - builder.Entity(b => - { - b.ToTable(TableNameResolver.GetFullTableName(nameof(TableNameEnum.SqlTable)), Prefix.DbSchema); - b.ConfigureByConvention(); - - b.HasKey(x => x.Id); - b.Property(x => x.Name).IsRequired().HasMaxLength(128); - b.Property(x => x.DisplayName).IsRequired().HasMaxLength(128); - b.Property(x => x.TableName).IsRequired().HasMaxLength(128); - b.Property(x => x.Description).HasMaxLength(512); - b.Property(x => x.EndpointStatus).IsRequired().HasMaxLength(64); - b.Property(x => x.Menu).IsRequired().HasMaxLength(128); - - b.HasMany(x => x.Fields) - .WithOne(x => x.Entity) - .HasForeignKey(x => x.EntityId) - .OnDelete(DeleteBehavior.Cascade); - }); - - builder.Entity(b => - { - b.ToTable(TableNameResolver.GetFullTableName(nameof(TableNameEnum.SqlTableField)), Prefix.DbSchema); - b.ConfigureByConvention(); - - b.HasKey(x => x.Id); - b.Property(x => x.Name).IsRequired().HasMaxLength(128); - b.Property(x => x.Type).IsRequired().HasMaxLength(64); - b.Property(x => x.Description).HasMaxLength(512); - b.Property(x => x.DefaultValue).HasMaxLength(256); - }); - builder.Entity(b => { b.ToTable(TableNameResolver.GetFullTableName(nameof(TableNameEnum.CrudEndpoint)), Prefix.DbSchema); @@ -557,11 +524,6 @@ public class PlatformDbContext : b.Property(x => x.Path).IsRequired().HasMaxLength(256); b.Property(x => x.OperationType).IsRequired().HasMaxLength(64); b.Property(x => x.CsharpCode).IsRequired(); - - b.HasOne(x => x.Entity) - .WithMany() - .HasForeignKey(x => x.EntityId) - .OnDelete(DeleteBehavior.Cascade); }); builder.Entity(b => diff --git a/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260302174517_Initial.Designer.cs b/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260302191746_Initial.Designer.cs similarity index 97% rename from api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260302174517_Initial.Designer.cs rename to api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260302191746_Initial.Designer.cs index 8cc2daa..a1c053a 100644 --- a/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260302174517_Initial.Designer.cs +++ b/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260302191746_Initial.Designer.cs @@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore; namespace Sozsoft.Platform.Migrations { [DbContext(typeof(PlatformDbContext))] - [Migration("20260302174517_Initial")] + [Migration("20260302191746_Initial")] partial class Initial { /// @@ -1368,9 +1368,6 @@ namespace Sozsoft.Platform.Migrations .HasColumnType("datetime2") .HasColumnName("DeletionTime"); - b.Property("EntityId") - .HasColumnType("uniqueidentifier"); - b.Property("EntityName") .IsRequired() .HasMaxLength(128) @@ -1414,8 +1411,6 @@ namespace Sozsoft.Platform.Migrations b.HasKey("Id"); - b.HasIndex("EntityId"); - b.ToTable("Adm_T_CrudEndpoint", (string)null); }); @@ -3772,163 +3767,6 @@ namespace Sozsoft.Platform.Migrations b.ToTable("Adm_T_SkillType", (string)null); }); - modelBuilder.Entity("Sozsoft.Platform.Entities.SqlTable", b => - { - b.Property("Id") - .HasColumnType("uniqueidentifier"); - - b.Property("CreationTime") - .HasColumnType("datetime2") - .HasColumnName("CreationTime"); - - b.Property("CreatorId") - .HasColumnType("uniqueidentifier") - .HasColumnName("CreatorId"); - - b.Property("DeleterId") - .HasColumnType("uniqueidentifier") - .HasColumnName("DeleterId"); - - b.Property("DeletionTime") - .HasColumnType("datetime2") - .HasColumnName("DeletionTime"); - - b.Property("Description") - .HasMaxLength(512) - .HasColumnType("nvarchar(512)"); - - b.Property("DisplayName") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("nvarchar(128)"); - - b.Property("EndpointStatus") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("nvarchar(64)"); - - b.Property("IsActive") - .HasColumnType("bit"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bit") - .HasDefaultValue(false) - .HasColumnName("IsDeleted"); - - b.Property("IsFullAuditedEntity") - .HasColumnType("bit"); - - b.Property("IsMultiTenant") - .HasColumnType("bit"); - - b.Property("LastModificationTime") - .HasColumnType("datetime2") - .HasColumnName("LastModificationTime"); - - b.Property("LastModifierId") - .HasColumnType("uniqueidentifier") - .HasColumnName("LastModifierId"); - - b.Property("Menu") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("nvarchar(128)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("nvarchar(128)"); - - b.Property("TableName") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("nvarchar(128)"); - - b.Property("TenantId") - .HasColumnType("uniqueidentifier") - .HasColumnName("TenantId"); - - b.HasKey("Id"); - - b.ToTable("Adm_T_SqlTable", (string)null); - }); - - modelBuilder.Entity("Sozsoft.Platform.Entities.SqlTableField", b => - { - b.Property("Id") - .HasColumnType("uniqueidentifier"); - - b.Property("CreationTime") - .HasColumnType("datetime2") - .HasColumnName("CreationTime"); - - b.Property("CreatorId") - .HasColumnType("uniqueidentifier") - .HasColumnName("CreatorId"); - - b.Property("DefaultValue") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("DeleterId") - .HasColumnType("uniqueidentifier") - .HasColumnName("DeleterId"); - - b.Property("DeletionTime") - .HasColumnType("datetime2") - .HasColumnName("DeletionTime"); - - b.Property("Description") - .HasMaxLength(512) - .HasColumnType("nvarchar(512)"); - - b.Property("DisplayOrder") - .HasColumnType("int"); - - b.Property("EntityId") - .HasColumnType("uniqueidentifier"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bit") - .HasDefaultValue(false) - .HasColumnName("IsDeleted"); - - b.Property("IsRequired") - .HasColumnType("bit"); - - b.Property("IsUnique") - .HasColumnType("bit"); - - b.Property("LastModificationTime") - .HasColumnType("datetime2") - .HasColumnName("LastModificationTime"); - - b.Property("LastModifierId") - .HasColumnType("uniqueidentifier") - .HasColumnName("LastModifierId"); - - b.Property("MaxLength") - .HasColumnType("int"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("nvarchar(128)"); - - b.Property("Type") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("nvarchar(64)"); - - b.HasKey("Id"); - - b.HasIndex("EntityId"); - - b.ToTable("Adm_T_SqlTableField", (string)null); - }); - modelBuilder.Entity("Sozsoft.Platform.Entities.Uom", b => { b.Property("Id") @@ -6402,17 +6240,6 @@ namespace Sozsoft.Platform.Migrations .OnDelete(DeleteBehavior.Restrict); }); - modelBuilder.Entity("Sozsoft.Platform.Entities.CrudEndpoint", b => - { - b.HasOne("Sozsoft.Platform.Entities.SqlTable", "Entity") - .WithMany() - .HasForeignKey("EntityId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Entity"); - }); - modelBuilder.Entity("Sozsoft.Platform.Entities.District", b => { b.HasOne("Sozsoft.Platform.Entities.City", null) @@ -6503,17 +6330,6 @@ namespace Sozsoft.Platform.Migrations b.Navigation("SkillType"); }); - modelBuilder.Entity("Sozsoft.Platform.Entities.SqlTableField", b => - { - b.HasOne("Sozsoft.Platform.Entities.SqlTable", "Entity") - .WithMany("Fields") - .HasForeignKey("EntityId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Entity"); - }); - modelBuilder.Entity("Sozsoft.Platform.Entities.Uom", b => { b.HasOne("Sozsoft.Platform.Entities.UomCategory", "UomCategory") @@ -6753,11 +6569,6 @@ namespace Sozsoft.Platform.Migrations b.Navigation("Skills"); }); - modelBuilder.Entity("Sozsoft.Platform.Entities.SqlTable", b => - { - b.Navigation("Fields"); - }); - modelBuilder.Entity("Sozsoft.Platform.Entities.UomCategory", b => { b.Navigation("Uoms"); diff --git a/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260302174517_Initial.cs b/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260302191746_Initial.cs similarity index 97% rename from api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260302174517_Initial.cs rename to api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260302191746_Initial.cs index 0128cbe..5522c3c 100644 --- a/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260302174517_Initial.cs +++ b/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260302191746_Initial.cs @@ -591,6 +591,31 @@ namespace Sozsoft.Platform.Migrations table.PrimaryKey("PK_Adm_T_Contact", x => x.Id); }); + migrationBuilder.CreateTable( + name: "Adm_T_CrudEndpoint", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + TenantId = table.Column(type: "uniqueidentifier", nullable: true), + EntityName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + Method = table.Column(type: "nvarchar(10)", maxLength: 10, nullable: false), + Path = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), + OperationType = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false), + CsharpCode = table.Column(type: "nvarchar(max)", nullable: false), + IsActive = table.Column(type: "bit", nullable: false), + CreationTime = table.Column(type: "datetime2", nullable: false), + CreatorId = table.Column(type: "uniqueidentifier", nullable: true), + LastModificationTime = table.Column(type: "datetime2", nullable: true), + LastModifierId = table.Column(type: "uniqueidentifier", nullable: true), + IsDeleted = table.Column(type: "bit", nullable: false, defaultValue: false), + DeleterId = table.Column(type: "uniqueidentifier", nullable: true), + DeletionTime = table.Column(type: "datetime2", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Adm_T_CrudEndpoint", x => x.Id); + }); + migrationBuilder.CreateTable( name: "Adm_T_CustomComponent", columns: table => new @@ -986,34 +1011,6 @@ namespace Sozsoft.Platform.Migrations table.PrimaryKey("PK_Adm_T_SkillType", x => x.Id); }); - migrationBuilder.CreateTable( - name: "Adm_T_SqlTable", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - TenantId = table.Column(type: "uniqueidentifier", nullable: true), - Menu = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), - Name = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), - DisplayName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), - TableName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), - Description = table.Column(type: "nvarchar(512)", maxLength: 512, nullable: true), - IsActive = table.Column(type: "bit", nullable: false), - IsFullAuditedEntity = table.Column(type: "bit", nullable: false), - IsMultiTenant = table.Column(type: "bit", nullable: false), - EndpointStatus = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false), - CreationTime = table.Column(type: "datetime2", nullable: false), - CreatorId = table.Column(type: "uniqueidentifier", nullable: true), - LastModificationTime = table.Column(type: "datetime2", nullable: true), - LastModifierId = table.Column(type: "uniqueidentifier", nullable: true), - IsDeleted = table.Column(type: "bit", nullable: false, defaultValue: false), - DeleterId = table.Column(type: "uniqueidentifier", nullable: true), - DeletionTime = table.Column(type: "datetime2", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Adm_T_SqlTable", x => x.Id); - }); - migrationBuilder.CreateTable( name: "Adm_T_UomCategory", columns: table => new @@ -1945,71 +1942,6 @@ namespace Sozsoft.Platform.Migrations onDelete: ReferentialAction.Cascade); }); - migrationBuilder.CreateTable( - name: "Adm_T_CrudEndpoint", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - TenantId = table.Column(type: "uniqueidentifier", nullable: true), - EntityName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), - Method = table.Column(type: "nvarchar(10)", maxLength: 10, nullable: false), - Path = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), - OperationType = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false), - CsharpCode = table.Column(type: "nvarchar(max)", nullable: false), - IsActive = table.Column(type: "bit", nullable: false), - EntityId = table.Column(type: "uniqueidentifier", nullable: false), - CreationTime = table.Column(type: "datetime2", nullable: false), - CreatorId = table.Column(type: "uniqueidentifier", nullable: true), - LastModificationTime = table.Column(type: "datetime2", nullable: true), - LastModifierId = table.Column(type: "uniqueidentifier", nullable: true), - IsDeleted = table.Column(type: "bit", nullable: false, defaultValue: false), - DeleterId = table.Column(type: "uniqueidentifier", nullable: true), - DeletionTime = table.Column(type: "datetime2", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Adm_T_CrudEndpoint", x => x.Id); - table.ForeignKey( - name: "FK_Adm_T_CrudEndpoint_Adm_T_SqlTable_EntityId", - column: x => x.EntityId, - principalTable: "Adm_T_SqlTable", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "Adm_T_SqlTableField", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - EntityId = table.Column(type: "uniqueidentifier", nullable: false), - Name = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), - Type = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false), - IsRequired = table.Column(type: "bit", nullable: false), - MaxLength = table.Column(type: "int", nullable: true), - IsUnique = table.Column(type: "bit", nullable: false), - DefaultValue = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - Description = table.Column(type: "nvarchar(512)", maxLength: 512, nullable: true), - DisplayOrder = table.Column(type: "int", nullable: false), - CreationTime = table.Column(type: "datetime2", nullable: false), - CreatorId = table.Column(type: "uniqueidentifier", nullable: true), - LastModificationTime = table.Column(type: "datetime2", nullable: true), - LastModifierId = table.Column(type: "uniqueidentifier", nullable: true), - IsDeleted = table.Column(type: "bit", nullable: false, defaultValue: false), - DeleterId = table.Column(type: "uniqueidentifier", nullable: true), - DeletionTime = table.Column(type: "datetime2", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Adm_T_SqlTableField", x => x.Id); - table.ForeignKey( - name: "FK_Adm_T_SqlTableField_Adm_T_SqlTable_EntityId", - column: x => x.EntityId, - principalTable: "Adm_T_SqlTable", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - migrationBuilder.CreateTable( name: "Adm_T_Uom", columns: table => new @@ -2840,11 +2772,6 @@ namespace Sozsoft.Platform.Migrations table: "Adm_T_BlogPost", column: "Slug"); - migrationBuilder.CreateIndex( - name: "IX_Adm_T_CrudEndpoint_EntityId", - table: "Adm_T_CrudEndpoint", - column: "EntityId"); - migrationBuilder.CreateIndex( name: "IX_Adm_T_OrderItem_OrderId", table: "Adm_T_OrderItem", @@ -2871,11 +2798,6 @@ namespace Sozsoft.Platform.Migrations table: "Adm_T_SkillLevel", column: "SkillTypeId"); - migrationBuilder.CreateIndex( - name: "IX_Adm_T_SqlTableField_EntityId", - table: "Adm_T_SqlTableField", - column: "EntityId"); - migrationBuilder.CreateIndex( name: "IX_Adm_T_Uom_UomCategoryId", table: "Adm_T_Uom", @@ -3151,9 +3073,6 @@ namespace Sozsoft.Platform.Migrations migrationBuilder.DropTable( name: "Adm_T_SkillLevel"); - migrationBuilder.DropTable( - name: "Adm_T_SqlTableField"); - migrationBuilder.DropTable( name: "Adm_T_Uom"); @@ -3238,9 +3157,6 @@ namespace Sozsoft.Platform.Migrations migrationBuilder.DropTable( name: "Adm_T_SkillType"); - migrationBuilder.DropTable( - name: "Adm_T_SqlTable"); - migrationBuilder.DropTable( name: "Adm_T_UomCategory"); diff --git a/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/PlatformDbContextModelSnapshot.cs b/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/PlatformDbContextModelSnapshot.cs index 09953b9..ee5bea1 100644 --- a/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/PlatformDbContextModelSnapshot.cs +++ b/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/PlatformDbContextModelSnapshot.cs @@ -1365,9 +1365,6 @@ namespace Sozsoft.Platform.Migrations .HasColumnType("datetime2") .HasColumnName("DeletionTime"); - b.Property("EntityId") - .HasColumnType("uniqueidentifier"); - b.Property("EntityName") .IsRequired() .HasMaxLength(128) @@ -1411,8 +1408,6 @@ namespace Sozsoft.Platform.Migrations b.HasKey("Id"); - b.HasIndex("EntityId"); - b.ToTable("Adm_T_CrudEndpoint", (string)null); }); @@ -3769,163 +3764,6 @@ namespace Sozsoft.Platform.Migrations b.ToTable("Adm_T_SkillType", (string)null); }); - modelBuilder.Entity("Sozsoft.Platform.Entities.SqlTable", b => - { - b.Property("Id") - .HasColumnType("uniqueidentifier"); - - b.Property("CreationTime") - .HasColumnType("datetime2") - .HasColumnName("CreationTime"); - - b.Property("CreatorId") - .HasColumnType("uniqueidentifier") - .HasColumnName("CreatorId"); - - b.Property("DeleterId") - .HasColumnType("uniqueidentifier") - .HasColumnName("DeleterId"); - - b.Property("DeletionTime") - .HasColumnType("datetime2") - .HasColumnName("DeletionTime"); - - b.Property("Description") - .HasMaxLength(512) - .HasColumnType("nvarchar(512)"); - - b.Property("DisplayName") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("nvarchar(128)"); - - b.Property("EndpointStatus") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("nvarchar(64)"); - - b.Property("IsActive") - .HasColumnType("bit"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bit") - .HasDefaultValue(false) - .HasColumnName("IsDeleted"); - - b.Property("IsFullAuditedEntity") - .HasColumnType("bit"); - - b.Property("IsMultiTenant") - .HasColumnType("bit"); - - b.Property("LastModificationTime") - .HasColumnType("datetime2") - .HasColumnName("LastModificationTime"); - - b.Property("LastModifierId") - .HasColumnType("uniqueidentifier") - .HasColumnName("LastModifierId"); - - b.Property("Menu") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("nvarchar(128)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("nvarchar(128)"); - - b.Property("TableName") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("nvarchar(128)"); - - b.Property("TenantId") - .HasColumnType("uniqueidentifier") - .HasColumnName("TenantId"); - - b.HasKey("Id"); - - b.ToTable("Adm_T_SqlTable", (string)null); - }); - - modelBuilder.Entity("Sozsoft.Platform.Entities.SqlTableField", b => - { - b.Property("Id") - .HasColumnType("uniqueidentifier"); - - b.Property("CreationTime") - .HasColumnType("datetime2") - .HasColumnName("CreationTime"); - - b.Property("CreatorId") - .HasColumnType("uniqueidentifier") - .HasColumnName("CreatorId"); - - b.Property("DefaultValue") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("DeleterId") - .HasColumnType("uniqueidentifier") - .HasColumnName("DeleterId"); - - b.Property("DeletionTime") - .HasColumnType("datetime2") - .HasColumnName("DeletionTime"); - - b.Property("Description") - .HasMaxLength(512) - .HasColumnType("nvarchar(512)"); - - b.Property("DisplayOrder") - .HasColumnType("int"); - - b.Property("EntityId") - .HasColumnType("uniqueidentifier"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bit") - .HasDefaultValue(false) - .HasColumnName("IsDeleted"); - - b.Property("IsRequired") - .HasColumnType("bit"); - - b.Property("IsUnique") - .HasColumnType("bit"); - - b.Property("LastModificationTime") - .HasColumnType("datetime2") - .HasColumnName("LastModificationTime"); - - b.Property("LastModifierId") - .HasColumnType("uniqueidentifier") - .HasColumnName("LastModifierId"); - - b.Property("MaxLength") - .HasColumnType("int"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("nvarchar(128)"); - - b.Property("Type") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("nvarchar(64)"); - - b.HasKey("Id"); - - b.HasIndex("EntityId"); - - b.ToTable("Adm_T_SqlTableField", (string)null); - }); - modelBuilder.Entity("Sozsoft.Platform.Entities.Uom", b => { b.Property("Id") @@ -6399,17 +6237,6 @@ namespace Sozsoft.Platform.Migrations .OnDelete(DeleteBehavior.Restrict); }); - modelBuilder.Entity("Sozsoft.Platform.Entities.CrudEndpoint", b => - { - b.HasOne("Sozsoft.Platform.Entities.SqlTable", "Entity") - .WithMany() - .HasForeignKey("EntityId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Entity"); - }); - modelBuilder.Entity("Sozsoft.Platform.Entities.District", b => { b.HasOne("Sozsoft.Platform.Entities.City", null) @@ -6500,17 +6327,6 @@ namespace Sozsoft.Platform.Migrations b.Navigation("SkillType"); }); - modelBuilder.Entity("Sozsoft.Platform.Entities.SqlTableField", b => - { - b.HasOne("Sozsoft.Platform.Entities.SqlTable", "Entity") - .WithMany("Fields") - .HasForeignKey("EntityId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Entity"); - }); - modelBuilder.Entity("Sozsoft.Platform.Entities.Uom", b => { b.HasOne("Sozsoft.Platform.Entities.UomCategory", "UomCategory") @@ -6750,11 +6566,6 @@ namespace Sozsoft.Platform.Migrations b.Navigation("Skills"); }); - modelBuilder.Entity("Sozsoft.Platform.Entities.SqlTable", b => - { - b.Navigation("Fields"); - }); - modelBuilder.Entity("Sozsoft.Platform.Entities.UomCategory", b => { b.Navigation("Uoms"); diff --git a/ui/src/contexts/EntityContext.tsx b/ui/src/contexts/EntityContext.tsx deleted file mode 100644 index 0bb11c0..0000000 --- a/ui/src/contexts/EntityContext.tsx +++ /dev/null @@ -1,212 +0,0 @@ -import React, { createContext, useContext, useState, useEffect } from "react"; -import { developerKitService } from "@/services/developerKit.service"; -import { - CrudEndpoint, - CreateUpdateSqlTableDto, - SqlTable, -} from "@/proxy/developerKit/models"; - -export const FIELD_TYPE_OPTIONS = [ - { label: "String", value: "string" }, - { label: "Number", value: "number" }, - { label: "Boolean", value: "boolean" }, - { label: "Date", value: "date" }, - { label: "Guid", value: "guid" }, - { label: "Decimal", value: "decimal" }, -] as const; - -export type EntityFieldType = (typeof FIELD_TYPE_OPTIONS)[number]["value"]; - -interface EntityContextType { - entities: SqlTable[]; - generatedEndpoints: CrudEndpoint[]; - loading: boolean; - error: string | null; - // Entity operations - addEntity: (entity: CreateUpdateSqlTableDto) => Promise; - updateEntity: (id: string, entity: CreateUpdateSqlTableDto) => Promise; - deleteEntity: (id: string) => Promise; - getEntity: (id: string) => SqlTable | undefined; - refreshEntities: () => Promise; - toggleEntityActiveStatus: (id: string) => Promise; - // Generated endpoint operations - generateCrudEndpoints: (entityId: string) => Promise; - toggleEndpoint: (endpointId: string) => Promise; - getEntityEndpoints: (entityId: string) => CrudEndpoint[]; - deleteGeneratedEndpoint: (id: string) => Promise; -} - -const EntityContext = createContext(undefined); - -// eslint-disable-next-line react-refresh/only-export-components -export const useEntities = () => { - const context = useContext(EntityContext); - if (context === undefined) { - throw new Error("useEntities must be used within an EntityProvider"); - } - return context; -}; - -export const EntityProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { - const [entities, setEntities] = useState([]); - const [generatedEndpoints, setGeneratedEndpoints] = useState([]); - const [loading, setLoading] = useState(false); - const [error, setError] = useState(null); - - const refreshEntities = async () => { - try { - setLoading(true); - setError(null); - const [entitiesData, generatedEndpointsData] = await Promise.all([ - developerKitService.getSqlTables(), - developerKitService.getGeneratedEndpoints(), - ]); - setEntities(entitiesData.items || []); - setGeneratedEndpoints(generatedEndpointsData.items || []); - } catch (err) { - setError(err instanceof Error ? err.message : "Failed to fetch data"); - console.error("Failed to fetch data:", err); - } finally { - setLoading(false); - } - }; - - useEffect(() => { - refreshEntities(); - }, []); - - const addEntity = async (entityData: CreateUpdateSqlTableDto) => { - try { - setLoading(true); - setError(null); - const newEntity = await developerKitService.createSqlTable(entityData); - setEntities((prev) => [...prev, newEntity]); - } catch (err) { - setError(err instanceof Error ? err.message : "Failed to create entity"); - throw err; - } finally { - setLoading(false); - } - }; - - const updateEntity = async (id: string, entityData: CreateUpdateSqlTableDto) => { - try { - setLoading(true); - setError(null); - const updatedEntity = await developerKitService.updateSqlTable(id, entityData); - setEntities((prev) => prev.map((e) => (e.id === id ? updatedEntity : e))); - await refreshEntities(); - } catch (err) { - setError(err instanceof Error ? err.message : "Failed to update entity"); - throw err; - } finally { - setLoading(false); - } - }; - - const deleteEntity = async (id: string) => { - try { - setLoading(true); - setError(null); - await developerKitService.deleteSqlTable(id); - setEntities((prev) => prev.filter((e) => e.id !== id)); - setGeneratedEndpoints((prev) => prev.filter((ep) => ep.entityId !== id)); - } catch (err) { - setError(err instanceof Error ? err.message : "Failed to delete entity"); - throw err; - } finally { - setLoading(false); - } - }; - - const toggleEntityActiveStatus = async (id: string) => { - try { - setLoading(true); - setError(null); - await developerKitService.toggleSqlTableActiveStatus(id); - await refreshEntities(); - } catch (err) { - setError(err instanceof Error ? err.message : "Failed to toggle entity status"); - throw err; - } finally { - setLoading(false); - } - }; - - const getEntity = (id: string): SqlTable | undefined => - entities.find((e) => e.id === id); - - const generateCrudEndpoints = async (entityId: string) => { - try { - setLoading(true); - setError(null); - const endpointsData = await developerKitService.generateCrudEndpoints(entityId); - setGeneratedEndpoints((prev) => [ - ...prev.filter((e) => e.entityId !== entityId), - ...(endpointsData.items || []), - ]); - await refreshEntities(); - } catch (err) { - setError(err instanceof Error ? err.message : "Failed to generate CRUD endpoints"); - throw err; - } finally { - setLoading(false); - } - }; - - const toggleEndpoint = async (endpointId: string) => { - try { - setLoading(true); - setError(null); - const updatedEndpoint = await developerKitService.toggleGeneratedEndpoint(endpointId); - setGeneratedEndpoints((prev) => - prev.map((e) => (e.id === endpointId ? updatedEndpoint : e)), - ); - } catch (err) { - setError(err instanceof Error ? err.message : "Failed to toggle endpoint"); - throw err; - } finally { - setLoading(false); - } - }; - - const getEntityEndpoints = (entityId: string): CrudEndpoint[] => - generatedEndpoints.filter((e) => e.entityId === entityId); - - const deleteGeneratedEndpoint = async (id: string) => { - try { - setLoading(true); - setError(null); - await developerKitService.deleteGeneratedEndpoint(id); - setGeneratedEndpoints((prev) => prev.filter((e) => e.id !== id)); - } catch (err) { - setError(err instanceof Error ? err.message : "Failed to delete generated endpoint"); - throw err; - } finally { - setLoading(false); - } - }; - - return ( - - {children} - - ); -}; diff --git a/ui/src/proxy/developerKit/models.ts b/ui/src/proxy/developerKit/models.ts index d56b21c..e8881a1 100644 --- a/ui/src/proxy/developerKit/models.ts +++ b/ui/src/proxy/developerKit/models.ts @@ -1,61 +1,7 @@ -export interface SqlTable { - id: string; - menu: string; - name: string; - displayName: string; - tableName: string; - description?: string; - isActive: boolean; - isFullAuditedEntity: boolean; - isMultiTenant: boolean; - endpointStatus: "pending" | "applied" | "failed"; - fields: SqlTableField[]; - creationTime: string; - lastModificationTime?: string; -} - -export interface SqlTableField { - id: string; - entityId: string; - name: string; - type: "string" | "number" | "boolean" | "date" | "guid" | "decimal"; - isRequired: boolean; - maxLength?: number; - isUnique?: boolean; - defaultValue?: string; - description?: string; - creationTime?: string; - lastModificationTime?: string; - displayOrder: number; -} - -export interface CreateUpdateSqlTableFieldDto { - id?: string; - name: string; - type: "string" | "number" | "boolean" | "date" | "guid" | "decimal"; - isRequired: boolean; - maxLength?: number; - isUnique?: boolean; - defaultValue?: string; - description?: string; - displayOrder: number; -} - -export interface CreateUpdateSqlTableDto { - menu: string; - name: string; - displayName: string; - tableName: string; - description?: string; - isActive: boolean; - isFullAuditedEntity: boolean; - isMultiTenant: boolean; - fields: CreateUpdateSqlTableFieldDto[]; -} - export interface CrudEndpoint { id: string; - entityId: string; + + tenantId: string; entityName: string; method: "GET" | "POST" | "PUT" | "DELETE"; path: string; @@ -67,7 +13,7 @@ export interface CrudEndpoint { } export interface CreateUpdateCrudEndpointDto { - entityId: string; + tenantId: string; entityName: string; method: "GET" | "POST" | "PUT" | "DELETE"; path: string; @@ -78,6 +24,7 @@ export interface CreateUpdateCrudEndpointDto { export interface CustomComponent { id: string; + tenantId?: string; name: string; code: string; props?: string; @@ -90,6 +37,7 @@ export interface CustomComponent { export interface CustomComponentDto { id: string; + tenantId?: string; name: string; code: string; props?: string; @@ -101,10 +49,28 @@ export interface CustomComponentDto { } export interface CreateUpdateCustomComponentDto { + tenantId?: string; name: string; code: string; props?: string; description?: string; isActive: boolean; dependencies?: string; +} + +export type RelationshipType = 'OneToOne' | 'OneToMany' +export type CascadeBehavior = 'NoAction' | 'Cascade' | 'SetNull' | 'Restrict' + +export interface SqlTableRelation { + id: string + /** Existing DB constraint name — set when loaded from database (edit mode) */ + constraintName?: string + relationshipType: RelationshipType + fkColumnName: string + referencedTable: string + referencedColumn: string + cascadeDelete: CascadeBehavior + cascadeUpdate: CascadeBehavior + isRequired: boolean + description: string } \ No newline at end of file diff --git a/ui/src/services/developerKit.service.ts b/ui/src/services/developerKit.service.ts index 0b15832..4ee36e3 100644 --- a/ui/src/services/developerKit.service.ts +++ b/ui/src/services/developerKit.service.ts @@ -4,69 +4,11 @@ import { CrudEndpoint, CreateUpdateCrudEndpointDto, CreateUpdateCustomComponentDto, - CreateUpdateSqlTableDto, CustomComponent, CustomComponentDto, - SqlTable, } from '@/proxy/developerKit/models' class DeveloperKitService { - async getSqlTables(): Promise> { - const response = await apiService.fetchData>({ - url: '/api/app/sql-table', - method: 'GET', - }) - return response.data - } - - async getActiveSqlTables(): Promise> { - const response = await apiService.fetchData>({ - url: '/api/app/sql-table/active-tables', - method: 'GET', - }) - return response.data - } - - async getSqlTable(id: string): Promise { - const response = await apiService.fetchData({ - url: `/api/app/sql-table/${id}`, - method: 'GET', - }) - return response.data - } - - async createSqlTable(data: CreateUpdateSqlTableDto): Promise { - const response = await apiService.fetchData({ - url: '/api/app/sql-table', - method: 'POST', - data: data as any, - }) - return response.data - } - - async updateSqlTable(id: string, entity: CreateUpdateSqlTableDto): Promise { - const response = await apiService.fetchData({ - url: `/api/app/sql-table/${id}`, - method: 'PUT', - data: entity as any, - }) - return response.data - } - - async deleteSqlTable(id: string): Promise { - await apiService.fetchData({ - url: `/api/app/sql-table/${id}`, - method: 'DELETE', - }) - } - - async toggleSqlTableActiveStatus(id: string): Promise { - await apiService.fetchData({ - url: `/api/app/sql-table/${id}/toggle-active-status`, - method: 'POST', - }) - } - // Custom Component endpoints async getCustomComponents(): Promise> { const response = await apiService.fetchData>({ @@ -121,14 +63,6 @@ class DeveloperKitService { } // Generated Endpoint endpoints - async getGeneratedEndpoints(): Promise> { - const response = await apiService.fetchData>({ - url: '/api/app/crud-endpoint-generate', - method: 'GET', - }) - return response.data - } - async getActiveGeneratedEndpoints(): Promise> { const response = await apiService.fetchData>({ url: '/api/app/crud-endpoint-generate/active-endpoints', @@ -137,9 +71,25 @@ class DeveloperKitService { return response.data } - async getEndpointsByEntity(entityId: string): Promise> { + async toggleGeneratedEndpoint(id: string): Promise { + const response = await apiService.fetchData({ + url: `/api/app/crud-endpoint-generate/${id}/toggle`, + method: 'POST', + }) + return response.data + } + + async generateCrudEndpoints(entityName: string): Promise> { const response = await apiService.fetchData>({ - url: `/api/app/crud-endpoint-generate/endpoints-by-entity/${entityId}`, + url: `/api/app/crud-endpoint-generate/generate-crud-endpoints/${entityName}`, + method: 'POST', + }) + return response.data + } + + async getGeneratedListEndpoints(): Promise> { + const response = await apiService.fetchData>({ + url: '/api/app/crud-endpoint-generate', method: 'GET', }) return response.data @@ -180,38 +130,6 @@ class DeveloperKitService { method: 'DELETE', }) } - - async toggleGeneratedEndpoint(id: string): Promise { - const response = await apiService.fetchData({ - url: `/api/app/crud-endpoint-generate/${id}/toggle`, - method: 'POST', - }) - return response.data - } - - async generateCrudEndpoints(entityId: string): Promise> { - const response = await apiService.fetchData>({ - url: `/api/app/crud-endpoint-generate/generate-crud-endpoints/${entityId}`, - method: 'POST', - }) - return response.data - } - - // Health Check - async healthCheck(): Promise<{ status: string; timestamp: string }> { - try { - const response = await apiService.fetchData({ - url: '/api/app/health', - method: 'GET', - }) - if (response.status === 200) { - return { status: 'healthy', timestamp: new Date().toISOString() } - } - return { status: 'unhealthy', timestamp: new Date().toISOString() } - } catch { - return { status: 'offline', timestamp: new Date().toISOString() } - } - } } export const developerKitService = new DeveloperKitService() diff --git a/ui/src/views/developerKit/CrudEndpointManager.tsx b/ui/src/views/developerKit/CrudEndpointManager.tsx index 3274f43..05cf77f 100644 --- a/ui/src/views/developerKit/CrudEndpointManager.tsx +++ b/ui/src/views/developerKit/CrudEndpointManager.tsx @@ -24,7 +24,7 @@ import { sqlObjectManagerService } from '@/services/sql-query-manager.service' import { developerKitService } from '@/services/developerKit.service' import type { DataSourceDto } from '@/proxy/data-source' import type { DatabaseTableDto } from '@/proxy/sql-query-manager/models' -import type { CrudEndpoint, SqlTable } from '@/proxy/developerKit/models' +import type { CrudEndpoint } from '@/proxy/developerKit/models' import { Helmet } from 'react-helmet' import { APP_NAME } from '@/constants/app.constant' @@ -62,25 +62,14 @@ const METHOD_COLOR: Record = { const CrudEndpointManager: React.FC = () => { const { translate } = useLocalization() - // Local entity + endpoint state (no context dependency) - const [entities, setEntities] = useState([]) + // Endpoint state const [generatedEndpoints, setGeneratedEndpoints] = useState([]) - const refreshData = useCallback(async () => { - try { - const [entitiesRes, endpointsRes] = await Promise.all([ - developerKitService.getSqlTables(), - developerKitService.getGeneratedEndpoints(), - ]) - setEntities(entitiesRes.items || []) - setGeneratedEndpoints(endpointsRes.items || []) - } catch (err) { - console.error('Failed to refresh data', err) - } - }, []) - useEffect(() => { - refreshData() + developerKitService + .getGeneratedListEndpoints() + .then((res) => setGeneratedEndpoints(res.items || [])) + .catch((err) => console.error('Failed to load endpoints', err)) }, []) // Data source + tables @@ -135,19 +124,12 @@ const CrudEndpointManager: React.FC = () => { }, [selectedDataSource]) // Helpers - const getEntityForTable = useCallback( - (tableName: string): SqlTable | undefined => - entities.find((e) => e.tableName.toLowerCase() === tableName.toLowerCase()), - [entities], - ) - const getEndpointsForTable = useCallback( (tableName: string): CrudEndpoint[] => { - const entity = getEntityForTable(tableName) - if (!entity) return [] - return generatedEndpoints.filter((ep) => ep.entityId === entity.id) + const entityName = toPascalCase(tableName) + return generatedEndpoints.filter((ep) => ep.entityName === entityName) }, - [entities, generatedEndpoints, getEntityForTable], + [generatedEndpoints], ) const activeEndpointCount = (tableName: string) => @@ -181,30 +163,10 @@ const CrudEndpointManager: React.FC = () => { const key = table.fullName setGeneratingFor(key) try { - let entity = getEntityForTable(table.tableName) - if (!entity) { - const entityName = toPascalCase(table.tableName) - await developerKitService.createSqlTable({ - menu: 'auto', - name: entityName, - displayName: entityName, - tableName: table.tableName, - description: `Auto-created from ${table.fullName}`, - isActive: true, - isFullAuditedEntity: false, - isMultiTenant: false, - fields: [], - }) - const fresh = await developerKitService.getSqlTables() - setEntities(fresh.items || []) - entity = (fresh.items || []).find( - (e) => e.tableName.toLowerCase() === table.tableName.toLowerCase(), - ) - } - if (!entity) throw new Error('Entity could not be created') - const result = await developerKitService.generateCrudEndpoints(entity.id) + const entityName = toPascalCase(table.tableName) + const result = await developerKitService.generateCrudEndpoints(entityName) setGeneratedEndpoints((prev) => [ - ...prev.filter((ep) => ep.entityId !== entity!.id), + ...prev.filter((ep) => ep.entityName !== entityName), ...(result.items || []), ]) } catch (err) { @@ -352,7 +314,6 @@ const CrudEndpointManager: React.FC = () => { const tablesWithEndpoints = dbTables.filter((t) => allEndpointCount(t.tableName) > 0).length const totalActiveEndpoints = generatedEndpoints.filter((ep) => ep.isActive).length const selectedTableEndpoints = selectedTable ? getEndpointsForTable(selectedTable.tableName) : [] - const selectedEntity = selectedTable ? getEntityForTable(selectedTable.tableName) : undefined return (
@@ -553,11 +514,6 @@ const CrudEndpointManager: React.FC = () => {

{selectedTable.schemaName}.{selectedTable.tableName}

- {selectedEntity && ( - - Entity: {selectedEntity.name} - - )}
{selectedTableEndpoints.length > 0 && ( @@ -598,9 +554,7 @@ const CrudEndpointManager: React.FC = () => {

Henuz endpoint yok

- {selectedEntity - ? '"CRUD Endpoint Olustur" butonuna tiklayin' - : 'Bu tablo icin Sql Table otomatik olusturulacak ve 5 endpoint uretilecektir'} + "CRUD Endpoint Olustur" butonuna tiklayin

) : ( @@ -655,22 +609,6 @@ const CrudEndpointManager: React.FC = () => { - {/* Status */} - {ep.isActive ? ( - Aktif - ) : ( - Devre disi - )} - - {/* Copy path */} - - {/* Expand */} +
+
+ + +
+
+ + +
{/* Header row */} -
+
Sutun Adi *
Veri Tipi *
Max
@@ -1032,7 +910,7 @@ const SqlTableDesignerDialog = ({ {/* Editable column rows */} {duplicateColumnNames.size > 0 && ( -
+
⚠️ Aynı isimde sütun tanımlanamaz:{' '} {[...duplicateColumnNames].map((n) => ( @@ -1079,7 +957,7 @@ const SqlTableDesignerDialog = ({ return (
- {/* Id warning when Full Audited is off */} - {!isEditMode && !settings.isFullAudited && + {/* Id warning */} + {!isEditMode && !columns.some((c) => c.columnName.trim().toLowerCase() === 'id') && (
⚠️ Full Audited Entity seçilmedi. Tablonun birincil anahtarı için{' '} @@ -1194,14 +1072,14 @@ const SqlTableDesignerDialog = ({
{/* Menu Name */} -
+
onEntityNameChange(e.target.value)} @@ -1238,101 +1117,12 @@ const SqlTableDesignerDialog = ({ value={isEditMode ? (initialTableData?.tableName ?? '') : settings.tableName} placeholder="Menu ve Entity Name seçince otomatik oluşur" /> - {isEditMode && ( -

Mevcut tablo — ad değiştirilemez.

- )} -
- - {/* Display Name */} -
- - setSettings((s) => ({ ...s, displayName: e.target.value }))} - placeholder="e.g. Ürün, Kullanıcı, Sipariş" - /> -
- - {/* Description */} -
- -