Files
PSMinIO/src/Cmdlets/MinIOBaseCmdlet.cs
T
PSMinIO Developer b237838ff1 Complete rebuild: Custom REST API implementation
- Removed MinIO SDK dependency completely
- Built custom HTTP client with AWS S3 signature v4 support
- Created synchronous operations optimized for PowerShell
- Added real progress reporting during transfers
- Implemented performance metrics in result objects
- Fixed null pointer warnings for code quality
- Removed Write-Host usage from test scripts
- Cleaned up unnecessary DLL dependencies

Available cmdlets:
- Connect-MinIO: Enhanced connection with certificate options
- Get-MinIOBucket: List buckets with sorting and filtering
- New-MinIOBucket: Create buckets with region support
- Test-MinIOBucketExists: Check bucket existence
- Get-MinIOObject: List objects with advanced filtering
- New-MinIOObject: Upload files with real progress
- Get-MinIOObjectContent: Download files with progress

This represents a complete architectural overhaul for better PowerShell compatibility and performance.
2025-07-11 14:36:39 -04:00

275 lines
11 KiB
C#

using System;
using System.Management.Automation;
using PSMinIO.Core;
using PSMinIO.Core.S3;
using PSMinIO.Utils;
namespace PSMinIO.Cmdlets
{
/// <summary>
/// Base class for all MinIO PowerShell cmdlets
/// Provides common functionality including connection management, logging, and client access
/// </summary>
public abstract class MinIOBaseCmdlet : PSCmdlet
{
private MinIOConnection? _connection;
/// <summary>
/// MinIO connection to use for operations. Can be provided via parameter or retrieved from session.
/// </summary>
[Parameter(ValueFromPipeline = true)]
[Alias("Connection")]
public MinIOConnection? MinIOConnection { get; set; }
/// <summary>
/// Name of session variable containing the MinIO connection (default: MinIOConnection)
/// </summary>
[Parameter]
[ValidateNotNullOrEmpty]
public string SessionVariable { get; set; } = "MinIOConnection";
/// <summary>
/// Gets the MinIO connection instance
/// </summary>
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!;
}
}
/// <summary>
/// Gets the MinIO S3 client instance
/// </summary>
protected MinIOS3Client S3Client => Connection.S3Client;
/// <summary>
/// Gets the connection from parameter or session variable
/// </summary>
/// <returns>MinIO connection or null if not found</returns>
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;
}
/// <summary>
/// Executes an operation with consistent error handling and logging
/// </summary>
/// <param name="operationName">Name of the operation for logging</param>
/// <param name="operation">Operation to execute</param>
/// <param name="details">Optional operation details for logging</param>
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));
}
}
/// <summary>
/// Executes an operation with return value and consistent error handling and logging
/// </summary>
/// <typeparam name="T">Return type</typeparam>
/// <param name="operationName">Name of the operation for logging</param>
/// <param name="operation">Operation to execute</param>
/// <param name="details">Optional operation details for logging</param>
/// <returns>Operation result</returns>
protected T ExecuteOperation<T>(string operationName, Func<T> 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;
}
}
/// <summary>
/// Determines the appropriate PowerShell error category for an exception
/// </summary>
/// <param name="exception">Exception to categorize</param>
/// <returns>Appropriate error category</returns>
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
};
}
/// <summary>
/// Validates that a bucket name is valid according to S3 naming rules
/// </summary>
/// <param name="bucketName">Bucket name to validate</param>
/// <param name="parameterName">Parameter name for error reporting</param>
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));
}
}
/// <summary>
/// Validates that an object name is valid
/// </summary>
/// <param name="objectName">Object name to validate</param>
/// <param name="parameterName">Parameter name for error reporting</param>
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));
}
}
/// <summary>
/// Writes verbose output with consistent formatting
/// </summary>
/// <param name="message">Message to write</param>
/// <param name="args">Format arguments</param>
protected void WriteVerboseMessage(string message, params object[] args)
{
MinIOLogger.WriteVerbose(this, message, args);
}
/// <summary>
/// Writes warning output with consistent formatting
/// </summary>
/// <param name="message">Warning message to write</param>
/// <param name="args">Format arguments</param>
protected void WriteWarningMessage(string message, params object[] args)
{
MinIOLogger.WriteWarning(this, message, args);
}
/// <summary>
/// Writes debug output with consistent formatting
/// </summary>
/// <param name="message">Debug message to write</param>
/// <param name="args">Format arguments</param>
protected void WriteDebugMessage(string message, params object[] args)
{
MinIOLogger.WriteDebug(this, message, args);
}
}
}