Skip to content

Commit

Permalink
Fix PR comments: fix test, refactor recurringJobs
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolaes committed Dec 7, 2023
1 parent 60dac77 commit 7079007
Show file tree
Hide file tree
Showing 14 changed files with 133 additions and 28 deletions.
7 changes: 7 additions & 0 deletions TeacherWorkout.Api/Jobs/Config/DeleteOldFileBlobsConfig.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace TeacherWorkout.Api.Jobs.Config
{
public record DeleteOldFileBlobsConfig : RecurringJobConfig
{
public int DaysInThePast { get; set; }
}
}
8 changes: 8 additions & 0 deletions TeacherWorkout.Api/Jobs/Config/RecurringJobConfig.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace TeacherWorkout.Api.Jobs.Config
{
public record RecurringJobConfig
{
public bool IsEnabled { get; set; }
public string CronExpression { get; set; }
}
}
20 changes: 20 additions & 0 deletions TeacherWorkout.Api/Jobs/DeleteOldFileBlobsJob.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using DeUrgenta.RecurringJobs.Jobs.Config;

Check failure on line 1 in TeacherWorkout.Api/Jobs/DeleteOldFileBlobsJob.cs

View workflow job for this annotation

GitHub Actions / build

The type or namespace name 'DeUrgenta' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 1 in TeacherWorkout.Api/Jobs/DeleteOldFileBlobsJob.cs

View workflow job for this annotation

GitHub Actions / build

The type or namespace name 'DeUrgenta' could not be found (are you missing a using directive or an assembly reference?)
using Microsoft.Extensions.Options;
using TeacherWorkout.Api.Jobs.Interfaces;
using TeacherWorkout.Domain.FileBlobs;

namespace TeacherWorkout.Api.Jobs
{

public class DeleteOldFileBlobsJob(IFileBlobRepository repository, IOptions<DeleteOldFileBlobsConfig> config) : IDeleteOldFileBlobsJob

Check failure on line 9 in TeacherWorkout.Api/Jobs/DeleteOldFileBlobsJob.cs

View workflow job for this annotation

GitHub Actions / build

The type or namespace name 'DeleteOldFileBlobsConfig' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 9 in TeacherWorkout.Api/Jobs/DeleteOldFileBlobsJob.cs

View workflow job for this annotation

GitHub Actions / build

The type or namespace name 'DeleteOldFileBlobsConfig' could not be found (are you missing a using directive or an assembly reference?)
{
private readonly IFileBlobRepository _repository = repository;
private readonly DeleteOldFileBlobsConfig _config = config.Value;

Check failure on line 12 in TeacherWorkout.Api/Jobs/DeleteOldFileBlobsJob.cs

View workflow job for this annotation

GitHub Actions / build

The type or namespace name 'DeleteOldFileBlobsConfig' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 12 in TeacherWorkout.Api/Jobs/DeleteOldFileBlobsJob.cs

View workflow job for this annotation

GitHub Actions / build

The type or namespace name 'DeleteOldFileBlobsConfig' could not be found (are you missing a using directive or an assembly reference?)

public void Run()
{
;
_repository.DeleteOldEntries(_config.DaysInThePast);
}
}
}
10 changes: 10 additions & 0 deletions TeacherWorkout.Api/Jobs/Interfaces/IDeleteOldFileBlobsJob.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System.Threading;
using System.Threading.Tasks;

namespace TeacherWorkout.Api.Jobs.Interfaces
{
public interface IDeleteOldFileBlobsJob
{
void Run();
}
}
21 changes: 17 additions & 4 deletions TeacherWorkout.Api/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using TeacherWorkout.Api.GraphQL;
using TeacherWorkout.Api.Jobs;
using TeacherWorkout.Api.Jobs.Config;
using TeacherWorkout.Api.Jobs.Interfaces;
using TeacherWorkout.Data;
using TeacherWorkout.Domain.Common;
using TeacherWorkout.Domain.FileBlobs;

namespace TeacherWorkout.Api
{
Expand Down Expand Up @@ -67,6 +69,9 @@ public void ConfigureServices(IServiceCollection services)
c.UseNpgsqlConnection(Configuration.GetConnectionString("TeacherWorkoutContext")));
});
services.AddHangfireServer(config => config.WorkerCount = 1);

