Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[57] get and clean images #84

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -336,3 +336,10 @@ ASALocalRun/
# Project specific
postgres_data
.env

# VSCode
.vscode

# MacOS
.DS_Store
Thumbs.db
25 changes: 25 additions & 0 deletions TeacherWorkout.Api/Controllers/FileController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using TeacherWorkout.Data;

namespace TeacherWorkout.Api.Controllers
{
[Route("file")]
[ApiController]
public class FileController(TeacherWorkoutContext context) : ControllerBase
{
private readonly TeacherWorkoutContext _context = context;

[HttpGet("{id}")]
public async Task<IActionResult> GetImage(string id)
{
var fileBlob = await _context.FileBlobs.FindAsync(id);
if (fileBlob == null)
{
return NotFound();
}

return File(fileBlob.Content, fileBlob.Mimetype);
}
}
}
38 changes: 36 additions & 2 deletions TeacherWorkout.Api/GraphQL/TeacherWorkoutMutation.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
using System.ComponentModel.DataAnnotations;
using System.IO;
using GraphQL;
using GraphQL.Types;
using GraphQL.Upload.AspNetCore;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using TeacherWorkout.Api.GraphQL.Resolvers;
using TeacherWorkout.Api.GraphQL.Types.Inputs;
using TeacherWorkout.Api.GraphQL.Types.Payloads;
using TeacherWorkout.Domain.FileBlobs;
using TeacherWorkout.Domain.Lessons;
using TeacherWorkout.Domain.Models.Inputs;
using TeacherWorkout.Domain.Models.Payloads;
Expand All @@ -15,7 +21,9 @@ public class TeacherWorkoutMutation : ObjectGraphType<object>
{
public TeacherWorkoutMutation(CompleteStep completeStep,
CreateTheme createTheme,
UpdateTheme updateTheme)
UpdateTheme updateTheme,
SingleUpload singleUpload,
IConfiguration configuration)
{
Name = "Mutation";

Expand Down Expand Up @@ -50,6 +58,32 @@ public TeacherWorkoutMutation(CompleteStep completeStep,
var input = context.GetArgument<ThemeUpdateInput>("input");
return updateTheme.Execute(input);
});


Field<SingleUploadPayloadType>("singleUpload")
.Argument<NonNullGraphType<UploadGraphType>>(Name = "file")
.Resolve(context =>
{
var file = context.GetArgument<IFormFile>("file");

var maxFileSizeMb = configuration.GetValue("TeacherWorkout:MaxFileSizeMb", 5);
if (file.Length > maxFileSizeMb * 1024 * 1024)
{
throw new ValidationException($"File size exceeds the limit of {maxFileSizeMb}MB.");
}

using var memoryStream = new MemoryStream();
file.CopyTo(memoryStream);
var fileBytes = memoryStream.ToArray();

return singleUpload.Execute(new SingleUploadInput
{
Content = fileBytes,
Mimetype = file.ContentType,
FileName = file.FileName,
});
});

}
}
}
}
9 changes: 8 additions & 1 deletion TeacherWorkout.Api/GraphQL/TeacherWorkoutQuery.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using GraphQL;
using GraphQL.Types;
using TeacherWorkout.Api.GraphQL.Types;
using TeacherWorkout.Api.GraphQL.Utils;
using TeacherWorkout.Domain.Common;
using TeacherWorkout.Domain.FileBlobs;
using TeacherWorkout.Domain.Lessons;
using TeacherWorkout.Domain.Themes;

Expand All @@ -12,7 +14,8 @@ public class TeacherWorkoutQuery : ObjectGraphType<object>
public TeacherWorkoutQuery(GetThemes getThemes,
GetLessons getLessons,
GetLessonStatuses getLessonStatuses,
GetStep getStep)
GetStep getStep,
GetFileBlobs getFileBlobs)
{
Name = "Query";

Expand All @@ -35,6 +38,10 @@ public TeacherWorkoutQuery(GetThemes getThemes,
Field<ListGraphType<NonNullGraphType<LessonStatusType>>>("lessonStatuses")
.Argument<NonNullGraphType<ListGraphType<NonNullGraphType<IdGraphType>>>>(Name = "lessonIds", Description = "Id's of leassons")
.Resolve(context => getLessonStatuses.Execute(context.ToInput<LessonStatusFilter>()));

Field<ListGraphType<NonNullGraphType<FileBlobType>>>("recentImageUploads")
.Argument<NonNullGraphType<IntGraphType>>("limit", "The number of recent images to return.")
.Resolve(context => getFileBlobs.Execute(context.GetArgument<int>("limit")));
}
}
}
Expand Down
16 changes: 16 additions & 0 deletions TeacherWorkout.Api/GraphQL/Types/FileBlobType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using GraphQL.Types;
using TeacherWorkout.Domain.Models;

