Skip to content

Commit

Permalink
Add User Module support to new Service code
Browse files Browse the repository at this point in the history
IUserModule was moved to LukeBot.User.Common module. Additionally,
Services supporting user modules should inherit IUserModuleFactory.

Service code was expanded to support dependencies + tests were written
for it.

The rest of the project was adjusted to adapt to above changes.
  • Loading branch information
lookeypl committed Feb 6, 2025
1 parent 5be5e3b commit 62fd070
Show file tree
Hide file tree
Showing 73 changed files with 1,738 additions and 1,100 deletions.
2 changes: 1 addition & 1 deletion LukeBot.API/AuthManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ private AuthManager()
mOpenBrowserURLDelegate = callbacks[0];
}

public string GetName()
public string GetEventPublisherName()
{
return "AuthManager";
}
Expand Down
14 changes: 0 additions & 14 deletions LukeBot.API/Constants.cs

This file was deleted.

1 change: 1 addition & 0 deletions LukeBot.API/SpotifyToken.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using LukeBot.Common;
using LukeBot.Config;


Expand Down
1 change: 1 addition & 0 deletions LukeBot.API/TwitchToken.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using LukeBot.Common;
using LukeBot.Config;


Expand Down
8 changes: 4 additions & 4 deletions LukeBot.Common/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@ public class Constants

public const int SERVERCLI_DEFAULT_PORT = 55268; // in T9: LKBOT

public const string SPOTIFY_MODULE_NAME = "spotify";
public const string TWITCH_MODULE_NAME = "twitch";
public const string USER_MODULE_NAME = "user";
public const string WIDGET_MODULE_NAME = "widget";
public const string SPOTIFY_SERVICE_NAME = "spotify";
public const string TWITCH_SERVICE_NAME = "twitch";
public const string USER_SERVICE_NAME = "user";
public const string WIDGET_SERVICE_NAME = "widget";

public const string PROP_STORE_MODULES_DOMAIN = "modules";
public const string PROP_STORE_WIDGETS_DOMAIN = "widgets";
Expand Down
4 changes: 4 additions & 0 deletions LukeBot.Common/Exception.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ public Exception(string msg): base(msg)
{
}

public Exception(string msg, System.Exception e): base(msg, e)
{
}

public void Print(LogLevel level)
{
Logger.Log().Message(level, "{0} caught: {1}", this.GetType().FullName, Message);
Expand Down
33 changes: 33 additions & 0 deletions LukeBot.Common/Utils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using LukeBot.Config;


namespace LukeBot.Common
Expand Down Expand Up @@ -221,5 +222,37 @@ public static List<string> SplitJSONs(string message)

return messages;
}

// Common Config interactions //

private static Path GetUserModulesPath(string service)
{
return Path.Start()
.Push(service)
.Push(Constants.PROP_STORE_MODULES_DOMAIN);
}

public static void AddUserModuleToConfig(string service, string lbUser)
{
ConfUtil.ArrayAppendUnique(GetUserModulesPath(service), lbUser);
}

public static string[] GetUserModulesFromConfig(string service)
{
string[] users;
if (!Conf.TryGet<string[]>(GetUserModulesPath(service), out users))
{
// Couldn't find the config entry, meaning there is no enabled modules.
// Not considered an error.
users = new string[0];
}

return users;
}

public static void RemoveUserModuleFromConfig(string service, string lbUser)
{
ConfUtil.ArrayRemove(GetUserModulesPath(service), lbUser);
}
}
}
6 changes: 3 additions & 3 deletions LukeBot.Communication/EventSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ namespace LukeBot.Communication
*/
public interface IEventPublisher
{
public string GetName();
public string GetEventPublisherName();
public List<EventDescriptor> GetEvents();
}