services.Configure<DeleteOldFileBlobsConfig>(Configuration.GetSection("TeacherWorkout:RecurringJobs:DeleteOldFileBlobs"));
services.AddScoped<IDeleteOldFileBlobsJob, DeleteOldFileBlobsJob>();
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
Expand All @@ -90,9 +95,17 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env, TeacherW
.UseGraphQL<ISchema>();
app.UseGraphQLGraphiQL();

var fileBlobRepository = serviceProvider.GetRequiredService<IFileBlobRepository>();
RecurringJob.AddOrUpdate("DeleteOldFileBlobs",
() => fileBlobRepository.DeleteOldEntries(), Cron.Daily);
var deleteOldFileBlobsConfig = Configuration.GetSection("TeacherWorkout:RecurringJobs:DeleteOldFileBlobs")
.Get<DeleteOldFileBlobsConfig>();
if (deleteOldFileBlobsConfig.IsEnabled)
{
RecurringJob.AddOrUpdate<IDeleteOldFileBlobsJob>(
nameof(DeleteOldFileBlobsJob),
job => job.Run(),
deleteOldFileBlobsConfig.CronExpression,
new RecurringJobOptions { TimeZone = TimeZoneInfo.Utc }
);
}
}

private static void AddOperations(IServiceCollection services)
Expand Down
9 changes: 8 additions & 1 deletion TeacherWorkout.Api/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@
"TeacherWorkoutContext": "Server=postgres;Port=5432;Database=teacher_workout;User Id=docker;Password=docker;"
},
"TeacherWorkout": {
"MaxFileSizeMb": 5
"MaxFileSizeMb": 5,
"RecurringJobs": {
"DeleteOldFileBlobs": {
"IsEnabled": "true",
"CronExpression": "0 0 * * *",
"DaysInThePast": "1"
}
}
}
}
4 changes: 2 additions & 2 deletions TeacherWorkout.Data/Repositories/FileBlobRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ public List<FileBlob> FindRecent(string[] mimetypes, int? limit)
.ToList();
}

