ByteShelfCommon 1.2.2

dotnet add package ByteShelfCommon --version 1.2.2
                    
NuGet\Install-Package ByteShelfCommon -Version 1.2.2
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="ByteShelfCommon" Version="1.2.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="ByteShelfCommon" Version="1.2.2" />
                    
Directory.Packages.props
<PackageReference Include="ByteShelfCommon" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add ByteShelfCommon --version 1.2.2
                    
#r "nuget: ByteShelfCommon, 1.2.2"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package ByteShelfCommon@1.2.2
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=ByteShelfCommon&version=1.2.2
                    
Install as a Cake Addin
#tool nuget:?package=ByteShelfCommon&version=1.2.2
                    
Install as a Cake Tool

ByteShelfCommon

ByteShelfCommon is a shared library that contains the core data structures, interfaces, and models used across the ByteShelf ecosystem. It ensures type safety and consistency between the client and server components, providing a common contract for file storage operations.

๐ŸŽฏ Purpose

This library serves as the foundation for the ByteShelf system by defining:

  • Data Structures: File metadata, tenant information, and storage details
  • Interfaces: Contracts for file operations and content providers
  • Models: Request/response models for API communication
  • Types: Common types and enums used throughout the system

๐Ÿ“ฆ Project Structure

ByteShelfCommon/
โ”œโ”€โ”€ ShelfFileMetadata.cs           # File metadata structure
โ”œโ”€โ”€ ShelfFile.cs                   # File representation with content
โ”œโ”€โ”€ IContentProvider.cs            # Content provider interface
โ”œโ”€โ”€ IShelfFileProvider.cs          # File provider interface
โ”œโ”€โ”€ TenantInfo.cs                  # Tenant information model with hierarchy
โ”œโ”€โ”€ TenantStorageInfo.cs           # Tenant storage usage information
โ”œโ”€โ”€ QuotaCheckResult.cs            # Storage quota check results
โ”œโ”€โ”€ CreateTenantRequest.cs         # Tenant creation request model
โ”œโ”€โ”€ CreateSubTenantRequest.cs      # Subtenant creation request model
โ”œโ”€โ”€ UpdateStorageLimitRequest.cs   # Storage limit update request
โ””โ”€โ”€ ByteShelfCommon.csproj         # Project file

๐Ÿ”Œ Core Interfaces

IShelfFileProvider

The main interface for file storage operations, implemented by both client and server components.

public interface IShelfFileProvider
{
    // Core file operations
    Task<Guid> WriteFileAsync(string filename, string contentType, Stream content);
    Task<ShelfFile> ReadFileAsync(Guid fileId);
    Task<ShelfFile> ReadFileAsync(Guid fileId, bool useChunked);
    Task DeleteFileAsync(Guid fileId);
    Task<IEnumerable<ShelfFileMetadata>> GetFilesAsync();
    
    // Enhanced file operations with quota checking
    Task<Guid> WriteFileWithQuotaCheckAsync(string filename, string contentType, Stream content, bool checkQuotaFirst = true);
    
    // Tenant-specific file operations (parent access required)
    Task<Guid> WriteFileForTenantAsync(string targetTenantId, string filename, string contentType, Stream content);
    Task<ShelfFile> ReadFileForTenantAsync(string targetTenantId, Guid fileId);
    Task DeleteFileForTenantAsync(string targetTenantId, Guid fileId);
    Task<IEnumerable<ShelfFileMetadata>> GetFilesForTenantAsync(string targetTenantId);
    
    // Storage management
    Task<TenantStorageInfo> GetStorageInfoAsync();
    Task<QuotaCheckResult> CanStoreFileAsync(long fileSizeBytes);
    
    // Tenant information and management
    Task<TenantInfoResponse> GetTenantInfoAsync();
    Task<Dictionary<string, TenantInfoResponse>> GetSubTenantsAsync();
    Task<TenantInfoResponse> GetSubTenantAsync(string subTenantId);
    Task<Dictionary<string, TenantInfoResponse>> GetSubTenantsUnderSubTenantAsync(string parentSubtenantId);
    
