Skip to content

Commit

Permalink
Merge branch 'main' into add-csv-export
Browse files Browse the repository at this point in the history
  • Loading branch information
MiraGeowerkstatt authored Dec 10, 2024
2 parents 1025a89 + f909fae commit 880e6c5
Show file tree
Hide file tree
Showing 23 changed files with 3,436 additions and 142 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
- The `alternate_name` is now displayed in the borehole detail header and the map markers.
- From depth and to depth are no longer displayed in groundwater level measurements.
- Updated the layout of the borehole general tab.
- Removed deduplication check when adding and detaching attachments.
- When copying a borehole, attachments won't be copied.
- Removed layers settings for anonymous users.

### Fixed

Expand All @@ -37,6 +40,7 @@
- Filtering striae for `not specified` returned wrong results.
- Filtering by `borehole status` did not work.
- When saving with ctrl+s in the borehole sections, the form content was reset.
- There was a bug when changing the order, transparency or visibility of custom WMS user layers.

## v2.1.870 - 2024-09-27

Expand Down
1 change: 0 additions & 1 deletion src/api/BdmsContextExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,6 @@ public static void SeedData(this BdmsContext context)
.RuleFor(o => o.UpdatedBy, _ => default!)
.RuleFor(o => o.Updated, _ => default!)
.RuleFor(o => o.Name, f => f.Random.Word())
.RuleFor(o => o.Hash, f => f.Random.Hash())
.RuleFor(o => o.Type, f => f.Random.Word())
.RuleFor(o => o.Created, f => f.Date.Past().ToUniversalTime().OrNull(f, .05f))
.RuleFor(o => o.NameUuid, f => null);
Expand Down
56 changes: 21 additions & 35 deletions src/api/BoreholeFileCloudService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,22 +31,10 @@ public BoreholeFileCloudService(BdmsContext context, IConfiguration configuratio
/// <summary>
/// Uploads a file to the cloud storage and links it to the borehole.
/// </summary>
/// <param name="file">The file to upload and link to the <see cref="Borehole"/>.</param>
/// <param name="boreholeId">The <see cref="Borehole.Id"/> to link the uploaded <paramref name="file"/> to.</param>
public async Task<BoreholeFile> UploadFileAndLinkToBorehole(IFormFile file, int boreholeId)
/// <param name="formFile">The file to upload and link to the <see cref="Borehole"/>.</param>
/// <param name="boreholeId">The <see cref="Borehole.Id"/> to link the uploaded <paramref name="formFile"/> to.</param>
public async Task<BoreholeFile> UploadFileAndLinkToBorehole(IFormFile formFile, int boreholeId)
{
// Generate a hash based on the file content.
var base64Hash = "";
using (SHA256 sha256Hash = SHA256.Create())
{
using Stream stream = file.OpenReadStream();
byte[] hashBytes = await sha256Hash.ComputeHashAsync(stream).ConfigureAwait(false);
base64Hash = Convert.ToBase64String(hashBytes);
}

// Check any file with the same hash already exists in the database.
var fileId = context.Files.FirstOrDefault(f => f.Hash == base64Hash)?.Id;

// Use transaction to ensure data is only stored to db if the file upload was sucessful. Only create a transaction if there is not already one from the calling method.
using var transaction = context.Database.CurrentTransaction == null ? await context.Database.BeginTransactionAsync().ConfigureAwait(false) : null;
try
Expand All @@ -60,28 +48,26 @@ public async Task<BoreholeFile> UploadFileAndLinkToBorehole(IFormFile file, int

if (user == null || subjectId == null) throw new InvalidOperationException($"No user with subject_id <{subjectId}> found.");

// If file does not exist on storage, upload it and create file in database.
if (fileId == null)
{
var fileExtension = Path.GetExtension(file.FileName);
var fileNameGuid = $"{Guid.NewGuid()}{fileExtension}";
// Register the new file in the boreholes database.
var fileExtension = Path.GetExtension(formFile.FileName);
var fileNameGuid = $"{Guid.NewGuid()}{fileExtension}";

var bdmsFile = new Models.File { Name = file.FileName, NameUuid = fileNameGuid, Hash = base64Hash, Type = file.ContentType };
var file = new Models.File { Name = formFile.FileName, NameUuid = fileNameGuid, Type = formFile.ContentType };

await context.Files.AddAsync(bdmsFile).ConfigureAwait(false);
await context.UpdateChangeInformationAndSaveChangesAsync(httpContextAccessor.HttpContext!).ConfigureAwait(false);
await context.Files.AddAsync(file).ConfigureAwait(false);
await context.UpdateChangeInformationAndSaveChangesAsync(httpContextAccessor.HttpContext!).ConfigureAwait(false);

fileId = bdmsFile.Id;
var fileId = file.Id;

// Upload the file to the cloud storage.
await UploadObject(file, fileNameGuid).ConfigureAwait(false);
}
// Upload the file to the cloud storage.
await UploadObject(formFile, fileNameGuid).ConfigureAwait(false);

// If file is already linked to the borehole, throw an exception.
if (context.BoreholeFiles.Any(bf => bf.BoreholeId == boreholeId && bf.FileId == fileId)) throw new InvalidOperationException($"File <{file.FileName}> is already attached to borehole with Id <{boreholeId}>.");
if (await context.BoreholeFiles.AnyAsync(bf => bf.BoreholeId == boreholeId && bf.FileId == fileId).ConfigureAwait(false))
throw new InvalidOperationException($"File <{formFile.FileName}> is already attached to borehole with Id <{boreholeId}>.");

// Link file to the borehole.
var boreholeFile = new BoreholeFile { FileId = (int)fileId, BoreholeId = boreholeId, UserId = user.Id, Attached = DateTime.UtcNow };
var boreholeFile = new BoreholeFile { FileId = fileId, BoreholeId = boreholeId, UserId = user.Id, Attached = DateTime.UtcNow };

var entityEntry = await context.BoreholeFiles.AddAsync(boreholeFile).ConfigureAwait(false);
await context.UpdateChangeInformationAndSaveChangesAsync(httpContextAccessor.HttpContext!).ConfigureAwait(false);
Expand All @@ -91,7 +77,7 @@ public async Task<BoreholeFile> UploadFileAndLinkToBorehole(IFormFile file, int
}
catch (Exception ex)
{
logger.LogError(ex, $"Error attaching file <{file.FileName}> to borehole with Id <{boreholeId}>.");
logger.LogError(ex, "Error attaching file <{FileName}> to borehole with Id <{BoreholeId}>.", formFile.FileName, boreholeId);
throw;
}
}
Expand All @@ -111,7 +97,7 @@ internal async Task UploadObject(IFormFile file, string objectName)
}
catch (AmazonS3Exception ex)
{
logger.LogError(ex, $"Error uploading file <{file.FileName}> to cloud storage.");
logger.LogError(ex, "Error uploading file to cloud storage.");
throw;
}
}
Expand All @@ -135,7 +121,7 @@ public async Task<byte[]> GetObject(string objectName)
}
catch (AmazonS3Exception ex)
{
logger.LogError(ex, $"Error downloading file from cloud storage.");
logger.LogError(ex, "Error downloading file from cloud storage.");
throw;
}
}
Expand Down Expand Up @@ -170,7 +156,7 @@ public async Task<int> CountDataExtractionObjects(string objectName)
}
catch (AmazonS3Exception ex)
{
logger.LogError(ex, $"Error counting files in data extraction folder in cloud storage.");
logger.LogError(ex, "Error counting files in data extraction folder in cloud storage.");
throw;
}
}
Expand Down Expand Up @@ -223,7 +209,7 @@ public async Task<int> CountDataExtractionObjects(string objectName)
}
catch (AmazonS3Exception ex)
{
logger.LogError(ex, $"Error retrieving image information from data extraction folder in cloud storage.");
logger.LogError(ex, "Error retrieving image information from data extraction folder in cloud storage.");
throw;
}
}
Expand All @@ -241,7 +227,7 @@ public async Task DeleteObject(string objectName)
}
catch (AmazonS3Exception ex)
{
logger.LogError(ex, $"Error deleting file <{objectName}> from cloud storage.");
logger.LogError(ex, "Error deleting file from cloud storage.");
throw;
}
}
Expand Down
6 changes: 2 additions & 4 deletions src/api/Controllers/BoreholeController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -360,10 +360,8 @@ await Context.Entry(hydrotest)
}
}