public void DeleteOldEntries()
public void DeleteOldEntries(int daysInThePast)
{
var cutoffDate = DateTime.Now.AddDays(-1).ToUniversalTime();
var cutoffDate = DateTime.Now.AddDays(-daysInThePast).ToUniversalTime();
var oldEntries = _context.FileBlobs
.Where(fb => fb.CreatedAt < cutoffDate && !_context.Images.Any(i => i.FileBlobId == fb.Id));
_logger.LogInformation("Deleting {EntryCount} old file blobs", oldEntries.Count());
Expand Down
2 changes: 1 addition & 1 deletion TeacherWorkout.Domain/FileBlobs/IFileBlobRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ public interface IFileBlobRepository
void Add(FileBlob fileBlob);
FileBlob Find(string id);
List<FileBlob> FindRecent(string[] mimetypes, int? limit);
void DeleteOldEntries();
void DeleteOldEntries(int daysInThePast);
}
}
4 changes: 2 additions & 2 deletions TeacherWorkout.Domain/Themes/CreateTheme.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public class CreateTheme(IThemeRepository themeRepository,
{
private readonly IThemeRepository _themeRepository = themeRepository;
private readonly IImageRepository _imageRepository = imageRepository;
private readonly IFileBlobRepository _ifileBlobRepository = fileBlobRepository;
private readonly IFileBlobRepository _fileBlobRepository = fileBlobRepository;

public ThemeCreatePayload Execute(ThemeCreateInput input)
{
Expand All @@ -25,7 +25,7 @@ public ThemeCreatePayload Execute(ThemeCreateInput input)

if (!string.IsNullOrEmpty(input.FileBlobId))
{
var fileBlob = _ifileBlobRepository.Find(input.FileBlobId) ?? throw new ValidationException("FileBlob ID not found");
var fileBlob = _fileBlobRepository.Find(input.FileBlobId) ?? throw new ValidationException("FileBlob ID not found");
var thumbnail = new Image
{
Description = fileBlob.Description,
Expand Down
6 changes: 3 additions & 3 deletions TeacherWorkout.Specs/Features/Themes.feature
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
Feature: Themes
As a user
I want to be able to list themes

Scenario: Admin user can create a theme
Given Ion is an admin
When Ion creates a theme
When Ion creates a theme with image
Then the theme was created successfully

Scenario: Anonymous user can list themes
Given Ion is an admin
And Vasile is an anonymous user
And Ion creates a theme
And Ion creates a theme with image
When Vasile requests themes
Then Vasile receives the theme
4 changes: 2 additions & 2 deletions TeacherWorkout.Specs/Features/Themes.feature.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions TeacherWorkout.Specs/GraphQL/Mutation/SingleUpload.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
mutation SingleUpload($file: Upload!) {
singleUpload(file: $file) {
fileBlobId
}
}
31 changes: 21 additions & 10 deletions TeacherWorkout.Specs/Steps/ThemeStepDefinitions.cs
Original file line number Diff line number Diff line change
@@ -1,25 +1,36 @@
using System;
using System.Threading.Tasks;
using FluentAssertions;
using Newtonsoft.Json.Linq;
using SpecFlow.Internal.Json;
using TeacherWorkout.Domain.Models;
using TeacherWorkout.Specs.Extensions;
using TechTalk.SpecFlow;
using Xunit.Abstractions;

namespace TeacherWorkout.Specs.Steps
{
[Binding]
public class ThemeStepDefinitions
public class ThemeStepDefinitions(ScenarioContext scenarioContext)
{
private readonly ScenarioContext _scenarioContext;
private readonly ScenarioContext _scenarioContext = scenarioContext;

public ThemeStepDefinitions(ScenarioContext scenarioContext)
[Given(@"Ion creates a theme with image")]
[When(@"Ion creates a theme with image")]
public async Task GivenIonCreatesAThemeWithImage()
{
_scenarioContext = scenarioContext;
}
FileBlob imageFile = new()
{
Id = "FileBlob_1",
Content = Convert.FromBase64String("iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII="),
Mimetype = "image/png",
Description = "tiny_image.png",
CreatedAt = DateTime.Now.ToUniversalTime()
};
string uploadJson = await ((TeacherWorkoutApiClient)_scenarioContext["Ion"]).UploadImage(imageFile);
string fileBlobId = JObject.Parse(uploadJson)["data"]["singleUpload"]["fileBlobId"].ToString();

[Given(@"Ion creates a theme")]
[When(@"Ion creates a theme")]
public async Task GivenIonCreatesATheme()
{
_scenarioContext["theme-create-response"] = await ((TeacherWorkoutApiClient) _scenarioContext["Ion"]).ThemeCreateAsync();
_scenarioContext["theme-create-response"] = await ((TeacherWorkoutApiClient) _scenarioContext["Ion"]).ThemeCreateAsync(fileBlobId);
}

[When(@"Vasile requests themes")]
Expand Down
30 changes: 27 additions & 3 deletions TeacherWorkout.Specs/TeacherWorkoutApiClient.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
using System;
using System.Globalization;
using System.IO;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using TeacherWorkout.Domain.Models;
using TeacherWorkout.Specs.Extensions;

namespace TeacherWorkout.Specs
Expand All @@ -15,7 +18,8 @@ enum Queries

enum Mutations
{
ThemeCreate
ThemeCreate,
SingleUpload
}

private readonly HttpClient _client;
Expand All @@ -25,18 +29,38 @@ public TeacherWorkoutApiClient(HttpClient client)
_client = client;
}

public async Task<string> UploadImage(FileBlob imageFile)
{

var operations = new StringContent(new {query = GraphQL("Mutation", "SingleUpload"), variables = new {file = (string)null}}.ToJson(), Encoding.UTF8, "application/json");
var map = new StringContent("{\"0\": [\"variables.file\"]}", Encoding.UTF8, "application/json");
var imageBytes = new ByteArrayContent(imageFile.Content);
imageBytes.Headers.Add("Content-Type", imageFile.Mimetype);

var multipartContent = new MultipartFormDataContent("Upload----" + DateTime.Now.ToString(CultureInfo.InvariantCulture))
{
{ operations, "operations" },
{ map, "map" },
{ imageBytes, "0", imageFile.Description }
};
var response = await _client.PostAsync("http://localhost/graphql", multipartContent);

return await response.Content.ReadAsStringAsync();
}

public async Task<string> ThemesAsync()
{
return await SendRequest(QueryFor(Queries.Themes), new {});
}

public async Task<string> ThemeCreateAsync()
public async Task<string> ThemeCreateAsync(string fileBlobId)
{
return await SendRequest(MutationFor(Mutations.ThemeCreate), new
{
input = new
{
title = "foo"
title = "foo",
fileBlobId,
}
});
}
Expand Down

0 comments on commit 7079007

Please sign in to comment.