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

Tst/docs assembly xml #22

Merged
merged 10 commits into from
Nov 24, 2023
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
4 changes: 4 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
root = true

[*]
insert_final_newline = true
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ node_modules
/.github/workflows/package-lock.json
/artifacts
*.csproj.user
.idea
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@
<LocalExternalpath>$(MSBuildThisFileDirectory)/external</LocalExternalpath>
<ManifestSDKPath>$(MSBuildThisFileDirectory)</ManifestSDKPath>
</PropertyGroup>
</Project>
</Project>
14 changes: 14 additions & 0 deletions EAVFW.Extensions.Manifest.ManifestEnricherTool.sln
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EAVFramework", "external\EA
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EAVFW.Extensions.Manifest.SDK", "external\EAVFramework\sdk\EAVFW.Extensions.Manifest.SDK.csproj", "{3BEF0769-ABD1-4D34-8004-C98DE9FB0339}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EAVFW.Extensions.Docs.Extractor", "src\EAVFW.Extensions.Docs.Extractor\EAVFW.Extensions.Docs.Extractor.csproj", "{432042AB-6A78-4ED7-B8AC-73B047F6630F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EAVFW.Extensions.Docs.Generator", "src\EAVFW.Extensions.Docs.Generator\EAVFW.Extensions.Docs.Generator.csproj", "{71FCB365-F4AE-4578-BF86-B4A3C278B6B1}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -64,6 +68,14 @@ Global
{2F30C24A-421A-4309-9F07-99668EA70C25}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3BEF0769-ABD1-4D34-8004-C98DE9FB0339}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3BEF0769-ABD1-4D34-8004-C98DE9FB0339}.Debug|Any CPU.Build.0 = Debug|Any CPU
{432042AB-6A78-4ED7-B8AC-73B047F6630F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{432042AB-6A78-4ED7-B8AC-73B047F6630F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{432042AB-6A78-4ED7-B8AC-73B047F6630F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{432042AB-6A78-4ED7-B8AC-73B047F6630F}.Release|Any CPU.Build.0 = Release|Any CPU
{71FCB365-F4AE-4578-BF86-B4A3C278B6B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{71FCB365-F4AE-4578-BF86-B4A3C278B6B1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{71FCB365-F4AE-4578-BF86-B4A3C278B6B1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{71FCB365-F4AE-4578-BF86-B4A3C278B6B1}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -75,6 +87,8 @@ Global
{79505337-2855-4CC7-8FD3-D5AF31B18278} = {CAE8FBC9-BDC3-4F45-81E1-57506D609011}
{2F30C24A-421A-4309-9F07-99668EA70C25} = {CAE8FBC9-BDC3-4F45-81E1-57506D609011}
{3BEF0769-ABD1-4D34-8004-C98DE9FB0339} = {CAE8FBC9-BDC3-4F45-81E1-57506D609011}
{432042AB-6A78-4ED7-B8AC-73B047F6630F} = {0D61C87C-0809-4CD7-9200-D78AD11A1E22}
{71FCB365-F4AE-4578-BF86-B4A3C278B6B1} = {0D61C87C-0809-4CD7-9200-D78AD11A1E22}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {036D5D82-DDA4-4600-B190-739D512F1062}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=7ff53623_002D25f7_002D4650_002D83cc_002Da1cf7cca21fd/@EntryIndexedValue">&lt;SessionState ContinuousTestingMode="0" IsActive="True" Name="ZipAndUnZipTests" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"&gt;
&lt;Solution /&gt;
&lt;/SessionState&gt;</s:String></wpf:ResourceDictionary>
4 changes: 2 additions & 2 deletions global.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"sdk": {
"version": "6.0.414"
"version": "8.0.100-rc.2.23502.2"
}
}
}
36 changes: 36 additions & 0 deletions src/EAVFW.Extensions.Docs.Extractor/CustomAssemblyResolver.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace EAVFW.Extensions.Docs.Extractor
{
public static class CustomAssemblyResolver
{
public static Dictionary<string, AssemblyInfo> Dictionary { get; set; } = new();

public static Assembly? CustomAssemblyResolverEventHandler(object? sender, ResolveEventArgs args)
{
// Ignore missing resources
if (args.Name.Contains(".resources"))
return null;

// check for assemblies already loaded
var assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.FullName == args.Name);
if (assembly != null)
return assembly;

if (!Dictionary.TryGetValue(args.Name.Split(',').First(), out var assemblyInfo))
return null;

try
{
return Assembly.LoadFrom(assemblyInfo.Path);
}
catch (Exception)
{
return null;
}
}
}
}
165 changes: 165 additions & 0 deletions src/EAVFW.Extensions.Docs.Extractor/DocumentLogic.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;
using System.Text.Json;
using EAVFramework.Plugins;
using EAVFW.Extensions.Manifest.SDK;
using WorkflowEngine.Core;

namespace EAVFW.Extensions.Docs.Extractor
{
public class DocumentLogic : IDocumentLogic
{
private static Dictionary<string, AssemblyInfo> BuildAssemblyDictionary(IEnumerable<string> binDirectories)
{
var dictionary = new Dictionary<string, AssemblyInfo>();

foreach (var directory in binDirectories)
{
var dlls = Directory.GetFiles(directory, "*.dll");
foreach (var dll in dlls)
{
var assemblyName = AssemblyName.GetAssemblyName(dll);

dictionary.TryAdd(assemblyName.Name!, new AssemblyInfo
{
Name = assemblyName.Name!,
Version = assemblyName.Version!.ToString(),
Path = dll
});
}
}

return dictionary;
}

/// <inheritdoc />
public IEnumerable<PluginDocumentation> ExtractPluginDocumentation(PluginInfo pluginInfo)
{
var assembly = LoadAssembly(pluginInfo);

var implementingTypes = assembly.GetTypes()
.Where(t => typeof(IPlugin).IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract);

var plugins =
from implementingType in implementingTypes
let pluginRegistrations = implementingType.GetCustomAttributes<PluginRegistrationAttribute>()
let _interface = implementingType.GetInterfaces()
.FirstOrDefault(i => i.GenericTypeArguments.Length == 2)
select new PluginDocumentation
{
PluginRegistrations = pluginRegistrations.Select(x => new PluginRegistrationAttributeData
{ Order = x.Order, Execution = x.Execution, Operation = x.Operation, Mode = x.Mode }),
Name = implementingType.Name,
Summary = implementingType.GetDocumentation(),

Check warning on line 57 in src/EAVFW.Extensions.Docs.Extractor/DocumentLogic.cs

View workflow job for this annotation

GitHub Actions / Building

Possible null reference assignment.
Context = new TypeInformation(_interface.GetGenericArguments().First()),
Entity = new TypeInformation(_interface.GetGenericArguments().Last())
};

return plugins;
}

private static Assembly LoadAssembly(PluginInfo pluginInfo)
{
var subDirectories = pluginInfo.RootPath.EnumerateDirectories("*", SearchOption.AllDirectories);

var directoriesWithBin =
from d in subDirectories
where d.FullName.EndsWith($"bin/{pluginInfo.Configuration}/{pluginInfo.Framework}")
select d.FullName;

CustomAssemblyResolver.Dictionary = BuildAssemblyDictionary(directoriesWithBin.AsQueryable());

var currentDomain = AppDomain.CurrentDomain;
currentDomain.AssemblyResolve += CustomAssemblyResolver.CustomAssemblyResolverEventHandler;

var assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(pluginInfo.AssemblyPath.FullName);
return assembly;
}

/// <inheritdoc/>
public Dictionary<string, EntityDefinition> ExtractWizardDocumentation(FileInfo manifestFile,
PluginInfo pluginInfo)
{
var assembly = LoadAssembly(pluginInfo);

// Preloading types to easily query for documentation
var workflows = assembly.GetTypes()
.Where(type => !type.IsAbstract && !type.IsInterface && typeof(IWorkflow).IsAssignableFrom(type))
.ToDictionary(x => x.Name, x => x);

// Load manifest
using var openStream = manifestFile.OpenRead();
var jsonManifest = JsonDocument.ParseAsync(openStream).Result;

// Find Wizards
var simpleManifest = new Dictionary<string, EntityDefinition>();
ExtractEntitiesWithWizards(jsonManifest.RootElement, simpleManifest);

var tabs =
(from entity in simpleManifest
from wizard in entity.Value.Wizards
from _tabs in wizard.Value.Tabs
select _tabs).AsEnumerable();

// Glorified for loop?
var tabsWithWorkflows =
from tab in tabs
where tab.Value.OnTransitionOut?.Workflow != null || tab.Value.OnTransitionIn?.Workflow != null
select tab;

foreach (var (key, value) in tabsWithWorkflows)
{
if (!string.IsNullOrWhiteSpace(value?.OnTransitionIn?.Workflow) &&
workflows.TryGetValue(value.OnTransitionIn.Workflow, out var type1))
{
value.OnTransitionIn.AdditionalData["x-workflowSummary"] = type1.GetDocumentation();
}

if (!string.IsNullOrWhiteSpace(value?.OnTransitionOut?.Workflow) &&
workflows.TryGetValue(value.OnTransitionOut.Workflow, out var type2))
{
value.OnTransitionOut.AdditionalData["x-workflowSummary"] = type2.GetDocumentation();
}
}

var actionsWithWorkflows =
from tab in tabs
where tab.Value.Actions != null
from action in tab.Value.Actions
where action.Value.Workflow != null
select action;

foreach (var (key, value) in actionsWithWorkflows)
{
if (workflows.TryGetValue(value.Workflow, out var type))
value.AdditionalFields["x-workflowSummary"] = type.GetDocumentation();
}

return simpleManifest;
}

private void ExtractEntitiesWithWizards(JsonElement element, IDictionary<string, EntityDefinition> entities)
{
if (element.ValueKind != JsonValueKind.Object) return;

foreach (var property in element.EnumerateObject())
{
if (!property.NameEquals("entities") || property.Value.ValueKind != JsonValueKind.Object) continue;

var localEntities =
JsonSerializer.Deserialize<Dictionary<string, EntityDefinition>>(property.Value.GetRawText());

if (localEntities == null) return;

foreach (var (key, value) in localEntities.Where(x => x.Value?.Wizards?.Any() ?? false))
{
entities[key] = value;
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<LangVersion>9.0</LangVersion>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<RootNamespace>EAVFW.Extensions.Docs.Extracter</RootNamespace>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\external\EAVFramework\src\EAVFramework.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="System.ComponentModel.Composition" Version="6.0.0" />
</ItemGroup>

<ItemGroup Condition="$(UseEAVFromNuget) != 'false'">
<PackageReference Include="EAVFW.Extensions.Manifest.SDK" Version="4.2.0"/>
<PackageReference Include="EAVFW.Extensions.WorkflowEngine" Version="3.1.4" />
</ItemGroup>

<ItemGroup Condition="$(UseEAVFromNuget) == 'false'">
<ProjectReference Include="$(LocalExternalpath)/EAVFramework/src/EAVFramework.csproj" />
</ItemGroup>


</Project>
27 changes: 27 additions & 0 deletions src/EAVFW.Extensions.Docs.Extractor/IDocumentLogic.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System.Collections.Generic;
using System.IO;
using EAVFW.Extensions.Manifest.SDK;

namespace EAVFW.Extensions.Docs.Extractor
{
public interface IDocumentLogic
{
/// <summary>
/// Extract plugin metadata and summary from given Assembly using the DLL and .xml documentation files
/// created during a build.
///
/// Remember to enable GenerateDocumentationFile for the project.
/// <param name="pluginInfo"></param>
/// </summary>
IEnumerable<PluginDocumentation> ExtractPluginDocumentation(PluginInfo pluginInfo);

/// <summary>
/// Extract Wizards from the given Manifest and generate documentation based on manifest metadata and workflow
/// CLR types and Actions
/// </summary>
/// <param name="manifestFile"></param>
/// <param name="pluginInfo"></param>
/// <returns></returns>
Dictionary<string, EntityDefinition> ExtractWizardDocumentation(FileInfo manifestFile, PluginInfo pluginInfo);
}
}
9 changes: 9 additions & 0 deletions src/EAVFW.Extensions.Docs.Extractor/Plugin/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace EAVFW.Extensions.Docs.Extractor
{
public class AssemblyInfo
{
public string Name { get; set; } = "";
public string Version { get; set; } = "";
public string Path { get; set; }

Check warning on line 7 in src/EAVFW.Extensions.Docs.Extractor/Plugin/AssemblyInfo.cs

View workflow job for this annotation

GitHub Actions / Building

Non-nullable property 'Path' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.
}
}
20 changes: 20 additions & 0 deletions src/EAVFW.Extensions.Docs.Extractor/Plugin/PluginDocumentation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;

namespace EAVFW.Extensions.Docs.Extractor
{
public class PluginDocumentation
{
public IEnumerable<PluginRegistrationAttributeData> PluginRegistrations { get; set; } =
Array.Empty<PluginRegistrationAttributeData>();

public string? Name { get; set; }

[JsonPropertyName("context")] public TypeInformation Context { get; set; }

Check warning on line 14 in src/EAVFW.Extensions.Docs.Extractor/Plugin/PluginDocumentation.cs

View workflow job for this annotation

GitHub Actions / Building

Non-nullable property 'Context' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

[JsonPropertyName("entity")] public TypeInformation Entity { get; set; }

Check warning on line 16 in src/EAVFW.Extensions.Docs.Extractor/Plugin/PluginDocumentation.cs

View workflow job for this annotation

GitHub Actions / Building

Non-nullable property 'Entity' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

public string Summary { get; set; } = "";
}
}
28 changes: 28 additions & 0 deletions src/EAVFW.Extensions.Docs.Extractor/Plugin/PluginInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System;
using System.IO;

namespace EAVFW.Extensions.Docs.Extractor
{
public struct PluginInfo
{
public PluginInfo(DirectoryInfo rootPath, FileInfo assemblyPath, string configuration, string framework)
{
RootPath = !rootPath.Exists
? throw new ArgumentException($"Directory {nameof(rootPath)} does not exists")
: rootPath;
AssemblyPath = !assemblyPath.Exists
? throw new ArgumentException($"File {nameof(assemblyPath)} does not exists")
: assemblyPath;

Configuration = string.IsNullOrWhiteSpace(configuration)
? throw new ArgumentNullException(configuration)
: configuration;
Framework = string.IsNullOrWhiteSpace(framework) ? throw new ArgumentNullException(framework) : framework;
}

public DirectoryInfo RootPath { get; }
public FileInfo AssemblyPath { get; }
public string Configuration { get; }
public string Framework { get; }
}
}
Loading
Loading