feat(auth.apikeys.sqlserver): package skeleton, options (schema default zbauth, validated) and connection factory

Claude-Session: https://claude.ai/code/session_013L3XdmVii9i5sY4FChU8pB
This commit is contained in:
Joseph Doherty
2026-09-02 14:08:22 -04:00
parent 3f11704d10
commit 2969805e4e
8 changed files with 201 additions and 0 deletions
@@ -0,0 +1,64 @@
using Microsoft.Data.SqlClient;
namespace ZB.MOM.WW.Auth.ApiKeys.SqlServer;
/// <summary>
/// Opens connections to the shared SQL Server API-key database and carries the settings every
/// command needs (schema name, command timeout).
/// </summary>
/// <param name="options">The SQL Server API-key store options; validated in the constructor.</param>
/// <exception cref="InvalidOperationException"><paramref name="options"/> is not usable.</exception>
public sealed class AuthSqlServerConnectionFactory(SqlServerApiKeyOptions options)
{
private readonly SqlServerApiKeyOptions _options = Validate(options);
private static SqlServerApiKeyOptions Validate(SqlServerApiKeyOptions options)
{
ArgumentNullException.ThrowIfNull(options);
options.Validate();
return options;
}
/// <summary>The schema that owns the three API-key tables.</summary>
public string Schema => _options.Schema;
/// <summary>Command timeout, in whole seconds, for every command issued against this store.</summary>
public int CommandTimeoutSeconds => Math.Max(1, (int)_options.CommandTimeout.TotalSeconds);
/// <summary>Opens a connection to the shared API-key database.</summary>
/// <param name="cancellationToken">A token to cancel the connect.</param>
/// <returns>An opened connection. The caller owns disposal.</returns>
public async Task<SqlConnection> OpenConnectionAsync(CancellationToken cancellationToken)
{
var connection = new SqlConnection(_options.ConnectionString);
try
{
await connection.OpenAsync(cancellationToken).ConfigureAwait(false);
return connection;
}
catch
{
await connection.DisposeAsync().ConfigureAwait(false);
throw;
}
}
/// <summary>
/// Creates a command on <paramref name="connection"/> with the configured timeout applied.
/// </summary>
/// <param name="connection">The open connection.</param>
/// <param name="commandText">The command text.</param>
/// <param name="transaction">The ambient transaction, if any.</param>
/// <returns>The configured command. The caller owns disposal.</returns>
public SqlCommand CreateCommand(
SqlConnection connection, string commandText, SqlTransaction? transaction = null)
{
ArgumentNullException.ThrowIfNull(connection);
SqlCommand command = connection.CreateCommand();
command.CommandText = commandText;
command.CommandTimeout = CommandTimeoutSeconds;
command.Transaction = transaction;
return command;
}
}