ByteShelfCommon 1.2.2
dotnet add package ByteShelfCommon --version 1.2.2
NuGet\Install-Package ByteShelfCommon -Version 1.2.2
<PackageReference Include="ByteShelfCommon" Version="1.2.2" />
<PackageVersion Include="ByteShelfCommon" Version="1.2.2" />
<PackageReference Include="ByteShelfCommon" />
paket add ByteShelfCommon --version 1.2.2
#r "nuget: ByteShelfCommon, 1.2.2"
#:package ByteShelfCommon@1.2.2
#addin nuget:?package=ByteShelfCommon&version=1.2.2
#tool nuget:?package=ByteShelfCommon&version=1.2.2
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 chunkingReadFileAsync()
- Read a file with configurable retrieval method (chunked vs single endpoint)DeleteFileAsync()
- Delete a file and all its chunksGetFilesAsync()
- 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 tenantReadFileForTenantAsync()
- Read file from a specific tenantDeleteFileForTenantAsync()
- Delete file from a specific tenantGetFilesForTenantAsync()
- Get files from a specific tenant
Storage Management:
GetStorageInfoAsync()
- Get current storage usage and limitsCanStoreFileAsync()
- Check if a file can be stored within quota
Tenant Information:
GetTenantInfoAsync()
- Get current tenant information including admin statusGetSubTenantsAsync()
- Get all subtenants of the current tenantGetSubTenantAsync()
- Get information about a specific subtenantGetSubTenantsUnderSubTenantAsync()
- Get subtenants under a specific subtenant
Subtenant Management:
CreateSubTenantAsync()
- Create a new subtenant under the current tenantCreateSubTenantUnderSubTenantAsync()
- Create a subtenant under another subtenantUpdateSubTenantStorageLimitAsync()
- Update a subtenant's storage limitDeleteSubTenantAsync()
- 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 fileOriginalFilename
: Original name of the uploaded fileContentType
: MIME type of the fileFileSize
: Total size of the file in bytesChunkIds
: List of chunk IDs that make up the fileCreatedAt
: 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 authenticationStorageLimitBytes
: Maximum storage allowed (0 = unlimited for admins)DisplayName
: Human-readable name for the tenantIsAdmin
: Whether the tenant has administrative privilegesParent
: 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 bytesLimitBytes
: Storage limit in bytes (0 = unlimited)AvailableBytes
: Available storage spaceFileCount
: 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 tenantDisplayName
: Human-readable name for the tenantIsAdmin
: Whether the tenant has administrative privilegesStorageLimitBytes
: Maximum storage allowed (0 = unlimited for admins)CurrentUsageBytes
: Current storage usage in bytesAvailableSpaceBytes
: Available storage space in bytesUsagePercentage
: 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 storedRequiredBytes
: Bytes required for the fileAvailableBytes
: Available storage spaceErrorMessage
: 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
- Create the new model class
- Add appropriate properties with proper types
- Include XML documentation
- Add unit tests
- Update this README if needed
Adding New Interfaces
- Define the interface contract
- Document all methods and properties
- Add unit tests for interface compliance
- 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.
๐ Related Documentation
- ByteShelf API Server - Server implementation
- ByteShelfClient - Client implementation
- Main README - Overview of the entire solution
Product | Versions 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. |
-
net8.0
- System.Text.Json (>= 9.0.6)
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.