    // Subtenant management
    Task<string> CreateSubTenantAsync(string displayName);
    Task<string> CreateSubTenantUnderSubTenantAsync(string parentSubtenantId, string displayName);
    Task UpdateSubTenantStorageLimitAsync(string subTenantId, long storageLimitBytes);
    Task DeleteSubTenantAsync(string subTenantId);
}

Core File Operations:

  • WriteFileAsync() - Write a file to storage with automatic chunking
  • ReadFileAsync() - Read a file with configurable retrieval method (chunked vs single endpoint)
  • DeleteFileAsync() - Delete a file and all its chunks
  • GetFilesAsync() - Get metadata for all stored files

Enhanced File Operations:

  • WriteFileWithQuotaCheckAsync() - Write file with optional quota checking before upload

Tenant-Specific Operations:

  • WriteFileForTenantAsync() - Write file to a specific tenant
  • ReadFileForTenantAsync() - Read file from a specific tenant
  • DeleteFileForTenantAsync() - Delete file from a specific tenant
  • GetFilesForTenantAsync() - Get files from a specific tenant

Storage Management:

  • GetStorageInfoAsync() - Get current storage usage and limits
  • CanStoreFileAsync() - Check if a file can be stored within quota

Tenant Information:

  • GetTenantInfoAsync() - Get current tenant information including admin status
  • GetSubTenantsAsync() - Get all subtenants of the current tenant
  • GetSubTenantAsync() - Get information about a specific subtenant
  • GetSubTenantsUnderSubTenantAsync() - Get subtenants under a specific subtenant

Subtenant Management:

  • CreateSubTenantAsync() - Create a new subtenant under the current tenant
  • CreateSubTenantUnderSubTenantAsync() - Create a subtenant under another subtenant
  • UpdateSubTenantStorageLimitAsync() - Update a subtenant's storage limit
  • DeleteSubTenantAsync() - Delete a subtenant

IContentProvider

Interface for providing file content streams, used for efficient content delivery.

public interface IContentProvider
{
    Stream GetContentStream();
}

๐Ÿ“Š Data Models

ShelfFileMetadata

Represents file metadata without the actual content.

public class ShelfFileMetadata
{
    public Guid FileId { get; set; }
    public string OriginalFilename { get; set; }
    public string ContentType { get; set; }
    public long FileSize { get; set; }
    public List<Guid> ChunkIds { get; set; }
    public DateTime CreatedAt { get; set; }
}

Properties:

  • FileId: Unique identifier for the file
  • OriginalFilename: Original name of the uploaded file
  • ContentType: MIME type of the file
  • FileSize: Total size of the file in bytes
  • ChunkIds: List of chunk IDs that make up the file
  • CreatedAt: Timestamp when the file was created

ShelfFile

Represents a complete file with both metadata and content.

public class ShelfFile
{
    public ShelfFileMetadata Metadata { get; }
    public IContentProvider ContentProvider { get; }
    
    public Stream GetContentStream() => ContentProvider.GetContentStream();
}

Usage:

ShelfFile file = await provider.ReadFileAsync(fileId);

// Access metadata
Console.WriteLine($"File: {file.Metadata.OriginalFilename}");
Console.WriteLine($"Size: {file.Metadata.FileSize} bytes");

// Access content
using Stream content = file.GetContentStream();
// Process the content...

TenantInfo

Represents tenant configuration and information, including hierarchical relationships.

public class TenantInfo
{
    public string ApiKey { get; set; }
    public long StorageLimitBytes { get; set; }
    public string DisplayName { get; set; }
    public bool IsAdmin { get; set; }
    public TenantInfo? Parent { get; set; }
    public Dictionary<string, TenantInfo> SubTenants { get; set; }
}

Properties:

  • ApiKey: API key for authentication
  • StorageLimitBytes: Maximum storage allowed (0 = unlimited for admins)
  • DisplayName: Human-readable name for the tenant
  • IsAdmin: Whether the tenant has administrative privileges
  • Parent: Reference to the parent tenant (null for root tenants)
  • SubTenants: Dictionary of subtenants keyed by tenant ID