namespace TeacherWorkout.Api.GraphQL.Types
{
public class FileBlobType : ObjectGraphType<FileBlob>
{
public FileBlobType()
{
Name = "FileBlob";

Field(x => x.Id, nullable: false).Description("The unique identifier of the file blob.");
Field(x => x.CreatedAt, nullable: false).Description("The creation time of the file blob.");
}
}
}
3 changes: 2 additions & 1 deletion TeacherWorkout.Api/GraphQL/Types/ImageType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ public ImageType()
{
Name = "Image";

Field(x => x.Url, true).Description("URL to the image.");
Field(x => x.Url, true).Description("URL to the image. If null, use FileBlob ID to generate an URL: /file/<fileBlobId>");
Field(x => x.Description, true).Description("Image description for accessibility.");
Field(x => x.FileBlobId, true).Description("Reference to local file. If null, use Url property.");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@ public ThemeCreateInputType()
{
Name = "ThemeCreateInput";

Field(x => x.ThumbnailId, type: typeof(IdGraphType));
Field(x => x.Title);
Field(x => x.FileBlobId, true, type: typeof(IdGraphType))
.Description("Id of uploaded image blob (precedes and overwrites ThumbnailId)");
Field(x => x.ThumbnailId, true, type: typeof(IdGraphType))
.Description("Id of existing thumbnail");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System;
using GraphQL.Types;
using TeacherWorkout.Domain.Models.Payloads;

namespace TeacherWorkout.Api.GraphQL.Types.Payloads
{
public class SingleUploadPayloadType : ObjectGraphType<SingleUploadPayload>
{
public SingleUploadPayloadType()
{
Name = "SingleUploadPayload";

Field(x => x.FileBlobId).Description("The ID of the created file blob.");
}
}
}
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 Microsoft.Extensions.Options;
using TeacherWorkout.Api.Jobs.Interfaces;
using TeacherWorkout.Domain.FileBlobs;
using TeacherWorkout.Api.Jobs.Config;

namespace TeacherWorkout.Api.Jobs
{

public class DeleteOldFileBlobsJob(IFileBlobRepository repository, IOptions<DeleteOldFileBlobsConfig> config) : IDeleteOldFileBlobsJob
{
private readonly IFileBlobRepository _repository = repository;
private readonly DeleteOldFileBlobsConfig _config = config.Value;

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();
}
}
49 changes: 43 additions & 6 deletions TeacherWorkout.Api/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,18 @@
using System.Linq;
using GraphQL;
using GraphQL.Types;
using Hangfire;
using Hangfire.PostgreSql;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
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;

Expand Down Expand Up @@ -41,18 +46,36 @@ public void ConfigureServices(IServiceCollection services)
AddOperations(services);
AddRepositories(services, "TeacherWorkout.Data");

services.AddControllers();
services.AddHttpContextAccessor();
services.AddGraphQL(b => b
.AddErrorInfoProvider(opt => opt.ExposeExceptionDetails = true)
.AddGraphTypes()
.AddSystemTextJson());
services
.AddGraphQLUpload()
.AddGraphQL(b => b
.AddErrorInfoProvider(opt => opt.ExposeExceptionDetails = true)
.AddGraphTypes()
.AddSystemTextJson());

services.AddDbContext<TeacherWorkoutContext>(options =>
options.UseNpgsql(Configuration.GetConnectionString("TeacherWorkoutContext")));

services.AddHangfire(configuration =>
{
configuration.SetDataCompatibilityLevel(CompatibilityLevel.Version_170);
configuration.UseSimpleAssemblyNameTypeSerializer();
configuration.UseRecommendedSerializerSettings();

// Initialize JobStorage
configuration.UsePostgreSqlStorage(c =>
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.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, TeacherWorkoutContext db)
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, TeacherWorkoutContext db, IServiceProvider serviceProvider)
{
app.UseCors();

Expand All @@ -66,9 +89,23 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env, TeacherW
}

app.UseRouting();
app.UseEndpoints(endpoints => endpoints.MapControllers());

app.UseGraphQL<ISchema>();
app.UseGraphQLUpload<ISchema>()
.UseGraphQL<ISchema>();
app.UseGraphQLGraphiQL();

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
3 changes: 3 additions & 0 deletions TeacherWorkout.Api/TeacherWorkout.Api.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
<ItemGroup>
<PackageReference Include="GraphQL.Server.All" Version="7.6.0" />
<PackageReference Include="GraphQL.Server.Ui.Graphiql" Version="7.6.0" />
<PackageReference Include="GraphQL.Upload.AspNetCore" Version="3.0.3" />
<PackageReference Include="Hangfire" Version="1.8.6" />
<PackageReference Include="Hangfire.PostgreSql" Version="1.20.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
Expand Down
10 changes: 10 additions & 0 deletions TeacherWorkout.Api/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,15 @@
"ConnectionStrings": {
//connection string for release (docker-compose) enviroment
"TeacherWorkoutContext": "Server=postgres;Port=5432;Database=teacher_workout;User Id=docker;Password=docker;"
},
"TeacherWorkout": {
"MaxFileSizeMb": 5,
"RecurringJobs": {
"DeleteOldFileBlobs": {
"IsEnabled": "true",
"CronExpression": "0 0 * * *",
"DaysInThePast": "1"
}
}
}
}
Loading
Loading