using System;
using System.Management.Automation;
using PSMinIO.Core;
using PSMinIO.Core.S3;
using PSMinIO.Utils;
namespace PSMinIO.Cmdlets
{
///
/// Base class for all MinIO PowerShell cmdlets
/// Provides common functionality including connection management, logging, and client access
///
public abstract class MinIOBaseCmdlet : PSCmdlet
{
private MinIOConnection? _connection;
///
/// MinIO connection to use for operations. Can be provided via parameter or retrieved from session.
///
[Parameter(ValueFromPipeline = true)]
[Alias("Connection")]
public MinIOConnection? MinIOConnection { get; set; }
///
/// Name of session variable containing the MinIO connection (default: MinIOConnection)
///
[Parameter]
[ValidateNotNullOrEmpty]
public string SessionVariable { get; set; } = "MinIOConnection";
///
/// Gets the MinIO connection instance
///
protected MinIOConnection Connection
{
get
{
if (_connection == null)
{
_connection = GetConnection();
if (_connection == null)
{
ThrowTerminatingError(new ErrorRecord(
new InvalidOperationException("No MinIO connection available. Use Connect-MinIO to establish a connection, or provide a connection via the -MinIOConnection parameter."),
"NoConnection",
ErrorCategory.ConnectionError,
null));
}
if (!_connection!.IsValid)
{
ThrowTerminatingError(new ErrorRecord(
new InvalidOperationException($"MinIO connection is not valid. Status: {_connection.Status}"),
"InvalidConnection",
ErrorCategory.ConnectionError,
_connection));
}
}
return _connection!;
}
}
///
/// Gets the MinIO S3 client instance
///
protected MinIOS3Client S3Client => Connection.S3Client;
///
/// Gets the connection from parameter or session variable
///
/// MinIO connection or null if not found
private MinIOConnection? GetConnection()
{
// First, try the parameter
if (MinIOConnection != null)
{
return MinIOConnection;
}
// Then try the session variable
try
{
var sessionConnection = SessionState.PSVariable.GetValue(SessionVariable);
if (sessionConnection is MinIOConnection connection)
{
return connection;
}
}
catch
{
// Ignore errors when accessing session variables
}
return null;
}
///
/// Executes an operation with consistent error handling and logging
///
/// Name of the operation for logging
/// Operation to execute
/// Optional operation details for logging
protected void ExecuteOperation(string operationName, Action operation, string? details = null)
{
var startTime = DateTime.UtcNow;
MinIOLogger.LogOperationStart(this, operationName, details);
try
{
operation();
var duration = DateTime.UtcNow - startTime;
MinIOLogger.LogOperationComplete(this, operationName, duration, details);
}
catch (Exception ex)
{
MinIOLogger.LogOperationFailure(this, operationName, ex, details);
// Determine appropriate error category
var category = GetErrorCategory(ex);
ThrowTerminatingError(new ErrorRecord(ex, $"{operationName}Failed", category, null));
}
}
///
/// Executes an operation with return value and consistent error handling and logging
///
/// Return type
/// Name of the operation for logging
/// Operation to execute
/// Optional operation details for logging
/// Operation result
protected T ExecuteOperation(string operationName, Func operation, string? details = null)
{
var startTime = DateTime.UtcNow;
MinIOLogger.LogOperationStart(this, operationName, details);
try
{
var result = operation();
var duration = DateTime.UtcNow - startTime;
MinIOLogger.LogOperationComplete(this, operationName, duration, details);
return result;
}
catch (Exception ex)
{
MinIOLogger.LogOperationFailure(this, operationName, ex, details);
// Determine appropriate error category
var category = GetErrorCategory(ex);
ThrowTerminatingError(new ErrorRecord(ex, $"{operationName}Failed", category, null));
// This line will never be reached, but is required for compilation
throw;
}
}
///
/// Determines the appropriate PowerShell error category for an exception
///
/// Exception to categorize
/// Appropriate error category
protected ErrorCategory GetErrorCategory(Exception exception)
{
return exception switch
{
ArgumentException or ArgumentNullException => ErrorCategory.InvalidArgument,
UnauthorizedAccessException => ErrorCategory.PermissionDenied,
System.Net.Http.HttpRequestException => ErrorCategory.ConnectionError,
TimeoutException => ErrorCategory.OperationTimeout,
InvalidOperationException => ErrorCategory.InvalidOperation,
System.IO.FileNotFoundException => ErrorCategory.ObjectNotFound,
System.IO.DirectoryNotFoundException => ErrorCategory.ObjectNotFound,
System.IO.IOException => ErrorCategory.WriteError,
NotSupportedException => ErrorCategory.NotImplemented,
_ => ErrorCategory.NotSpecified
};
}
///
/// Validates that a bucket name is valid according to S3 naming rules
///
/// Bucket name to validate
/// Parameter name for error reporting
protected void ValidateBucketName(string bucketName, string parameterName = "BucketName")
{
if (string.IsNullOrWhiteSpace(bucketName))
{
ThrowTerminatingError(new ErrorRecord(
new ArgumentException($"Bucket name cannot be null or empty", parameterName),
"InvalidBucketName",
ErrorCategory.InvalidArgument,
bucketName));
}
// Basic S3 bucket name validation
if (bucketName.Length < 3 || bucketName.Length > 63)
{
ThrowTerminatingError(new ErrorRecord(
new ArgumentException($"Bucket name must be between 3 and 63 characters long", parameterName),
"InvalidBucketName",
ErrorCategory.InvalidArgument,
bucketName));
}
if (!System.Text.RegularExpressions.Regex.IsMatch(bucketName, @"^[a-z0-9][a-z0-9\-]*[a-z0-9]$"))
{
ThrowTerminatingError(new ErrorRecord(
new ArgumentException($"Bucket name contains invalid characters. Must contain only lowercase letters, numbers, and hyphens", parameterName),
"InvalidBucketName",
ErrorCategory.InvalidArgument,
bucketName));
}
}
///
/// Validates that an object name is valid
///
/// Object name to validate
/// Parameter name for error reporting
protected void ValidateObjectName(string objectName, string parameterName = "ObjectName")
{
if (string.IsNullOrWhiteSpace(objectName))
{
ThrowTerminatingError(new ErrorRecord(
new ArgumentException($"Object name cannot be null or empty", parameterName),
"InvalidObjectName",
ErrorCategory.InvalidArgument,
objectName));
}
if (objectName.Length > 1024)
{
ThrowTerminatingError(new ErrorRecord(
new ArgumentException($"Object name cannot be longer than 1024 characters", parameterName),
"InvalidObjectName",
ErrorCategory.InvalidArgument,
objectName));
}
}
///
/// Writes verbose output with consistent formatting
///
/// Message to write
/// Format arguments
protected void WriteVerboseMessage(string message, params object[] args)
{
MinIOLogger.WriteVerbose(this, message, args);
}
///
/// Writes warning output with consistent formatting
///
/// Warning message to write
/// Format arguments
protected void WriteWarningMessage(string message, params object[] args)
{
MinIOLogger.WriteWarning(this, message, args);
}
///
/// Writes debug output with consistent formatting
///
/// Debug message to write
/// Format arguments
protected void WriteDebugMessage(string message, params object[] args)
{
MinIOLogger.WriteDebug(this, message, args);
}
}
}