Hierarchical Features:

  • Parent References: Runtime navigation to parent tenant (not serialized to avoid circular references)
  • Subtenant Management: Support for nested tenant structures up to 10 levels deep
  • Shared Storage: Parent and subtenants can share storage quotas
  • API Key Inheritance: Subtenants can access parent's files, but not vice versa

TenantStorageInfo

Represents current storage usage for a tenant.

public class TenantStorageInfo
{
    public long UsedBytes { get; set; }
    public long LimitBytes { get; set; }
    public long AvailableBytes { get; set; }
    public int FileCount { get; set; }
}

Properties:

  • UsedBytes: Current storage usage in bytes
  • LimitBytes: Storage limit in bytes (0 = unlimited)
  • AvailableBytes: Available storage space
  • FileCount: Number of files stored

TenantInfoResponse

Represents tenant information returned by the API, including admin status and current usage.

public class TenantInfoResponse
{
    public string TenantId { get; }
    public string DisplayName { get; }
    public bool IsAdmin { get; }
    public long StorageLimitBytes { get; }
    public long CurrentUsageBytes { get; }
    public long AvailableSpaceBytes { get; }
    public double UsagePercentage { get; }
}

Properties:

  • TenantId: Unique identifier for the tenant
  • DisplayName: Human-readable name for the tenant
  • IsAdmin: Whether the tenant has administrative privileges
  • StorageLimitBytes: Maximum storage allowed (0 = unlimited for admins)
  • CurrentUsageBytes: Current storage usage in bytes
  • AvailableSpaceBytes: Available storage space in bytes
  • UsagePercentage: Percentage of storage used (0-100)

QuotaCheckResult

Result of a storage quota check operation.

public class QuotaCheckResult
{
    public bool CanStore { get; set; }
    public long RequiredBytes { get; set; }
    public long AvailableBytes { get; set; }
    public string? ErrorMessage { get; set; }
}

Properties:

  • CanStore: Whether the file can be stored
  • RequiredBytes: Bytes required for the file
  • AvailableBytes: Available storage space
  • ErrorMessage: Error message if storage is not possible

๐Ÿ“ Request/Response Models

UpdateStorageLimitRequest

Request model for updating tenant storage limits.

public class UpdateStorageLimitRequest
{
    public long StorageLimitBytes { get; set; }
}

CreateTenantRequest

Request model for creating a new tenant.

public class CreateTenantRequest
{
    public string TenantId { get; set; }
    public string DisplayName { get; set; }
    public long StorageLimitBytes { get; set; }
    public bool IsAdmin { get; set; }
}

CreateSubTenantRequest

Request model for creating a new subtenant.

public class CreateSubTenantRequest
{
    public string DisplayName { get; set; }
}

Properties:

  • DisplayName: Human-readable name for the subtenant

Notes:

  • The subtenant ID is automatically generated as a GUID
  • The API key is automatically generated as a unique key
  • The storage limit is initially set to match the parent's limit
  • The subtenant inherits the parent's storage quota

๐Ÿ”ง Usage Examples

Working with File Metadata

// Create metadata
ShelfFileMetadata metadata = new ShelfFileMetadata
{
    FileId = Guid.NewGuid(),
    OriginalFilename = "example.txt",
    ContentType = "text/plain",
    FileSize = 1024,
    ChunkIds = new List<Guid> { Guid.NewGuid() },
    CreatedAt = DateTime.UtcNow
};

// Access metadata properties
Console.WriteLine($"File ID: {metadata.FileId}");
Console.WriteLine($"Filename: {metadata.OriginalFilename}");
Console.WriteLine($"Size: {metadata.FileSize} bytes");
Console.WriteLine($"Chunks: {metadata.ChunkIds.Count}");

Working with Tenant Information

// Create tenant info
TenantInfo tenant = new TenantInfo
{
    TenantId = "tenant1",
    DisplayName = "Tenant 1",
    ApiKey = "secure-api-key",
    StorageLimitBytes = 1073741824, // 1GB
    IsAdmin = false
};

