Skip to content
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
24 changes: 19 additions & 5 deletions ServiceHub/Modules/ServiceHub.Modules.StaticWeb/StaticWebModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,38 @@ namespace ServiceHub.Modules.StaticWeb
{
public class StaticWebModule : IServiceModule
{
private WebServer _server;
private int _port = 8080;
private string _directory = "wwwroot";

private WebServer? _server;
private CancellationToken _token;
private ILogContext? _logContext;
private IConfigContext? _configContext;

public string Name => "StaticWebModule";

public void Initialize(IServiceContext context)
public void Initialize(ILogContext log, IConfigContext config)
{
context.Log($"{Name} initialized.");
_logContext = log;
_configContext = config;

var port = _configContext.Get("port") ?? "8080";
var root = _configContext.Get("directory") ?? "wwwroot";

_port = int.Parse(port);
_directory = root;

_logContext.Info($"{Name} initialized.");
}

public async Task StartAsync(CancellationToken token)
{
_token = token;

var fileProvider = new PhysicalFileProvider(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Modules", "WebModule", "wwwroot"));
var fileProvider = new PhysicalFileProvider(_directory);

_server = new WebServer(o => o
.WithUrlPrefix("http://localhost:8080/")
.WithUrlPrefix($"http://localhost:{_port}/")
.WithMode(HttpListenerMode.EmbedIO))
.WithLocalSessionManager()
.WithModule(new FileModule("wwwroot", (EmbedIO.Files.IFileProvider)fileProvider));
Expand Down
28 changes: 28 additions & 0 deletions ServiceHub/ServiceHub.Contracts/Classes/ConfigNode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
namespace ServiceHub.Contracts.Classes
{
public sealed class ConfigNode
{
private readonly Dictionary<string, ConfigNode> _children = new();
private string? _value;

public string? Value
{
get => _value;
set => _value = value;
}

public IReadOnlyDictionary<string, ConfigNode> Children => _children;

public ConfigNode GetOrAdd(string key)
{
if (!_children.TryGetValue(key, out var node))
{
node = new ConfigNode();
_children[key] = node;
}
return node;
}

public bool TryGet(string key, out ConfigNode node) => _children.TryGetValue(key, out node!);
}
}
13 changes: 13 additions & 0 deletions ServiceHub/ServiceHub.Contracts/Interfaces/IConfigContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace ServiceHub.Contracts.Interfaces
{
public interface IConfigContext
{
string? Get(string path);

IConfigContext? GetConfig(string path);

string? Value { get; }

IConfigContext[] Items { get; }
}
}
9 changes: 9 additions & 0 deletions ServiceHub/ServiceHub.Contracts/Interfaces/ILogContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace ServiceHub.Contracts.Interfaces
{
public interface ILogContext
{
void Info(string message);
void Warning(string message);
void Error(string message);
}
}
7 changes: 0 additions & 7 deletions ServiceHub/ServiceHub.Contracts/Interfaces/IServiceContext.cs

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace ServiceHub.Contracts.Interfaces
public interface IServiceModule
{
string Name { get; }
void Initialize(IServiceContext context);
void Initialize(ILogContext log, IConfigContext config);
Task StartAsync(CancellationToken token);
void Stop();
ServiceState State();
Expand Down
32 changes: 32 additions & 0 deletions ServiceHub/ServiceHub.Core/Classes/ConfigNode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
namespace ServiceHub.Core.Classes
{
internal class ConfigNode
{
private readonly Dictionary<string, ConfigNode> _children = new();
private readonly List<ConfigNode> _items = new();

public string? Value { get; set; }

public IReadOnlyDictionary<string, ConfigNode> Children => _children;
public IReadOnlyList<ConfigNode> Items => _items;

public bool IsArray => _items.Count > 0;

public ConfigNode GetOrAdd(string key)
{
if (!_children.TryGetValue(key, out var node))
{
node = new ConfigNode();
_children[key] = node;
}
return node;
}

public ConfigNode AddItem()
{
var node = new ConfigNode();
_items.Add(node);
return node;
}
}
}
84 changes: 84 additions & 0 deletions ServiceHub/ServiceHub.Core/Classes/SimpleContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
using ServiceHub.Contracts.Interfaces;
using System.Text.Json;

namespace ServiceHub.Core.Classes
{
public class SimpleContext : IConfigContext
{
internal ConfigNode Root { get; } = new();

public string? Value
{
get
{
if (Root == null)
return null;
return Root.Value;
}
}

public IConfigContext[] Items { get => Root.Items.Select(x => new SimpleContext(x)).ToArray(); }

public SimpleContext() { }
internal SimpleContext(ConfigNode root) { Root = root; }

public void LoadJson(string json)
{
using var doc = JsonDocument.Parse(json);
LoadElement(Root, doc.RootElement);
}

private void LoadElement(ConfigNode node, JsonElement element)
{
switch (element.ValueKind)
{
case JsonValueKind.Object:
foreach (var prop in element.EnumerateObject())
{
var child = node.GetOrAdd(prop.Name);
LoadElement(child, prop.Value);
}
break;

case JsonValueKind.Array:
foreach (var item in element.EnumerateArray())
{
var child = node.AddItem();
LoadElement(child, item);
}
break;

case JsonValueKind.String:
case JsonValueKind.Number:
case JsonValueKind.True:
case JsonValueKind.False:
node.Value = element.ToString();
break;
}
}

public string? Get(string path) => Traverse(path)?.Value;

public IConfigContext? GetConfig(string path) => Traverse(path);

private IConfigContext? Traverse(string path)
{
var parts = path.Split(':', StringSplitOptions.RemoveEmptyEntries);
ConfigNode current = Root;

foreach (var part in parts)
{
if (int.TryParse(part, out var index))
{
if (current.Items.Count <= index)
return null;
current = current.Items[index];
}
else if (!current.Children.TryGetValue(part, out current))

Check warning on line 77 in ServiceHub/ServiceHub.Core/Classes/SimpleContext.cs

View workflow job for this annotation

GitHub Actions / build

Converting null literal or possible null value to non-nullable type.

Check warning on line 77 in ServiceHub/ServiceHub.Core/Classes/SimpleContext.cs

View workflow job for this annotation

GitHub Actions / build

Converting null literal or possible null value to non-nullable type.
return null;
}

return new SimpleContext(current);
}
}
}
32 changes: 21 additions & 11 deletions ServiceHub/ServiceHub.Core/Model/ServiceInstance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,41 @@
internal sealed class ServiceInstance
{
private string _serviceName;
private IServiceModule? ServiceModule { get; set; }
private IServiceContext Context { get; set; }
public ServiceState State => ServiceModule?.State() ?? ServiceState.Error;
private IServiceModule? _serviceModule { get; set; }
private IConfigContext _config { get; set; }
private ILogContext _log { get; set; }
public ServiceState State => _serviceModule?.State() ?? ServiceState.Error;

public string Name => _serviceName;

public void StartAsync(CancellationToken ct) => ServiceModule?.StartAsync(ct);
public void StartAsync(CancellationToken ct) => _serviceModule?.StartAsync(ct);

public void Stop(CancellationToken ct) => ServiceModule?.Stop();
public void Stop(CancellationToken ct) => _serviceModule?.Stop();

public ServiceInstance(string name, Type type, IServiceContext context)
public ServiceInstance(ILogContext log, IConfigContext config, Type type)
{
Context = context;
_serviceName = name;
_config = config;
_log = log;

_serviceName = config.Get("name") ?? string.Empty;
if (string.IsNullOrEmpty(_serviceName))
throw new ArgumentNullException("name");

IConfigContext? contextValue = config.GetConfig("parameters");
if (contextValue == null)
throw new ArgumentNullException("parameters");

try
{
if (!typeof(IServiceModule).IsAssignableFrom(type) || type.IsAbstract)
throw new Exception($"The {type.Name} Module is not valid" );

ServiceModule = (IServiceModule)Activator.CreateInstance(type);
ServiceModule?.Initialize(context);
_serviceModule = (IServiceModule)Activator.CreateInstance(type);

Check warning on line 38 in ServiceHub/ServiceHub.Core/Model/ServiceInstance.cs

View workflow job for this annotation

GitHub Actions / build

Converting null literal or possible null value to non-nullable type.

Check warning on line 38 in ServiceHub/ServiceHub.Core/Model/ServiceInstance.cs

View workflow job for this annotation

GitHub Actions / build

Converting null literal or possible null value to non-nullable type.
_serviceModule?.Initialize(log, contextValue);
}
catch (Exception ex)
{
Context.Log($"Error : {ex.Message}");
_log.Error($"Error : {ex.Message}");
throw;
}
}
Expand Down
17 changes: 11 additions & 6 deletions ServiceHub/ServiceHub.Core/ServiceHub.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,19 @@

#region Public Methods

public bool InitModule(string typeService, string name, IServiceContext context)
public bool InitModule(ILogContext log, IConfigContext config)
{
var typeService = config.Get("type") ?? string.Empty;

if (string.IsNullOrEmpty(typeService))
throw new ArgumentNullException("type");

if (!_availableModules.TryGetValue(typeService, out Type? type))
return false;

try {
var instance = new ServiceInstance(name, type, context);
_modulesLoaded.Add(name.ToLower().Trim(), instance);
var instance = new ServiceInstance(log, config, type);
_modulesLoaded.Add(instance.Name.ToLower().Trim(), instance);
}
catch {
return false;
Expand Down Expand Up @@ -65,7 +70,7 @@

#endregion

public void LoadModules(string folderPath, IServiceContext context)
public void LoadModules(ILogContext log, string folderPath)
{
if (!Directory.Exists(folderPath))
Directory.CreateDirectory(folderPath);
Expand All @@ -82,17 +87,17 @@
if (!typeof(IServiceModule).IsAssignableFrom(type) || type.IsAbstract)
continue;

var name = ((IServiceModule)Activator.CreateInstance(type))?.Name;

Check warning on line 90 in ServiceHub/ServiceHub.Core/ServiceHub.cs

View workflow job for this annotation

GitHub Actions / build

Converting null literal or possible null value to non-nullable type.

Check warning on line 90 in ServiceHub/ServiceHub.Core/ServiceHub.cs

View workflow job for this annotation

GitHub Actions / build

Converting null literal or possible null value to non-nullable type.
if (!string.IsNullOrWhiteSpace(name))
{
_availableModules.Add(name ?? string.Empty, type);
context.Log($"Loaded module: {name}");
log.Info($"Loaded module: {name}");
}
}
}
catch (Exception ex)
{
context.Log($"Failed to load {file}: {ex.Message}");
log.Warning($"Failed to load {file}: {ex.Message}");
}
}
}
Expand Down
21 changes: 21 additions & 0 deletions ServiceHub/ServiceHub.Host/LogContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using ServiceHub.Contracts.Classes;
using ServiceHub.Contracts.Interfaces;

namespace ServiceHub.Host
{
internal class LogContext : ILogContext
{
public void Info(string message) => Write(ConsoleColor.Blue, message);

public void Warning(string message) => Write(ConsoleColor.Yellow, message);

public void Error(string message) => Write(ConsoleColor.Red,message);

private void Write(ConsoleColor Color, string message)
{
Console.ForegroundColor = Color;
Console.WriteLine(message);
Console.ForegroundColor = ConsoleColor.White;
}
}
}
Loading