322 lines
11 KiB
C#
322 lines
11 KiB
C#
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);
|
|
}
|
|
}
|