// Check if tenant is admin
if (tenant.IsAdmin)
{
    Console.WriteLine($"{tenant.DisplayName} has admin privileges");
}

// Check storage limit
if (tenant.StorageLimitBytes == 0)
{
    Console.WriteLine($"{tenant.DisplayName} has unlimited storage");
}
else
{
    Console.WriteLine($"{tenant.DisplayName} has {tenant.StorageLimitBytes} bytes limit");
}

Working with Storage Information

// Get storage info
TenantStorageInfo storageInfo = await provider.GetStorageInfoAsync();

// Display usage information
Console.WriteLine($"Used: {storageInfo.UsedBytes} bytes");
Console.WriteLine($"Limit: {storageInfo.LimitBytes} bytes");
Console.WriteLine($"Available: {storageInfo.AvailableBytes} bytes");
Console.WriteLine($"Files: {storageInfo.FileCount}");

// Check if storage is unlimited
bool isUnlimited = storageInfo.LimitBytes == 0;
if (isUnlimited)
{
    Console.WriteLine("Storage is unlimited");
}
else
{
    double usagePercent = (double)storageInfo.UsedBytes / storageInfo.LimitBytes * 100;
    Console.WriteLine($"Usage: {usagePercent:F1}%");
}

Working with Tenant Information and Admin Status

// Get tenant information including admin status
TenantInfoResponse tenantInfo = await provider.GetTenantInfoAsync();

// Display tenant information
Console.WriteLine($"Tenant: {tenantInfo.DisplayName}");
Console.WriteLine($"Admin: {tenantInfo.IsAdmin}");
Console.WriteLine($"Storage Limit: {tenantInfo.StorageLimitBytes} bytes");
Console.WriteLine($"Current Usage: {tenantInfo.CurrentUsageBytes} bytes");
Console.WriteLine($"Usage Percentage: {tenantInfo.UsagePercentage:F1}%");

// Check admin privileges
if (tenantInfo.IsAdmin)
{
    Console.WriteLine("This tenant has administrative privileges");
    // Enable admin-specific features
    // Show admin UI controls
    // Allow access to admin endpoints
}
else
{
    Console.WriteLine("This is a regular tenant");
    // Show regular user interface
    // Hide admin-specific features
}

Working with Quota Checks

// Check if a file can be stored
long fileSize = 1024 * 1024; // 1MB
QuotaCheckResult result = await provider.CheckQuotaAsync(fileSize);

if (result.CanStore)
{
    Console.WriteLine($"Can store {fileSize} bytes");
    Console.WriteLine($"Available: {result.AvailableBytes} bytes");
}
else
{
    Console.WriteLine($"Cannot store file: {result.ErrorMessage}");
    Console.WriteLine($"Required: {result.RequiredBytes} bytes");
    Console.WriteLine($"Available: {result.AvailableBytes} bytes");
}

Working with Hierarchical Tenant Structures

// Create a subtenant
string subTenantId = await provider.CreateSubTenantAsync("Department A");

// List all subtenants
var subTenants = await provider.GetSubTenantsAsync();
foreach (var subTenant in subTenants)
{
    Console.WriteLine($"Subtenant: {subTenant.DisplayName}");
    Console.WriteLine($"Storage Limit: {subTenant.StorageLimitBytes} bytes");
}

// Get specific subtenant information
var subTenant = await provider.GetSubTenantAsync(subTenantId);
Console.WriteLine($"Subtenant ID: {subTenantId}");
Console.WriteLine($"Display Name: {subTenant.DisplayName}");
Console.WriteLine($"Storage Limit: {subTenant.StorageLimitBytes} bytes");
Console.WriteLine($"Current Usage: {subTenant.CurrentUsageBytes} bytes");

// Update subtenant storage limit
long newLimit = 500 * 1024 * 1024; // 500MB
await provider.UpdateSubTenantStorageLimitAsync(subTenantId, newLimit);

// Delete a subtenant
await provider.DeleteSubTenantAsync(subTenantId);

