Skip to content

Commit

Permalink
Merge pull request #49 from Traeger-GmbH/feature/ensure-async-operations
Browse files Browse the repository at this point in the history
Ensure async operations
  • Loading branch information
tiwalter authored Mar 26, 2020
2 parents 0a37b86 + adaeb59 commit 46b4902
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 66 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,11 @@ public ReleaseArtifactServiceTest()
}

[Fact]
public void TestGetLatestVersion()
public async void TestGetLatestVersion()
{
//Act
var testVersions1 = FsReleaseArtifactService.GetLatestVersion("productx", "debian", "amd64");
var testVersions2 = FsReleaseArtifactService.GetLatestVersion("productx", "ubuntu", "amd64");
var testVersions1 = await FsReleaseArtifactService.GetLatestVersion("productx", "debian", "amd64");
var testVersions2 = await FsReleaseArtifactService.GetLatestVersion("productx", "ubuntu", "amd64");

//Assert
Assert.Equal("1.2-beta", testVersions1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,56 +33,62 @@ public ReleaseArtifactController(ILogger<ReleaseArtifactController> logger,
[HttpPut("upload/{product}/{os}/{architecture}/{version}")]
//Max. 500 MB
[RequestSizeLimit(524288000)]
public IActionResult UploadSpecificArtifact([Required] string product, [Required] string os, [Required] string architecture, [Required] string version)
public async Task<IActionResult> UploadSpecificArtifact([Required] string product, [Required] string os, [Required] string architecture, [Required] string version)
{
var file = Request.Form.Files.FirstOrDefault();

if (file == null)
return BadRequest();

ReleaseArtifactService.StoreArtifact(product, os, architecture, version, file);
await ReleaseArtifactService.StoreArtifact(product, os, architecture, version, file);

return Ok("Upload of the artifact successful!");
}

[AllowAnonymous]
[HttpGet("versions/{product}")]
public ProductInformationListResponseModel GetProductInfos([Required] string product)
public async Task<ProductInformationListResponseModel> GetProductInfos([Required] string product)
{
return ReleaseArtifactService.GetProductInfos(product).ToProductInfoListResponse();
var productInfos = await ReleaseArtifactService.GetProductInfos(product);

return productInfos.ToProductInfoListResponse();
}

[AllowAnonymous]
[HttpGet("platforms/{product}/{version}")]
public PlatformsResponseModel GetPlatforms([Required] string product, [Required]string version)
public async Task<PlatformsResponseModel> GetPlatforms([Required] string product, [Required]string version)
{
var platformsList = ReleaseArtifactService.GetPlatforms(product, version);
var platformsList = await ReleaseArtifactService.GetPlatforms(product, version);

return platformsList.ToPlatformsResponse();
}

[AllowAnonymous]
[HttpGet("info/{product}/{os}/{architecture}/{version}")]
public ChangelogResponseModel GetReleaseInfo([Required] string product, [Required] string os, [Required] string architecture, [Required] string version)
public async Task<ChangelogResponseModel> GetReleaseInfo([Required] string product, [Required] string os, [Required] string architecture, [Required] string version)
{
return ReleaseArtifactService.GetReleaseInfo(product, os, architecture, version).toChangelogResponse();
var releaseInfo = await ReleaseArtifactService.GetReleaseInfo(product, os, architecture, version);

return releaseInfo.toChangelogResponse();
}

[AllowAnonymous]
[HttpGet("versions/{product}/{os}/{architecture}")]
public ProductVersionListResponseModel GetVersions([Required] string product, [Required] string os, [Required] string architecture)
public async Task<ProductVersionListResponseModel> GetVersions([Required] string product, [Required] string os, [Required] string architecture)
{
return ReleaseArtifactService.GetVersions(product, os, architecture).ToProductVersionListResponse();
var productVersions = await ReleaseArtifactService.GetVersions(product, os, architecture);

return productVersions.ToProductVersionListResponse();
}

[AllowAnonymous]
[HttpGet("download/{product}/{os}/{architecture}/{version}")]
public IActionResult GetSpecificArtifact([Required] string product, [Required] string os, [Required] string architecture, string version)
public async Task<IActionResult> GetSpecificArtifact([Required] string product, [Required] string os, [Required] string architecture, string version)
{
var provider = new FileExtensionContentTypeProvider();
string contentType;

var response = ReleaseArtifactService.GetSpecificArtifact(product, os, architecture, version);
var response = await ReleaseArtifactService.GetSpecificArtifact(product, os, architecture, version);

//Determine the content type
if (!provider.TryGetContentType(response.FileName, out contentType))
Expand All @@ -102,12 +108,12 @@ public IActionResult GetSpecificArtifact([Required] string product, [Required]

[AllowAnonymous]
[HttpGet("download/{product}/{os}/{architecture}/latest")]
public IActionResult GetLatestArtifact([Required] string product, [Required] string os, [Required] string architecture)
public async Task<IActionResult> GetLatestArtifact([Required] string product, [Required] string os, [Required] string architecture)
{
var provider = new FileExtensionContentTypeProvider();
string contentType;

var response = ReleaseArtifactService.GetLatestArtifact(product, os, architecture);
var response = await ReleaseArtifactService.GetLatestArtifact(product, os, architecture);

//Determine the content type
if (!provider.TryGetContentType(response.FileName, out contentType))
Expand All @@ -127,9 +133,11 @@ public IActionResult GetLatestArtifact([Required] string product, [Required] st

[AllowAnonymous]
[HttpGet("latest/{product}/{os}/{architecture}")]
public ProductVersionResponseModel GetLatestVersion([Required] string product, [Required] string os, [Required] string architecture)
public async Task<ProductVersionResponseModel> GetLatestVersion([Required] string product, [Required] string os, [Required] string architecture)
{
return ReleaseArtifactService.GetLatestVersion(product, os, architecture).ToProductVersionResponse();
var latestVersion = await ReleaseArtifactService.GetLatestVersion(product, os, architecture);

return latestVersion.ToProductVersionResponse();
}

[HttpDelete("{product}/{os}/{architecture}/{version}")]
Expand All @@ -141,20 +149,20 @@ public IActionResult DeleteSpecificArtifact ([Required] string product, [Require
}

[HttpDelete("{product}")]
public IActionResult DeleteProduct ([Required] string product)
public async Task<IActionResult> DeleteProduct ([Required] string product)
{
ReleaseArtifactService.DeleteProduct(product);

return Ok("product successfully deleted");
}

[HttpGet("backup")]
public FileStreamResult Backup()
public async Task<FileStreamResult> Backup()
{
var provider = new FileExtensionContentTypeProvider();
string contentType;

var backupInfo = ReleaseArtifactService.RunBackup();
var backupInfo = await ReleaseArtifactService.RunBackup();

var stream = new FileStream(backupInfo.FullPath, FileMode.Open, FileAccess.Read);

Expand All @@ -171,14 +179,14 @@ public FileStreamResult Backup()
}

[HttpPut("restore")]
public IActionResult Restore()
public async Task<IActionResult> Restore()
{
var payload = Request.Form.Files.FirstOrDefault();

if (payload == null)
return BadRequest();

ReleaseArtifactService.RestoreBackup(payload);
await ReleaseArtifactService.RestoreBackup(payload);

return Ok("backup successfully restored");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using ReleaseServer.WebApi.Mappers;
Expand All @@ -22,7 +24,7 @@ public FsReleaseArtifactService( IReleaseArtifactRepository fsReleaseArtifactRep
DirectoryLock = new SemaphoreSlim(1,1);
}

public void StoreArtifact(string product, string os, string architecture, string version, IFormFile payload)
public async Task StoreArtifact(string product, string os, string architecture, string version, IFormFile payload)
{
using (var zipMapper = new ZipArchiveMapper())
{
Expand All @@ -31,93 +33,142 @@ public void StoreArtifact(string product, string os, string architecture, string

var artifact = ReleaseArtifactMapper.ConvertToReleaseArtifact(product, os, architecture, version, zipPayload);

lock (DirectoryLock)
FsReleaseArtifactRepository.StoreArtifact(artifact);
await DirectoryLock.WaitAsync();

//It's important to release the semaphore. try / finally block ensures a guaranteed release (also if the operation may crash)
try
{
await Task.Run(() => FsReleaseArtifactRepository.StoreArtifact(artifact));
}
finally
{
DirectoryLock.Release();
}
}
}
public List<ProductInformationModel> GetProductInfos(string productName)
public async Task<List<ProductInformationModel>> GetProductInfos(string productName)
{
return FsReleaseArtifactRepository.GetInfosByProductName(productName);
return await Task.Run(() => FsReleaseArtifactRepository.GetInfosByProductName(productName));
}

public List<string> GetPlatforms(string productName, string version)
public async Task<List<string>> GetPlatforms(string productName, string version)
{
return FsReleaseArtifactRepository.GetPlatforms(productName, version);
return await Task.Run(() => FsReleaseArtifactRepository.GetPlatforms(productName, version));
}

public string GetReleaseInfo(string productName, string os, string architecture, string version)
public async Task<string> GetReleaseInfo(string productName, string os, string architecture, string version)
{
return FsReleaseArtifactRepository.GetReleaseInfo(productName, os, architecture, version);
return await Task.Run(() => FsReleaseArtifactRepository.GetReleaseInfo(productName, os, architecture, version));
}

public List<string> GetVersions(string productName, string os, string architecture)
public async Task<List<string>> GetVersions(string productName, string os, string architecture)
{
return FsReleaseArtifactRepository.GetVersions(productName, os, architecture);
return await Task.Run(() => FsReleaseArtifactRepository.GetVersions(productName, os, architecture));
}

public string GetLatestVersion(string productName, string os, string architecture)
public async Task<string> GetLatestVersion(string productName, string os, string architecture)
{
var versions = FsReleaseArtifactRepository.GetVersions(productName, os, architecture);
var versions = await Task.Run(() => FsReleaseArtifactRepository.GetVersions(productName, os, architecture));

return versions.First();
}

public ArtifactDownloadModel GetSpecificArtifact(string productName, string os, string architecture, string version)
public async Task<ArtifactDownloadModel> GetSpecificArtifact(string productName, string os, string architecture, string version)
{
return FsReleaseArtifactRepository.GetSpecificArtifact(productName, os, architecture, version);
return await Task.Run(() => FsReleaseArtifactRepository.GetSpecificArtifact(productName, os, architecture, version));
}

public ArtifactDownloadModel GetLatestArtifact(string productName, string os, string architecture)
public async Task<ArtifactDownloadModel> GetLatestArtifact(string productName, string os, string architecture)
{
var latestVersion = GetLatestVersion(productName, os, architecture);
var latestVersion = await Task.Run(() => GetLatestVersion(productName, os, architecture));

return FsReleaseArtifactRepository.GetSpecificArtifact(productName, os, architecture, latestVersion);
return await Task.Run(() => FsReleaseArtifactRepository.GetSpecificArtifact(productName, os, architecture, latestVersion));
}

public void DeleteSpecificArtifact(string productName, string os, string architecture, string version)
public async Task DeleteSpecificArtifact(string productName, string os, string architecture, string version)
{
lock(DirectoryLock)
FsReleaseArtifactRepository.DeleteSpecificArtifact(productName, os, architecture, version);
await DirectoryLock.WaitAsync();

//It's important to release the semaphore. try / finally block ensures a guaranteed release (also if the operation may crash)
try
{
await Task.Run(() => FsReleaseArtifactRepository.DeleteSpecificArtifact(productName, os, architecture, version));
}
finally
{
DirectoryLock.Release();
}
}

public void DeleteProduct(string productName)
public async Task DeleteProduct(string productName)
{
lock (DirectoryLock)
FsReleaseArtifactRepository.DeleteProduct(productName);
await DirectoryLock.WaitAsync();

//It's important to release the semaphore. try / finally block ensures a guaranteed release (also if the operation may crash)
try
{
await Task.Run(() => FsReleaseArtifactRepository.DeleteProduct(productName));
}
finally
{
DirectoryLock.Release();
}
}

public BackupInformationModel RunBackup()
public async Task<BackupInformationModel> RunBackup()
{
lock (DirectoryLock)
return FsReleaseArtifactRepository.RunBackup();
BackupInformationModel backup;

await DirectoryLock.WaitAsync();

//It's important to release the semaphore. try / finally block ensures a guaranteed release (also if the operation may crash)
try
{
backup = await Task.Run(() =>FsReleaseArtifactRepository.RunBackup());
}
finally
{
DirectoryLock.Release();
}

return backup;
}

public void RestoreBackup(IFormFile payload)
public async Task RestoreBackup(IFormFile payload)
{
using (var zipMapper = new ZipArchiveMapper())
{
Logger.LogDebug("convert the uploaded backup payload to a ZIP archive");
var zipPayload = zipMapper.FormFileToZipArchive(payload);

lock (DirectoryLock)
FsReleaseArtifactRepository.RestoreBackup(zipPayload);
await DirectoryLock.WaitAsync();

//It's important to release the semaphore. try / finally block ensures a guaranteed release (also if the operation may crash)
try
{
await Task.Run(() => FsReleaseArtifactRepository.RestoreBackup(zipPayload));
}
finally
{
DirectoryLock.Release();
}
}
}
}

public interface IReleaseArtifactService
{
void StoreArtifact(string product, string os, string architecture, string version, IFormFile payload);
List<ProductInformationModel> GetProductInfos(string productName);
List<string> GetPlatforms(string productName, string version);
string GetReleaseInfo(string productName, string os, string architecture, string version);
List<string> GetVersions(string productName, string os, string architecture);
string GetLatestVersion(string productName, string os, string architecture);
ArtifactDownloadModel GetSpecificArtifact(string productName, string os, string architecture, string version);
ArtifactDownloadModel GetLatestArtifact(string productName, string os, string architecture);
void DeleteSpecificArtifact(string productName, string os, string architecture, string version);
void DeleteProduct(string productName);
BackupInformationModel RunBackup();
void RestoreBackup(IFormFile payload);
Task StoreArtifact(string product, string os, string architecture, string version, IFormFile payload);
Task<List<ProductInformationModel>> GetProductInfos(string productName);
Task<List<string>> GetPlatforms(string productName, string version);
Task<string> GetReleaseInfo(string productName, string os, string architecture, string version);
Task<List<string>> GetVersions(string productName, string os, string architecture);
Task<string> GetLatestVersion(string productName, string os, string architecture);
Task<ArtifactDownloadModel> GetSpecificArtifact(string productName, string os, string architecture, string version);
Task<ArtifactDownloadModel> GetLatestArtifact(string productName, string os, string architecture);
Task DeleteSpecificArtifact(string productName, string os, string architecture, string version);
Task DeleteProduct(string productName);
Task<BackupInformationModel> RunBackup();
Task RestoreBackup(IFormFile payload);
}
}

0 comments on commit 46b4902

Please sign in to comment.