Skip to content

Commit

Permalink
Add namespaces to the diagrams files system path (#92)
Browse files Browse the repository at this point in the history
  • Loading branch information
SlavaVedernikov authored Aug 7, 2024
2 parents e6b7c07 + a711d39 commit 3c214b3
Show file tree
Hide file tree
Showing 5,746 changed files with 4,618 additions and 2,839 deletions.
The diff you're trying to view is too large. We only load the first 3000 changed files.
4 changes: 2 additions & 2 deletions C4InterFlow.Automation/C4InterFlow.Automation.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
<Deterministic>true</Deterministic>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<Version>1.6.3</Version>
<Version>1.6.4</Version>
</PropertyGroup>

<PropertyGroup>
<PackageId>C4InterFlow.Automation</PackageId>
<PackageVersion>1.6.3</PackageVersion>
<PackageVersion>1.6.4</PackageVersion>
<Authors>Slava Vedernikov</Authors>
<Description>Revolutionise your Application Architecture Documentation with C4InterFlow. Designed for Architects and Engineers, this tool leverages the widely-recognised C4 Model (Architecture Visualisation framework), enhanced with unique features like Interface and Flow, to describe your Application Architecture as Code. Experience an intuitive, efficient way to document complex systems, ensuring clarity and consistency across your teams and products.</Description>
<Copyright>Copyright 2024 Slava Vedernikov</Copyright>
Expand Down
2 changes: 1 addition & 1 deletion C4InterFlow.Cli/C4InterFlow.Cli.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Version>1.6.3</Version>
<Version>1.6.4</Version>
<PublishSingleFile>true</PublishSingleFile>
<SelfContained>true</SelfContained>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
Expand Down
2 changes: 1 addition & 1 deletion C4InterFlow.Cli/Properties/launchSettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"profiles": {
"C4InterFlow.Cli": {
"commandName": "Project",
"commandLineArgs": "draw-diagrams --interfaces TraderXExample.SoftwareSystems.*.Containers.*.Interfaces.* --levels-of-details context container --scopes namespace --aac-reader-strategy \"C4InterFlow.Automation.Readers.YamlAaCReaderStrategy,C4InterFlow.Automation\" --aac-input-paths \"C:\\C4InterFlow\\Samples\\TraderX\\CSV\\Architecture\\Yaml\" --output-dir \"C:\\C4InterFlow\\Samples\\TraderX\\CSV\\Diagrams\\Temp\""
"commandLineArgs": "draw-diagrams --interfaces ECommercePlatform.SoftwareSystems.*.Interfaces.* ECommercePlatform.SoftwareSystems.*.Containers.*.Interfaces.* ECommercePlatform.SoftwareSystems.*.Containers.*.Components.*.Interfaces.* ECommercePlatform.*.SoftwareSystems.*.Interfaces.* ECommercePlatform.*.SoftwareSystems.*.Containers.*.Interfaces.* ECommercePlatform.*.SoftwareSystems.*.Containers.*.Components.*.Interfaces.* --namespaces ECommercePlatform ECommercePlatform.FrontEnd --levels-of-details context container --scopes namespace namespace-software-systems --types c4-static --aac-reader-strategy \"C4InterFlow.Automation.Readers.YamlAaCReaderStrategy,C4InterFlow.Automation\" --aac-input-paths \"C:\\C4InterFlow\\Samples\\Temp\\Architecture\" --output-dir \"C:\\C4InterFlow\\Samples\\Temp\\Diagrams\""
}
}
}
4 changes: 2 additions & 2 deletions C4InterFlow/C4InterFlow.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
<Deterministic>true</Deterministic>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<Version>1.6.3</Version>
<Version>1.6.4</Version>
</PropertyGroup>

<PropertyGroup>
<PackageId>C4InterFlow</PackageId>
<PackageVersion>1.6.3</PackageVersion>
<PackageVersion>1.6.4</PackageVersion>
<Authors>Slava Vedernikov</Authors>
<Description>Revolutionise your Application Architecture Documentation with C4InterFlow. Designed for Architects and Engineers, this tool leverages the widely-recognised C4 Model (Architecture Visualisation framework), enhanced with unique features like Interface and Flow, to describe your Application Architecture as Code. Experience an intuitive, efficient way to document complex systems, ensuring clarity and consistency across your teams and products.</Description>
<Copyright>Copyright 2024 Slava Vedernikov</Copyright>
Expand Down
14 changes: 14 additions & 0 deletions C4InterFlow/Cli/Commands/Binders/DiagramOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,20 @@ public static bool IsSupported(string scope, string type, string levelOfDetail)