foreach (var boreholeFile in borehole.BoreholeFiles)
{
boreholeFile.BoreholeId = 0;
}
// Do not copy borehole attachments
borehole.BoreholeFiles.Clear();

foreach (var boreholeGeometry in borehole.BoreholeGeometry)
{
Expand Down
12 changes: 5 additions & 7 deletions src/api/Controllers/BoreholeFileController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -210,21 +210,19 @@ public async Task<IActionResult> DetachFromBorehole([Required, Range(1, int.MaxV
try
{
// Get the file and its borehole files from the database.
var boreholeFile = await context.BoreholeFiles.Include(f => f.File).FirstOrDefaultAsync(f => f.FileId == boreholeFileId).ConfigureAwait(false);
var boreholeFile = await context.BoreholeFiles.Include(f => f.File).SingleOrDefaultAsync(f => f.FileId == boreholeFileId).ConfigureAwait(false);

if (boreholeFile == null) return NotFound();
if (boreholeFile == null) return NotFound($"Borehole file for the provided {nameof(boreholeFileId)} not found.");

var fileId = boreholeFile.File.Id;

// Remove the requested borehole file from the database.
context.BoreholeFiles.Remove(boreholeFile);
await context.SaveChangesAsync().ConfigureAwait(false);

// Get the file and its borehole files from the database.
var file = await context.Files.Include(f => f.BoreholeFiles).FirstOrDefaultAsync(f => f.Id == fileId).ConfigureAwait(false);

// If the file is not linked to any boreholes, delete it from the cloud storage and the database.
if (file?.NameUuid != null && file.BoreholeFiles.Count == 0)
// Delete the file from the cloud storage and the database.
var file = await context.Files.SingleOrDefaultAsync(f => f.Id == fileId).ConfigureAwait(false);
if (file?.NameUuid != null)
{
await boreholeFileCloudService.DeleteObject(file.NameUuid).ConfigureAwait(false);
context.Files.Remove(file);
Expand Down
Loading

0 comments on commit 880e6c5

Please sign in to comment.