diff --git a/api/Erp.Platform.sln b/api/Erp.Platform.sln index b88e9b38..e8dba0e3 100644 --- a/api/Erp.Platform.sln +++ b/api/Erp.Platform.sln @@ -80,6 +80,18 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution README.md = README.md EndProjectSection EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Erp.SqlQueryManager", "Erp.SqlQueryManager", "{2889482E-64CA-4A25-91D8-5B963D83681B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Erp.SqlQueryManager.Domain.Shared", "modules\Erp.SqlQueryManager\Erp.SqlQueryManager.Domain.Shared\Erp.SqlQueryManager.Domain.Shared.csproj", "{10D71F44-C9FD-41F2-8F1A-D93FAE3CE696}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Erp.SqlQueryManager.Domain", "modules\Erp.SqlQueryManager\Erp.SqlQueryManager.Domain\Erp.SqlQueryManager.Domain.csproj", "{8730045F-91F5-4438-8772-C6E31E89AACC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Erp.SqlQueryManager.Application.Contracts", "modules\Erp.SqlQueryManager\Erp.SqlQueryManager.Application.Contracts\Erp.SqlQueryManager.Application.Contracts.csproj", "{B45A3E8B-286B-4A74-9602-FC192ACEE8C4}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Erp.SqlQueryManager.Application", "modules\Erp.SqlQueryManager\Erp.SqlQueryManager.Application\Erp.SqlQueryManager.Application.csproj", "{ED9C639A-A706-4ECB-9638-A15B3681BDEC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Erp.SqlQueryManager.EntityFrameworkCore", "modules\Erp.SqlQueryManager\Erp.SqlQueryManager.EntityFrameworkCore\Erp.SqlQueryManager.EntityFrameworkCore.csproj", "{1DA666D8-DBFE-40F7-8EBF-95CC892E4EB6}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -198,6 +210,26 @@ Global {D9E0D333-60F3-493F-A5B2-5758ACA42A17}.Debug|Any CPU.Build.0 = Debug|Any CPU {D9E0D333-60F3-493F-A5B2-5758ACA42A17}.Release|Any CPU.ActiveCfg = Release|Any CPU {D9E0D333-60F3-493F-A5B2-5758ACA42A17}.Release|Any CPU.Build.0 = Release|Any CPU + {10D71F44-C9FD-41F2-8F1A-D93FAE3CE696}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {10D71F44-C9FD-41F2-8F1A-D93FAE3CE696}.Debug|Any CPU.Build.0 = Debug|Any CPU + {10D71F44-C9FD-41F2-8F1A-D93FAE3CE696}.Release|Any CPU.ActiveCfg = Release|Any CPU + {10D71F44-C9FD-41F2-8F1A-D93FAE3CE696}.Release|Any CPU.Build.0 = Release|Any CPU + {8730045F-91F5-4438-8772-C6E31E89AACC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8730045F-91F5-4438-8772-C6E31E89AACC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8730045F-91F5-4438-8772-C6E31E89AACC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8730045F-91F5-4438-8772-C6E31E89AACC}.Release|Any CPU.Build.0 = Release|Any CPU + {B45A3E8B-286B-4A74-9602-FC192ACEE8C4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B45A3E8B-286B-4A74-9602-FC192ACEE8C4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B45A3E8B-286B-4A74-9602-FC192ACEE8C4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B45A3E8B-286B-4A74-9602-FC192ACEE8C4}.Release|Any CPU.Build.0 = Release|Any CPU + {ED9C639A-A706-4ECB-9638-A15B3681BDEC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ED9C639A-A706-4ECB-9638-A15B3681BDEC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ED9C639A-A706-4ECB-9638-A15B3681BDEC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ED9C639A-A706-4ECB-9638-A15B3681BDEC}.Release|Any CPU.Build.0 = Release|Any CPU + {1DA666D8-DBFE-40F7-8EBF-95CC892E4EB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1DA666D8-DBFE-40F7-8EBF-95CC892E4EB6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1DA666D8-DBFE-40F7-8EBF-95CC892E4EB6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1DA666D8-DBFE-40F7-8EBF-95CC892E4EB6}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -236,10 +268,14 @@ Global {631092C7-B59D-4EA7-92D0-5E181AB4F9F6} = {41A473FE-2537-4223-8CF3-A4A2A4A4F41E} {23659070-58F7-403B-8973-B2E20B5E9BE1} = {41A473FE-2537-4223-8CF3-A4A2A4A4F41E} {D9E0D333-60F3-493F-A5B2-5758ACA42A17} = {41A473FE-2537-4223-8CF3-A4A2A4A4F41E} + {2889482E-64CA-4A25-91D8-5B963D83681B} = {03E1C8DA-035E-4882-AF81-F392139FCF38} + {10D71F44-C9FD-41F2-8F1A-D93FAE3CE696} = {2889482E-64CA-4A25-91D8-5B963D83681B} + {8730045F-91F5-4438-8772-C6E31E89AACC} = {2889482E-64CA-4A25-91D8-5B963D83681B} + {B45A3E8B-286B-4A74-9602-FC192ACEE8C4} = {2889482E-64CA-4A25-91D8-5B963D83681B} + {ED9C639A-A706-4ECB-9638-A15B3681BDEC} = {2889482E-64CA-4A25-91D8-5B963D83681B} + {1DA666D8-DBFE-40F7-8EBF-95CC892E4EB6} = {2889482E-64CA-4A25-91D8-5B963D83681B} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {28315BFD-90E7-4E14-A2EA-F3D23AF4126F} EndGlobalSection EndGlobal - - diff --git a/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application.Contracts/Erp.SqlQueryManager.Application.Contracts.csproj b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application.Contracts/Erp.SqlQueryManager.Application.Contracts.csproj new file mode 100644 index 00000000..eb7458db --- /dev/null +++ b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application.Contracts/Erp.SqlQueryManager.Application.Contracts.csproj @@ -0,0 +1,16 @@ + + + + + + net9.0 + Erp.SqlQueryManager + + + + + + + + + diff --git a/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application.Contracts/FodyWeavers.xml b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application.Contracts/FodyWeavers.xml new file mode 100644 index 00000000..00e1d9a1 --- /dev/null +++ b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application.Contracts/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application.Contracts/FodyWeavers.xsd b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application.Contracts/FodyWeavers.xsd new file mode 100644 index 00000000..3f3946e2 --- /dev/null +++ b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application.Contracts/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application.Contracts/ISqlFunctionAppService.cs b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application.Contracts/ISqlFunctionAppService.cs new file mode 100644 index 00000000..99179ef5 --- /dev/null +++ b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application.Contracts/ISqlFunctionAppService.cs @@ -0,0 +1,29 @@ +using System; +using System.Threading.Tasks; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Application.Services; + +namespace Erp.SqlQueryManager.Application.Contracts; + +public interface ISqlFunctionAppService : ICrudAppService< + SqlFunctionDto, + Guid, + PagedAndSortedResultRequestDto, + CreateSqlFunctionDto, + UpdateSqlFunctionDto> +{ + /// + /// Deploy function to database + /// + Task DeployAsync(DeployFunctionDto input); + + /// + /// Check if function exists in database + /// + Task CheckExistsAsync(Guid id); + + /// + /// Drop function from database + /// + Task DropAsync(Guid id); +} diff --git a/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application.Contracts/ISqlQueryAppService.cs b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application.Contracts/ISqlQueryAppService.cs new file mode 100644 index 00000000..f7a0e794 --- /dev/null +++ b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application.Contracts/ISqlQueryAppService.cs @@ -0,0 +1,39 @@ +using System; +using System.Threading.Tasks; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Application.Services; + +namespace Erp.SqlQueryManager.Application.Contracts; + +public interface ISqlQueryAppService : ICrudAppService< + SqlQueryDto, + Guid, + PagedAndSortedResultRequestDto, + CreateSqlQueryDto, + UpdateSqlQueryDto> +{ + /// + /// Execute a SQL query + /// + Task ExecuteQueryAsync(ExecuteSqlQueryDto input); + + /// + /// Execute a saved query by ID + /// + Task ExecuteSavedQueryAsync(Guid id); + + /// + /// Validate SQL query syntax + /// + Task<(bool IsValid, string ErrorMessage)> ValidateQueryAsync(string sql); + + /// + /// Activate query + /// + Task ActivateAsync(Guid id); + + /// + /// Archive query + /// + Task ArchiveAsync(Guid id); +} diff --git a/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application.Contracts/ISqlStoredProcedureAppService.cs b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application.Contracts/ISqlStoredProcedureAppService.cs new file mode 100644 index 00000000..48edcb87 --- /dev/null +++ b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application.Contracts/ISqlStoredProcedureAppService.cs @@ -0,0 +1,29 @@ +using System; +using System.Threading.Tasks; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Application.Services; + +namespace Erp.SqlQueryManager.Application.Contracts; + +public interface ISqlStoredProcedureAppService : ICrudAppService< + SqlStoredProcedureDto, + Guid, + PagedAndSortedResultRequestDto, + CreateSqlStoredProcedureDto, + UpdateSqlStoredProcedureDto> +{ + /// + /// Deploy stored procedure to database + /// + Task DeployAsync(DeployStoredProcedureDto input); + + /// + /// Check if procedure exists in database + /// + Task CheckExistsAsync(Guid id); + + /// + /// Drop procedure from database + /// + Task DropAsync(Guid id); +} diff --git a/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application.Contracts/ISqlTemplateAppService.cs b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application.Contracts/ISqlTemplateAppService.cs new file mode 100644 index 00000000..9f192b33 --- /dev/null +++ b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application.Contracts/ISqlTemplateAppService.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Erp.SqlQueryManager.Domain.Shared; +using Volo.Abp.Application.Services; + +namespace Erp.SqlQueryManager.Application.Contracts; + +public interface ISqlTemplateAppService : IApplicationService +{ + /// + /// Get all available query templates + /// + Task> GetQueryTemplatesAsync(); + + /// + /// Get stored procedure template + /// + Task GetStoredProcedureTemplateAsync(string procedureName, string schemaName = "dbo"); + + /// + /// Get view template + /// + Task GetViewTemplateAsync(string viewName, string schemaName = "dbo", bool withSchemaBinding = false); + + /// + /// Get function template + /// + Task GetFunctionTemplateAsync(string functionName, SqlFunctionType functionType, string schemaName = "dbo"); + + /// + /// Get specific query template + /// + Task GetQueryTemplateAsync(string templateType); +} diff --git a/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application.Contracts/ISqlViewAppService.cs b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application.Contracts/ISqlViewAppService.cs new file mode 100644 index 00000000..4fb02584 --- /dev/null +++ b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application.Contracts/ISqlViewAppService.cs @@ -0,0 +1,29 @@ +using System; +using System.Threading.Tasks; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Application.Services; + +namespace Erp.SqlQueryManager.Application.Contracts; + +public interface ISqlViewAppService : ICrudAppService< + SqlViewDto, + Guid, + PagedAndSortedResultRequestDto, + CreateSqlViewDto, + UpdateSqlViewDto> +{ + /// + /// Deploy view to database + /// + Task DeployAsync(DeployViewDto input); + + /// + /// Check if view exists in database + /// + Task CheckExistsAsync(Guid id); + + /// + /// Drop view from database + /// + Task DropAsync(Guid id); +} diff --git a/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application.Contracts/SqlFunctionDto.cs b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application.Contracts/SqlFunctionDto.cs new file mode 100644 index 00000000..785a704a --- /dev/null +++ b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application.Contracts/SqlFunctionDto.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using Erp.SqlQueryManager.Domain.Shared; +using Volo.Abp.Application.Dtos; + +namespace Erp.SqlQueryManager.Application.Contracts; + +public class SqlFunctionDto : FullAuditedEntityDto +{ + public string FunctionName { get; set; } + public string SchemaName { get; set; } + public string DisplayName { get; set; } + public string Description { get; set; } + public SqlFunctionType FunctionType { get; set; } + public string FunctionBody { get; set; } + public string ReturnType { get; set; } + public string DataSourceCode { get; set; } + public SqlQueryStatus Status { get; set; } + public string Category { get; set; } + public bool IsDeployed { get; set; } + public DateTime? LastDeployedAt { get; set; } + public string Parameters { get; set; } +} + +public class CreateSqlFunctionDto +{ + public string FunctionName { get; set; } + public string SchemaName { get; set; } + public string DisplayName { get; set; } + public string Description { get; set; } + public SqlFunctionType FunctionType { get; set; } + public string FunctionBody { get; set; } + public string ReturnType { get; set; } + public string DataSourceCode { get; set; } + public string Category { get; set; } + public string Parameters { get; set; } +} + +public class UpdateSqlFunctionDto +{ + public string DisplayName { get; set; } + public string Description { get; set; } + public string FunctionBody { get; set; } + public string ReturnType { get; set; } + public string Category { get; set; } + public string Parameters { get; set; } +} + +public class DeployFunctionDto +{ + public Guid Id { get; set; } + public bool DropIfExists { get; set; } +} diff --git a/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application.Contracts/SqlQueryDto.cs b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application.Contracts/SqlQueryDto.cs new file mode 100644 index 00000000..08870104 --- /dev/null +++ b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application.Contracts/SqlQueryDto.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using Erp.SqlQueryManager.Domain.Shared; +using Volo.Abp.Application.Dtos; + +namespace Erp.SqlQueryManager.Application.Contracts; + +public class SqlQueryDto : FullAuditedEntityDto +{ + public string Code { get; set; } + public string Name { get; set; } + public string Description { get; set; } + public string QueryText { get; set; } + public string DataSourceCode { get; set; } + public SqlQueryStatus Status { get; set; } + public string Category { get; set; } + public string Tags { get; set; } + public DateTime? LastExecutedAt { get; set; } + public int ExecutionCount { get; set; } + public bool IsModifyingData { get; set; } + public string Parameters { get; set; } +} + +public class CreateSqlQueryDto +{ + public string Code { get; set; } + public string Name { get; set; } + public string Description { get; set; } + public string QueryText { get; set; } + public string DataSourceCode { get; set; } + public string Category { get; set; } + public string Tags { get; set; } + public bool IsModifyingData { get; set; } + public string Parameters { get; set; } +} + +public class UpdateSqlQueryDto +{ + public string Code { get; set; } + public string Name { get; set; } + public string Description { get; set; } + public string QueryText { get; set; } + public string DataSourceCode { get; set; } + public string Category { get; set; } + public string Tags { get; set; } + public bool IsModifyingData { get; set; } + public string Parameters { get; set; } +} + +public class ExecuteSqlQueryDto +{ + public string QueryText { get; set; } + public string DataSourceCode { get; set; } + public Dictionary Parameters { get; set; } +} + +public class SqlQueryExecutionResultDto +{ + public bool Success { get; set; } + public string Message { get; set; } + public IEnumerable Data { get; set; } + public int RowsAffected { get; set; } + public long ExecutionTimeMs { get; set; } + public Dictionary Metadata { get; set; } +} diff --git a/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application.Contracts/SqlQueryManagerApplicationContractsModule.cs b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application.Contracts/SqlQueryManagerApplicationContractsModule.cs new file mode 100644 index 00000000..f4bb644f --- /dev/null +++ b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application.Contracts/SqlQueryManagerApplicationContractsModule.cs @@ -0,0 +1,14 @@ +using Volo.Abp.Application; +using Volo.Abp.Authorization; +using Volo.Abp.Modularity; + +namespace Erp.SqlQueryManager; + +[DependsOn( + typeof(SqlQueryManagerDomainSharedModule), + typeof(AbpDddApplicationContractsModule), + typeof(AbpAuthorizationModule) +)] +public class SqlQueryManagerApplicationContractsModule : AbpModule +{ +} diff --git a/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application.Contracts/SqlStoredProcedureDto.cs b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application.Contracts/SqlStoredProcedureDto.cs new file mode 100644 index 00000000..ca4bf544 --- /dev/null +++ b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application.Contracts/SqlStoredProcedureDto.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using Erp.SqlQueryManager.Domain.Shared; +using Volo.Abp.Application.Dtos; + +namespace Erp.SqlQueryManager.Application.Contracts; + +public class SqlStoredProcedureDto : FullAuditedEntityDto +{ + public string ProcedureName { get; set; } + public string SchemaName { get; set; } + public string DisplayName { get; set; } + public string Description { get; set; } + public string ProcedureBody { get; set; } + public string DataSourceCode { get; set; } + public SqlQueryStatus Status { get; set; } + public string Category { get; set; } + public bool IsDeployed { get; set; } + public DateTime? LastDeployedAt { get; set; } + public string Parameters { get; set; } +} + +public class CreateSqlStoredProcedureDto +{ + public string ProcedureName { get; set; } + public string SchemaName { get; set; } + public string DisplayName { get; set; } + public string Description { get; set; } + public string ProcedureBody { get; set; } + public string DataSourceCode { get; set; } + public string Category { get; set; } + public string Parameters { get; set; } +} + +public class UpdateSqlStoredProcedureDto +{ + public string DisplayName { get; set; } + public string Description { get; set; } + public string ProcedureBody { get; set; } + public string Category { get; set; } + public string Parameters { get; set; } +} + +public class DeployStoredProcedureDto +{ + public Guid Id { get; set; } + public bool DropIfExists { get; set; } +} diff --git a/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application.Contracts/SqlTemplateDto.cs b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application.Contracts/SqlTemplateDto.cs new file mode 100644 index 00000000..5fdf5ae0 --- /dev/null +++ b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application.Contracts/SqlTemplateDto.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; + +namespace Erp.SqlQueryManager.Application.Contracts; + +public class SqlTemplateDto +{ + public string Type { get; set; } + public string Name { get; set; } + public string Description { get; set; } + public string Template { get; set; } +} + +public class GetTemplateInput +{ + public string TemplateType { get; set; } + public string ObjectName { get; set; } + public string SchemaName { get; set; } +} diff --git a/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application.Contracts/SqlViewDto.cs b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application.Contracts/SqlViewDto.cs new file mode 100644 index 00000000..e8bcb9c4 --- /dev/null +++ b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application.Contracts/SqlViewDto.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using Erp.SqlQueryManager.Domain.Shared; +using Volo.Abp.Application.Dtos; + +namespace Erp.SqlQueryManager.Application.Contracts; + +public class SqlViewDto : FullAuditedEntityDto +{ + public string ViewName { get; set; } + public string SchemaName { get; set; } + public string DisplayName { get; set; } + public string Description { get; set; } + public string ViewDefinition { get; set; } + public string DataSourceCode { get; set; } + public SqlQueryStatus Status { get; set; } + public string Category { get; set; } + public bool IsDeployed { get; set; } + public DateTime? LastDeployedAt { get; set; } + public bool WithSchemaBinding { get; set; } +} + +public class CreateSqlViewDto +{ + public string ViewName { get; set; } + public string SchemaName { get; set; } + public string DisplayName { get; set; } + public string Description { get; set; } + public string ViewDefinition { get; set; } + public string DataSourceCode { get; set; } + public string Category { get; set; } + public bool WithSchemaBinding { get; set; } +} + +public class UpdateSqlViewDto +{ + public string DisplayName { get; set; } + public string Description { get; set; } + public string ViewDefinition { get; set; } + public string Category { get; set; } + public bool WithSchemaBinding { get; set; } +} + +public class DeployViewDto +{ + public Guid Id { get; set; } + public bool DropIfExists { get; set; } +} diff --git a/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application/Erp.SqlQueryManager.Application.csproj b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application/Erp.SqlQueryManager.Application.csproj new file mode 100644 index 00000000..5f4683e5 --- /dev/null +++ b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application/Erp.SqlQueryManager.Application.csproj @@ -0,0 +1,18 @@ + + + + + + net9.0 + Erp.SqlQueryManager + + + + + + + + + + + diff --git a/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application/FodyWeavers.xml b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application/FodyWeavers.xml new file mode 100644 index 00000000..00e1d9a1 --- /dev/null +++ b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application/FodyWeavers.xsd b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application/FodyWeavers.xsd new file mode 100644 index 00000000..3f3946e2 --- /dev/null +++ b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application/SqlFunctionAppService.cs b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application/SqlFunctionAppService.cs new file mode 100644 index 00000000..2ce9b3bc --- /dev/null +++ b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application/SqlFunctionAppService.cs @@ -0,0 +1,139 @@ +using System; +using System.Threading.Tasks; +using Erp.SqlQueryManager.Application.Contracts; +using Erp.SqlQueryManager.Domain.Entities; +using Erp.SqlQueryManager.Domain.Services; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Application.Services; +using Volo.Abp.Domain.Repositories; + +namespace Erp.SqlQueryManager.Application; + +public class SqlFunctionAppService : CrudAppService< + SqlFunction, + SqlFunctionDto, + Guid, + PagedAndSortedResultRequestDto, + CreateSqlFunctionDto, + UpdateSqlFunctionDto>, ISqlFunctionAppService +{ + private readonly ISqlExecutorService _sqlExecutorService; + + public SqlFunctionAppService( + IRepository repository, + ISqlExecutorService sqlExecutorService) : base(repository) + { + _sqlExecutorService = sqlExecutorService; + } + + public override async Task CreateAsync(CreateSqlFunctionDto input) + { + var entity = new SqlFunction( + GuidGenerator.Create(), + input.FunctionName, + input.SchemaName ?? "dbo", + input.DisplayName, + input.FunctionType, + input.FunctionBody, + input.ReturnType, + input.DataSourceCode, + CurrentTenant.Id) + { + Description = input.Description, + Category = input.Category, + Parameters = input.Parameters + }; + + await Repository.InsertAsync(entity); + return ObjectMapper.Map(entity); + } + + public override async Task UpdateAsync(Guid id, UpdateSqlFunctionDto input) + { + var entity = await Repository.GetAsync(id); + + entity.DisplayName = input.DisplayName; + entity.Description = input.Description; + entity.UpdateBody(input.FunctionBody); + entity.ReturnType = input.ReturnType; + entity.Category = input.Category; + entity.Parameters = input.Parameters; + + await Repository.UpdateAsync(entity); + return ObjectMapper.Map(entity); + } + + public async Task DeployAsync(DeployFunctionDto input) + { + var function = await Repository.GetAsync(input.Id); + + // Drop if exists and requested + if (input.DropIfExists) + { + var objectType = function.FunctionType.ToString().ToUpperInvariant().Replace("FUNCTION", "_FUNCTION"); + await _sqlExecutorService.DropObjectAsync( + function.FunctionName, + $"SQL_{objectType}", + function.DataSourceCode, + function.SchemaName); + } + + // Deploy the function + var result = await _sqlExecutorService.DeployFunctionAsync( + function.FunctionBody, + function.DataSourceCode); + + if (result.Success) + { + function.MarkAsDeployed(); + await Repository.UpdateAsync(function); + } + + return MapExecutionResult(result); + } + + public async Task CheckExistsAsync(Guid id) + { + var function = await Repository.GetAsync(id); + var objectType = function.FunctionType.ToString().ToUpperInvariant().Replace("FUNCTION", "_FUNCTION"); + + return await _sqlExecutorService.CheckObjectExistsAsync( + function.FunctionName, + $"SQL_{objectType}", + function.DataSourceCode, + function.SchemaName); + } + + public async Task DropAsync(Guid id) + { + var function = await Repository.GetAsync(id); + var objectType = function.FunctionType.ToString().ToUpperInvariant().Replace("FUNCTION", "_FUNCTION"); + + var result = await _sqlExecutorService.DropObjectAsync( + function.FunctionName, + $"SQL_{objectType}", + function.DataSourceCode, + function.SchemaName); + + if (result.Success) + { + function.IsDeployed = false; + await Repository.UpdateAsync(function); + } + + return MapExecutionResult(result); + } + + private SqlQueryExecutionResultDto MapExecutionResult(SqlExecutionResult result) + { + return new SqlQueryExecutionResultDto + { + Success = result.Success, + Message = result.Message, + Data = result.Data, + RowsAffected = result.RowsAffected, + ExecutionTimeMs = result.ExecutionTimeMs, + Metadata = result.Metadata + }; + } +} diff --git a/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application/SqlQueryAppService.cs b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application/SqlQueryAppService.cs new file mode 100644 index 00000000..c0d55036 --- /dev/null +++ b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application/SqlQueryAppService.cs @@ -0,0 +1,124 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Erp.SqlQueryManager.Application.Contracts; +using Erp.SqlQueryManager.Domain.Entities; +using Erp.SqlQueryManager.Domain.Services; +using Microsoft.AspNetCore.Mvc; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Application.Services; +using Volo.Abp.Domain.Repositories; + +namespace Erp.SqlQueryManager.Application; + +public class SqlQueryAppService : CrudAppService< + SqlQuery, + SqlQueryDto, + Guid, + PagedAndSortedResultRequestDto, + CreateSqlQueryDto, + UpdateSqlQueryDto>, ISqlQueryAppService +{ + private readonly ISqlExecutorService _sqlExecutorService; + + public SqlQueryAppService( + IRepository repository, + ISqlExecutorService sqlExecutorService) : base(repository) + { + _sqlExecutorService = sqlExecutorService; + } + + public override async Task CreateAsync(CreateSqlQueryDto input) + { + var entity = new SqlQuery( + GuidGenerator.Create(), + input.Code, + input.Name, + input.QueryText, + input.DataSourceCode, + CurrentTenant.Id) + { + Description = input.Description, + Category = input.Category, + Tags = input.Tags, + IsModifyingData = input.IsModifyingData, + Parameters = input.Parameters + }; + + await Repository.InsertAsync(entity); + return ObjectMapper.Map(entity); + } + + public override async Task UpdateAsync(Guid id, UpdateSqlQueryDto input) + { + var entity = await Repository.GetAsync(id); + + entity.Name = input.Name; + entity.Description = input.Description; + entity.UpdateQueryText(input.QueryText); + entity.DataSourceCode = input.DataSourceCode; + entity.Category = input.Category; + entity.Tags = input.Tags; + entity.IsModifyingData = input.IsModifyingData; + entity.Parameters = input.Parameters; + + await Repository.UpdateAsync(entity); + return ObjectMapper.Map(entity); + } + + public async Task ExecuteQueryAsync(ExecuteSqlQueryDto input) + { + var result = input.QueryText.TrimStart().StartsWith("SELECT", StringComparison.OrdinalIgnoreCase) + ? await _sqlExecutorService.ExecuteQueryAsync(input.QueryText, input.DataSourceCode, input.Parameters) + : await _sqlExecutorService.ExecuteNonQueryAsync(input.QueryText, input.DataSourceCode, input.Parameters); + + return MapExecutionResult(result); + } + + public async Task ExecuteSavedQueryAsync(Guid id) + { + var query = await Repository.GetAsync(id); + + var result = query.IsModifyingData + ? await _sqlExecutorService.ExecuteNonQueryAsync(query.QueryText, query.DataSourceCode) + : await _sqlExecutorService.ExecuteQueryAsync(query.QueryText, query.DataSourceCode); + + // Update execution statistics + query.MarkAsExecuted(); + await Repository.UpdateAsync(query); + + return MapExecutionResult(result); + } + + public async Task<(bool IsValid, string ErrorMessage)> ValidateQueryAsync(string sql) + { + return await _sqlExecutorService.ValidateSqlAsync(sql); + } + + public async Task ActivateAsync(Guid id) + { + var entity = await Repository.GetAsync(id); + entity.Activate(); + await Repository.UpdateAsync(entity); + } + + public async Task ArchiveAsync(Guid id) + { + var entity = await Repository.GetAsync(id); + entity.Archive(); + await Repository.UpdateAsync(entity); + } + + private SqlQueryExecutionResultDto MapExecutionResult(SqlExecutionResult result) + { + return new SqlQueryExecutionResultDto + { + Success = result.Success, + Message = result.Message, + Data = result.Data, + RowsAffected = result.RowsAffected, + ExecutionTimeMs = result.ExecutionTimeMs, + Metadata = result.Metadata + }; + } +} diff --git a/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application/SqlQueryManagerApplicationModule.cs b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application/SqlQueryManagerApplicationModule.cs new file mode 100644 index 00000000..146b8f00 --- /dev/null +++ b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application/SqlQueryManagerApplicationModule.cs @@ -0,0 +1,25 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Application; +using Volo.Abp.AutoMapper; +using Volo.Abp.Modularity; + +namespace Erp.SqlQueryManager; + +[DependsOn( + typeof(SqlQueryManagerDomainModule), + typeof(SqlQueryManagerApplicationContractsModule), + typeof(AbpDddApplicationModule), + typeof(AbpAutoMapperModule) +)] +public class SqlQueryManagerApplicationModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddAutoMapperObjectMapper(); + + Configure(options => + { + options.AddMaps(validate: true); + }); + } +} diff --git a/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application/SqlQueryManagerAutoMapperProfile.cs b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application/SqlQueryManagerAutoMapperProfile.cs new file mode 100644 index 00000000..48c7eadf --- /dev/null +++ b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application/SqlQueryManagerAutoMapperProfile.cs @@ -0,0 +1,87 @@ +using AutoMapper; +using Erp.SqlQueryManager.Application.Contracts; +using Erp.SqlQueryManager.Domain.Entities; +using Volo.Abp.AutoMapper; + +namespace Erp.SqlQueryManager.Application; + +public class SqlQueryManagerAutoMapperProfile : Profile +{ + public SqlQueryManagerAutoMapperProfile() + { + CreateMap(); + CreateMap() + .IgnoreFullAuditedObjectProperties() + .Ignore(x => x.Id) + .Ignore(x => x.TenantId) + .Ignore(x => x.Status) + .Ignore(x => x.LastExecutedAt) + .Ignore(x => x.ExecutionCount); + + CreateMap() + .IgnoreFullAuditedObjectProperties() + .Ignore(x => x.Id) + .Ignore(x => x.TenantId) + .Ignore(x => x.Status) + .Ignore(x => x.LastExecutedAt) + .Ignore(x => x.ExecutionCount); + + CreateMap(); + CreateMap() + .IgnoreFullAuditedObjectProperties() + .Ignore(x => x.Id) + .Ignore(x => x.TenantId) + .Ignore(x => x.Status) + .Ignore(x => x.IsDeployed) + .Ignore(x => x.LastDeployedAt); + CreateMap() + .IgnoreFullAuditedObjectProperties() + .Ignore(x => x.Id) + .Ignore(x => x.TenantId) + .Ignore(x => x.ProcedureName) + .Ignore(x => x.SchemaName) + .Ignore(x => x.DataSourceCode) + .Ignore(x => x.Status) + .Ignore(x => x.IsDeployed) + .Ignore(x => x.LastDeployedAt); + + CreateMap(); + CreateMap() + .IgnoreFullAuditedObjectProperties() + .Ignore(x => x.Id) + .Ignore(x => x.TenantId) + .Ignore(x => x.Status) + .Ignore(x => x.IsDeployed) + .Ignore(x => x.LastDeployedAt); + CreateMap() + .IgnoreFullAuditedObjectProperties() + .Ignore(x => x.Id) + .Ignore(x => x.TenantId) + .Ignore(x => x.ViewName) + .Ignore(x => x.SchemaName) + .Ignore(x => x.DataSourceCode) + .Ignore(x => x.Status) + .Ignore(x => x.IsDeployed) + .Ignore(x => x.LastDeployedAt); + + CreateMap(); + CreateMap() + .IgnoreFullAuditedObjectProperties() + .Ignore(x => x.Id) + .Ignore(x => x.TenantId) + .Ignore(x => x.Status) + .Ignore(x => x.IsDeployed) + .Ignore(x => x.LastDeployedAt); + CreateMap() + .IgnoreFullAuditedObjectProperties() + .Ignore(x => x.Id) + .Ignore(x => x.TenantId) + .Ignore(x => x.FunctionName) + .Ignore(x => x.SchemaName) + .Ignore(x => x.FunctionType) + .Ignore(x => x.DataSourceCode) + .Ignore(x => x.Status) + .Ignore(x => x.IsDeployed) + .Ignore(x => x.LastDeployedAt); + } +} diff --git a/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application/SqlStoredProcedureAppService.cs b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application/SqlStoredProcedureAppService.cs new file mode 100644 index 00000000..2208577c --- /dev/null +++ b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application/SqlStoredProcedureAppService.cs @@ -0,0 +1,134 @@ +using System; +using System.Threading.Tasks; +using Erp.SqlQueryManager.Application.Contracts; +using Erp.SqlQueryManager.Domain.Entities; +using Erp.SqlQueryManager.Domain.Services; +using Microsoft.AspNetCore.Mvc; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Application.Services; +using Volo.Abp.Domain.Repositories; + +namespace Erp.SqlQueryManager.Application; + +public class SqlStoredProcedureAppService : CrudAppService< + SqlStoredProcedure, + SqlStoredProcedureDto, + Guid, + PagedAndSortedResultRequestDto, + CreateSqlStoredProcedureDto, + UpdateSqlStoredProcedureDto>, ISqlStoredProcedureAppService +{ + private readonly ISqlExecutorService _sqlExecutorService; + + public SqlStoredProcedureAppService( + IRepository repository, + ISqlExecutorService sqlExecutorService) : base(repository) + { + _sqlExecutorService = sqlExecutorService; + } + + public override async Task CreateAsync(CreateSqlStoredProcedureDto input) + { + var entity = new SqlStoredProcedure( + GuidGenerator.Create(), + input.ProcedureName, + input.SchemaName ?? "dbo", + input.DisplayName, + input.ProcedureBody, + input.DataSourceCode, + CurrentTenant.Id) + { + Description = input.Description, + Category = input.Category, + Parameters = input.Parameters + }; + + await Repository.InsertAsync(entity); + return ObjectMapper.Map(entity); + } + + public override async Task UpdateAsync(Guid id, UpdateSqlStoredProcedureDto input) + { + var entity = await Repository.GetAsync(id); + + entity.DisplayName = input.DisplayName; + entity.Description = input.Description; + entity.UpdateBody(input.ProcedureBody); + entity.Category = input.Category; + entity.Parameters = input.Parameters; + + await Repository.UpdateAsync(entity); + return ObjectMapper.Map(entity); + } + + public async Task DeployAsync(DeployStoredProcedureDto input) + { + var procedure = await Repository.GetAsync(input.Id); + + // Drop if exists and requested + if (input.DropIfExists) + { + await _sqlExecutorService.DropObjectAsync( + procedure.ProcedureName, + "SQL_STORED_PROCEDURE", + procedure.DataSourceCode, + procedure.SchemaName); + } + + // Deploy the procedure + var result = await _sqlExecutorService.DeployStoredProcedureAsync( + procedure.ProcedureBody, + procedure.DataSourceCode); + + if (result.Success) + { + procedure.MarkAsDeployed(); + await Repository.UpdateAsync(procedure); + } + + return MapExecutionResult(result); + } + + public async Task CheckExistsAsync(Guid id) + { + var procedure = await Repository.GetAsync(id); + + return await _sqlExecutorService.CheckObjectExistsAsync( + procedure.ProcedureName, + "SQL_STORED_PROCEDURE", + procedure.DataSourceCode, + procedure.SchemaName); + } + + public async Task DropAsync(Guid id) + { + var procedure = await Repository.GetAsync(id); + + var result = await _sqlExecutorService.DropObjectAsync( + procedure.ProcedureName, + "SQL_STORED_PROCEDURE", + procedure.DataSourceCode, + procedure.SchemaName); + + if (result.Success) + { + procedure.IsDeployed = false; + await Repository.UpdateAsync(procedure); + } + + return MapExecutionResult(result); + } + + private SqlQueryExecutionResultDto MapExecutionResult(SqlExecutionResult result) + { + return new SqlQueryExecutionResultDto + { + Success = result.Success, + Message = result.Message, + Data = result.Data, + RowsAffected = result.RowsAffected, + ExecutionTimeMs = result.ExecutionTimeMs, + Metadata = result.Metadata + }; + } +} diff --git a/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application/SqlTemplateAppService.cs b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application/SqlTemplateAppService.cs new file mode 100644 index 00000000..3387159f --- /dev/null +++ b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application/SqlTemplateAppService.cs @@ -0,0 +1,59 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Erp.SqlQueryManager.Application.Contracts; +using Erp.SqlQueryManager.Domain.Services; +using Erp.SqlQueryManager.Domain.Shared; +using Microsoft.AspNetCore.Mvc; +using Volo.Abp.Application.Services; + +namespace Erp.SqlQueryManager.Application; + +public class SqlTemplateAppService : ApplicationService, ISqlTemplateAppService +{ + private readonly ISqlTemplateProvider _templateProvider; + + public SqlTemplateAppService(ISqlTemplateProvider templateProvider) + { + _templateProvider = templateProvider; + } + + public Task> GetQueryTemplatesAsync() + { + var templates = _templateProvider.GetAvailableQueryTemplates() + .Select(t => new SqlTemplateDto + { + Type = t.Type, + Name = t.Name, + Description = t.Description, + Template = _templateProvider.GetQueryTemplate(t.Type) + }) + .ToList(); + + return Task.FromResult(templates); + } + + public Task GetStoredProcedureTemplateAsync(string procedureName, string schemaName = "dbo") + { + var template = _templateProvider.GetStoredProcedureTemplate(procedureName, schemaName); + return Task.FromResult(template); + } + + public Task GetViewTemplateAsync(string viewName, string schemaName = "dbo", bool withSchemaBinding = false) + { + var template = _templateProvider.GetViewTemplate(viewName, schemaName, withSchemaBinding); + return Task.FromResult(template); + } + + public Task GetFunctionTemplateAsync(string functionName, SqlFunctionType functionType, string schemaName = "dbo") + { + var template = _templateProvider.GetFunctionTemplate(functionName, functionType, schemaName); + return Task.FromResult(template); + } + + public Task GetQueryTemplateAsync(string templateType) + { + var template = _templateProvider.GetQueryTemplate(templateType); + return Task.FromResult(template); + } +} diff --git a/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application/SqlViewAppService.cs b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application/SqlViewAppService.cs new file mode 100644 index 00000000..447aecca --- /dev/null +++ b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Application/SqlViewAppService.cs @@ -0,0 +1,133 @@ +using System; +using System.Threading.Tasks; +using Erp.SqlQueryManager.Application.Contracts; +using Erp.SqlQueryManager.Domain.Entities; +using Erp.SqlQueryManager.Domain.Services; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Application.Services; +using Volo.Abp.Domain.Repositories; + +namespace Erp.SqlQueryManager.Application; + +public class SqlViewAppService : CrudAppService< + SqlView, + SqlViewDto, + Guid, + PagedAndSortedResultRequestDto, + CreateSqlViewDto, + UpdateSqlViewDto>, ISqlViewAppService +{ + private readonly ISqlExecutorService _sqlExecutorService; + + public SqlViewAppService( + IRepository repository, + ISqlExecutorService sqlExecutorService) : base(repository) + { + _sqlExecutorService = sqlExecutorService; + } + + public override async Task CreateAsync(CreateSqlViewDto input) + { + var entity = new SqlView( + GuidGenerator.Create(), + input.ViewName, + input.SchemaName ?? "dbo", + input.DisplayName, + input.ViewDefinition, + input.DataSourceCode, + CurrentTenant.Id) + { + Description = input.Description, + Category = input.Category, + WithSchemaBinding = input.WithSchemaBinding + }; + + await Repository.InsertAsync(entity); + return ObjectMapper.Map(entity); + } + + public override async Task UpdateAsync(Guid id, UpdateSqlViewDto input) + { + var entity = await Repository.GetAsync(id); + + entity.DisplayName = input.DisplayName; + entity.Description = input.Description; + entity.UpdateDefinition(input.ViewDefinition); + entity.Category = input.Category; + entity.WithSchemaBinding = input.WithSchemaBinding; + + await Repository.UpdateAsync(entity); + return ObjectMapper.Map(entity); + } + + public async Task DeployAsync(DeployViewDto input) + { + var view = await Repository.GetAsync(input.Id); + + // Drop if exists and requested + if (input.DropIfExists) + { + await _sqlExecutorService.DropObjectAsync( + view.ViewName, + "VIEW", + view.DataSourceCode, + view.SchemaName); + } + + // Deploy the view + var result = await _sqlExecutorService.DeployViewAsync( + view.ViewDefinition, + view.DataSourceCode); + + if (result.Success) + { + view.MarkAsDeployed(); + await Repository.UpdateAsync(view); + } + + return MapExecutionResult(result); + } + + public async Task CheckExistsAsync(Guid id) + { + var view = await Repository.GetAsync(id); + + return await _sqlExecutorService.CheckObjectExistsAsync( + view.ViewName, + "VIEW", + view.DataSourceCode, + view.SchemaName); + } + + public async Task DropAsync(Guid id) + { + var view = await Repository.GetAsync(id); + + var result = await _sqlExecutorService.DropObjectAsync( + view.ViewName, + "VIEW", + view.DataSourceCode, + view.SchemaName); + + if (result.Success) + { + view.IsDeployed = false; + await Repository.UpdateAsync(view); + } + + return MapExecutionResult(result); + } + + private SqlQueryExecutionResultDto MapExecutionResult(SqlExecutionResult result) + { + return new SqlQueryExecutionResultDto + { + Success = result.Success, + Message = result.Message, + Data = result.Data, + RowsAffected = result.RowsAffected, + ExecutionTimeMs = result.ExecutionTimeMs, + Metadata = result.Metadata + }; + } +} diff --git a/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain.Shared/Erp.SqlQueryManager.Domain.Shared.csproj b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain.Shared/Erp.SqlQueryManager.Domain.Shared.csproj new file mode 100644 index 00000000..adc9c7dc --- /dev/null +++ b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain.Shared/Erp.SqlQueryManager.Domain.Shared.csproj @@ -0,0 +1,24 @@ + + + + + + net9.0 + true + Erp.SqlQueryManager + + + + + + + + + + + + + + + + diff --git a/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain.Shared/ErpSqlQueryManagerResource.cs b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain.Shared/ErpSqlQueryManagerResource.cs new file mode 100644 index 00000000..30b86d00 --- /dev/null +++ b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain.Shared/ErpSqlQueryManagerResource.cs @@ -0,0 +1,8 @@ +using Volo.Abp.Localization; + +namespace Erp.SqlQueryManager.Domain.Shared; + +[LocalizationResourceName("SqlQueryManager")] +public class ErpSqlQueryManagerResource +{ +} diff --git a/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain.Shared/FodyWeavers.xml b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain.Shared/FodyWeavers.xml new file mode 100644 index 00000000..00e1d9a1 --- /dev/null +++ b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain.Shared/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain.Shared/FodyWeavers.xsd b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain.Shared/FodyWeavers.xsd new file mode 100644 index 00000000..3f3946e2 --- /dev/null +++ b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain.Shared/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain.Shared/SqlObjectType.cs b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain.Shared/SqlObjectType.cs new file mode 100644 index 00000000..939b244e --- /dev/null +++ b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain.Shared/SqlObjectType.cs @@ -0,0 +1,23 @@ +namespace Erp.SqlQueryManager.Domain.Shared; + +public enum SqlObjectType +{ + Query = 1, + StoredProcedure = 2, + View = 3, + Function = 4 +} + +public enum SqlFunctionType +{ + ScalarFunction = 1, + TableValuedFunction = 2, + InlineTableValuedFunction = 3 +} + +public enum SqlQueryStatus +{ + Draft = 1, + Active = 2, + Archived = 3 +} diff --git a/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain.Shared/SqlQueryManagerDomainSharedModule.cs b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain.Shared/SqlQueryManagerDomainSharedModule.cs new file mode 100644 index 00000000..d5294314 --- /dev/null +++ b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain.Shared/SqlQueryManagerDomainSharedModule.cs @@ -0,0 +1,34 @@ +using Erp.SqlQueryManager.Domain.Shared; +using Volo.Abp.Domain; +using Volo.Abp.Localization; +using Volo.Abp.Localization.ExceptionHandling; +using Volo.Abp.Modularity; +using Volo.Abp.Validation; +using Volo.Abp.VirtualFileSystem; + +namespace Erp.SqlQueryManager; + +[DependsOn( + typeof(AbpValidationModule), + typeof(AbpDddDomainSharedModule) +)] +public class SqlQueryManagerDomainSharedModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.FileSets.AddEmbedded(); + }); + + Configure(options => + { + options.Resources.Add("en"); + }); + + Configure(options => + { + options.MapCodeNamespace("SqlQueryManager", typeof(ErpSqlQueryManagerResource)); + }); + } +} diff --git a/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain.Shared/SqlQueryManagerErrorCodes.cs b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain.Shared/SqlQueryManagerErrorCodes.cs new file mode 100644 index 00000000..e74a6deb --- /dev/null +++ b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain.Shared/SqlQueryManagerErrorCodes.cs @@ -0,0 +1,11 @@ +namespace Erp.SqlQueryManager.Domain.Shared; + +public static class SqlQueryManagerErrorCodes +{ + public const string InvalidSqlQuery = "SqlQueryManager:InvalidSqlQuery"; + public const string DataSourceNotFound = "SqlQueryManager:DataSourceNotFound"; + public const string ExecutionFailed = "SqlQueryManager:ExecutionFailed"; + public const string PermissionDenied = "SqlQueryManager:PermissionDenied"; + public const string InvalidObjectName = "SqlQueryManager:InvalidObjectName"; + public const string ObjectAlreadyExists = "SqlQueryManager:ObjectAlreadyExists"; +} diff --git a/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain.Shared/SqlQueryManagerPermissions.cs b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain.Shared/SqlQueryManagerPermissions.cs new file mode 100644 index 00000000..64cb894d --- /dev/null +++ b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain.Shared/SqlQueryManagerPermissions.cs @@ -0,0 +1,57 @@ +using Volo.Abp.Reflection; + +namespace Erp.SqlQueryManager.Domain.Shared; + +public static class SqlQueryManagerPermissions +{ + public const string GroupName = "SqlQueryManager"; + + public static class SqlQuery + { + public const string Default = GroupName + ".SqlQuery"; + public const string Create = Default + ".Create"; + public const string Update = Default + ".Update"; + public const string Delete = Default + ".Delete"; + public const string Execute = Default + ".Execute"; + } + + public static class SqlStoredProcedure + { + public const string Default = GroupName + ".SqlStoredProcedure"; + public const string Create = Default + ".Create"; + public const string Update = Default + ".Update"; + public const string Delete = Default + ".Delete"; + public const string Deploy = Default + ".Deploy"; + public const string Drop = Default + ".Drop"; + } + + public static class SqlView + { + public const string Default = GroupName + ".SqlView"; + public const string Create = Default + ".Create"; + public const string Update = Default + ".Update"; + public const string Delete = Default + ".Delete"; + public const string Deploy = Default + ".Deploy"; + public const string Drop = Default + ".Drop"; + } + + public static class SqlFunction + { + public const string Default = GroupName + ".SqlFunction"; + public const string Create = Default + ".Create"; + public const string Update = Default + ".Update"; + public const string Delete = Default + ".Delete"; + public const string Deploy = Default + ".Deploy"; + public const string Drop = Default + ".Drop"; + } + + public static class Templates + { + public const string Default = GroupName + ".Templates"; + } + + public static string[] GetAll() + { + return ReflectionHelper.GetPublicConstantsRecursively(typeof(SqlQueryManagerPermissions)); + } +} diff --git a/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain/Entities/SqlFunction.cs b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain/Entities/SqlFunction.cs new file mode 100644 index 00000000..24b537cf --- /dev/null +++ b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain/Entities/SqlFunction.cs @@ -0,0 +1,120 @@ +using System; +using Erp.SqlQueryManager.Domain.Shared; +using Volo.Abp.Domain.Entities.Auditing; +using Volo.Abp.MultiTenancy; + +namespace Erp.SqlQueryManager.Domain.Entities; + +/// +/// SQL Function entity for creating and managing database functions +/// +public class SqlFunction : FullAuditedEntity, IMultiTenant +{ + public Guid? TenantId { get; set; } + + /// + /// Function name in database + /// + public string FunctionName { get; set; } + + /// + /// Schema name (default: dbo) + /// + public string SchemaName { get; set; } + + /// + /// Display name + /// + public string DisplayName { get; set; } + + /// + /// Description + /// + public string Description { get; set; } + + /// + /// Function type + /// + public SqlFunctionType FunctionType { get; set; } + + /// + /// Full function definition (CREATE/ALTER) + /// + public string FunctionBody { get; set; } + + /// + /// Return type definition + /// + public string ReturnType { get; set; } + + /// + /// DataSource code + /// + public string DataSourceCode { get; set; } + + /// + /// Status + /// + public SqlQueryStatus Status { get; set; } + + /// + /// Category + /// + public string Category { get; set; } + + /// + /// Whether function exists in database + /// + public bool IsDeployed { get; set; } + + /// + /// Last deployment time + /// + public DateTime? LastDeployedAt { get; set; } + + /// + /// Parameter definitions (JSON) + /// + public string Parameters { get; set; } + + protected SqlFunction() + { + } + + public SqlFunction( + Guid id, + string functionName, + string schemaName, + string displayName, + SqlFunctionType functionType, + string functionBody, + string returnType, + string dataSourceCode, + Guid? tenantId = null) : base(id) + { + FunctionName = functionName; + SchemaName = schemaName ?? "dbo"; + DisplayName = displayName; + FunctionType = functionType; + FunctionBody = functionBody; + ReturnType = returnType; + DataSourceCode = dataSourceCode; + TenantId = tenantId; + Status = SqlQueryStatus.Draft; + IsDeployed = false; + } + + public void UpdateBody(string body) + { + FunctionBody = body; + } + + public void MarkAsDeployed() + { + IsDeployed = true; + LastDeployedAt = DateTime.UtcNow; + Status = SqlQueryStatus.Active; + } + + public string GetFullName() => $"{SchemaName}.{FunctionName}"; +} diff --git a/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain/Entities/SqlQuery.cs b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain/Entities/SqlQuery.cs new file mode 100644 index 00000000..e982758a --- /dev/null +++ b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain/Entities/SqlQuery.cs @@ -0,0 +1,116 @@ +using System; +using Erp.SqlQueryManager.Domain.Shared; +using Volo.Abp.Domain.Entities.Auditing; +using Volo.Abp.MultiTenancy; + +namespace Erp.SqlQueryManager.Domain.Entities; + +/// +/// SQL Query entity for storing and managing SQL queries +/// +public class SqlQuery : FullAuditedEntity, IMultiTenant +{ + public Guid? TenantId { get; set; } + + /// + /// Unique code for the query + /// + public string Code { get; set; } + + /// + /// Display name + /// + public string Name { get; set; } + + /// + /// Description of the query + /// + public string Description { get; set; } + + /// + /// SQL query content + /// + public string QueryText { get; set; } + + /// + /// DataSource code to use for execution + /// + public string DataSourceCode { get; set; } + + /// + /// Query status + /// + public SqlQueryStatus Status { get; set; } + + /// + /// Category for organization + /// + public string Category { get; set; } + + /// + /// Tags for filtering + /// + public string Tags { get; set; } + + /// + /// Last execution time + /// + public DateTime? LastExecutedAt { get; set; } + + /// + /// Execution count + /// + public int ExecutionCount { get; set; } + + /// + /// Whether query modifies data + /// + public bool IsModifyingData { get; set; } + + /// + /// Expected parameter definitions (JSON format) + /// + public string Parameters { get; set; } + + protected SqlQuery() + { + } + + public SqlQuery( + Guid id, + string code, + string name, + string queryText, + string dataSourceCode, + Guid? tenantId = null) : base(id) + { + Code = code; + Name = name; + QueryText = queryText; + DataSourceCode = dataSourceCode; + TenantId = tenantId; + Status = SqlQueryStatus.Draft; + ExecutionCount = 0; + } + + public void UpdateQueryText(string queryText) + { + QueryText = queryText; + } + + public void MarkAsExecuted() + { + LastExecutedAt = DateTime.UtcNow; + ExecutionCount++; + } + + public void Activate() + { + Status = SqlQueryStatus.Active; + } + + public void Archive() + { + Status = SqlQueryStatus.Archived; + } +} diff --git a/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain/Entities/SqlStoredProcedure.cs b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain/Entities/SqlStoredProcedure.cs new file mode 100644 index 00000000..bd7d0f97 --- /dev/null +++ b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain/Entities/SqlStoredProcedure.cs @@ -0,0 +1,106 @@ +using System; +using Erp.SqlQueryManager.Domain.Shared; +using Volo.Abp.Domain.Entities.Auditing; +using Volo.Abp.MultiTenancy; + +namespace Erp.SqlQueryManager.Domain.Entities; + +/// +/// SQL Stored Procedure entity for creating and managing stored procedures +/// +public class SqlStoredProcedure : FullAuditedEntity, IMultiTenant +{ + public Guid? TenantId { get; set; } + + /// + /// Procedure name in database + /// + public string ProcedureName { get; set; } + + /// + /// Schema name (default: dbo) + /// + public string SchemaName { get; set; } + + /// + /// Display name + /// + public string DisplayName { get; set; } + + /// + /// Description + /// + public string Description { get; set; } + + /// + /// Full procedure definition (CREATE/ALTER) + /// + public string ProcedureBody { get; set; } + + /// + /// DataSource code + /// + public string DataSourceCode { get; set; } + + /// + /// Status + /// + public SqlQueryStatus Status { get; set; } + + /// + /// Category + /// + public string Category { get; set; } + + /// + /// Whether procedure exists in database + /// + public bool IsDeployed { get; set; } + + /// + /// Last deployment time + /// + public DateTime? LastDeployedAt { get; set; } + + /// + /// Parameter definitions (JSON) + /// + public string Parameters { get; set; } + + protected SqlStoredProcedure() + { + } + + public SqlStoredProcedure( + Guid id, + string procedureName, + string schemaName, + string displayName, + string procedureBody, + string dataSourceCode, + Guid? tenantId = null) : base(id) + { + ProcedureName = procedureName; + SchemaName = schemaName ?? "dbo"; + DisplayName = displayName; + ProcedureBody = procedureBody; + DataSourceCode = dataSourceCode; + TenantId = tenantId; + Status = SqlQueryStatus.Draft; + IsDeployed = false; + } + + public void UpdateBody(string body) + { + ProcedureBody = body; + } + + public void MarkAsDeployed() + { + IsDeployed = true; + LastDeployedAt = DateTime.UtcNow; + Status = SqlQueryStatus.Active; + } + + public string GetFullName() => $"{SchemaName}.{ProcedureName}"; +} diff --git a/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain/Entities/SqlView.cs b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain/Entities/SqlView.cs new file mode 100644 index 00000000..643ba09b --- /dev/null +++ b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain/Entities/SqlView.cs @@ -0,0 +1,107 @@ +using System; +using Erp.SqlQueryManager.Domain.Shared; +using Volo.Abp.Domain.Entities.Auditing; +using Volo.Abp.MultiTenancy; + +namespace Erp.SqlQueryManager.Domain.Entities; + +/// +/// SQL View entity for creating and managing database views +/// +public class SqlView : FullAuditedEntity, IMultiTenant +{ + public Guid? TenantId { get; set; } + + /// + /// View name in database + /// + public string ViewName { get; set; } + + /// + /// Schema name (default: dbo) + /// + public string SchemaName { get; set; } + + /// + /// Display name + /// + public string DisplayName { get; set; } + + /// + /// Description + /// + public string Description { get; set; } + + /// + /// View definition (SELECT statement) + /// + public string ViewDefinition { get; set; } + + /// + /// DataSource code + /// + public string DataSourceCode { get; set; } + + /// + /// Status + /// + public SqlQueryStatus Status { get; set; } + + /// + /// Category + /// + public string Category { get; set; } + + /// + /// Whether view exists in database + /// + public bool IsDeployed { get; set; } + + /// + /// Last deployment time + /// + public DateTime? LastDeployedAt { get; set; } + + /// + /// Whether to use WITH SCHEMABINDING + /// + public bool WithSchemaBinding { get; set; } + + protected SqlView() + { + } + + public SqlView( + Guid id, + string viewName, + string schemaName, + string displayName, + string viewDefinition, + string dataSourceCode, + Guid? tenantId = null) : base(id) + { + ViewName = viewName; + SchemaName = schemaName ?? "dbo"; + DisplayName = displayName; + ViewDefinition = viewDefinition; + DataSourceCode = dataSourceCode; + TenantId = tenantId; + Status = SqlQueryStatus.Draft; + IsDeployed = false; + WithSchemaBinding = false; + } + + public void UpdateDefinition(string definition) + { + ViewDefinition = definition; + } + + public void MarkAsDeployed() + { + IsDeployed = true; + LastDeployedAt = DateTime.UtcNow; + Status = SqlQueryStatus.Active; + } + + public string GetFullName() => $"{SchemaName}.{ViewName}"; +} diff --git a/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain/Erp.SqlQueryManager.Domain.csproj b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain/Erp.SqlQueryManager.Domain.csproj new file mode 100644 index 00000000..d457b53e --- /dev/null +++ b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain/Erp.SqlQueryManager.Domain.csproj @@ -0,0 +1,16 @@ + + + + + + net9.0 + Erp.SqlQueryManager + + + + + + + + + diff --git a/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain/FodyWeavers.xml b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain/FodyWeavers.xml new file mode 100644 index 00000000..00e1d9a1 --- /dev/null +++ b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain/FodyWeavers.xsd b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain/FodyWeavers.xsd new file mode 100644 index 00000000..3f3946e2 --- /dev/null +++ b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain/Services/ISqlExecutorService.cs b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain/Services/ISqlExecutorService.cs new file mode 100644 index 00000000..5c6fff3b --- /dev/null +++ b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain/Services/ISqlExecutorService.cs @@ -0,0 +1,96 @@ +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Erp.SqlQueryManager.Domain.Services; + +/// +/// Result of SQL query execution +/// +public class SqlExecutionResult +{ + public bool Success { get; set; } + public string Message { get; set; } + public IEnumerable Data { get; set; } + public int RowsAffected { get; set; } + public long ExecutionTimeMs { get; set; } + public Dictionary Metadata { get; set; } + + public SqlExecutionResult() + { + Metadata = new Dictionary(); + } +} + +/// +/// Service for executing SQL commands +/// +public interface ISqlExecutorService +{ + /// + /// Execute a SELECT query and return results + /// + Task ExecuteQueryAsync( + string sql, + string dataSourceCode, + Dictionary parameters = null); + + /// + /// Execute a non-query command (INSERT, UPDATE, DELETE) + /// + Task ExecuteNonQueryAsync( + string sql, + string dataSourceCode, + Dictionary parameters = null); + + /// + /// Execute scalar query (returns single value) + /// + Task ExecuteScalarAsync( + string sql, + string dataSourceCode, + Dictionary parameters = null); + + /// + /// Deploy stored procedure to database + /// + Task DeployStoredProcedureAsync( + string procedureBody, + string dataSourceCode); + + /// + /// Deploy view to database + /// + Task DeployViewAsync( + string viewDefinition, + string dataSourceCode); + + /// + /// Deploy function to database + /// + Task DeployFunctionAsync( + string functionBody, + string dataSourceCode); + + /// + /// Check if database object exists + /// + Task CheckObjectExistsAsync( + string objectName, + string objectType, + string dataSourceCode, + string schemaName = "dbo"); + + /// + /// Drop database object + /// + Task DropObjectAsync( + string objectName, + string objectType, + string dataSourceCode, + string schemaName = "dbo"); + + /// + /// Validate SQL syntax (basic validation) + /// + Task<(bool IsValid, string ErrorMessage)> ValidateSqlAsync(string sql); +} diff --git a/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain/Services/ISqlTemplateProvider.cs b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain/Services/ISqlTemplateProvider.cs new file mode 100644 index 00000000..0b0c26c8 --- /dev/null +++ b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain/Services/ISqlTemplateProvider.cs @@ -0,0 +1,42 @@ +using System.Collections.Generic; +using Erp.SqlQueryManager.Domain.Shared; + +namespace Erp.SqlQueryManager.Domain.Services; + +/// +/// Provides SQL templates for creating database objects +/// +public interface ISqlTemplateProvider +{ + /// + /// Get stored procedure template + /// + string GetStoredProcedureTemplate(string procedureName, string schemaName = "dbo"); + + /// + /// Get view template + /// + string GetViewTemplate(string viewName, string schemaName = "dbo", bool withSchemaBinding = false); + + /// + /// Get function template + /// + string GetFunctionTemplate(string functionName, SqlFunctionType functionType, string schemaName = "dbo"); + + /// + /// Get query template with common patterns + /// + string GetQueryTemplate(string queryType); + + /// + /// Get available query template types + /// + List GetAvailableQueryTemplates(); +} + +public class QueryTemplateInfo +{ + public string Type { get; set; } + public string Name { get; set; } + public string Description { get; set; } +} diff --git a/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain/Services/SqlExecutorService.cs b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain/Services/SqlExecutorService.cs new file mode 100644 index 00000000..f94baffd --- /dev/null +++ b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain/Services/SqlExecutorService.cs @@ -0,0 +1,301 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using Erp.Platform.DynamicData; +using Erp.Platform.Queries; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp; +using Volo.Abp.Domain.Services; + +namespace Erp.SqlQueryManager.Domain.Services; + +public class SqlExecutorService : DomainService, ISqlExecutorService +{ + private readonly IDataSourceManager _dataSourceManager; + private readonly IServiceProvider _serviceProvider; + + public SqlExecutorService( + IDataSourceManager dataSourceManager, + IServiceProvider serviceProvider) + { + _dataSourceManager = dataSourceManager; + _serviceProvider = serviceProvider; + } + + private async Task GetRepositoryAsync(string dataSourceCode) + { + // Get DataSource to determine database type + var dataSource = await _dataSourceManager.GetDataSourceAsync( + CurrentTenant.IsAvailable, + dataSourceCode); + + if (dataSource == null) + { + throw new BusinessException("SqlQueryManager:DataSourceNotFound") + .WithData("DataSourceCode", dataSourceCode); + } + + // Get appropriate repository based on database type + // For now, using MS SQL Server repository + var repository = _serviceProvider.GetKeyedService("Ms"); + + if (repository == null) + { + throw new BusinessException("SqlQueryManager:RepositoryNotFound") + .WithData("DatabaseType", "Ms"); + } + + return repository; + } + + public async Task ExecuteQueryAsync( + string sql, + string dataSourceCode, + Dictionary parameters = null) + { + var stopwatch = Stopwatch.StartNew(); + var result = new SqlExecutionResult(); + + try + { + var connectionString = await _dataSourceManager.GetConnectionStringAsync( + CurrentTenant.IsAvailable, + dataSourceCode); + + if (string.IsNullOrWhiteSpace(connectionString)) + { + throw new BusinessException("SqlQueryManager:InvalidConnectionString") + .WithData("DataSourceCode", dataSourceCode); + } + + var repository = await GetRepositoryAsync(dataSourceCode); + var data = await repository.QueryAsync(sql, connectionString, parameters); + + stopwatch.Stop(); + + result.Success = true; + result.Data = data; + result.RowsAffected = data?.Count() ?? 0; + result.ExecutionTimeMs = stopwatch.ElapsedMilliseconds; + result.Message = $"Query executed successfully. Rows returned: {result.RowsAffected}"; + } + catch (Exception ex) + { + stopwatch.Stop(); + result.Success = false; + result.Message = $"Query execution failed: {ex.Message}"; + result.ExecutionTimeMs = stopwatch.ElapsedMilliseconds; + result.Metadata["ErrorDetail"] = ex.ToString(); + } + + return result; + } + + public async Task ExecuteNonQueryAsync( + string sql, + string dataSourceCode, + Dictionary parameters = null) + { + var stopwatch = Stopwatch.StartNew(); + var result = new SqlExecutionResult(); + + try + { + var connectionString = await _dataSourceManager.GetConnectionStringAsync( + CurrentTenant.IsAvailable, + dataSourceCode); + + if (string.IsNullOrWhiteSpace(connectionString)) + { + throw new BusinessException("SqlQueryManager:InvalidConnectionString") + .WithData("DataSourceCode", dataSourceCode); + } + + var repository = await GetRepositoryAsync(dataSourceCode); + var rowsAffected = await repository.ExecuteAsync(sql, connectionString, parameters); + + stopwatch.Stop(); + + result.Success = true; + result.RowsAffected = rowsAffected; + result.ExecutionTimeMs = stopwatch.ElapsedMilliseconds; + result.Message = $"Command executed successfully. Rows affected: {rowsAffected}"; + } + catch (Exception ex) + { + stopwatch.Stop(); + result.Success = false; + result.Message = $"Command execution failed: {ex.Message}"; + result.ExecutionTimeMs = stopwatch.ElapsedMilliseconds; + result.Metadata["ErrorDetail"] = ex.ToString(); + } + + return result; + } + + public async Task ExecuteScalarAsync( + string sql, + string dataSourceCode, + Dictionary parameters = null) + { + var stopwatch = Stopwatch.StartNew(); + var result = new SqlExecutionResult(); + + try + { + var connectionString = await _dataSourceManager.GetConnectionStringAsync( + CurrentTenant.IsAvailable, + dataSourceCode); + + if (string.IsNullOrWhiteSpace(connectionString)) + { + throw new BusinessException("SqlQueryManager:InvalidConnectionString") + .WithData("DataSourceCode", dataSourceCode); + } + + var repository = await GetRepositoryAsync(dataSourceCode); + var scalarValue = await repository.ExecuteScalarAsync(sql, connectionString, parameters); + + stopwatch.Stop(); + + result.Success = true; + result.Data = new[] { new { Value = scalarValue } }; + result.ExecutionTimeMs = stopwatch.ElapsedMilliseconds; + result.Message = "Scalar query executed successfully"; + result.Metadata["ScalarValue"] = scalarValue; + } + catch (Exception ex) + { + stopwatch.Stop(); + result.Success = false; + result.Message = $"Scalar query execution failed: {ex.Message}"; + result.ExecutionTimeMs = stopwatch.ElapsedMilliseconds; + result.Metadata["ErrorDetail"] = ex.ToString(); + } + + return result; + } + + public async Task DeployStoredProcedureAsync( + string procedureBody, + string dataSourceCode) + { + return await ExecuteNonQueryAsync(procedureBody, dataSourceCode); + } + + public async Task DeployViewAsync( + string viewDefinition, + string dataSourceCode) + { + return await ExecuteNonQueryAsync(viewDefinition, dataSourceCode); + } + + public async Task DeployFunctionAsync( + string functionBody, + string dataSourceCode) + { + return await ExecuteNonQueryAsync(functionBody, dataSourceCode); + } + + public async Task CheckObjectExistsAsync( + string objectName, + string objectType, + string dataSourceCode, + string schemaName = "dbo") + { + try + { + var sql = $@" + SELECT COUNT(*) + FROM sys.objects o + INNER JOIN sys.schemas s ON o.schema_id = s.schema_id + WHERE s.name = @SchemaName + AND o.name = @ObjectName + AND o.type_desc = @ObjectType"; + + var parameters = new Dictionary + { + { "SchemaName", schemaName }, + { "ObjectName", objectName }, + { "ObjectType", objectType.ToUpperInvariant() } + }; + + var result = await ExecuteScalarAsync(sql, dataSourceCode, parameters); + + return result.Success && result.Metadata.ContainsKey("ScalarValue") + && Convert.ToInt32(result.Metadata["ScalarValue"]) > 0; + } + catch + { + return false; + } + } + + public async Task DropObjectAsync( + string objectName, + string objectType, + string dataSourceCode, + string schemaName = "dbo") + { + var dropCommand = objectType.ToUpperInvariant() switch + { + "SQL_STORED_PROCEDURE" => $"DROP PROCEDURE IF EXISTS [{schemaName}].[{objectName}]", + "VIEW" => $"DROP VIEW IF EXISTS [{schemaName}].[{objectName}]", + "SQL_SCALAR_FUNCTION" => $"DROP FUNCTION IF EXISTS [{schemaName}].[{objectName}]", + "SQL_TABLE_VALUED_FUNCTION" => $"DROP FUNCTION IF EXISTS [{schemaName}].[{objectName}]", + "SQL_INLINE_TABLE_VALUED_FUNCTION" => $"DROP FUNCTION IF EXISTS [{schemaName}].[{objectName}]", + _ => throw new BusinessException("SqlQueryManager:UnsupportedObjectType") + .WithData("ObjectType", objectType) + }; + + return await ExecuteNonQueryAsync(dropCommand, dataSourceCode); + } + + public Task<(bool IsValid, string ErrorMessage)> ValidateSqlAsync(string sql) + { + try + { + if (string.IsNullOrWhiteSpace(sql)) + { + return Task.FromResult((false, "SQL query is empty")); + } + + // Basic validation - check for dangerous keywords + var dangerousPatterns = new[] + { + @"\bDROP\s+DATABASE\b", + @"\bDROP\s+SCHEMA\b", + @"\bTRUNCATE\s+TABLE\b", + @"\bALTER\s+DATABASE\b", + @"\bSHUTDOWN\b", + @"\bxp_cmdshell\b" + }; + + foreach (var pattern in dangerousPatterns) + { + if (Regex.IsMatch(sql, pattern, RegexOptions.IgnoreCase)) + { + return Task.FromResult((false, $"SQL contains potentially dangerous command: {pattern}")); + } + } + + // Check for balanced parentheses + var openCount = sql.Count(c => c == '('); + var closeCount = sql.Count(c => c == ')'); + + if (openCount != closeCount) + { + return Task.FromResult((false, "Unbalanced parentheses in SQL")); + } + + return Task.FromResult((true, string.Empty)); + } + catch (Exception ex) + { + return Task.FromResult((false, $"Validation error: {ex.Message}")); + } + } +} diff --git a/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain/Services/SqlTemplateProvider.cs b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain/Services/SqlTemplateProvider.cs new file mode 100644 index 00000000..75ca9384 --- /dev/null +++ b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain/Services/SqlTemplateProvider.cs @@ -0,0 +1,395 @@ +using System.Collections.Generic; +using System.Text; +using Erp.SqlQueryManager.Domain.Shared; +using Volo.Abp.DependencyInjection; + +namespace Erp.SqlQueryManager.Domain.Services; + +public class SqlTemplateProvider : ISqlTemplateProvider, ITransientDependency +{ + public string GetStoredProcedureTemplate(string procedureName, string schemaName = "dbo") + { + return $@"-- ============================================= +-- Author: +-- Create date: +-- Description: +-- ============================================= +CREATE OR ALTER PROCEDURE [{schemaName}].[{procedureName}] + -- Add parameters here + @Parameter1 INT = NULL, + @Parameter2 NVARCHAR(100) = NULL +AS +BEGIN + -- SET NOCOUNT ON added to prevent extra result sets from + -- interfering with SELECT statements. + SET NOCOUNT ON; + + -- Insert statements for procedure here + SELECT + @Parameter1 AS Parameter1, + @Parameter2 AS Parameter2 + + -- Example error handling + BEGIN TRY + -- Your logic here + + END TRY + BEGIN CATCH + -- Error handling + DECLARE @ErrorMessage NVARCHAR(4000) = ERROR_MESSAGE(); + DECLARE @ErrorSeverity INT = ERROR_SEVERITY(); + DECLARE @ErrorState INT = ERROR_STATE(); + + RAISERROR(@ErrorMessage, @ErrorSeverity, @ErrorState); + END CATCH +END +GO"; + } + + public string GetViewTemplate(string viewName, string schemaName = "dbo", bool withSchemaBinding = false) + { + var schemaBindingClause = withSchemaBinding ? " WITH SCHEMABINDING" : ""; + + return $@"-- ============================================= +-- Author: +-- Create date: +-- Description: +-- ============================================= +CREATE OR ALTER VIEW [{schemaName}].[{viewName}]{schemaBindingClause} +AS + -- Define your SELECT statement here + SELECT + Column1, + Column2, + Column3 + FROM + TableName + WHERE + -- Add your conditions + 1 = 1 +GO"; + } + + public string GetFunctionTemplate(string functionName, SqlFunctionType functionType, string schemaName = "dbo") + { + return functionType switch + { + SqlFunctionType.ScalarFunction => GetScalarFunctionTemplate(functionName, schemaName), + SqlFunctionType.TableValuedFunction => GetTableValuedFunctionTemplate(functionName, schemaName), + SqlFunctionType.InlineTableValuedFunction => GetInlineTableValuedFunctionTemplate(functionName, schemaName), + _ => GetScalarFunctionTemplate(functionName, schemaName) + }; + } + + private string GetScalarFunctionTemplate(string functionName, string schemaName) + { + return $@"-- ============================================= +-- Author: +-- Create date: +-- Description: Scalar function that returns a single value +-- ============================================= +CREATE OR ALTER FUNCTION [{schemaName}].[{functionName}] +( + -- Add parameters here + @Parameter1 INT, + @Parameter2 NVARCHAR(100) +) +RETURNS INT +AS +BEGIN + -- Declare the return variable here + DECLARE @Result INT; + + -- Add your logic here + SELECT @Result = COUNT(*) + FROM YourTable + WHERE Column1 = @Parameter1; + + -- Return the result + RETURN @Result; +END +GO"; + } + + private string GetTableValuedFunctionTemplate(string functionName, string schemaName) + { + return $@"-- ============================================= +-- Author: +-- Create date: +-- Description: Multi-statement table-valued function +-- ============================================= +CREATE OR ALTER FUNCTION [{schemaName}].[{functionName}] +( + -- Add parameters here + @Parameter1 INT, + @Parameter2 NVARCHAR(100) +) +RETURNS @ResultTable TABLE +( + -- Define the structure of the result table + Id INT, + Name NVARCHAR(100), + Value DECIMAL(18,2) +) +AS +BEGIN + -- Fill the table variable with data + INSERT INTO @ResultTable (Id, Name, Value) + SELECT + Id, + Name, + Value + FROM + YourTable + WHERE + Column1 = @Parameter1; + + -- Return + RETURN; +END +GO"; + } + + private string GetInlineTableValuedFunctionTemplate(string functionName, string schemaName) + { + return $@"-- ============================================= +-- Author: +-- Create date: +-- Description: Inline table-valued function +-- ============================================= +CREATE OR ALTER FUNCTION [{schemaName}].[{functionName}] +( + -- Add parameters here + @Parameter1 INT, + @Parameter2 NVARCHAR(100) +) +RETURNS TABLE +AS +RETURN +( + -- Add your SELECT statement here + SELECT + Id, + Name, + Value, + CreatedDate + FROM + YourTable + WHERE + Column1 = @Parameter1 + AND Column2 LIKE '%' + @Parameter2 + '%' +) +GO"; + } + + public string GetQueryTemplate(string queryType) + { + return queryType?.ToUpperInvariant() switch + { + "SELECT" => GetSelectTemplate(), + "INSERT" => GetInsertTemplate(), + "UPDATE" => GetUpdateTemplate(), + "DELETE" => GetDeleteTemplate(), + "JOIN" => GetJoinTemplate(), + "CTE" => GetCteTemplate(), + "PIVOT" => GetPivotTemplate(), + "TRANSACTION" => GetTransactionTemplate(), + _ => GetSelectTemplate() + }; + } + + private string GetSelectTemplate() + { + return @"-- Basic SELECT query +SELECT + Column1, + Column2, + Column3 +FROM + TableName +WHERE + -- Add your conditions + Column1 = 'value' + AND IsActive = 1 +ORDER BY + Column1 ASC;"; + } + + private string GetInsertTemplate() + { + return @"-- INSERT statement +INSERT INTO TableName +( + Column1, + Column2, + Column3, + CreatedDate +) +VALUES +( + 'Value1', + 'Value2', + 123, + GETDATE() +); + +-- Or with SELECT +INSERT INTO TableName (Column1, Column2) +SELECT + Column1, + Column2 +FROM + SourceTable +WHERE + Condition = 1;"; + } + + private string GetUpdateTemplate() + { + return @"-- UPDATE statement +UPDATE TableName +SET + Column1 = 'NewValue', + Column2 = 'NewValue2', + UpdatedDate = GETDATE() +WHERE + Id = 123 + AND IsActive = 1;"; + } + + private string GetDeleteTemplate() + { + return @"-- DELETE statement +DELETE FROM TableName +WHERE + Id = 123 + AND IsActive = 0; + +-- Soft delete pattern +UPDATE TableName +SET + IsDeleted = 1, + DeletedDate = GETDATE(), + DeletedBy = 'UserName' +WHERE + Id = 123;"; + } + + private string GetJoinTemplate() + { + return @"-- JOIN query example +SELECT + t1.Column1, + t1.Column2, + t2.Column3, + t3.Column4 +FROM + Table1 t1 + INNER JOIN Table2 t2 ON t1.Id = t2.Table1Id + LEFT JOIN Table3 t3 ON t2.Id = t3.Table2Id +WHERE + t1.IsActive = 1 + AND t2.Status = 'Active' +ORDER BY + t1.Column1;"; + } + + private string GetCteTemplate() + { + return @"-- Common Table Expression (CTE) example +WITH CTE_Name AS +( + SELECT + Column1, + Column2, + COUNT(*) as RecordCount + FROM + TableName + WHERE + IsActive = 1 + GROUP BY + Column1, Column2 +) +SELECT + * +FROM + CTE_Name +WHERE + RecordCount > 5 +ORDER BY + RecordCount DESC;"; + } + + private string GetPivotTemplate() + { + return @"-- PIVOT example +SELECT + * +FROM +( + SELECT + Category, + Year, + Amount + FROM + Sales +) AS SourceTable +PIVOT +( + SUM(Amount) + FOR Year IN ([2021], [2022], [2023], [2024]) +) AS PivotTable +ORDER BY + Category;"; + } + + private string GetTransactionTemplate() + { + return @"-- Transaction example +BEGIN TRANSACTION; + +BEGIN TRY + -- Your SQL statements here + UPDATE Table1 + SET Column1 = 'Value' + WHERE Id = 1; + + INSERT INTO Table2 (Column1, Column2) + VALUES ('Value1', 'Value2'); + + DELETE FROM Table3 + WHERE Id = 123; + + -- Commit if all successful + COMMIT TRANSACTION; +END TRY +BEGIN CATCH + -- Rollback on error + ROLLBACK TRANSACTION; + + -- Return error information + SELECT + ERROR_NUMBER() AS ErrorNumber, + ERROR_SEVERITY() AS ErrorSeverity, + ERROR_STATE() AS ErrorState, + ERROR_PROCEDURE() AS ErrorProcedure, + ERROR_LINE() AS ErrorLine, + ERROR_MESSAGE() AS ErrorMessage; +END CATCH;"; + } + + public List GetAvailableQueryTemplates() + { + return new List + { + new() { Type = "SELECT", Name = "Basic Select", Description = "Simple SELECT query with WHERE and ORDER BY" }, + new() { Type = "INSERT", Name = "Insert Data", Description = "INSERT statement with VALUES or SELECT" }, + new() { Type = "UPDATE", Name = "Update Data", Description = "UPDATE statement with conditions" }, + new() { Type = "DELETE", Name = "Delete Data", Description = "DELETE statement with soft delete example" }, + new() { Type = "JOIN", Name = "Join Tables", Description = "Query with INNER and LEFT JOINs" }, + new() { Type = "CTE", Name = "Common Table Expression", Description = "CTE (WITH clause) example" }, + new() { Type = "PIVOT", Name = "Pivot Table", Description = "PIVOT query for data transformation" }, + new() { Type = "TRANSACTION", Name = "Transaction", Description = "Transaction with error handling" } + }; + } +} diff --git a/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain/SqlQueryManagerDbProperties.cs b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain/SqlQueryManagerDbProperties.cs new file mode 100644 index 00000000..b50bf12a --- /dev/null +++ b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain/SqlQueryManagerDbProperties.cs @@ -0,0 +1,19 @@ +namespace Erp.SqlQueryManager; + +public static class Prefix +{ + public static string MenuPrefix { get; set; } = "Sqm"; + public static string HostPrefix { get; set; } = "T"; + public static string? DbSchema { get; set; } = null; + + public const string ConnectionStringName = "SqlQueryManager"; +} + +public static class TablePrefix +{ + public static string ByName(string tableName) + { + return $"{Prefix.MenuPrefix}_{Prefix.HostPrefix}_{tableName}"; + } +} + diff --git a/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain/SqlQueryManagerDomainModule.cs b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain/SqlQueryManagerDomainModule.cs new file mode 100644 index 00000000..e00c5838 --- /dev/null +++ b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.Domain/SqlQueryManagerDomainModule.cs @@ -0,0 +1,17 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Domain; +using Volo.Abp.Modularity; + +namespace Erp.SqlQueryManager; + +[DependsOn( + typeof(AbpDddDomainModule), + typeof(SqlQueryManagerDomainSharedModule) +)] +public class SqlQueryManagerDomainModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + // Domain services configuration can be added here + } +} diff --git a/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.EntityFrameworkCore/Erp.SqlQueryManager.EntityFrameworkCore.csproj b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.EntityFrameworkCore/Erp.SqlQueryManager.EntityFrameworkCore.csproj new file mode 100644 index 00000000..e129f51e --- /dev/null +++ b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.EntityFrameworkCore/Erp.SqlQueryManager.EntityFrameworkCore.csproj @@ -0,0 +1,15 @@ + + + + + + net9.0 + Erp.SqlQueryManager + + + + + + + + diff --git a/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.EntityFrameworkCore/FodyWeavers.xml b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.EntityFrameworkCore/FodyWeavers.xml new file mode 100644 index 00000000..00e1d9a1 --- /dev/null +++ b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.EntityFrameworkCore/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.EntityFrameworkCore/FodyWeavers.xsd b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.EntityFrameworkCore/FodyWeavers.xsd new file mode 100644 index 00000000..3f3946e2 --- /dev/null +++ b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.EntityFrameworkCore/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.EntityFrameworkCore/SqlQueryManagerDbContext.cs b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.EntityFrameworkCore/SqlQueryManagerDbContext.cs new file mode 100644 index 00000000..07e903ec --- /dev/null +++ b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.EntityFrameworkCore/SqlQueryManagerDbContext.cs @@ -0,0 +1,27 @@ +using Erp.SqlQueryManager.Domain.Entities; +using Microsoft.EntityFrameworkCore; +using Volo.Abp.Data; +using Volo.Abp.EntityFrameworkCore; + +namespace Erp.SqlQueryManager.EntityFrameworkCore; + +[ConnectionStringName("Default")] +public class SqlQueryManagerDbContext : AbpDbContext +{ + public DbSet SqlQueries { get; set; } + public DbSet SqlStoredProcedures { get; set; } + public DbSet SqlViews { get; set; } + public DbSet SqlFunctions { get; set; } + + public SqlQueryManagerDbContext(DbContextOptions options) + : base(options) + { + } + + protected override void OnModelCreating(ModelBuilder builder) + { + base.OnModelCreating(builder); + + builder.ConfigureSqlQueryManager(); + } +} diff --git a/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.EntityFrameworkCore/SqlQueryManagerDbContextModelCreatingExtensions.cs b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.EntityFrameworkCore/SqlQueryManagerDbContextModelCreatingExtensions.cs new file mode 100644 index 00000000..938f1be2 --- /dev/null +++ b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.EntityFrameworkCore/SqlQueryManagerDbContextModelCreatingExtensions.cs @@ -0,0 +1,104 @@ +using Erp.SqlQueryManager.Domain.Entities; +using Microsoft.EntityFrameworkCore; +using Volo.Abp; +using Volo.Abp.EntityFrameworkCore.Modeling; + +namespace Erp.SqlQueryManager.EntityFrameworkCore; + +public static class SqlQueryManagerDbContextModelCreatingExtensions +{ + public static void ConfigureSqlQueryManager(this ModelBuilder builder) + { + Check.NotNull(builder, nameof(builder)); + + // SqlQuery + builder.Entity(b => + { + b.ToTable(TablePrefix.ByName(nameof(SqlQuery)), Prefix.DbSchema); + b.ConfigureByConvention(); + + b.Property(x => x.Code).IsRequired().HasMaxLength(128); + b.Property(x => x.Name).IsRequired().HasMaxLength(256); + b.Property(x => x.Description).HasMaxLength(1000); + b.Property(x => x.QueryText).IsRequired(); + b.Property(x => x.DataSourceCode).IsRequired().HasMaxLength(128); + b.Property(x => x.Status).IsRequired(); + b.Property(x => x.Category).HasMaxLength(128); + b.Property(x => x.Tags).HasMaxLength(500); + b.Property(x => x.Parameters).HasMaxLength(4000); + + b.HasIndex(x => x.Code); + b.HasIndex(x => x.DataSourceCode); + b.HasIndex(x => x.Status); + b.HasIndex(x => x.Category); + }); + + // SqlStoredProcedure + builder.Entity(b => + { + b.ToTable(TablePrefix.ByName(nameof(SqlStoredProcedure)), Prefix.DbSchema); + b.ConfigureByConvention(); + + b.Property(x => x.ProcedureName).IsRequired().HasMaxLength(128); + b.Property(x => x.SchemaName).IsRequired().HasMaxLength(128); + b.Property(x => x.DisplayName).IsRequired().HasMaxLength(256); + b.Property(x => x.Description).HasMaxLength(1000); + b.Property(x => x.ProcedureBody).IsRequired(); + b.Property(x => x.DataSourceCode).IsRequired().HasMaxLength(128); + b.Property(x => x.Status).IsRequired(); + b.Property(x => x.Category).HasMaxLength(128); + b.Property(x => x.Parameters).HasMaxLength(4000); + + b.HasIndex(x => new { x.SchemaName, x.ProcedureName }); + b.HasIndex(x => x.DataSourceCode); + b.HasIndex(x => x.Status); + b.HasIndex(x => x.IsDeployed); + }); + + // SqlView + builder.Entity(b => + { + b.ToTable(TablePrefix.ByName(nameof(SqlView)), Prefix.DbSchema); + b.ConfigureByConvention(); + + b.Property(x => x.ViewName).IsRequired().HasMaxLength(128); + b.Property(x => x.SchemaName).IsRequired().HasMaxLength(128); + b.Property(x => x.DisplayName).IsRequired().HasMaxLength(256); + b.Property(x => x.Description).HasMaxLength(1000); + b.Property(x => x.ViewDefinition).IsRequired(); + b.Property(x => x.DataSourceCode).IsRequired().HasMaxLength(128); + b.Property(x => x.Status).IsRequired(); + b.Property(x => x.Category).HasMaxLength(128); + + b.HasIndex(x => new { x.SchemaName, x.ViewName }); + b.HasIndex(x => x.DataSourceCode); + b.HasIndex(x => x.Status); + b.HasIndex(x => x.IsDeployed); + }); + + // SqlFunction + builder.Entity(b => + { + b.ToTable(TablePrefix.ByName(nameof(SqlFunction)), Prefix.DbSchema); + b.ConfigureByConvention(); + + b.Property(x => x.FunctionName).IsRequired().HasMaxLength(128); + b.Property(x => x.SchemaName).IsRequired().HasMaxLength(128); + b.Property(x => x.DisplayName).IsRequired().HasMaxLength(256); + b.Property(x => x.Description).HasMaxLength(1000); + b.Property(x => x.FunctionType).IsRequired(); + b.Property(x => x.FunctionBody).IsRequired(); + b.Property(x => x.ReturnType).IsRequired().HasMaxLength(256); + b.Property(x => x.DataSourceCode).IsRequired().HasMaxLength(128); + b.Property(x => x.Status).IsRequired(); + b.Property(x => x.Category).HasMaxLength(128); + b.Property(x => x.Parameters).HasMaxLength(4000); + + b.HasIndex(x => new { x.SchemaName, x.FunctionName }); + b.HasIndex(x => x.DataSourceCode); + b.HasIndex(x => x.Status); + b.HasIndex(x => x.FunctionType); + b.HasIndex(x => x.IsDeployed); + }); + } +} diff --git a/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.EntityFrameworkCore/SqlQueryManagerEntityFrameworkCoreModule.cs b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.EntityFrameworkCore/SqlQueryManagerEntityFrameworkCoreModule.cs new file mode 100644 index 00000000..cc9c66ee --- /dev/null +++ b/api/modules/Erp.SqlQueryManager/Erp.SqlQueryManager.EntityFrameworkCore/SqlQueryManagerEntityFrameworkCoreModule.cs @@ -0,0 +1,20 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.Modularity; + +namespace Erp.SqlQueryManager.EntityFrameworkCore; + +[DependsOn( + typeof(SqlQueryManagerDomainModule), + typeof(AbpEntityFrameworkCoreModule) +)] +public class SqlQueryManagerEntityFrameworkCoreModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddAbpDbContext(options => + { + options.AddDefaultRepositories(includeAllEntities: true); + }); + } +} diff --git a/api/modules/Erp.SqlQueryManager/common.props b/api/modules/Erp.SqlQueryManager/common.props new file mode 100644 index 00000000..87cf88dc --- /dev/null +++ b/api/modules/Erp.SqlQueryManager/common.props @@ -0,0 +1,24 @@ + + + latest + 0.1.0 + $(NoWarn);CS1591 + module + + + + + + All + runtime; build; native; contentfiles; analyzers + + + + + + + $(NoWarn);0436 + + + + \ No newline at end of file diff --git a/api/src/Erp.Platform.EntityFrameworkCore/EntityFrameworkCore/PlatformDbContext.cs b/api/src/Erp.Platform.EntityFrameworkCore/EntityFrameworkCore/PlatformDbContext.cs index 46fb4c9f..980bed66 100644 --- a/api/src/Erp.Platform.EntityFrameworkCore/EntityFrameworkCore/PlatformDbContext.cs +++ b/api/src/Erp.Platform.EntityFrameworkCore/EntityFrameworkCore/PlatformDbContext.cs @@ -26,6 +26,7 @@ using System.Text.Json; using static Erp.Platform.PlatformConsts; using static Erp.Settings.SettingsConsts; using Erp.Platform.Enums; +using Erp.SqlQueryManager.EntityFrameworkCore; namespace Erp.Platform.EntityFrameworkCore; @@ -333,6 +334,7 @@ public class PlatformDbContext : builder.ConfigureSettings(); builder.ConfigureMailQueue(); builder.ConfigureNotification(); + builder.ConfigureSqlQueryManager(); //Saas builder.Entity(b => diff --git a/api/src/Erp.Platform.EntityFrameworkCore/EntityFrameworkCore/PlatformEntityFrameworkCoreModule.cs b/api/src/Erp.Platform.EntityFrameworkCore/EntityFrameworkCore/PlatformEntityFrameworkCoreModule.cs index 95c31d6d..725e2d7c 100644 --- a/api/src/Erp.Platform.EntityFrameworkCore/EntityFrameworkCore/PlatformEntityFrameworkCoreModule.cs +++ b/api/src/Erp.Platform.EntityFrameworkCore/EntityFrameworkCore/PlatformEntityFrameworkCoreModule.cs @@ -2,6 +2,7 @@ using Erp.Languages.EntityFrameworkCore; using Erp.Notifications.EntityFrameworkCore; using Erp.Settings.EntityFrameworkCore; +using Erp.SqlQueryManager.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.AuditLogging.EntityFrameworkCore; using Volo.Abp.BackgroundJobs.EntityFrameworkCore; @@ -34,7 +35,8 @@ namespace Erp.Platform.EntityFrameworkCore; typeof(AbpFeatureManagementEntityFrameworkCoreModule), typeof(LanguagesEntityFrameworkCoreModule), typeof(SettingsEntityFrameworkCoreModule), - typeof(NotificationEntityFrameworkCoreModule) + typeof(NotificationEntityFrameworkCoreModule), + typeof(SqlQueryManagerEntityFrameworkCoreModule) )] public class PlatformEntityFrameworkCoreModule : AbpModule { diff --git a/api/src/Erp.Platform.EntityFrameworkCore/Erp.Platform.EntityFrameworkCore.csproj b/api/src/Erp.Platform.EntityFrameworkCore/Erp.Platform.EntityFrameworkCore.csproj index a0e2e39a..36f011bf 100644 --- a/api/src/Erp.Platform.EntityFrameworkCore/Erp.Platform.EntityFrameworkCore.csproj +++ b/api/src/Erp.Platform.EntityFrameworkCore/Erp.Platform.EntityFrameworkCore.csproj @@ -28,6 +28,7 @@ + diff --git a/api/src/Erp.Platform.EntityFrameworkCore/Migrations/20251204192908_Initial.Designer.cs b/api/src/Erp.Platform.EntityFrameworkCore/Migrations/20251205085044_Initial.Designer.cs similarity index 98% rename from api/src/Erp.Platform.EntityFrameworkCore/Migrations/20251204192908_Initial.Designer.cs rename to api/src/Erp.Platform.EntityFrameworkCore/Migrations/20251205085044_Initial.Designer.cs index c5db0512..3fae3871 100644 --- a/api/src/Erp.Platform.EntityFrameworkCore/Migrations/20251204192908_Initial.Designer.cs +++ b/api/src/Erp.Platform.EntityFrameworkCore/Migrations/20251205085044_Initial.Designer.cs @@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore; namespace Erp.Platform.Migrations { [DbContext(typeof(PlatformDbContext))] - [Migration("20251204192908_Initial")] + [Migration("20251205085044_Initial")] partial class Initial { /// @@ -16250,6 +16250,409 @@ namespace Erp.Platform.Migrations b.ToTable("Plat_H_SettingDefinition", (string)null); }); + modelBuilder.Entity("Erp.SqlQueryManager.Domain.Entities.SqlFunction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Category") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DataSourceCode") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("FunctionBody") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("FunctionName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("FunctionType") + .HasColumnType("int"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsDeployed") + .HasColumnType("bit"); + + b.Property("LastDeployedAt") + .HasColumnType("datetime2"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Parameters") + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("ReturnType") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("SchemaName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("DataSourceCode"); + + b.HasIndex("FunctionType"); + + b.HasIndex("IsDeployed"); + + b.HasIndex("Status"); + + b.HasIndex("SchemaName", "FunctionName"); + + b.ToTable("Sqm_T_SqlFunction", (string)null); + }); + + modelBuilder.Entity("Erp.SqlQueryManager.Domain.Entities.SqlQuery", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Category") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DataSourceCode") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("ExecutionCount") + .HasColumnType("int"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsModifyingData") + .HasColumnType("bit"); + + b.Property("LastExecutedAt") + .HasColumnType("datetime2"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("Parameters") + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("QueryText") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("Tags") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("Category"); + + b.HasIndex("Code"); + + b.HasIndex("DataSourceCode"); + + b.HasIndex("Status"); + + b.ToTable("Sqm_T_SqlQuery", (string)null); + }); + + modelBuilder.Entity("Erp.SqlQueryManager.Domain.Entities.SqlStoredProcedure", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Category") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DataSourceCode") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsDeployed") + .HasColumnType("bit"); + + b.Property("LastDeployedAt") + .HasColumnType("datetime2"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Parameters") + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("ProcedureBody") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ProcedureName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("SchemaName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("DataSourceCode"); + + b.HasIndex("IsDeployed"); + + b.HasIndex("Status"); + + b.HasIndex("SchemaName", "ProcedureName"); + + b.ToTable("Sqm_T_SqlStoredProcedure", (string)null); + }); + + modelBuilder.Entity("Erp.SqlQueryManager.Domain.Entities.SqlView", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Category") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DataSourceCode") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsDeployed") + .HasColumnType("bit"); + + b.Property("LastDeployedAt") + .HasColumnType("datetime2"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("SchemaName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("ViewDefinition") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ViewName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("WithSchemaBinding") + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.HasIndex("DataSourceCode"); + + b.HasIndex("IsDeployed"); + + b.HasIndex("Status"); + + b.HasIndex("SchemaName", "ViewName"); + + b.ToTable("Sqm_T_SqlView", (string)null); + }); + modelBuilder.Entity("MaterialUom", b => { b.Property("AlternativeUomsId") diff --git a/api/src/Erp.Platform.EntityFrameworkCore/Migrations/20251204192908_Initial.cs b/api/src/Erp.Platform.EntityFrameworkCore/Migrations/20251205085044_Initial.cs similarity index 97% rename from api/src/Erp.Platform.EntityFrameworkCore/Migrations/20251204192908_Initial.cs rename to api/src/Erp.Platform.EntityFrameworkCore/Migrations/20251205085044_Initial.cs index 91b7cbd0..8a3673a2 100644 --- a/api/src/Erp.Platform.EntityFrameworkCore/Migrations/20251204192908_Initial.cs +++ b/api/src/Erp.Platform.EntityFrameworkCore/Migrations/20251205085044_Initial.cs @@ -2864,6 +2864,129 @@ namespace Erp.Platform.Migrations table.PrimaryKey("PK_Scp_T_SupplyType", x => x.Id); }); + migrationBuilder.CreateTable( + name: "Sqm_T_SqlFunction", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + TenantId = table.Column(type: "uniqueidentifier", nullable: true), + FunctionName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + SchemaName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + DisplayName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), + Description = table.Column(type: "nvarchar(1000)", maxLength: 1000, nullable: true), + FunctionType = table.Column(type: "int", nullable: false), + FunctionBody = table.Column(type: "nvarchar(max)", nullable: false), + ReturnType = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), + DataSourceCode = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + Status = table.Column(type: "int", nullable: false), + Category = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: true), + IsDeployed = table.Column(type: "bit", nullable: false), + LastDeployedAt = table.Column(type: "datetime2", nullable: true), + Parameters = table.Column(type: "nvarchar(4000)", maxLength: 4000, nullable: true), + 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_Sqm_T_SqlFunction", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Sqm_T_SqlQuery", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + TenantId = table.Column(type: "uniqueidentifier", nullable: true), + Code = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + Name = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), + Description = table.Column(type: "nvarchar(1000)", maxLength: 1000, nullable: true), + QueryText = table.Column(type: "nvarchar(max)", nullable: false), + DataSourceCode = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + Status = table.Column(type: "int", nullable: false), + Category = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: true), + Tags = table.Column(type: "nvarchar(500)", maxLength: 500, nullable: true), + LastExecutedAt = table.Column(type: "datetime2", nullable: true), + ExecutionCount = table.Column(type: "int", nullable: false), + IsModifyingData = table.Column(type: "bit", nullable: false), + Parameters = table.Column(type: "nvarchar(4000)", maxLength: 4000, nullable: true), + 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_Sqm_T_SqlQuery", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Sqm_T_SqlStoredProcedure", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + TenantId = table.Column(type: "uniqueidentifier", nullable: true), + ProcedureName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + SchemaName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + DisplayName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), + Description = table.Column(type: "nvarchar(1000)", maxLength: 1000, nullable: true), + ProcedureBody = table.Column(type: "nvarchar(max)", nullable: false), + DataSourceCode = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + Status = table.Column(type: "int", nullable: false), + Category = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: true), + IsDeployed = table.Column(type: "bit", nullable: false), + LastDeployedAt = table.Column(type: "datetime2", nullable: true), + Parameters = table.Column(type: "nvarchar(4000)", maxLength: 4000, nullable: true), + 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_Sqm_T_SqlStoredProcedure", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Sqm_T_SqlView", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + TenantId = table.Column(type: "uniqueidentifier", nullable: true), + ViewName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + SchemaName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + DisplayName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), + Description = table.Column(type: "nvarchar(1000)", maxLength: 1000, nullable: true), + ViewDefinition = table.Column(type: "nvarchar(max)", nullable: false), + DataSourceCode = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + Status = table.Column(type: "int", nullable: false), + Category = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: true), + IsDeployed = table.Column(type: "bit", nullable: false), + LastDeployedAt = table.Column(type: "datetime2", nullable: true), + WithSchemaBinding = 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_Sqm_T_SqlView", x => x.Id); + }); + migrationBuilder.CreateTable( name: "Str_T_Inventory", columns: table => new @@ -9041,6 +9164,91 @@ namespace Erp.Platform.Migrations table: "Scp_T_RequestItem", column: "RequestId"); + migrationBuilder.CreateIndex( + name: "IX_Sqm_T_SqlFunction_DataSourceCode", + table: "Sqm_T_SqlFunction", + column: "DataSourceCode"); + + migrationBuilder.CreateIndex( + name: "IX_Sqm_T_SqlFunction_FunctionType", + table: "Sqm_T_SqlFunction", + column: "FunctionType"); + + migrationBuilder.CreateIndex( + name: "IX_Sqm_T_SqlFunction_IsDeployed", + table: "Sqm_T_SqlFunction", + column: "IsDeployed"); + + migrationBuilder.CreateIndex( + name: "IX_Sqm_T_SqlFunction_SchemaName_FunctionName", + table: "Sqm_T_SqlFunction", + columns: new[] { "SchemaName", "FunctionName" }); + + migrationBuilder.CreateIndex( + name: "IX_Sqm_T_SqlFunction_Status", + table: "Sqm_T_SqlFunction", + column: "Status"); + + migrationBuilder.CreateIndex( + name: "IX_Sqm_T_SqlQuery_Category", + table: "Sqm_T_SqlQuery", + column: "Category"); + + migrationBuilder.CreateIndex( + name: "IX_Sqm_T_SqlQuery_Code", + table: "Sqm_T_SqlQuery", + column: "Code"); + + migrationBuilder.CreateIndex( + name: "IX_Sqm_T_SqlQuery_DataSourceCode", + table: "Sqm_T_SqlQuery", + column: "DataSourceCode"); + + migrationBuilder.CreateIndex( + name: "IX_Sqm_T_SqlQuery_Status", + table: "Sqm_T_SqlQuery", + column: "Status"); + + migrationBuilder.CreateIndex( + name: "IX_Sqm_T_SqlStoredProcedure_DataSourceCode", + table: "Sqm_T_SqlStoredProcedure", + column: "DataSourceCode"); + + migrationBuilder.CreateIndex( + name: "IX_Sqm_T_SqlStoredProcedure_IsDeployed", + table: "Sqm_T_SqlStoredProcedure", + column: "IsDeployed"); + + migrationBuilder.CreateIndex( + name: "IX_Sqm_T_SqlStoredProcedure_SchemaName_ProcedureName", + table: "Sqm_T_SqlStoredProcedure", + columns: new[] { "SchemaName", "ProcedureName" }); + + migrationBuilder.CreateIndex( + name: "IX_Sqm_T_SqlStoredProcedure_Status", + table: "Sqm_T_SqlStoredProcedure", + column: "Status"); + + migrationBuilder.CreateIndex( + name: "IX_Sqm_T_SqlView_DataSourceCode", + table: "Sqm_T_SqlView", + column: "DataSourceCode"); + + migrationBuilder.CreateIndex( + name: "IX_Sqm_T_SqlView_IsDeployed", + table: "Sqm_T_SqlView", + column: "IsDeployed"); + + migrationBuilder.CreateIndex( + name: "IX_Sqm_T_SqlView_SchemaName_ViewName", + table: "Sqm_T_SqlView", + columns: new[] { "SchemaName", "ViewName" }); + + migrationBuilder.CreateIndex( + name: "IX_Sqm_T_SqlView_Status", + table: "Sqm_T_SqlView", + column: "Status"); + migrationBuilder.CreateIndex( name: "IX_Str_T_Location_LocationTypeId", table: "Str_T_Location", @@ -9632,6 +9840,18 @@ namespace Erp.Platform.Migrations migrationBuilder.DropTable( name: "Scp_T_RequestItem"); + migrationBuilder.DropTable( + name: "Sqm_T_SqlFunction"); + + migrationBuilder.DropTable( + name: "Sqm_T_SqlQuery"); + + migrationBuilder.DropTable( + name: "Sqm_T_SqlStoredProcedure"); + + migrationBuilder.DropTable( + name: "Sqm_T_SqlView"); + migrationBuilder.DropTable( name: "Str_T_Inventory"); diff --git a/api/src/Erp.Platform.EntityFrameworkCore/Migrations/PlatformDbContextModelSnapshot.cs b/api/src/Erp.Platform.EntityFrameworkCore/Migrations/PlatformDbContextModelSnapshot.cs index 163d0c74..f5efc0e3 100644 --- a/api/src/Erp.Platform.EntityFrameworkCore/Migrations/PlatformDbContextModelSnapshot.cs +++ b/api/src/Erp.Platform.EntityFrameworkCore/Migrations/PlatformDbContextModelSnapshot.cs @@ -16247,6 +16247,409 @@ namespace Erp.Platform.Migrations b.ToTable("Plat_H_SettingDefinition", (string)null); }); + modelBuilder.Entity("Erp.SqlQueryManager.Domain.Entities.SqlFunction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Category") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DataSourceCode") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("FunctionBody") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("FunctionName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("FunctionType") + .HasColumnType("int"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsDeployed") + .HasColumnType("bit"); + + b.Property("LastDeployedAt") + .HasColumnType("datetime2"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Parameters") + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("ReturnType") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("SchemaName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("DataSourceCode"); + + b.HasIndex("FunctionType"); + + b.HasIndex("IsDeployed"); + + b.HasIndex("Status"); + + b.HasIndex("SchemaName", "FunctionName"); + + b.ToTable("Sqm_T_SqlFunction", (string)null); + }); + + modelBuilder.Entity("Erp.SqlQueryManager.Domain.Entities.SqlQuery", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Category") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DataSourceCode") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("ExecutionCount") + .HasColumnType("int"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsModifyingData") + .HasColumnType("bit"); + + b.Property("LastExecutedAt") + .HasColumnType("datetime2"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("Parameters") + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("QueryText") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("Tags") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("Category"); + + b.HasIndex("Code"); + + b.HasIndex("DataSourceCode"); + + b.HasIndex("Status"); + + b.ToTable("Sqm_T_SqlQuery", (string)null); + }); + + modelBuilder.Entity("Erp.SqlQueryManager.Domain.Entities.SqlStoredProcedure", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Category") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DataSourceCode") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsDeployed") + .HasColumnType("bit"); + + b.Property("LastDeployedAt") + .HasColumnType("datetime2"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Parameters") + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("ProcedureBody") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ProcedureName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("SchemaName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("DataSourceCode"); + + b.HasIndex("IsDeployed"); + + b.HasIndex("Status"); + + b.HasIndex("SchemaName", "ProcedureName"); + + b.ToTable("Sqm_T_SqlStoredProcedure", (string)null); + }); + + modelBuilder.Entity("Erp.SqlQueryManager.Domain.Entities.SqlView", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Category") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DataSourceCode") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsDeployed") + .HasColumnType("bit"); + + b.Property("LastDeployedAt") + .HasColumnType("datetime2"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("SchemaName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("ViewDefinition") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ViewName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("WithSchemaBinding") + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.HasIndex("DataSourceCode"); + + b.HasIndex("IsDeployed"); + + b.HasIndex("Status"); + + b.HasIndex("SchemaName", "ViewName"); + + b.ToTable("Sqm_T_SqlView", (string)null); + }); + modelBuilder.Entity("MaterialUom", b => { b.Property("AlternativeUomsId") diff --git a/api/src/Erp.Platform.HttpApi.Host/Erp.Platform.HttpApi.Host.csproj b/api/src/Erp.Platform.HttpApi.Host/Erp.Platform.HttpApi.Host.csproj index 9791c972..af547035 100644 --- a/api/src/Erp.Platform.HttpApi.Host/Erp.Platform.HttpApi.Host.csproj +++ b/api/src/Erp.Platform.HttpApi.Host/Erp.Platform.HttpApi.Host.csproj @@ -6,7 +6,6 @@ net9.0 Erp.Platform true - Erp.Platform-4681b4fd-151f-4221-84a4-929d86723e4c @@ -32,6 +31,7 @@ + diff --git a/api/src/Erp.Platform.HttpApi.Host/PlatformHttpApiHostModule.cs b/api/src/Erp.Platform.HttpApi.Host/PlatformHttpApiHostModule.cs index 76c52992..2c897eb9 100644 --- a/api/src/Erp.Platform.HttpApi.Host/PlatformHttpApiHostModule.cs +++ b/api/src/Erp.Platform.HttpApi.Host/PlatformHttpApiHostModule.cs @@ -9,6 +9,7 @@ using Erp.MailQueue; using Erp.Notifications.Application; using Erp.Platform.Classrooms; using Erp.Platform.EntityFrameworkCore; +using Erp.SqlQueryManager; using Erp.Platform.Extensions; using Erp.Platform.FileManagement; using Erp.Platform.Identity; @@ -51,7 +52,6 @@ using Volo.Abp.Swashbuckle; using Volo.Abp.UI.Navigation.Urls; using Volo.Abp.VirtualFileSystem; using Erp.Platform.DynamicServices; -using Erp.Platform.DeveloperKit; using static Erp.Platform.PlatformConsts; using static Erp.Settings.SettingsConsts; @@ -67,7 +67,8 @@ namespace Erp.Platform; typeof(AbpAccountWebOpenIddictModule), typeof(AbpAspNetCoreSerilogModule), typeof(AbpSwashbuckleModule), - typeof(AbpBackgroundWorkersHangfireModule) + typeof(AbpBackgroundWorkersHangfireModule), + typeof(SqlQueryManagerApplicationModule) )] public class PlatformHttpApiHostModule : AbpModule { @@ -204,6 +205,7 @@ public class PlatformHttpApiHostModule : AbpModule options.ConventionalControllers.Create(typeof(SettingsApplicationModule).Assembly); options.ConventionalControllers.Create(typeof(ErpMailQueueModule).Assembly); options.ConventionalControllers.Create(typeof(NotificationApplicationModule).Assembly); + options.ConventionalControllers.Create(typeof(SqlQueryManagerApplicationModule).Assembly); options.ChangeControllerModelApiExplorerGroupName = false; options.ConventionalControllers.FormBodyBindingIgnoredTypes.Add(typeof(PlatformUpdateProfileDto)); options.ConventionalControllers.FormBodyBindingIgnoredTypes.Add(typeof(UploadFileDto));