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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -397,3 +397,4 @@ FodyWeavers.xsd
# JetBrains Rider
*.sln.iml
.idea/
*.DotSettings
57 changes: 0 additions & 57 deletions src/InvvardDev.Ifttt.Trigger/Attributes/AttributeLookup.cs

This file was deleted.

This file was deleted.

12 changes: 12 additions & 0 deletions src/InvvardDev.Ifttt.Trigger/Contracts/IAssemblyAccessor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.Reflection;

namespace InvvardDev.Ifttt.Trigger.Contracts;

public interface IAssemblyAccessor
{
IEnumerable<Assembly> GetApplicationAssemblies();

TAttribute? GetAttribute<TAttribute>(Type classType) where TAttribute : Attribute;

void FilterOutAssemblies(params string [] assemblyNames);
}
9 changes: 9 additions & 0 deletions src/InvvardDev.Ifttt.Trigger/Contracts/ITriggerMapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace InvvardDev.Ifttt.Trigger.Contracts;

public interface ITriggerMapper
{
ITriggerMapper MapTriggerTypes();

ITriggerMapper MapTriggerFields();

}
12 changes: 6 additions & 6 deletions src/InvvardDev.Ifttt.Trigger/Contracts/ITriggerRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ namespace InvvardDev.Ifttt.Trigger.Contracts;

public interface ITriggerRepository
{
ITriggerRepository MapTriggerTypes();
ITriggerRepository MapTriggerFields();
ITrigger GetTriggerProcessorInstance(string triggerSlug);
void AddOrUpdateTrigger(string triggerSlug, Type triggerType);

void AddOrUpdateTriggerFields(string triggerSlug, Type triggerFieldsType);

ITrigger? GetTriggerProcessorInstance(string triggerSlug);

Type? GetTriggerFieldsType(string triggerSlug);
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
using System.Reflection;
using InvvardDev.Ifttt.Core;
using InvvardDev.Ifttt.Core.Configuration;
using InvvardDev.Ifttt.Trigger.Attributes;
using InvvardDev.Ifttt.Trigger.Contracts;
using InvvardDev.Ifttt.Trigger.Hooks;
using InvvardDev.Ifttt.Trigger.Reflection;
using InvvardDev.Ifttt.Trigger.Repositories;
using Microsoft.Extensions.Options;

Expand All @@ -23,6 +23,8 @@ public static IServiceCollection AddTriggers(this IServiceCollection services)
client.DefaultRequestHeaders.Add(IftttConstants.ServiceKeyHeader, options.ServiceKey);
});

