♻️ Extract the Storage service to DysonNetwork.Drive microservice
This commit is contained in:
321
DysonNetwork.Drive/Services/FileReferenceService.cs
Normal file
321
DysonNetwork.Drive/Services/FileReferenceService.cs
Normal file
@ -0,0 +1,321 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using DysonNetwork.Drive.Data;
|
||||
using DysonNetwork.Drive.Interfaces;
|
||||
using DysonNetwork.Drive.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NodaTime;
|
||||
|
||||
namespace DysonNetwork.Drive.Services;
|
||||
|
||||
public class FileReferenceService : IFileReferenceService, IDisposable
|
||||
{
|
||||
private readonly AppDatabase _dbContext;
|
||||
private readonly IFileService _fileService;
|
||||
private readonly IClock _clock;
|
||||
private readonly ILogger<FileReferenceService> _logger;
|
||||
private bool _disposed = false;
|
||||
|
||||
public FileReferenceService(
|
||||
AppDatabase dbContext,
|
||||
IFileService fileService,
|
||||
IClock clock,
|
||||
ILogger<FileReferenceService> logger)
|
||||
{
|
||||
_dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
|
||||
_fileService = fileService ?? throw new ArgumentNullException(nameof(fileService));
|
||||
_clock = clock ?? throw new ArgumentNullException(nameof(clock));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
public async Task<CloudFileReference> CreateReferenceAsync(
|
||||
Guid fileId,
|
||||
string resourceId,
|
||||
string resourceType,
|
||||
string referenceType,
|
||||
string? referenceId = null,
|
||||
string? referenceName = null,
|
||||
string? referenceMimeType = null,
|
||||
long? referenceSize = null,
|
||||
string? referenceUrl = null,
|
||||
string? referenceThumbnailUrl = null,
|
||||
string? referencePreviewUrl = null,
|
||||
string? referenceMetadata = null,
|
||||
IDictionary<string, object>? metadata = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
// Verify file exists
|
||||
var fileExists = await _fileService.FileExistsAsync(fileId, cancellationToken);
|
||||
if (!fileExists)
|
||||
{
|
||||
throw new FileNotFoundException($"File with ID {fileId} not found.");
|
||||
}
|
||||
|
||||
var reference = new CloudFileReference
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
FileId = fileId,
|
||||
ResourceId = resourceId,
|
||||
ResourceType = resourceType,
|
||||
ReferenceType = referenceType,
|
||||
ReferenceId = referenceId,
|
||||
ReferenceName = referenceName,
|
||||
ReferenceMimeType = referenceMimeType,
|
||||
ReferenceSize = referenceSize,
|
||||
ReferenceUrl = referenceUrl,
|
||||
ReferenceThumbnailUrl = referenceThumbnailUrl,
|
||||
ReferencePreviewUrl = referencePreviewUrl,
|
||||
ReferenceMetadata = referenceMetadata,
|
||||
IsActive = true,
|
||||
CreatedAt = _clock.GetCurrentInstant().ToDateTimeOffset()
|
||||
};
|
||||
|
||||
if (metadata != null && metadata.Any())
|
||||
{
|
||||
var options = new JsonSerializerOptions { WriteIndented = true };
|
||||
reference.Metadata = JsonDocument.Parse(JsonSerializer.Serialize(metadata, options));
|
||||
}
|
||||
|
||||
_dbContext.FileReferences.Add(reference);
|
||||
await _dbContext.SaveChangesAsync(cancellationToken);
|
||||
|
||||
_logger.LogInformation(
|
||||
"Created reference {ReferenceId} for file {FileId} to resource {ResourceType}/{ResourceId}",
|
||||
reference.Id, fileId, resourceType, resourceId);
|
||||
|
||||
return reference;
|
||||
}
|
||||
|
||||
public async Task<CloudFileReference> GetReferenceAsync(Guid referenceId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var reference = await _dbContext.FileReferences
|
||||
.AsNoTracking()
|
||||
.Include(r => r.File)
|
||||
.FirstOrDefaultAsync(r => r.Id == referenceId, cancellationToken);
|
||||
|
||||
if (reference == null)
|
||||
{
|
||||
throw new KeyNotFoundException($"Reference with ID {referenceId} not found.");
|
||||
}
|
||||
|
||||
return reference;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<CloudFileReference>> GetReferencesForFileAsync(Guid fileId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await _dbContext.FileReferences
|
||||
.AsNoTracking()
|
||||
.Where(r => r.FileId == fileId && r.IsActive)
|
||||
.ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<CloudFileReference>> GetReferencesForResourceAsync(
|
||||
string resourceId,
|
||||
string resourceType,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await _dbContext.FileReferences
|
||||
.AsNoTracking()
|
||||
.Where(r => r.ResourceId == resourceId &&
|
||||
r.ResourceType == resourceType &&
|
||||
r.IsActive)
|
||||
.ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<CloudFileReference>> GetReferencesOfTypeAsync(
|
||||
string referenceType,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await _dbContext.FileReferences
|
||||
.AsNoTracking()
|
||||
.Where(r => r.ReferenceType == referenceType && r.IsActive)
|
||||
.ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<bool> DeleteReferenceAsync(Guid referenceId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var reference = await _dbContext.FileReferences
|
||||
.FirstOrDefaultAsync(r => r.Id == referenceId, cancellationToken);
|
||||
|
||||
if (reference == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
reference.IsActive = false;
|
||||
await _dbContext.SaveChangesAsync(cancellationToken);
|
||||
|
||||
_logger.LogInformation("Deleted reference {ReferenceId}", referenceId);
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<int> DeleteReferencesForFileAsync(Guid fileId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var references = await _dbContext.FileReferences
|
||||
.Where(r => r.FileId == fileId && r.IsActive)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
foreach (var reference in references)
|
||||
{
|
||||
reference.IsActive = false;
|
||||
}
|
||||
|
||||
var count = await _dbContext.SaveChangesAsync(cancellationToken);
|
||||
|
||||
_logger.LogInformation("Deleted {Count} references for file {FileId}", count, fileId);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
public async Task<int> DeleteReferencesForResourceAsync(
|
||||
string resourceId,
|
||||
string resourceType,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var references = await _dbContext.FileReferences
|
||||
.Where(r => r.ResourceId == resourceId &&
|
||||
r.ResourceType == resourceType &&
|
||||
r.IsActive)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
foreach (var reference in references)
|
||||
{
|
||||
reference.IsActive = false;
|
||||
}
|
||||
|
||||
var count = await _dbContext.SaveChangesAsync(cancellationToken);
|
||||
|
||||
_logger.LogInformation(
|
||||
"Deleted {Count} references for resource {ResourceType}/{ResourceId}",
|
||||
count, resourceType, resourceId);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
public async Task<CloudFileReference> UpdateReferenceMetadataAsync(
|
||||
Guid referenceId,
|
||||
IDictionary<string, object> metadata,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var reference = await GetReferenceAsync(referenceId, cancellationToken);
|
||||
|
||||
var options = new JsonSerializerOptions { WriteIndented = true };
|
||||
reference.Metadata = JsonDocument.Parse(JsonSerializer.Serialize(metadata, options));
|
||||
reference.UpdatedAt = _clock.GetCurrentInstant().ToDateTimeOffset();
|
||||
|
||||
await _dbContext.SaveChangesAsync(cancellationToken);
|
||||
|
||||
_logger.LogInformation("Updated metadata for reference {ReferenceId}", referenceId);
|
||||
|
||||
return reference;
|
||||
}
|
||||
|
||||
public async Task<bool> ReferenceExistsAsync(Guid referenceId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await _dbContext.FileReferences
|
||||
.AsNoTracking()
|
||||
.AnyAsync(r => r.Id == referenceId && r.IsActive, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<bool> HasReferenceAsync(
|
||||
Guid fileId,
|
||||
string resourceId,
|
||||
string resourceType,
|
||||
string? referenceType = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var query = _dbContext.FileReferences
|
||||
.AsNoTracking()
|
||||
.Where(r => r.FileId == fileId &&
|
||||
r.ResourceId == resourceId &&
|
||||
r.ResourceType == resourceType &&
|
||||
r.IsActive);
|
||||
|
||||
if (!string.IsNullOrEmpty(referenceType))
|
||||
{
|
||||
query = query.Where(r => r.ReferenceType == referenceType);
|
||||
}
|
||||
|
||||
return await query.AnyAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<CloudFileReference> UpdateReferenceResourceAsync(
|
||||
Guid referenceId,
|
||||
string newResourceId,
|
||||
string newResourceType,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var reference = await GetReferenceAsync(referenceId, cancellationToken);
|
||||
|
||||
reference.ResourceId = newResourceId;
|
||||
reference.ResourceType = newResourceType;
|
||||
reference.UpdatedAt = _clock.GetCurrentInstant().ToDateTimeOffset();
|
||||
|
||||
await _dbContext.SaveChangesAsync(cancellationToken);
|
||||
|
||||
_logger.LogInformation(
|
||||
"Updated reference {ReferenceId} to point to resource {ResourceType}/{ResourceId}",
|
||||
referenceId, newResourceType, newResourceId);
|
||||
|
||||
return reference;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<CloudFile>> GetFilesForResourceAsync(
|
||||
string resourceId,
|
||||
string resourceType,
|
||||
string? referenceType = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var query = _dbContext.FileReferences
|
||||
.AsNoTracking()
|
||||
.Include(r => r.File)
|
||||
.Where(r => r.ResourceId == resourceId &&
|
||||
r.ResourceType == resourceType &&
|
||||
r.IsActive);
|
||||
|
||||
if (!string.IsNullOrEmpty(referenceType))
|
||||
{
|
||||
query = query.Where(r => r.ReferenceType == referenceType);
|
||||
}
|
||||
|
||||
var references = await query.ToListAsync(cancellationToken);
|
||||
return references.Select(r => r.File!);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<CloudFile>> GetFilesForReferenceTypeAsync(
|
||||
string referenceType,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var references = await _dbContext.FileReferences
|
||||
.AsNoTracking()
|
||||
.Include(r => r.File)
|
||||
.Where(r => r.ReferenceType == referenceType && r.IsActive)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
return references.Select(r => r.File!);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_dbContext?.Dispose();
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user