break;
}
case DiagramScopesOption.NAMESPACE_SOFTWARE_SYSTEMS:
{
supportedLevelsOfDetail.AddRange(new[] {
DiagramLevelsOfDetailsOption.CONTEXT,
DiagramLevelsOfDetailsOption.CONTAINER,
DiagramLevelsOfDetailsOption.COMPONENT
});
supportedTypes.AddRange(new[] {
DiagramTypesOption.C4_STATIC,
DiagramTypesOption.C4
});

break;
}
case DiagramScopesOption.SOFTWARE_SYSTEM:
{
supportedLevelsOfDetail.AddRange(new[] {
Expand Down
50 changes: 50 additions & 0 deletions C4InterFlow/Cli/Commands/Binders/InputOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using C4InterFlow.Structures;
using System;
using System.Collections.Generic;
using System.CommandLine.Binding;
using System.CommandLine;
using C4InterFlow.Cli.Commands.Options;

namespace C4InterFlow.Cli.Commands.Binders
{
public class InputOptions
{
public InputOptions(string[] interfaces, string interfacesInputFile, string[] businessProcesses, string[] namespaces)
{
Interfaces = interfaces;
InterfacesInputFile = interfacesInputFile;
BusinessProcesses = businessProcesses;
Namespaces = namespaces;
}
public string[] Interfaces { get; private set; }

public string InterfacesInputFile { get; private set; }

public string[] BusinessProcesses { get; private set; }

public string[] Namespaces { get; private set; }
}

public class InputOptionsBinder : BinderBase<InputOptions>
{
private readonly Option<string[]> _interfacesOption;
private readonly Option<string> _interfacesInputFileOption;
private readonly Option<string[]> _businessProcessesOption;
private readonly Option<string[]> _namespaces;

public InputOptionsBinder(Option<string[]> interfacesOption, Option<string> interfacesInputFileOption, Option<string[]> businessProcessesOption, Option<string[]> namespaces)
{
_interfacesOption = interfacesOption;
_interfacesInputFileOption = interfacesInputFileOption;
_businessProcessesOption = businessProcessesOption;
_namespaces = namespaces;
}

protected override InputOptions GetBoundValue(BindingContext bindingContext) =>
new InputOptions(
bindingContext.ParseResult.GetValueForOption(_interfacesOption),
bindingContext.ParseResult.GetValueForOption(_interfacesInputFileOption),
bindingContext.ParseResult.GetValueForOption(_businessProcessesOption),
bindingContext.ParseResult.GetValueForOption(_namespaces));
}
}
99 changes: 79 additions & 20 deletions C4InterFlow/Cli/Commands/DrawDiagramsCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public DrawDiagramsCommand() : base(COMMAND_NAME,
var interfacesOption = InterfacesOption.Get();
var interfacesInputFileOption = InterfacesInputFileOption.Get();
var businessProcesesOption = BusinessProcesesOption.Get();
var namespacesOption = NamespacesOption.Get();
var diagramFormatsOption = DiagramFormatsOption.Get();
var showBoundariesOption = ShowBoundariesOption.Get();
var showInterfaceInputAndOutputOption = ShowInterfaceInputAndOutputOption.Get();
Expand All @@ -41,6 +42,7 @@ public DrawDiagramsCommand() : base(COMMAND_NAME,
AddOption(interfacesOption);
AddOption(interfacesInputFileOption);
AddOption(businessProcesesOption);
AddOption(namespacesOption);
AddOption(diagramFormatsOption);
AddOption(showInterfaceInputAndOutputOption);
AddOption(outputDirectoryOption);
Expand All @@ -49,21 +51,26 @@ public DrawDiagramsCommand() : base(COMMAND_NAME,
AddOption(architectureAsCodeInputPathsOption);
AddOption(architectureAsCodeReaderStrategyTypeOption);

this.SetHandler(async (diagramOptions, interfaces, interfacesInputFile, businessProcesses, displayOptions, outputOptions, architectureAsCodeInputPaths, architectureAsCodeReaderStrategyType) =>
this.SetHandler(async (diagramOptions, inputOptions, displayOptions, outputOptions, architectureAsCodeInputPaths, architectureAsCodeReaderStrategyType) =>
{
await Execute(diagramOptions, interfaces, interfacesInputFile, businessProcesses, displayOptions, outputOptions, architectureAsCodeInputPaths, architectureAsCodeReaderStrategyType);
await Execute(diagramOptions, inputOptions, displayOptions, outputOptions, architectureAsCodeInputPaths, architectureAsCodeReaderStrategyType);
},
new DiagramOptionsBinder(diagramScopesOption, diagramTypesOption, diagramLevelsOfDetailsOption),
interfacesOption,
interfacesInputFileOption,
businessProcesesOption,
new DiagramOptionsBinder(
diagramScopesOption,
diagramTypesOption,
diagramLevelsOfDetailsOption),
new InputOptionsBinder(
interfacesOption,
interfacesInputFileOption,
businessProcesesOption,
namespacesOption),
new DisplayOptionsBinder(showInterfaceInputAndOutputOption),
new OutputOptionsBinder(outputDirectoryOption, outputSubDirectoryOption, diagramNamePrefixOption, diagramFormatsOption),
architectureAsCodeInputPathsOption,
architectureAsCodeReaderStrategyTypeOption);
}

public static async Task<int> Execute(DiagramOptions diagramOptions, string[]? interfaceAliases, string? interfacesInputFile, string[]? businessProcessTypeNames, DisplayOptions displayOptions, OutputOptions outputOptions, string[] architectureAsCodeInputPaths, string architectureAsCodeReaderStrategyType)
public static async Task<int> Execute(DiagramOptions diagramOptions, InputOptions inputOptions, DisplayOptions displayOptions, OutputOptions outputOptions, string[] architectureAsCodeInputPaths, string architectureAsCodeReaderStrategyType)
{
try
{
Expand All @@ -85,18 +92,18 @@ public static async Task<int> Execute(DiagramOptions diagramOptions, string[]? i
throw new InvalidDataException("AaC has errors. Please resolve and retry.");
}
var resolvedInterfaceAliases = new List<string>();
resolvedInterfaceAliases.AddRange(Utils.ResolveStructures(interfaceAliases));
resolvedInterfaceAliases.AddRange(Utils.ResolveStructures(inputOptions.Interfaces));

if(!string.IsNullOrEmpty(interfacesInputFile))
if(!string.IsNullOrEmpty(inputOptions.InterfacesInputFile))
{
Regex interfaceAliasRegex = new Regex(@"^[^\s]*\.Interfaces\.[^\s]*$");
var fileInputInterfaceAliases = Utils.ReadLines(interfacesInputFile).Where(x => interfaceAliasRegex.IsMatch(x));
var fileInputInterfaceAliases = Utils.ReadLines(inputOptions.InterfacesInputFile).Where(x => interfaceAliasRegex.IsMatch(x));
resolvedInterfaceAliases.AddRange(Utils.ResolveStructures(fileInputInterfaceAliases));
}

resolvedInterfaceAliases = resolvedInterfaceAliases.Distinct().ToList();

var resolvedBusinessProcessTypeNames = Utils.ResolveStructures(businessProcessTypeNames).Distinct();
var resolvedBusinessProcessTypeNames = Utils.ResolveStructures(inputOptions.BusinessProcesses).Distinct();

foreach (var diagramScope in diagramOptions.Scopes)
{
Expand Down Expand Up @@ -181,6 +188,7 @@ public static async Task<int> Execute(DiagramOptions diagramOptions, string[]? i
diagramScope,
levelOfDetails,
interfaces,
inputOptions.Namespaces,
outputOptions.Formats,
displayOptions.ShowBoundaries,
displayOptions.ShowInterfaceInputAndOutput,
Expand Down Expand Up @@ -214,7 +222,7 @@ public static async Task<int> Execute(DiagramOptions diagramOptions, string[]? i
}
catch (Exception e)
{
Console.WriteLine($"'{COMMAND_NAME}' command failed failed with exception(s) '{e.Message}'{(e.InnerException !=null ? $", '{e.InnerException}'" : string.Empty)}.");
Console.WriteLine($"'{COMMAND_NAME}' command failed with exception(s) '{e.Message}'{(e.InnerException !=null ? $", '{e.InnerException}'" : string.Empty)}.");
return 1;
}
}
Expand Down Expand Up @@ -248,6 +256,7 @@ private static IEnumerable<Interface> GetInterfaces(IEnumerable<string> interfac
{
case DiagramScopesOption.ALL_SOFTWARE_SYSTEMS:
case DiagramScopesOption.NAMESPACE:
case DiagramScopesOption.NAMESPACE_SOFTWARE_SYSTEMS:
case DiagramScopesOption.SOFTWARE_SYSTEM:
{
// Matches any string that ends with ".Interfaces.<word>"
Expand Down Expand Up @@ -633,7 +642,7 @@ private static void DrawSequenceDiagrams(string scope, string levelOfDetails, In
progress.Complete();
}

private static void DrawC4Diagrams(string scope, string levelOfDetails, Interface[] interfaces, string[] formats, bool showBoundaries, bool showInterfaceInputAndOutput, string outputDirectory, string? outputSubDirectory = null, bool isStatic = false, string? diagramNamePrefix = null)
private static void DrawC4Diagrams(string scope, string levelOfDetails, Interface[] interfaces, string[] namespaces, string[] formats, bool showBoundaries, bool showInterfaceInputAndOutput, string outputDirectory, string? outputSubDirectory = null, bool isStatic = false, string? diagramNamePrefix = null)
{
if (!interfaces.Any()) return;

Expand Down Expand Up @@ -666,20 +675,44 @@ private static void DrawC4Diagrams(string scope, string levelOfDetails, Interfac
context.Export(outputDirectory, diagram, path, fileName);
}
}
else if (scope == DiagramScopesOption.NAMESPACE)
else if (scope == DiagramScopesOption.NAMESPACE ||
scope == DiagramScopesOption.NAMESPACE_SOFTWARE_SYSTEMS)
{
string pattern = @"^(.*?)(?:\.SoftwareSystems)";

var namespaceAliases = interfaces.Select(x => Regex.Match(x.Alias, pattern))
var namespaceAliases = new List<string>();

interfaces.Select(x => Regex.Match(x.Alias, pattern))
.Where(m => m.Success)
.Select(m => m.Groups[1].Value)
.Distinct();
.Distinct()
.ToList()
.ForEach(x =>
{
var nameSpaceAlias = string.Empty;
var segments = x.Split('.');
foreach(var segment in segments)
{
nameSpaceAlias = string.IsNullOrEmpty(nameSpaceAlias) ? segment : $"{nameSpaceAlias}.{segment}";
if(!namespaceAliases.Contains(nameSpaceAlias))
{
namespaceAliases.Add(nameSpaceAlias);
}
}
});

var progress = new ConcurrentProgress(namespaceAliases.Count());

Parallel.ForEach(namespaceAliases, namespaceAlias =>
foreach(var namespaceAlias in namespaceAliases)
{
var namespaceInterfaces = interfaces.Where(x => x.Alias.StartsWith($"{namespaceAlias}.")).ToArray();
if (namespaces.Any() && !namespaces.Contains(namespaceAlias))
continue;

var namespaceInterfaces = scope == DiagramScopesOption.NAMESPACE ?
interfaces.Where(x => x.Alias.StartsWith($"{namespaceAlias}.")).ToArray() :
interfaces.Where(x => x.Alias.StartsWith($"{namespaceAlias}.SoftwareSystems.")).ToArray();

if (namespaceInterfaces.Any())
{
var diagram = GetDiagram(
Expand All @@ -695,7 +728,7 @@ private static void DrawC4Diagrams(string scope, string levelOfDetails, Interfac
scope,
levelOfDetails,
isStatic ? DiagramTypesOption.C4_STATIC : DiagramTypesOption.C4,
namespaceInterfaces.First(),
namespaceAlias,
out var path,
out var fileName,
outputSubDirectory,
Expand All @@ -707,7 +740,7 @@ private static void DrawC4Diagrams(string scope, string levelOfDetails, Interfac

progress.Increment();

});
}

progress.Complete();

Expand Down Expand Up @@ -883,6 +916,32 @@ private static bool TryGetDiagramPath(string scope, string levelOfDetails, strin

return path != null && !string.IsNullOrEmpty(fileName);
}

private static bool TryGetDiagramPath(string scope, string levelOfDetails, string diagramType, string namespaceAlias, out string path, out string fileName, string? outputSubDirectory = null, string? diagramNamePrefix = null)
{
if(scope != DiagramScopesOption.NAMESPACE &&
scope != DiagramScopesOption.NAMESPACE_SOFTWARE_SYSTEMS)
{
path = fileName = null;
return false;
}

path = !string.IsNullOrEmpty(outputSubDirectory) ? outputSubDirectory : string.Empty;

foreach (var segment in namespaceAlias!.Split('.'))
{
path = Path.Join(path, segment);
}

if(scope == DiagramScopesOption.NAMESPACE_SOFTWARE_SYSTEMS)
{
path = Path.Join(path, "Software Systems");
}

fileName = $"{(!string.IsNullOrEmpty(diagramNamePrefix) ? $"{diagramNamePrefix} - " : string.Empty)}{ToPrettyName(levelOfDetails)} - {ToPrettyName(diagramType)}.puml";

return !string.IsNullOrEmpty(path) && !string.IsNullOrEmpty(fileName);
}
private static bool TryGetDiagramPath(string scope, string levelOfDetails, string diagramType, Interface @interface, out string path, out string fileName, string? outputSubDirectory = null, string? diagramNamePrefix = null)
{
path = !string.IsNullOrEmpty(outputSubDirectory) ? outputSubDirectory : string.Empty;
Expand Down
2 changes: 1 addition & 1 deletion C4InterFlow/Cli/Commands/Options/BusinessProcesesOption.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public static class BusinessProcesesOption
public static Option<string[]> Get()
{
const string description =
"The full names of Business Process types for which to draw the Diagram(s).";
"The aliases of Business Processes for which to draw the Diagram(s).";

var option = new Option<string[]>(new[] { "--business-processes", "-bp" }, description)
{
Expand Down
3 changes: 3 additions & 0 deletions C4InterFlow/Cli/Commands/Options/DiagramScopesOption.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ public static class DiagramScopesOption
{
public const string ALL_SOFTWARE_SYSTEMS = "all-software-systems";
public const string NAMESPACE = "namespace";
public const string NAMESPACE_SOFTWARE_SYSTEMS = "namespace-software-systems";
public const string SOFTWARE_SYSTEM = "software-system";
public const string SOFTWARE_SYSTEM_INTERFACE = "software-system-interface";
public const string CONTAINER = "container";
Expand All @@ -27,6 +28,7 @@ public static Option<string[]> Get()
option.FromAmong(
ALL_SOFTWARE_SYSTEMS,
NAMESPACE,
NAMESPACE_SOFTWARE_SYSTEMS,
SOFTWARE_SYSTEM,
SOFTWARE_SYSTEM_INTERFACE,
CONTAINER,
Expand All @@ -45,6 +47,7 @@ public static string[] GetAllScopes()
return new[] {
ALL_SOFTWARE_SYSTEMS,
NAMESPACE,
NAMESPACE_SOFTWARE_SYSTEMS,
SOFTWARE_SYSTEM,
SOFTWARE_SYSTEM_INTERFACE,
CONTAINER,
Expand Down
21 changes: 21 additions & 0 deletions C4InterFlow/Cli/Commands/Options/NamespacesOption.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System.CommandLine;
using System.CommandLine.Parsing;

namespace C4InterFlow.Cli.Commands.Options;

public static class NamespacesOption
{
public static Option<string[]> Get()
{
const string description =
"The aliases of Namespaces for which to draw the Diagram(s).";

var option = new Option<string[]>(new[] { "--namespaces", "-ns" }, description)
{
AllowMultipleArgumentsPerToken = true
};
option.SetDefaultValue(null);

return option;
}
}
Loading

0 comments on commit 3c214b3

Please sign in to comment.