services.AddScoped<IAssemblyAccessor, AssemblyAccessor>();
services.AddTransient<ITriggerMapper, TriggerMapper>();
services.AddTransient<ITriggerHook, RealTimeNotificationWebHook>();
services.AddSingleton<ITriggerRepository, TriggerRepositoryService>();
services.AddKeyedTransient<IAttributeLookup, TriggerAttributeLookup>(nameof(TriggerAttributeLookup));
Expand All @@ -38,7 +40,7 @@ public static IServiceCollection AddTriggers(this IServiceCollection services)
public static IApplicationBuilder ConfigureTriggers(this WebApplication app)
{
app.Services
.GetRequiredService<ITriggerRepository>()
.GetRequiredService<ITriggerMapper>()
.MapTriggerTypes()
.MapTriggerFields();

Expand Down
52 changes: 52 additions & 0 deletions src/InvvardDev.Ifttt.Trigger/Reflection/AssemblyAccessor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using System.Reflection;
using InvvardDev.Ifttt.Trigger.Contracts;
using Microsoft.Extensions.DependencyModel;

namespace InvvardDev.Ifttt.Trigger.Reflection;

internal class AssemblyAccessor : IAssemblyAccessor
{
private readonly List<string> assemblyNamesToFilterOut =
[
"Microsoft.",
"mscorlib",
"netstandard",
"Newtonsoft.Json",
"System",
"System.",
"WindowsBase"
];

public IEnumerable<Assembly> GetApplicationAssemblies()
{
if (DependencyContext.Default is not { } defaultDependency) return Array.Empty<Assembly>();

var applicationAssemblies = defaultDependency
.GetDefaultAssemblyNames()
.Where(assembly => !IsFrameworkAssembly(assembly.Name))
.Select(Assembly.Load)
.ToList();

if (Assembly.GetEntryAssembly() is not { } entryAssembly) return applicationAssemblies.ToArray();

if (!IsFrameworkAssembly(entryAssembly.GetName().Name)
&& applicationAssemblies.Any(assembly => assembly.GetName() == entryAssembly.GetName()))
{
applicationAssemblies.Add(entryAssembly);
}

return applicationAssemblies.ToArray();
}

public TAttribute? GetAttribute<TAttribute>(Type classType)
where TAttribute : Attribute
=> classType.GetCustomAttribute<TAttribute>();

public void FilterOutAssemblies(params string[] assemblyNames)
{
assemblyNamesToFilterOut.AddRange(assemblyNames);
}

private bool IsFrameworkAssembly(string? assemblyName)
=> !string.IsNullOrWhiteSpace(assemblyName) && assemblyNamesToFilterOut.Any(a => a.StartsWith(assemblyName));
}
23 changes: 23 additions & 0 deletions src/InvvardDev.Ifttt.Trigger/Reflection/AttributeLookup.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using InvvardDev.Ifttt.Trigger.Contracts;

namespace InvvardDev.Ifttt.Trigger.Reflection;

internal abstract class AttributeLookup(IAssemblyAccessor assemblyAccessor) : IAttributeLookup
{
public IEnumerable<Type> GetAnnotatedTypes()
{
var annotatedTypes = new List<Type>();
var assemblies = assemblyAccessor.GetApplicationAssemblies();

foreach (var assembly in assemblies)
{
annotatedTypes.AddRange(assembly.GetTypes()
.Where(IsMatching)
.ToList());
}

return annotatedTypes;
}

protected abstract bool IsMatching(Type type);
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using InvvardDev.Ifttt.Trigger.Attributes;
using InvvardDev.Ifttt.Trigger.Contracts;

namespace InvvardDev.Ifttt.Trigger.Attributes;
namespace InvvardDev.Ifttt.Trigger.Reflection;

internal class TriggerAttributeLookup : AttributeLookup
internal class TriggerAttributeLookup(IAssemblyAccessor assemblyAccessor)
: AttributeLookup(assemblyAccessor)
{
protected override bool IsMatching(Type type)
=> type is { IsClass: true, IsAbstract: false }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using InvvardDev.Ifttt.Trigger.Attributes;
using InvvardDev.Ifttt.Trigger.Contracts;

namespace InvvardDev.Ifttt.Trigger.Reflection;

internal class TriggerFieldsAttributeLookup(IAssemblyAccessor assemblyAccessor)
: AttributeLookup(assemblyAccessor)
{
protected override bool IsMatching(Type type)
=> type is { IsClass: true, IsAbstract: false }
&& typeof(object) != type
&& type.GetCustomAttributes(typeof(TriggerFieldsAttribute), true).Length > 0
&& type.GetProperties().Any(p => p.GetCustomAttributes(typeof(TriggerFieldAttribute), true).Length > 0);
}
40 changes: 40 additions & 0 deletions src/InvvardDev.Ifttt.Trigger/Reflection/TriggerMapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using System.Reflection;
using InvvardDev.Ifttt.Trigger.Attributes;
using InvvardDev.Ifttt.Trigger.Contracts;

namespace InvvardDev.Ifttt.Trigger.Reflection;

internal class TriggerMapper(
ITriggerRepository triggerRepository,
IAssemblyAccessor assemblyAccessor,
[FromKeyedServices(nameof(TriggerAttributeLookup))] IAttributeLookup triggerAttributeLookup,
[FromKeyedServices(nameof(TriggerFieldsAttributeLookup))] IAttributeLookup triggerFieldsAttributeLookup) : ITriggerMapper
{
public ITriggerMapper MapTriggerTypes()
{
var types = triggerAttributeLookup.GetAnnotatedTypes();
foreach (var triggerType in types)
{
if (assemblyAccessor.GetAttribute<TriggerAttribute>(triggerType) is { } triggerAttribute)
{
triggerRepository.AddOrUpdateTrigger(triggerAttribute.Slug, triggerType);
}
}

return this;
}

public ITriggerMapper MapTriggerFields()
{
var types = triggerFieldsAttributeLookup.GetAnnotatedTypes();
foreach (var triggerFieldsType in types)
{
if (triggerFieldsType.GetCustomAttribute<TriggerFieldsAttribute>() is { } triggerFieldsAttribute)
{
triggerRepository.AddOrUpdateTriggerFields(triggerFieldsAttribute.Slug, triggerFieldsType);
}
}

return this;
}
}
Original file line number Diff line number Diff line change
@@ -1,56 +1,46 @@
using System.Reflection;
using InvvardDev.Ifttt.Trigger.Attributes;
using InvvardDev.Ifttt.Trigger.Contracts;
using InvvardDev.Ifttt.Trigger.Models;

namespace InvvardDev.Ifttt.Trigger.Repositories;

internal class TriggerRepositoryService(
[FromKeyedServices(nameof(TriggerAttributeLookup))] IAttributeLookup triggerAttributeLookup,
[FromKeyedServices(nameof(TriggerFieldsAttributeLookup))] IAttributeLookup triggerFieldsAttributeLookup
) : ITriggerRepository
internal class TriggerRepositoryService : ITriggerRepository
{
private readonly Dictionary<string, TriggerDataType> triggers = new();

public ITriggerRepository MapTriggerTypes()
public void AddOrUpdateTrigger(string triggerSlug, Type triggerType)
{
var types = triggerAttributeLookup.GetAnnotatedTypes();
foreach (var triggerType in types)
if (!triggers.TryGetValue(triggerSlug, out var triggerData))
{
if (triggerType.GetCustomAttribute<TriggerAttribute>() is { } triggerAttribute
&& !triggers.ContainsKey(triggerAttribute.Slug))
{
triggers.Add(triggerAttribute.Slug, new TriggerDataType(triggerAttribute.Slug, triggerType));
}
triggers.Add(triggerSlug, new TriggerDataType(triggerSlug, triggerType));
}
else
{
triggers[triggerSlug] = triggerData with
{
TriggerType = triggerType
};
}

return this;
}

public ITriggerRepository MapTriggerFields()
public void AddOrUpdateTriggerFields(string triggerSlug, Type triggerFieldsType)
{
var types = triggerFieldsAttributeLookup.GetAnnotatedTypes();
foreach (var triggerFieldsType in types)
if (!triggers.TryGetValue(triggerSlug, out var triggerData))
{
if (triggerFieldsType.GetCustomAttribute<TriggerFieldsAttribute>() is { } triggerFieldsAttribute
&& triggers.TryGetValue(triggerFieldsAttribute.Slug, out var triggerDataType))
{
triggers[triggerFieldsAttribute.Slug] = triggerDataType with
{
TriggerFieldsType = triggerFieldsType
};
}
throw new InvalidOperationException($"Trigger '{triggerSlug}' was not found.");
}

return this;
triggers[triggerSlug] = triggerData with
{
TriggerFieldsType = triggerFieldsType
};
}

public ITrigger GetTriggerProcessorInstance(string triggerSlug)
public ITrigger? GetTriggerProcessorInstance(string triggerSlug)
{
if (!triggers.TryGetValue(triggerSlug, out var triggerDataType)
|| Activator.CreateInstance(triggerDataType.TriggerType) is not ITrigger triggerInstance)
{
throw new InvalidOperationException();
return null;
}

return triggerInstance;
Expand Down
Loading