mirror of
https://github.com/alexmickelson/canvasManagement.git
synced 2026-03-25 23:28:33 -06:00
switched to xunit
This commit is contained in:
@@ -7,6 +7,8 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Akka" Version="1.5.20" />
|
||||
<PackageReference Include="Akka.DependencyInjection" Version="1.5.20" />
|
||||
<PackageReference Include="Markdig" Version="0.31.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR" Version="1.1.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Common" Version="8.0.3" />
|
||||
@@ -16,4 +18,7 @@
|
||||
<PackageReference Include="YamlDotNet" Version="13.1.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
9
Management/Services/Actors/CanvasQueue.cs
Normal file
9
Management/Services/Actors/CanvasQueue.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using Akka.Actor;
|
||||
|
||||
using Management.Services.Canvas;
|
||||
|
||||
public class CanvasQueue(IActorRef canvasQueueActor)
|
||||
{
|
||||
private readonly IActorRef canvasQueueActor = canvasQueueActor;
|
||||
|
||||
}
|
||||
18
Management/Services/Actors/CanvasQueueActor.cs
Normal file
18
Management/Services/Actors/CanvasQueueActor.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using Akka.Actor;
|
||||
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
public class CanvasQueueActor : ReceiveActor
|
||||
{
|
||||
private readonly IServiceProvider serviceProvider;
|
||||
private readonly IServiceScope scope;
|
||||
private readonly ILogger<CanvasQueueActor> logger;
|
||||
|
||||
public CanvasQueueActor(IServiceProvider serviceProviderArg)
|
||||
{
|
||||
serviceProvider = serviceProviderArg;
|
||||
scope = serviceProvider.CreateScope();
|
||||
logger = scope.ServiceProvider.GetRequiredService<ILogger<CanvasQueueActor>>();
|
||||
|
||||
}
|
||||
}
|
||||
61
Management/Services/Actors/LocalStorageActor.cs
Normal file
61
Management/Services/Actors/LocalStorageActor.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
using Akka.Actor;
|
||||
|
||||
using LocalModels;
|
||||
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
public class LocalStorageActor : ReceiveActor
|
||||
{
|
||||
private readonly IServiceProvider serviceProvider;
|
||||
private readonly IServiceScope scope;
|
||||
private readonly ILogger<CanvasQueueActor> logger;
|
||||
private readonly FileStorageManager storage;
|
||||
|
||||
private DateTime? cacheTime { get; set; } = null;
|
||||
private IEnumerable<LocalCourse>? cachedCourses { get; set; } = null;
|
||||
private readonly int cacheSeconds = 2;
|
||||
|
||||
public LocalStorageActor(IServiceProvider serviceProviderArg)
|
||||
{
|
||||
serviceProvider = serviceProviderArg;
|
||||
scope = serviceProvider.CreateScope();
|
||||
logger = scope.ServiceProvider.GetRequiredService<ILogger<CanvasQueueActor>>();
|
||||
storage = scope.ServiceProvider.GetRequiredService<FileStorageManager>();
|
||||
|
||||
Receive<EmptyDirectoryAsk>(m =>
|
||||
{
|
||||
storage
|
||||
.GetEmptyDirectories()
|
||||
.PipeTo(Sender);
|
||||
});
|
||||
|
||||
ReceiveAsync<SavedCoursesAsk>(async m =>
|
||||
{
|
||||
var secondsFromLastLoad = (DateTime.Now - cacheTime)?.Seconds;
|
||||
|
||||
if (cachedCourses != null && secondsFromLastLoad < cacheSeconds)
|
||||
{
|
||||
logger.LogInformation("returning cached courses from file");
|
||||
Sender.Tell(cachedCourses);
|
||||
return;
|
||||
}
|
||||
|
||||
cachedCourses = await storage.LoadSavedCourses();
|
||||
cacheTime = DateTime.Now;
|
||||
Sender.Tell(cachedCourses);
|
||||
});
|
||||
|
||||
ReceiveAsync<SaveCoursesRequest>(async m =>
|
||||
{
|
||||
cacheTime = null;
|
||||
cachedCourses = null;
|
||||
await storage.SaveCourseAsync(m.Course, m.PreviouslyStoredCourse);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public record EmptyDirectoryAsk();
|
||||
public record SavedCoursesAsk();
|
||||
|
||||
public record SaveCoursesRequest(LocalCourse Course, LocalCourse? PreviouslyStoredCourse);
|
||||
public record SaveCoursesResponseSuccess();
|
||||
23
Management/Services/Actors/LocalStorageCache.cs
Normal file
23
Management/Services/Actors/LocalStorageCache.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using Akka.Actor;
|
||||
|
||||
using LocalModels;
|
||||
|
||||
public class LocalStorageCache(IActorRef storageActor) : IFileStorageManager
|
||||
{
|
||||
private readonly IActorRef storageActor = storageActor;
|
||||
|
||||
public async Task<IEnumerable<string>> GetEmptyDirectories()
|
||||
{
|
||||
return await storageActor.Ask<IEnumerable<string>>(new EmptyDirectoryAsk());
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<LocalCourse>> LoadSavedCourses()
|
||||
{
|
||||
return await storageActor.Ask<IEnumerable<LocalCourse>>(new SavedCoursesAsk());
|
||||
}
|
||||
|
||||
public async Task SaveCourseAsync(LocalCourse course, LocalCourse? previouslyStoredCourse)
|
||||
{
|
||||
await storageActor.Ask<SaveCoursesResponseSuccess>(new SaveCoursesRequest(course, previouslyStoredCourse));
|
||||
}
|
||||
}
|
||||
60
Management/Services/AkkaService.cs
Normal file
60
Management/Services/AkkaService.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
|
||||
using Akka.Actor;
|
||||
using Akka.DependencyInjection;
|
||||
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
namespace Management.Services;
|
||||
|
||||
|
||||
public class AkkaService(
|
||||
IServiceProvider serviceProvider,
|
||||
IHostApplicationLifetime appLifetime,
|
||||
IConfiguration configuration
|
||||
) : IHostedService
|
||||
{
|
||||
private ActorSystem? actorSystem;
|
||||
private readonly IConfiguration configuration = configuration;
|
||||
private readonly IServiceProvider serviceProvider = serviceProvider;
|
||||
private readonly IHostApplicationLifetime applicationLifetime = appLifetime;
|
||||
public IActorRef? CanvasQueueActor { get; private set; }
|
||||
public IActorRef? StorageActor { get; private set; }
|
||||
|
||||
public Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var bootstrap = BootstrapSetup.Create();
|
||||
var dependencyInjectionSetup = DependencyResolverSetup.Create(serviceProvider);
|
||||
|
||||
var mergedSystemSetup = bootstrap.And(dependencyInjectionSetup);
|
||||
|
||||
actorSystem = ActorSystem.Create("canavas-management-actor-system", mergedSystemSetup);
|
||||
|
||||
var canvasQueueProps = DependencyResolver.For(actorSystem).Props<CanvasQueueActor>();
|
||||
CanvasQueueActor = actorSystem.ActorOf(canvasQueueProps, "canvasQueue");
|
||||
var localStorageProps = DependencyResolver.For(actorSystem).Props<LocalStorageActor>();
|
||||
StorageActor = actorSystem.ActorOf(localStorageProps, "localStorage");
|
||||
|
||||
// crash if the actor system crashes, awaiting never returns...
|
||||
actorSystem.WhenTerminated.ContinueWith(tr =>
|
||||
{
|
||||
applicationLifetime.StopApplication();
|
||||
});
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
||||
|
||||
|
||||
// public void Tell(object message)
|
||||
// {
|
||||
// userSessionSupervisor?.Tell(message);
|
||||
// }
|
||||
|
||||
// public Task<T> Ask<T>(object message)
|
||||
// {
|
||||
// return userSessionSupervisor.Ask<T>(message);
|
||||
// }
|
||||
|
||||
}
|
||||
@@ -4,7 +4,6 @@ using CanvasModel.Courses;
|
||||
using CanvasModel.EnrollmentTerms;
|
||||
using CanvasModel.Modules;
|
||||
using CanvasModel.Pages;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using RestSharp;
|
||||
|
||||
namespace Management.Services.Canvas;
|
||||
@@ -35,7 +34,7 @@ public class CanvasService(
|
||||
ICanvasQuizService Quizzes,
|
||||
ICanvasCoursePageService Pages,
|
||||
MyLogger<ICanvasService> logger
|
||||
):ICanvasService
|
||||
) : ICanvasService
|
||||
{
|
||||
private readonly IWebRequestor webRequestor = webRequestor;
|
||||
private readonly CanvasServiceUtils utils = utils;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using LocalModels;
|
||||
using Management.Services;
|
||||
|
||||
public class FileStorageManager : IFileStorageManager
|
||||
public class FileStorageManager
|
||||
{
|
||||
private readonly MyLogger<FileStorageManager> logger;
|
||||
private readonly CourseMarkdownLoader _courseMarkdownLoader;
|
||||
@@ -39,7 +39,7 @@ public class FileStorageManager : IFileStorageManager
|
||||
return await _courseMarkdownLoader.LoadSavedCourses();
|
||||
}
|
||||
|
||||
public IEnumerable<string> GetEmptyDirectories()
|
||||
public async Task<IEnumerable<string>> GetEmptyDirectories()
|
||||
{
|
||||
if (!Directory.Exists(_basePath))
|
||||
throw new DirectoryNotFoundException($"Cannot get empty directories, {_basePath} does not exist");
|
||||
|
||||
@@ -1,50 +1,50 @@
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using LocalModels;
|
||||
// using System.Diagnostics.CodeAnalysis;
|
||||
// using LocalModels;
|
||||
|
||||
public class FileStorageManagerCached : IFileStorageManager
|
||||
{
|
||||
private readonly FileStorageManager manager;
|
||||
// public class FileStorageManagerCached : IFileStorageManager
|
||||
// {
|
||||
// private readonly FileStorageManager manager;
|
||||
|
||||
private readonly object cacheLock = new object(); // Lock object for synchronization
|
||||
// private readonly object cacheLock = new object(); // Lock object for synchronization
|
||||
|
||||
|
||||
private DateTime? cacheTime { get; set; } = null;
|
||||
private IEnumerable<LocalCourse>? cachedCourses { get; set; } = null;
|
||||
private ILogger<FileStorageManagerCached> logger { get; }
|
||||
// private DateTime? cacheTime { get; set; } = null;
|
||||
// private IEnumerable<LocalCourse>? cachedCourses { get; set; } = null;
|
||||
// private ILogger<FileStorageManagerCached> logger { get; }
|
||||
|
||||
private readonly int cacheSeconds = 2;
|
||||
public FileStorageManagerCached(FileStorageManager manager, ILogger<FileStorageManagerCached> logger)
|
||||
{
|
||||
this.manager = manager;
|
||||
this.logger = logger;
|
||||
}
|
||||
public IEnumerable<string> GetEmptyDirectories()
|
||||
{
|
||||
return manager.GetEmptyDirectories();
|
||||
}
|
||||
// private readonly int cacheSeconds = 2;
|
||||
// public FileStorageManagerCached(FileStorageManager manager, ILogger<FileStorageManagerCached> logger)
|
||||
// {
|
||||
// this.manager = manager;
|
||||
// this.logger = logger;
|
||||
// }
|
||||
// public Task<IEnumerable<string>> GetEmptyDirectories()
|
||||
// {
|
||||
// return manager.GetEmptyDirectories();
|
||||
// }
|
||||
|
||||
public async Task<IEnumerable<LocalCourse>> LoadSavedCourses()
|
||||
{
|
||||
// public async Task<IEnumerable<LocalCourse>> LoadSavedCourses()
|
||||
// {
|
||||
|
||||
var secondsFromLastLoad = (DateTime.Now - cacheTime)?.Seconds;
|
||||
// var secondsFromLastLoad = (DateTime.Now - cacheTime)?.Seconds;
|
||||
|
||||
if (cachedCourses != null && secondsFromLastLoad < cacheSeconds)
|
||||
{
|
||||
logger.LogInformation("returning cached courses from file");
|
||||
return cachedCourses;
|
||||
}
|
||||
// if (cachedCourses != null && secondsFromLastLoad < cacheSeconds)
|
||||
// {
|
||||
// logger.LogInformation("returning cached courses from file");
|
||||
// return cachedCourses;
|
||||
// }
|
||||
|
||||
cachedCourses = await manager.LoadSavedCourses();
|
||||
cacheTime = DateTime.Now;
|
||||
return cachedCourses;
|
||||
}
|
||||
// cachedCourses = await manager.LoadSavedCourses();
|
||||
// cacheTime = DateTime.Now;
|
||||
// return cachedCourses;
|
||||
// }
|
||||
|
||||
public async Task SaveCourseAsync(LocalCourse course, LocalCourse? previouslyStoredCourse)
|
||||
{
|
||||
// race condition...
|
||||
cacheTime = null;
|
||||
cachedCourses = null;
|
||||
await manager.SaveCourseAsync(course, previouslyStoredCourse);
|
||||
}
|
||||
}
|
||||
// public async Task SaveCourseAsync(LocalCourse course, LocalCourse? previouslyStoredCourse)
|
||||
// {
|
||||
// // race condition...
|
||||
// cacheTime = null;
|
||||
// cachedCourses = null;
|
||||
// await manager.SaveCourseAsync(course, previouslyStoredCourse);
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -4,5 +4,5 @@ public interface IFileStorageManager
|
||||
{
|
||||
Task SaveCourseAsync(LocalCourse course, LocalCourse? previouslyStoredCourse);
|
||||
Task<IEnumerable<LocalCourse>> LoadSavedCourses();
|
||||
IEnumerable<string> GetEmptyDirectories();
|
||||
Task<IEnumerable<string>> GetEmptyDirectories();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user