Sql Query Manager düzenlemesi
This commit is contained in:
parent
4a7f2ee853
commit
9b2c1c25ee
11 changed files with 417 additions and 83 deletions
|
|
@ -41,6 +41,11 @@ public interface ISqlObjectManagerAppService : IApplicationService
|
|||
|
||||
// Database Metadata Operations
|
||||
Task<List<DatabaseColumnDto>> GetTableColumnsAsync(string dataSourceCode, string schemaName, string tableName);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the SQL definition/body of a native SQL Server object (Stored Procedure, View, or Function)
|
||||
/// </summary>
|
||||
Task<string> GetNativeObjectDefinitionAsync(string dataSourceCode, string schemaName, string objectName);
|
||||
|
||||
// Smart Save - Analyzes SQL and saves to appropriate table with auto-deploy
|
||||
Task<SmartSaveResultDto> SmartSaveAsync(SmartSaveInputDto input);
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ public class SqlFunctionDto : FullAuditedEntityDto<Guid>
|
|||
public bool IsDeployed { get; set; }
|
||||
public DateTime? LastDeployedAt { get; set; }
|
||||
public string Parameters { get; set; }
|
||||
public bool IsCustom { get; set; } // true = stored in database, false = native SQL Server object
|
||||
}
|
||||
|
||||
public class CreateSqlFunctionDto
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ public class SqlStoredProcedureDto : FullAuditedEntityDto<Guid>
|
|||
public bool IsDeployed { get; set; }
|
||||
public DateTime? LastDeployedAt { get; set; }
|
||||
public string Parameters { get; set; }
|
||||
public bool IsCustom { get; set; } // true = stored in database, false = native SQL Server object
|
||||
}
|
||||
|
||||
public class CreateSqlStoredProcedureDto
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ public class SqlViewDto : FullAuditedEntityDto<Guid>
|
|||
public bool IsDeployed { get; set; }
|
||||
public DateTime? LastDeployedAt { get; set; }
|
||||
public bool WithSchemaBinding { get; set; }
|
||||
public bool IsCustom { get; set; } // true = stored in database, false = native SQL Server object
|
||||
}
|
||||
|
||||
public class CreateSqlViewDto
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Erp.SqlQueryManager.Application.Contracts;
|
||||
using Erp.SqlQueryManager.Domain.Entities;
|
||||
|
|
@ -100,67 +102,14 @@ public class SqlObjectManagerAppService : ApplicationService, ISqlObjectManagerA
|
|||
})
|
||||
.ToList();
|
||||
|
||||
// Get all stored procedures for this data source
|
||||
var procedures = await _procedureRepository.GetListAsync();
|
||||
result.StoredProcedures = procedures
|
||||
.Where(p => p.DataSourceCode == dataSourceCode)
|
||||
.Select(p => new SqlStoredProcedureDto
|
||||
{
|
||||
Id = p.Id,
|
||||
ProcedureName = p.ProcedureName,
|
||||
SchemaName = p.SchemaName,
|
||||
DisplayName = p.DisplayName,
|
||||
Description = p.Description,
|
||||
ProcedureBody = p.ProcedureBody,
|
||||
DataSourceCode = p.DataSourceCode,
|
||||
Category = p.Category,
|
||||
Parameters = p.Parameters,
|
||||
IsDeployed = p.IsDeployed,
|
||||
LastDeployedAt = p.LastDeployedAt
|
||||
})
|
||||
.ToList();
|
||||
// Get all stored procedures for this data source (custom + native merged)
|
||||
result.StoredProcedures = await GetMergedStoredProceduresAsync(dataSourceCode);
|
||||
|
||||
// Get all views for this data source
|
||||
var views = await _viewRepository.GetListAsync();
|
||||
result.Views = views
|
||||
.Where(v => v.DataSourceCode == dataSourceCode)
|
||||
.Select(v => new SqlViewDto
|
||||
{
|
||||
Id = v.Id,
|
||||
ViewName = v.ViewName,
|
||||
SchemaName = v.SchemaName,
|
||||
DisplayName = v.DisplayName,
|
||||
Description = v.Description,
|
||||
ViewDefinition = v.ViewDefinition,
|
||||
DataSourceCode = v.DataSourceCode,
|
||||
Category = v.Category,
|
||||
WithSchemaBinding = v.WithSchemaBinding,
|
||||
IsDeployed = v.IsDeployed,
|
||||
LastDeployedAt = v.LastDeployedAt
|
||||
})
|
||||
.ToList();
|
||||
// Get all views for this data source (custom + native merged)
|
||||
result.Views = await GetMergedViewsAsync(dataSourceCode);
|
||||
|
||||
// Get all functions for this data source
|
||||
var functions = await _functionRepository.GetListAsync();
|
||||
result.Functions = functions
|
||||
.Where(f => f.DataSourceCode == dataSourceCode)
|
||||
.Select(f => new SqlFunctionDto
|
||||
{
|
||||
Id = f.Id,
|
||||
FunctionName = f.FunctionName,
|
||||
SchemaName = f.SchemaName,
|
||||
DisplayName = f.DisplayName,
|
||||
Description = f.Description,
|
||||
FunctionType = f.FunctionType,
|
||||
FunctionBody = f.FunctionBody,
|
||||
ReturnType = f.ReturnType,
|
||||
DataSourceCode = f.DataSourceCode,
|
||||
Category = f.Category,
|
||||
Parameters = f.Parameters,
|
||||
IsDeployed = f.IsDeployed,
|
||||
LastDeployedAt = f.LastDeployedAt
|
||||
})
|
||||
.ToList();
|
||||
// Get all functions for this data source (custom + native merged)
|
||||
result.Functions = await GetMergedFunctionsAsync(dataSourceCode);
|
||||
|
||||
// Get all database tables
|
||||
result.Tables = await GetTablesAsync(dataSourceCode);
|
||||
|
|
@ -214,6 +163,244 @@ public class SqlObjectManagerAppService : ApplicationService, ISqlObjectManagerA
|
|||
return tables;
|
||||
}
|
||||
|
||||
private async Task<List<SqlStoredProcedureDto>> GetMergedStoredProceduresAsync(string dataSourceCode)
|
||||
{
|
||||
// Get custom stored procedures from database
|
||||
var customProcedures = await _procedureRepository.GetListAsync();
|
||||
var customList = customProcedures
|
||||
.Where(p => p.DataSourceCode == dataSourceCode)
|
||||
.Select(p => new SqlStoredProcedureDto
|
||||
{
|
||||
Id = p.Id,
|
||||
ProcedureName = p.ProcedureName,
|
||||
SchemaName = p.SchemaName,
|
||||
DisplayName = p.DisplayName,
|
||||
Description = p.Description,
|
||||
ProcedureBody = p.ProcedureBody,
|
||||
DataSourceCode = p.DataSourceCode,
|
||||
Category = p.Category,
|
||||
Parameters = p.Parameters,
|
||||
IsDeployed = p.IsDeployed,
|
||||
LastDeployedAt = p.LastDeployedAt,
|
||||
IsCustom = true
|
||||
})
|
||||
.ToList();
|
||||
|
||||
// Get native stored procedures from SQL Server
|
||||
var nativeQuery = @"
|
||||
SELECT
|
||||
SCHEMA_NAME(p.schema_id) AS SchemaName,
|
||||
p.name AS ProcedureName,
|
||||
p.create_date AS CreatedDate,
|
||||
p.modify_date AS ModifiedDate
|
||||
FROM
|
||||
sys.procedures p
|
||||
WHERE
|
||||
p.is_ms_shipped = 0
|
||||
ORDER BY
|
||||
SCHEMA_NAME(p.schema_id), p.name";
|
||||
|
||||
var result = await _sqlExecutorService.ExecuteQueryAsync(nativeQuery, dataSourceCode);
|
||||
var nativeList = new List<SqlStoredProcedureDto>();
|
||||
|
||||
if (result.Success && result.Data != null)
|
||||
{
|
||||
foreach (var row in result.Data)
|
||||
{
|
||||
var dict = row as System.Collections.Generic.IDictionary<string, object>;
|
||||
if (dict != null)
|
||||
{
|
||||
var schemaName = dict["SchemaName"]?.ToString() ?? "dbo";
|
||||
var procName = dict["ProcedureName"]?.ToString() ?? "";
|
||||
|
||||
// Skip if already exists in custom list
|
||||
if (!customList.Any(c => c.SchemaName == schemaName && c.ProcedureName == procName))
|
||||
{
|
||||
// Generate deterministic GUID from schema and name to ensure uniqueness
|
||||
var uniqueId = GenerateDeterministicGuid($"SP_{dataSourceCode}_{schemaName}_{procName}");
|
||||
|
||||
nativeList.Add(new SqlStoredProcedureDto
|
||||
{
|
||||
Id = uniqueId,
|
||||
SchemaName = schemaName,
|
||||
ProcedureName = procName,
|
||||
DisplayName = $"[{schemaName}].[{procName}]",
|
||||
DataSourceCode = dataSourceCode,
|
||||
IsCustom = false,
|
||||
IsDeployed = true // Native objects are already deployed
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Merge and return
|
||||
return customList.Concat(nativeList).ToList();
|
||||
}
|
||||
|
||||
private async Task<List<SqlViewDto>> GetMergedViewsAsync(string dataSourceCode)
|
||||
{
|
||||
// Get custom views from database
|
||||
var customViews = await _viewRepository.GetListAsync();
|
||||
var customList = customViews
|
||||
.Where(v => v.DataSourceCode == dataSourceCode)
|
||||
.Select(v => new SqlViewDto
|
||||
{
|
||||
Id = v.Id,
|
||||
ViewName = v.ViewName,
|
||||
SchemaName = v.SchemaName,
|
||||
DisplayName = v.DisplayName,
|
||||
Description = v.Description,
|
||||
ViewDefinition = v.ViewDefinition,
|
||||
DataSourceCode = v.DataSourceCode,
|
||||
Category = v.Category,
|
||||
WithSchemaBinding = v.WithSchemaBinding,
|
||||
IsDeployed = v.IsDeployed,
|
||||
LastDeployedAt = v.LastDeployedAt,
|
||||
IsCustom = true
|
||||
})
|
||||
.ToList();
|
||||
|
||||
// Get native views from SQL Server
|
||||
var nativeQuery = @"
|
||||
SELECT
|
||||
SCHEMA_NAME(v.schema_id) AS SchemaName,
|
||||
v.name AS ViewName,
|
||||
v.create_date AS CreatedDate,
|
||||
v.modify_date AS ModifiedDate
|
||||
FROM
|
||||
sys.views v
|
||||
WHERE
|
||||
v.is_ms_shipped = 0
|
||||
ORDER BY
|
||||
SCHEMA_NAME(v.schema_id), v.name";
|
||||
|
||||
var result = await _sqlExecutorService.ExecuteQueryAsync(nativeQuery, dataSourceCode);
|
||||
var nativeList = new List<SqlViewDto>();
|
||||
|
||||
if (result.Success && result.Data != null)
|
||||
{
|
||||
foreach (var row in result.Data)
|
||||
{
|
||||
var dict = row as System.Collections.Generic.IDictionary<string, object>;
|
||||
if (dict != null)
|
||||
{
|
||||
var schemaName = dict["SchemaName"]?.ToString() ?? "dbo";
|
||||
var viewName = dict["ViewName"]?.ToString() ?? "";
|
||||
|
||||
// Skip if already exists in custom list
|
||||
if (!customList.Any(c => c.SchemaName == schemaName && c.ViewName == viewName))
|
||||
{
|
||||
// Generate deterministic GUID from schema and name to ensure uniqueness
|
||||
var uniqueId = GenerateDeterministicGuid($"VIEW_{dataSourceCode}_{schemaName}_{viewName}");
|
||||
|
||||
nativeList.Add(new SqlViewDto
|
||||
{
|
||||
Id = uniqueId,
|
||||
SchemaName = schemaName,
|
||||
ViewName = viewName,
|
||||
DisplayName = $"[{schemaName}].[{viewName}]",
|
||||
DataSourceCode = dataSourceCode,
|
||||
IsCustom = false,
|
||||
IsDeployed = true
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return customList.Concat(nativeList).ToList();
|
||||
}
|
||||
|
||||
private async Task<List<SqlFunctionDto>> GetMergedFunctionsAsync(string dataSourceCode)
|
||||
{
|
||||
// Get custom functions from database
|
||||
var customFunctions = await _functionRepository.GetListAsync();
|
||||
var customList = customFunctions
|
||||
.Where(f => f.DataSourceCode == dataSourceCode)
|
||||
.Select(f => new SqlFunctionDto
|
||||
{
|
||||
Id = f.Id,
|
||||
FunctionName = f.FunctionName,
|
||||
SchemaName = f.SchemaName,
|
||||
DisplayName = f.DisplayName,
|
||||
Description = f.Description,
|
||||
FunctionType = f.FunctionType,
|
||||
FunctionBody = f.FunctionBody,
|
||||
ReturnType = f.ReturnType,
|
||||
DataSourceCode = f.DataSourceCode,
|
||||
Category = f.Category,
|
||||
Parameters = f.Parameters,
|
||||
IsDeployed = f.IsDeployed,
|
||||
LastDeployedAt = f.LastDeployedAt,
|
||||
IsCustom = true
|
||||
})
|
||||
.ToList();
|
||||
|
||||
// Get native functions from SQL Server
|
||||
var nativeQuery = @"
|
||||
SELECT
|
||||
SCHEMA_NAME(o.schema_id) AS SchemaName,
|
||||
o.name AS FunctionName,
|
||||
o.create_date AS CreatedDate,
|
||||
o.modify_date AS ModifiedDate,
|
||||
CASE o.type
|
||||
WHEN 'FN' THEN 'Scalar'
|
||||
WHEN 'IF' THEN 'InlineTableValued'
|
||||
WHEN 'TF' THEN 'TableValued'
|
||||
ELSE 'Unknown'
|
||||
END AS FunctionType
|
||||
FROM
|
||||
sys.objects o
|
||||
WHERE
|
||||
o.type IN ('FN', 'IF', 'TF')
|
||||
AND o.is_ms_shipped = 0
|
||||
ORDER BY
|
||||
SCHEMA_NAME(o.schema_id), o.name";
|
||||
|
||||
var result = await _sqlExecutorService.ExecuteQueryAsync(nativeQuery, dataSourceCode);
|
||||
var nativeList = new List<SqlFunctionDto>();
|
||||
|
||||
if (result.Success && result.Data != null)
|
||||
{
|
||||
foreach (var row in result.Data)
|
||||
{
|
||||
var dict = row as System.Collections.Generic.IDictionary<string, object>;
|
||||
if (dict != null)
|
||||
{
|
||||
var schemaName = dict["SchemaName"]?.ToString() ?? "dbo";
|
||||
var funcName = dict["FunctionName"]?.ToString() ?? "";
|
||||
|
||||
// Skip if already exists in custom list
|
||||
if (!customList.Any(c => c.SchemaName == schemaName && c.FunctionName == funcName))
|
||||
{
|
||||
var funcTypeStr = dict["FunctionType"]?.ToString() ?? "Scalar";
|
||||
var funcType = funcTypeStr == "Scalar" ? SqlFunctionType.ScalarFunction :
|
||||
funcTypeStr == "InlineTableValued" ? SqlFunctionType.InlineTableValuedFunction :
|
||||
SqlFunctionType.TableValuedFunction;
|
||||
|
||||
// Generate deterministic GUID from schema and name to ensure uniqueness
|
||||
var uniqueId = GenerateDeterministicGuid($"FUNC_{dataSourceCode}_{schemaName}_{funcName}");
|
||||
|
||||
nativeList.Add(new SqlFunctionDto
|
||||
{
|
||||
Id = uniqueId,
|
||||
SchemaName = schemaName,
|
||||
FunctionName = funcName,
|
||||
DisplayName = $"[{schemaName}].[{funcName}]",
|
||||
DataSourceCode = dataSourceCode,
|
||||
FunctionType = funcType,
|
||||
IsCustom = false,
|
||||
IsDeployed = true
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return customList.Concat(nativeList).ToList();
|
||||
}
|
||||
|
||||
#region Query Operations
|
||||
|
||||
public async Task<SqlQueryDto> CreateQueryAsync(CreateSqlQueryDto input)
|
||||
|
|
@ -551,6 +738,34 @@ public class SqlObjectManagerAppService : ApplicationService, ISqlObjectManagerA
|
|||
|
||||
#region Database Metadata Operations
|
||||
|
||||
public async Task<string> GetNativeObjectDefinitionAsync(string dataSourceCode, string schemaName, string objectName)
|
||||
{
|
||||
ValidateTenantAccess();
|
||||
var query = @"
|
||||
SELECT OBJECT_DEFINITION(OBJECT_ID(@ObjectName)) AS Definition";
|
||||
|
||||
var fullObjectName = $"[{schemaName}].[{objectName}]";
|
||||
var result = await _sqlExecutorService.ExecuteQueryAsync(
|
||||
query.Replace("@ObjectName", $"'{fullObjectName}'"),
|
||||
dataSourceCode
|
||||
);
|
||||
|
||||
if (result.Success && result.Data != null)
|
||||
{
|
||||
var dataList = result.Data.ToList();
|
||||
if (dataList.Count > 0)
|
||||
{
|
||||
var row = dataList[0] as System.Collections.Generic.IDictionary<string, object>;
|
||||
if (row != null && row.ContainsKey("Definition"))
|
||||
{
|
||||
return row["Definition"]?.ToString() ?? string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public async Task<List<DatabaseColumnDto>> GetTableColumnsAsync(string dataSourceCode, string schemaName, string tableName)
|
||||
{
|
||||
ValidateTenantAccess();
|
||||
|
|
@ -838,4 +1053,21 @@ public class SqlObjectManagerAppService : ApplicationService, ISqlObjectManagerA
|
|||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helper Methods
|
||||
|
||||
/// <summary>
|
||||
/// Generates a deterministic GUID from a string input using MD5 hash
|
||||
/// This ensures same input always produces same GUID
|
||||
/// </summary>
|
||||
private static Guid GenerateDeterministicGuid(string input)
|
||||
{
|
||||
using (var md5 = MD5.Create())
|
||||
{
|
||||
var hash = md5.ComputeHash(Encoding.UTF8.GetBytes(input));
|
||||
return new Guid(hash);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ public class SqlQueryManagerAutoMapperProfile : Profile
|
|||
.Ignore(x => x.LastExecutedAt)
|
||||
.Ignore(x => x.ExecutionCount);
|
||||
|
||||
CreateMap<SqlStoredProcedure, SqlStoredProcedureDto>();
|
||||
CreateMap<SqlStoredProcedure, SqlStoredProcedureDto>().Ignore(x => x.IsCustom);
|
||||
CreateMap<CreateSqlStoredProcedureDto, SqlStoredProcedure>()
|
||||
.IgnoreFullAuditedObjectProperties()
|
||||
.Ignore(x => x.Id)
|
||||
|
|
@ -34,6 +34,7 @@ public class SqlQueryManagerAutoMapperProfile : Profile
|
|||
.Ignore(x => x.Status)
|
||||
.Ignore(x => x.IsDeployed)
|
||||
.Ignore(x => x.LastDeployedAt);
|
||||
|
||||
CreateMap<UpdateSqlStoredProcedureDto, SqlStoredProcedure>()
|
||||
.IgnoreFullAuditedObjectProperties()
|
||||
.Ignore(x => x.Id)
|
||||
|
|
@ -45,7 +46,7 @@ public class SqlQueryManagerAutoMapperProfile : Profile
|
|||
.Ignore(x => x.IsDeployed)
|
||||
.Ignore(x => x.LastDeployedAt);
|
||||
|
||||
CreateMap<SqlView, SqlViewDto>();
|
||||
CreateMap<SqlView, SqlViewDto>().Ignore(x => x.IsCustom);
|
||||
CreateMap<CreateSqlViewDto, SqlView>()
|
||||
.IgnoreFullAuditedObjectProperties()
|
||||
.Ignore(x => x.Id)
|
||||
|
|
@ -59,12 +60,12 @@ public class SqlQueryManagerAutoMapperProfile : Profile
|
|||
.Ignore(x => x.TenantId)
|
||||
.Ignore(x => x.ViewName)
|
||||
.Ignore(x => x.SchemaName)
|
||||
.Ignore(x => x.DataSourceCode)
|
||||
.Ignore(x => x.DataSourceCode)
|
||||
.Ignore(x => x.Status)
|
||||
.Ignore(x => x.IsDeployed)
|
||||
.Ignore(x => x.LastDeployedAt);
|
||||
|
||||
CreateMap<SqlFunction, SqlFunctionDto>();
|
||||
CreateMap<SqlFunction, SqlFunctionDto>().Ignore(x => x.IsCustom);
|
||||
CreateMap<CreateSqlFunctionDto, SqlFunction>()
|
||||
.IgnoreFullAuditedObjectProperties()
|
||||
.Ignore(x => x.Id)
|
||||
|
|
@ -79,7 +80,7 @@ public class SqlQueryManagerAutoMapperProfile : Profile
|
|||
.Ignore(x => x.FunctionName)
|
||||
.Ignore(x => x.SchemaName)
|
||||
.Ignore(x => x.FunctionType)
|
||||
.Ignore(x => x.DataSourceCode)
|
||||
.Ignore(x => x.DataSourceCode)
|
||||
.Ignore(x => x.Status)
|
||||
.Ignore(x => x.IsDeployed)
|
||||
.Ignore(x => x.LastDeployedAt);
|
||||
|
|
|
|||
|
|
@ -10327,6 +10327,12 @@
|
|||
"tr": "Şablon Kullan",
|
||||
"en": "Use Template"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "App.Platform.NativeObjectViewOnly",
|
||||
"tr": "Yerel Nesne Sadece Görüntüle",
|
||||
"en": "Native Object View Only"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "App.Platform.Error",
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ export interface SqlFunctionDto extends FullAuditedEntityDto<string> {
|
|||
isDeployed: boolean
|
||||
lastDeployedAt?: string
|
||||
parameters: string
|
||||
isCustom: boolean // true = stored in database, false = native SQL Server object
|
||||
}
|
||||
|
||||
export interface CreateSqlFunctionDto {
|
||||
|
|
@ -132,6 +133,7 @@ export interface SqlStoredProcedureDto extends FullAuditedEntityDto<string> {
|
|||
isDeployed: boolean
|
||||
lastDeployedAt?: string
|
||||
parameters: string
|
||||
isCustom: boolean // true = stored in database, false = native SQL Server object
|
||||
}
|
||||
|
||||
export interface CreateSqlStoredProcedureDto {
|
||||
|
|
@ -171,6 +173,7 @@ export interface SqlViewDto extends FullAuditedEntityDto<string> {
|
|||
isDeployed: boolean
|
||||
lastDeployedAt?: string
|
||||
withSchemaBinding: boolean
|
||||
isCustom: boolean // true = stored in database, false = native SQL Server object
|
||||
}
|
||||
|
||||
export interface CreateSqlViewDto {
|
||||
|
|
|
|||
|
|
@ -185,6 +185,16 @@ export class SqlObjectManagerService {
|
|||
{ apiName: this.apiName, ...config },
|
||||
)
|
||||
|
||||
getNativeObjectDefinition = (dataSourceCode: string, schemaName: string, objectName: string, config?: Partial<Config>) =>
|
||||
apiService.fetchData<string, void>(
|
||||
{
|
||||
method: 'GET',
|
||||
url: '/api/app/sql-object-manager/native-object-definition',
|
||||
params: { dataSourceCode, schemaName, objectName },
|
||||
},
|
||||
{ apiName: this.apiName, ...config },
|
||||
)
|
||||
|
||||
// Smart Save - Analyzes SQL and saves to appropriate table with auto-deploy
|
||||
smartSave = (input: { sqlText: string; dataSourceCode: string; name?: string; description?: string }, config?: Partial<Config>) =>
|
||||
apiService.fetchData<{ objectType: string; objectId: string; deployed: boolean; message: string }, typeof input>(
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ const SqlQueryManager = () => {
|
|||
}, [])
|
||||
|
||||
const handleObjectSelect = useCallback(
|
||||
(object: SqlObject | null, objectType: SqlObjectType | null) => {
|
||||
async (object: SqlObject | null, objectType: SqlObjectType | null) => {
|
||||
if (state.isDirty) {
|
||||
if (!confirm(translate('::App.Platform.UnsavedChangesConfirmation'))) {
|
||||
return
|
||||
|
|
@ -124,16 +124,76 @@ const SqlQueryManager = () => {
|
|||
if (object) {
|
||||
if (objectType === 1) {
|
||||
// Query
|
||||
content = (object as SqlQueryDto).queryText
|
||||
content = (object as SqlQueryDto).queryText || ''
|
||||
} else if (objectType === 2) {
|
||||
// Stored Procedure
|
||||
content = (object as SqlStoredProcedureDto).procedureBody
|
||||
const sp = object as SqlStoredProcedureDto
|
||||
if (sp.isCustom) {
|
||||
content = sp.procedureBody || ''
|
||||
} else {
|
||||
// Native SP - fetch definition from SQL Server
|
||||
try {
|
||||
const result = await sqlObjectManagerService.getNativeObjectDefinition(
|
||||
state.selectedDataSource || '',
|
||||
sp.schemaName,
|
||||
sp.procedureName
|
||||
)
|
||||
content = result.data || ''
|
||||
} catch (error) {
|
||||
toast.push(
|
||||
<Notification type="danger" title={translate('::App.Platform.Error')}>
|
||||
{translate('::App.Platform.FailedToLoadDefinition')}
|
||||
</Notification>,
|
||||
{ placement: 'top-center' },
|
||||
)
|
||||
}
|
||||
}
|
||||
} else if (objectType === 3) {
|
||||
// View
|
||||
content = (object as SqlViewDto).viewDefinition
|
||||
const view = object as SqlViewDto
|
||||
if (view.isCustom) {
|
||||
content = view.viewDefinition || ''
|
||||
} else {
|
||||
// Native View - fetch definition from SQL Server
|
||||
try {
|
||||
const result = await sqlObjectManagerService.getNativeObjectDefinition(
|
||||
state.selectedDataSource || '',
|
||||
view.schemaName,
|
||||
view.viewName
|
||||
)
|
||||
content = result.data || ''
|
||||
} catch (error) {
|
||||
toast.push(
|
||||
<Notification type="danger" title={translate('::App.Platform.Error')}>
|
||||
{translate('::App.Platform.FailedToLoadDefinition')}
|
||||
</Notification>,
|
||||
{ placement: 'top-center' },
|
||||
)
|
||||
}
|
||||
}
|
||||
} else if (objectType === 4) {
|
||||
// Function
|
||||
content = (object as SqlFunctionDto).functionBody
|
||||
const func = object as SqlFunctionDto
|
||||
if (func.isCustom) {
|
||||
content = func.functionBody || ''
|
||||
} else {
|
||||
// Native Function - fetch definition from SQL Server
|
||||
try {
|
||||
const result = await sqlObjectManagerService.getNativeObjectDefinition(
|
||||
state.selectedDataSource || '',
|
||||
func.schemaName,
|
||||
func.functionName
|
||||
)
|
||||
content = result.data || ''
|
||||
} catch (error) {
|
||||
toast.push(
|
||||
<Notification type="danger" title={translate('::App.Platform.Error')}>
|
||||
{translate('::App.Platform.FailedToLoadDefinition')}
|
||||
</Notification>,
|
||||
{ placement: 'top-center' },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -148,7 +208,7 @@ const SqlQueryManager = () => {
|
|||
isSaved: false,
|
||||
}))
|
||||
},
|
||||
[state.isDirty, translate],
|
||||
[state.isDirty, state.selectedDataSource, translate],
|
||||
)
|
||||
|
||||
const handleEditorChange = useCallback((value: string | undefined) => {
|
||||
|
|
@ -345,7 +405,7 @@ GO`,
|
|||
const handleUseTemplateFromDialog = useCallback(
|
||||
(templateContent: string, templateType: string) => {
|
||||
// Check if editor has content
|
||||
const hasUserContent = state.editorContent.trim() && state.isDirty
|
||||
const hasUserContent = state.editorContent?.trim() && state.isDirty
|
||||
|
||||
if (hasUserContent) {
|
||||
// Ask for confirmation
|
||||
|
|
@ -365,7 +425,7 @@ GO`,
|
|||
const templateContent = template || getTemplateContent(templateType)
|
||||
|
||||
// Check if editor has content and it's not from a previous template
|
||||
const hasUserContent = state.editorContent.trim() && state.isDirty
|
||||
const hasUserContent = state.editorContent?.trim() && state.isDirty
|
||||
|
||||
if (hasUserContent) {
|
||||
// Ask for confirmation
|
||||
|
|
@ -405,7 +465,7 @@ GO`,
|
|||
|
||||
// Seçili text varsa onu, yoksa tüm editor içeriğini kullan
|
||||
const selectedText = editorRef.current?.getSelectedText() || ''
|
||||
const queryToExecute = selectedText.trim() || state.editorContent.trim()
|
||||
const queryToExecute = selectedText.trim() || state.editorContent?.trim() || ''
|
||||
|
||||
if (!queryToExecute) {
|
||||
toast.push(
|
||||
|
|
@ -471,7 +531,7 @@ GO`,
|
|||
return
|
||||
}
|
||||
|
||||
if (!state.editorContent.trim()) {
|
||||
if (!state.editorContent?.trim()) {
|
||||
toast.push(
|
||||
<Notification type="warning" title={translate('::App.Platform.Warning')}>
|
||||
{translate('::App.Platform.PleaseEnterContentToSave')}
|
||||
|
|
@ -771,7 +831,7 @@ GO`,
|
|||
onClick={handleSave}
|
||||
disabled={
|
||||
!state.selectedDataSource ||
|
||||
!state.editorContent.trim() ||
|
||||
!state.editorContent?.trim() ||
|
||||
(state.isSaved && !state.isDirty) ||
|
||||
!state.executionResult?.success
|
||||
}
|
||||
|
|
|
|||
|
|
@ -120,9 +120,10 @@ const SqlObjectExplorer = ({
|
|||
const deployInfo = p.isDeployed && p.lastDeployedAt
|
||||
? ` (${new Date(p.lastDeployedAt).toLocaleString('tr-TR', { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit' })})`
|
||||
: '';
|
||||
const customBadge = p.isCustom ? ' 📝' : ' 🗄️';
|
||||
return {
|
||||
id: p.id || '',
|
||||
label: `${p.displayName || p.procedureName}${p.isDeployed ? ' ✅' : ' ❌'}${deployInfo}`,
|
||||
id: p.id!,
|
||||
label: `${p.displayName || p.procedureName}${customBadge}${p.isDeployed ? ' ✅' : ' ❌'}${deployInfo}`,
|
||||
type: 'object' as const,
|
||||
objectType: 2 as SqlObjectType,
|
||||
data: p,
|
||||
|
|
@ -140,9 +141,10 @@ const SqlObjectExplorer = ({
|
|||
const deployInfo = v.isDeployed && v.lastDeployedAt
|
||||
? ` (${new Date(v.lastDeployedAt).toLocaleString('tr-TR', { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit' })})`
|
||||
: '';
|
||||
const customBadge = v.isCustom ? ' 📝' : ' 🗄️';
|
||||
return {
|
||||
id: v.id || '',
|
||||
label: `${v.displayName || v.viewName}${v.isDeployed ? ' ✅' : ' ❌'}${deployInfo}`,
|
||||
id: v.id!,
|
||||
label: `${v.displayName || v.viewName}${customBadge}${v.isDeployed ? ' ✅' : ' ❌'}${deployInfo}`,
|
||||
type: 'object' as const,
|
||||
objectType: 3 as SqlObjectType,
|
||||
data: v,
|
||||
|
|
@ -160,9 +162,10 @@ const SqlObjectExplorer = ({
|
|||
const deployInfo = f.isDeployed && f.lastDeployedAt
|
||||
? ` (${new Date(f.lastDeployedAt).toLocaleString('tr-TR', { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit' })})`
|
||||
: '';
|
||||
const customBadge = f.isCustom ? ' 📝' : ' 🗄️';
|
||||
return {
|
||||
id: f.id || '',
|
||||
label: `${f.displayName || f.functionName}${f.isDeployed ? ' ✅' : ' ❌'}${deployInfo}`,
|
||||
id: f.id!,
|
||||
label: `${f.displayName || f.functionName}${customBadge}${f.isDeployed ? ' ✅' : ' ❌'}${deployInfo}`,
|
||||
type: 'object' as const,
|
||||
objectType: 4 as SqlObjectType,
|
||||
data: f,
|
||||
|
|
@ -289,7 +292,12 @@ const SqlObjectExplorer = ({
|
|||
// Column clicked - do nothing or show info
|
||||
return
|
||||
} else if (node.type === 'object' && node.data) {
|
||||
if (node.objectType) {
|
||||
// Check if it's a table - generate SELECT template
|
||||
if (node.id.startsWith('table-') && onTemplateSelect) {
|
||||
const table = node.data as any
|
||||
const selectQuery = `SELECT TOP 10 * \nFROM ${table.fullName || `[${table.schemaName}].[${table.tableName}]`};`
|
||||
onTemplateSelect(selectQuery, 'table-select')
|
||||
} else if (node.objectType) {
|
||||
onObjectSelect(node.data, node.objectType)
|
||||
}
|
||||
}
|
||||
|
|
@ -498,7 +506,7 @@ const SqlObjectExplorer = ({
|
|||
className="fixed z-50 bg-white dark:bg-gray-800 shadow-lg rounded border border-gray-200 dark:border-gray-700 py-1"
|
||||
style={{ top: contextMenu.y, left: contextMenu.x }}
|
||||
>
|
||||
{contextMenu.node?.type === 'object' && contextMenu.node?.objectType && (
|
||||
{contextMenu.node?.type === 'object' && contextMenu.node?.objectType && contextMenu.node?.data?.isCustom && (
|
||||
<>
|
||||
<button
|
||||
className="w-full px-4 py-2 text-left hover:bg-gray-100 dark:hover:bg-gray-700 text-sm"
|
||||
|
|
@ -531,6 +539,12 @@ const SqlObjectExplorer = ({
|
|||
</button>
|
||||
</>
|
||||
)}
|
||||
|
||||
{contextMenu.node?.type === 'object' && contextMenu.node?.objectType && !contextMenu.node?.data?.isCustom && (
|
||||
<div className="px-4 py-2 text-xs text-gray-500 italic">
|
||||
{translate('::App.Platform.NativeObjectViewOnly')}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{contextMenu.node?.type === 'folder' && (
|
||||
<button
|
||||
|
|
|
|||
Loading…
Reference in a new issue