Files
123123/ZA.CoreService.ESBCertificateManager/Services/DeployTargetRepository.cs
T
GizzlerandCursor 0a449727ad Fix invalid CertificateCheckUrl column errors on existing databases.
Auto-migrate SonicContainer schema on startup, rename legacy typo columns, and use fallback SQL when the column is not yet present.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-03 13:24:20 +02:00

296 lines
9.7 KiB
C#

using System.Data;
using Microsoft.Data.SqlClient;
using ZA.CoreService.ESBCertificateManager.Models;
namespace ZA.CoreService.ESBCertificateManager.Services;
public sealed class DeploymentTargetRepository
{
private readonly string _connectionString;
public DeploymentTargetRepository(string connectionString)
{
if (string.IsNullOrWhiteSpace(connectionString))
{
throw new ArgumentException(
"Der SQL-Connection-String ist nicht konfiguriert.",
nameof(connectionString));
}
_connectionString = connectionString;
}
public async Task<IReadOnlyList<DeploymentTarget>> GetActiveAsync(
string environmentCode,
CancellationToken cancellationToken = default)
{
if (string.IsNullOrWhiteSpace(environmentCode))
{
throw new ArgumentException(
"Der EnvironmentCode fehlt.",
nameof(environmentCode));
}
const string sqlWithCheckUrl = """
SELECT
target.[CertificateTargetId],
target.[TargetDirectory],
target.[TargetFileName],
target.[BackupEnabled],
target.[BackupDirectoryName],
target.[IsActive] AS [TargetIsActive],
container.[SonicContainerId],
container.[ContainerName],
container.[ContainerDisplayName],
container.[CertificateCheckUrl],
container.[RestartTimeoutSeconds],
connection.[ConnectionName],
environment.[EnvironmentCode],
environment.[SortOrder] AS [EnvironmentSortOrder],
company.[CompanyCode]
FROM [dbo].[CertificateTarget] AS target
INNER JOIN [dbo].[SonicContainer] AS container
ON container.[SonicContainerId] = target.[SonicContainerId]
INNER JOIN [dbo].[SonicConnection] AS connection
ON connection.[SonicConnectionId] = container.[SonicConnectionId]
INNER JOIN [dbo].[Environment] AS environment
ON environment.[EnvironmentId] = connection.[EnvironmentId]
INNER JOIN [dbo].[Company] AS company
ON company.[CompanyId] = container.[CompanyId]
WHERE target.[IsActive] = 1
AND container.[IsActive] = 1
AND connection.[IsActive] = 1
AND company.[IsActive] = 1
AND environment.[IsActive] = 1
AND environment.[EnvironmentCode] = @EnvironmentCode
ORDER BY
company.[CompanyCode],
environment.[SortOrder],
container.[ContainerName],
target.[TargetDirectory],
target.[TargetFileName];
""";
const string sqlWithoutCheckUrl = """
SELECT
target.[CertificateTargetId],
target.[TargetDirectory],
target.[TargetFileName],
target.[BackupEnabled],
target.[BackupDirectoryName],
target.[IsActive] AS [TargetIsActive],
container.[SonicContainerId],
container.[ContainerName],
container.[ContainerDisplayName],
container.[RestartTimeoutSeconds],
connection.[ConnectionName],
environment.[EnvironmentCode],
environment.[SortOrder] AS [EnvironmentSortOrder],
company.[CompanyCode]
FROM [dbo].[CertificateTarget] AS target
INNER JOIN [dbo].[SonicContainer] AS container
ON container.[SonicContainerId] = target.[SonicContainerId]
INNER JOIN [dbo].[SonicConnection] AS connection
ON connection.[SonicConnectionId] = container.[SonicConnectionId]
INNER JOIN [dbo].[Environment] AS environment
ON environment.[EnvironmentId] = connection.[EnvironmentId]
INNER JOIN [dbo].[Company] AS company
ON company.[CompanyId] = container.[CompanyId]
WHERE target.[IsActive] = 1
AND container.[IsActive] = 1
AND connection.[IsActive] = 1
AND company.[IsActive] = 1
AND environment.[IsActive] = 1
AND environment.[EnvironmentCode] = @EnvironmentCode
ORDER BY
company.[CompanyCode],
environment.[SortOrder],
container.[ContainerName],
target.[TargetDirectory],
target.[TargetFileName];
""";
List<DeploymentTarget> targets = [];
await using SqlConnection connection = new(_connectionString);
await connection.OpenAsync(cancellationToken);
bool hasCertificateCheckUrl =
await DatabaseSchemaPatcher.HasCertificateCheckUrlColumnAsync(
_connectionString,
cancellationToken);
string sql = hasCertificateCheckUrl
? sqlWithCheckUrl
: sqlWithoutCheckUrl;
await using SqlCommand command = new(sql, connection);
command.Parameters.Add(
new SqlParameter(
"@EnvironmentCode",
SqlDbType.NVarChar,
30)
{
Value = environmentCode
.Trim()
.ToUpperInvariant()
});
await using SqlDataReader reader =
await command.ExecuteReaderAsync(cancellationToken);
int targetIdOrdinal =
reader.GetOrdinal("CertificateTargetId");
int targetDirectoryOrdinal =
reader.GetOrdinal("TargetDirectory");
int targetFileNameOrdinal =
reader.GetOrdinal("TargetFileName");
int backupEnabledOrdinal =
reader.GetOrdinal("BackupEnabled");
int backupDirectoryNameOrdinal =
reader.GetOrdinal("BackupDirectoryName");
int targetIsActiveOrdinal =
reader.GetOrdinal("TargetIsActive");
int containerNameOrdinal =
reader.GetOrdinal("ContainerName");
int containerDisplayNameOrdinal =
reader.GetOrdinal("ContainerDisplayName");
int? certificateCheckUrlOrdinal = hasCertificateCheckUrl
? reader.GetOrdinal("CertificateCheckUrl")
: null;
int restartTimeoutOrdinal =
reader.GetOrdinal("RestartTimeoutSeconds");
int connectionNameOrdinal =
reader.GetOrdinal("ConnectionName");
int environmentCodeOrdinal =
reader.GetOrdinal("EnvironmentCode");
int environmentSortOrderOrdinal =
reader.GetOrdinal("EnvironmentSortOrder");
int companyCodeOrdinal =
reader.GetOrdinal("CompanyCode");
while (await reader.ReadAsync(cancellationToken))
{
string containerName =
reader.GetString(containerNameOrdinal);
string? containerDisplayName =
ReadNullableString(
reader,
containerDisplayNameOrdinal);
string? certificateCheckUrl =
certificateCheckUrlOrdinal is int checkUrlOrdinal
? ReadNullableString(
reader,
checkUrlOrdinal)
: null;
string companyCode =
reader.GetString(companyCodeOrdinal);
string displayName =
string.IsNullOrWhiteSpace(containerDisplayName)
? containerName
: containerDisplayName;
string tlsHost = string.Empty;
int? tlsPort = null;
string tlsServerName = string.Empty;
if (TlsEndpointProbeService.TryParseEndpoint(
certificateCheckUrl,
out string host,
out int port,
out string serverName,
out _))
{
tlsHost = host;
tlsPort = port;
tlsServerName = serverName;
}
targets.Add(new DeploymentTarget
{
Id = reader.GetInt32(targetIdOrdinal),
Name = $"{companyCode} / {displayName}",
Environment =
reader.GetString(environmentCodeOrdinal),
IsActive =
reader.GetBoolean(targetIsActiveOrdinal),
TargetDirectory =
reader.GetString(targetDirectoryOrdinal),
CertificateFileName =
reader.GetString(targetFileNameOrdinal),
BackupEnabled =
reader.GetBoolean(backupEnabledOrdinal),
BackupDirectoryName =
reader.GetString(backupDirectoryNameOrdinal),
BackupRetentionDays = null,
ContainerName = containerName,
RestartType = RestartType.SonicContainer,
SonicConnectionName =
reader.GetString(connectionNameOrdinal),
RestartTimeoutSeconds =
reader.GetInt32(restartTimeoutOrdinal),
TlsHost = tlsHost,
TlsPort = tlsPort,
TlsServerName = tlsServerName,
ExpectedFingerprint = null,
SortOrder =
reader.GetInt32(environmentSortOrderOrdinal)
});
}
return targets;
}
private static string? ReadNullableString(
SqlDataReader reader,
int ordinal)
{
return reader.IsDBNull(ordinal)
? null
: reader.GetString(ordinal);
}
}