Expand Down Expand Up @@ -140,7 +140,7 @@ private EventCallback AddEvent(EventDescriptor ed)
*/
public List<EventCallback> RegisterPublisher(IEventPublisher p)
{
string pubName = p.GetName();
string pubName = p.GetEventPublisherName();

Logger.Log().Debug("Registering publisher {0}", pubName);

Expand Down Expand Up @@ -173,7 +173,7 @@ public List<EventCallback> RegisterPublisher(IEventPublisher p)
*/
public void UnregisterPublisher(IEventPublisher p)
{
string pubName = p.GetName();
string pubName = p.GetEventPublisherName();

Logger.Log().Debug("Unregistering publisher {0}", pubName);

Expand Down
75 changes: 75 additions & 0 deletions LukeBot.Config/ConfUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,81 @@ public static void ArrayAppend<T>(Path path, T[] entries, IComparer<T> comparer)
Conf.Modify<T[]>(path, array);
}

private static T[] ArrayUnique<T>(T[] a)
{
List<T> newList = new();

for (int i = 0; i < a.Length; ++i)
{
if (!newList.Contains(a[i]))
{
newList.Add(a[i]);
}
}

return newList.ToArray();
}

/**
* Append an entry to an array. Sorts using default comparers. No change is made
* if element already exists in the array.
*
* If Array does not exist in Config, it will be created.
*/
public static void ArrayAppendUnique<T>(Path path, T entry)
{
ArrayAppendUnique<T>(path, entry, null);
}

/**
* Append an array of entries to an array. Sorts using default comparers. No change is made
* if element already exists in the array.
*
* If Array does not exist in Config, it will be created.
*/
public static void ArrayAppendUnique<T>(Path path, T[] entries)
{
ArrayAppendUnique<T>(path, entries, null);
}

/**
* Append an entry to an array and sort the array contents. No change is made
* if element already exists in the array.
*
* If Array does not exist in Config, it will be created.
*/
public static void ArrayAppendUnique<T>(Path path, T entry, IComparer<T> comparer)
{
ArrayAppendUnique<T>(path, new T[] { entry }, comparer);
}

/**
* Append an entry to an array and sort the array contents. If an entry exists
* in the array already no modification is made.
*
* If Array does not exist in Config it will be created.
*/
public static void ArrayAppendUnique<T>(Path path, T[] entries, IComparer<T> comparer)
{
T[] array;
if (!Conf.TryGet<T[]>(path, out array))
{
array = new T[entries.Length];
Array.Copy(entries, 0, array, 0, entries.Length);
Array.Sort<T>(array, comparer);
array = ArrayUnique(array);
Conf.Add(path, Property.Create<T[]>(array));
return;
}

int oldLength = array.Length;
Array.Resize(ref array, array.Length + entries.Length);
Array.Copy(entries, 0, array, oldLength, entries.Length);
Array.Sort<T>(array, comparer);
array = ArrayUnique(array);
Conf.Modify<T[]>(path, array);
}

/**
* Remove an entry to an array. Removing a last element will remove the whole Path
* from the Config.
Expand Down
1 change: 1 addition & 0 deletions LukeBot.Endpoint/LukeBot.Endpoint.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
<ProjectReference Include="../LukeBot.Communication/LukeBot.Communication.csproj" />
<ProjectReference Include="../LukeBot.Config/LukeBot.Config.csproj" />
<ProjectReference Include="../LukeBot.Logging/LukeBot.Logging.csproj" />
<ProjectReference Include="../LukeBot.Services/LukeBot.Services.csproj" />
<ProjectReference Include="../LukeBot.Widget.Common/LukeBot.Widget.Common.csproj" />
</ItemGroup>

Expand Down
43 changes: 16 additions & 27 deletions LukeBot.Endpoint/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,20 @@
using LukeBot.Config;
using LukeBot.Logging;
using Intercom = LukeBot.Communication.Common.Intercom;
using LukeBot.Services;
using LukeBot.Widget.Common;
using Microsoft.Extensions.FileProviders;



namespace LukeBot.Endpoint
{
public class Startup
{
private IWidgetService GetWidgetService()
{
return Service.Get(Constants.WIDGET_SERVICE_NAME) as IWidgetService;
}

async Task LoadPage(string page, HttpContext context)
{
StreamReader reader = File.OpenText("LukeBot.Endpoint/Pages/" + page);
Expand Down Expand Up @@ -125,19 +130,15 @@ async Task HandleWidgetCallback(string widgetUUID, HttpContext context)
{
Logger.Log().Debug("Widget requested - handling {0}", widgetUUID);

GetWidgetPageMessage msg = new GetWidgetPageMessage(widgetUUID);
GetWidgetPageResponse page =
Comms.Intercom.Request<GetWidgetPageResponse, GetWidgetPageMessage>(msg);

page.Wait();

if (page.Status == Intercom::MessageStatus.SUCCESS)
try
{
await context.Response.WriteAsync(page.pageContents);
IWidgetUserModule wum = GetWidgetService().GetModuleByWidgetUUID(widgetUUID);
string pageContents = wum.GetWidgetPage(widgetUUID);
await context.Response.WriteAsync(pageContents);
}
else
catch (Exception e)
{
await context.Response.WriteAsync("Couldn't load widget: " + page.ErrorReason);
await context.Response.WriteAsync("Couldn't load widget: " + e.Message);
}
}

Expand All @@ -156,24 +157,12 @@ async Task HandleWidgetWSCallback(string widgetUUID, HttpContext context)
{
WebSocket ws = await context.WebSockets.AcceptWebSocketAsync();

AssignWSMessage msg = new AssignWSMessage(widgetUUID, ws);
AssignWSResponse resp =
Comms.Intercom.Request<AssignWSResponse, AssignWSMessage>(msg);

resp.Wait();

if (resp.Status != Intercom::MessageStatus.SUCCESS)
{
await ws.CloseAsync(WebSocketCloseStatus.InternalServerError,
string.Format(resp.ErrorReason.Substring(0, 120)),
CancellationToken.None
);
return;
}
IWidgetUserModule wum = GetWidgetService().GetModuleByWidgetUUID(widgetUUID);
Task lifetimeTask = wum.AssignWidgetWebSocket(widgetUUID, ws);

Logger.Log().Debug("Awaiting lifetime task to keep connection to {0} Widget WS alive", widgetUUID);
// await for ws to complete, it will be closed in IWidget.cs when needed
await resp.lifetimeTask;
await lifetimeTask;
Logger.Log().Debug("Lifetime task for Widget WS {0} finished", widgetUUID);

// TODO at this point Kestrel logs "the application completed without reading the entire request body."
Expand All @@ -182,7 +171,7 @@ await ws.CloseAsync(WebSocketCloseStatus.InternalServerError,
}
catch (Exception e)
{
Logger.Log().Error("Error while processing WS connection for widgets: {0}", e.Message);
Logger.Log().Error("Error while processing WS connection for widget: {0}", e.Message);
context.Response.StatusCode = StatusCodes.Status500InternalServerError;
}
}
Expand Down
32 changes: 32 additions & 0 deletions LukeBot.Services/Exception/CircularServiceDependencyChain.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System.Collections.Generic;
using LukeBot.Common;

namespace LukeBot.Services
{
internal class CircularServiceDependencyChain: System.Exception
{
private List<string> mChain = new();

internal CircularServiceDependencyChain()
{}

internal void Add(string service)
{
mChain.Add(service);
}

internal string FormMessage()
{
string ret = "";

foreach (string s in mChain)
{
ret += s;
ret += " -> ";
}

ret += mChain[0];
return ret;
}
}
}
15 changes: 15 additions & 0 deletions LukeBot.Services/Exception/CircularServiceDependencyException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System.Collections.Generic;
using LukeBot.Common;

namespace LukeBot.Services
{
public class CircularServiceDependencyException: Exception
{
List<string> mChain = new();

internal CircularServiceDependencyException(string service, CircularServiceDependencyChain chain)
: base(string.Format("While resolving service {0} dependencies found a circular dependency: {1}", service, chain.FormMessage()))
{
}
}
}
4 changes: 2 additions & 2 deletions LukeBot.Services/Exception/InvalidServiceException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ namespace LukeBot.Services
{
public class InvalidServiceException: Exception
{
public InvalidServiceException(System.Type serviceType)
: base(string.Format("Invalid service type provided - {0}", serviceType.ToString()))
public InvalidServiceException(string serviceType)
: base(string.Format("Invalid service type provided - {0}", serviceType))
{
}
}
Expand Down
12 changes: 0 additions & 12 deletions LukeBot.Services/Exception/ModuleAlreadyRegisteredException.cs

This file was deleted.

12 changes: 0 additions & 12 deletions LukeBot.Services/Exception/PrerequisiteNotMetException.cs

This file was deleted.

Loading

0 comments on commit 62fd070

Please sign in to comment.