Working with Shared Storage Quotas

// Check storage availability considering shared quotas
TenantInfoResponse tenantInfo = await provider.GetTenantInfoAsync();

if (tenantInfo.StorageLimitBytes > 0)
{
    // Tenant has a specific storage limit
    double usagePercent = tenantInfo.UsagePercentage;
    long availableBytes = tenantInfo.AvailableSpaceBytes;
    
    Console.WriteLine($"Usage: {usagePercent:F1}%");
    Console.WriteLine($"Available: {availableBytes} bytes");
    
    if (usagePercent > 90)
    {
        Console.WriteLine("Warning: Storage usage is high!");
    }
}
else
{
    // Unlimited storage (admin tenant)
    Console.WriteLine("Unlimited storage available");
}

// Check if a large file can be stored (considers shared quotas)
long largeFileSize = 100 * 1024 * 1024; // 100MB
bool canStore = await provider.CanStoreFileAsync(largeFileSize);

if (canStore)
{
    Console.WriteLine("Large file can be stored");
}
else
{
    Console.WriteLine("Cannot store large file - quota exceeded");
}

๐Ÿงช Testing

Unit Tests

dotnet test ByteShelfCommon.Tests

Test Coverage

The ByteShelfCommon library includes comprehensive unit tests for:

  • Data model validation
  • Interface contract verification
  • Serialization/deserialization
  • Edge cases and error conditions

๐Ÿ”’ Type Safety

Nullable Reference Types

The library uses nullable reference types to ensure type safety:

  • Required properties are non-nullable
  • Optional properties are nullable
  • Proper null checking is enforced at compile time

Validation

Data models include validation attributes where appropriate:

  • Required fields are marked as required
  • String lengths are validated
  • Numeric ranges are enforced

๐Ÿ“š Integration

Client Integration

The ByteShelfClient library implements IShelfFileProvider and uses these data models for all operations.

Server Integration

The ByteShelf API server uses these models for:

  • API request/response serialization
  • Internal data representation
  • Database storage (where applicable)

Cross-Platform Compatibility

The library is designed to work across different .NET platforms:

  • .NET 8.0+
  • ASP.NET Core
  • Console applications
  • Desktop applications

๐Ÿ”ง Development

Adding New Models

  1. Create the new model class
  2. Add appropriate properties with proper types
  3. Include XML documentation
  4. Add unit tests
  5. Update this README if needed

Adding New Interfaces

  1. Define the interface contract
  2. Document all methods and properties
  3. Add unit tests for interface compliance
  4. Update implementations in client and server

Versioning

  • Follow semantic versioning
  • Maintain backward compatibility when possible
  • Document breaking changes clearly

Working with Parent Access to Subtenant Files

The ByteShelf system supports hierarchical access where parent tenants can access files from their subtenants:

// List files from a subtenant
IEnumerable<ShelfFileMetadata> subtenantFiles = await provider.GetFilesForTenantAsync("subtenant-id");

// Download a file from a subtenant
ShelfFile subtenantFile = await provider.ReadFileForTenantAsync("subtenant-id", fileId);

// Upload a file to a subtenant
Guid uploadedFileId = await provider.WriteFileForTenantAsync("subtenant-id", "filename.txt", "text/plain", contentStream);

// Delete a file from a subtenant
await provider.DeleteFileForTenantAsync("subtenant-id", fileId);

Access Control: All tenant-specific operations require that the authenticated tenant has access to the target tenant (either be the same tenant or a parent). If access is denied, an UnauthorizedAccessException is thrown.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (2)

Showing the top 2 NuGet packages that depend on ByteShelfCommon:

Package Downloads
ByteShelfClient

Client for ByteShelf which is a three-part C# file storage system: server, client and common

EasyReasy.ByteShelfProvider

Enables resources from ByteShelf for EasyReasy

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.2.2 144 7/14/2025
1.2.1 139 7/13/2025
1.2.0 135 7/13/2025
1.1.1 125 7/11/2025
1.1.0 143 7/3/2025
1.0.0 150 6/18/2025