Skip to content

Commit

Permalink
Added support for publishing a simple ReactJS Diagrams Visualiser site
Browse files Browse the repository at this point in the history
  • Loading branch information
SlavaVedernikov committed May 5, 2024
1 parent 90435ab commit 49d19a0
Show file tree
Hide file tree
Showing 15 changed files with 132 additions and 47 deletions.
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.2.0</Version>
<Version>1.3.0</Version>
</PropertyGroup>

<PropertyGroup>
<PackageId>C4InterFlow.Automation</PackageId>
<PackageVersion>1.2.0</PackageVersion>
<PackageVersion>1.3.0</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.2.0</Version>
<Version>1.3.0</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": "publish-site --site-source-dir \"C:\\C4InterFlow\\Publishers\\StaticSite\" --output-dir \"C:\\architecture-as-code-samples-visualiser\\trader-x\" --environment-variables \"HOMEPAGE=architecture-as-code-samples-visualiser/trader-x\""
"commandLineArgs": "publish-site --site-source-dir \"C:\\C4InterFlow\\Publishers\\StaticSite\" --output-dir \"C:\\architecture-as-code-samples-visualiser\\e-commerce-platform\" --environment-variables \"HOMEPAGE=architecture-as-code-samples-visualiser/e-commerce-platform\" --site-content-sub-dirs \"Software Systems\" \".c4s\" --site-no-sitemap-sub-dirs \".c4s\""
}
}
}
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.2.0</Version>
<Version>1.3.0</Version>
</PropertyGroup>

<PropertyGroup>
<PackageId>C4InterFlow</PackageId>
<PackageVersion>1.2.0</PackageVersion>
<PackageVersion>1.3.0</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
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public static class OutputSubDirectoryOption
public static Option<string> Get()
{
const string description =
"The sub-directory where the Diagram(s) should be saved.";
"The output sub-directory for the current command";

var option = new Option<string>(new[] { "--output-sub-dir", "-osd" }, description);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System.CommandLine;

namespace C4InterFlow.Cli.Commands.Options;

public static class SiteContentSubDirectoriesOption
{
public static Option<string[]> Get()
{
const string description =
$"The sub-directories where site content can be found.";

var option = new Option<string[]>(new[] { "--site-content-sub-dirs", "-scsds" }, description)
{
AllowMultipleArgumentsPerToken = true
};
option.IsRequired = true;

return option;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System.CommandLine;

namespace C4InterFlow.Cli.Commands.Options;

public static class SiteNoSitemapSubDirectoriesOption
{
public static Option<string[]> Get()
{
const string description =
$"The sub-directories that shoud be excluded from a sitemap.";

var option = new Option<string[]>(new[] { "--site-no-sitemap-sub-dirs", "-snssds" }, description)
{
AllowMultipleArgumentsPerToken = true
};
option.SetDefaultValue(null);

return option;
}
}
71 changes: 50 additions & 21 deletions C4InterFlow/Cli/Commands/PublishSiteCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,33 +17,41 @@ public PublishSiteCommand() : base(COMMAND_NAME,
var siteBuildDirectoryOption = SiteBuildDirectoryOption.Get();
var diagramFormatsOption = DiagramFormatsOption.Get();
var environmentVariablesOption = EnvironmentVariablesOption.Get();
var siteContentSubDirectoriesOption = SiteContentSubDirectoriesOption.Get();
var siteNoSitemapSubDirectoriesOption = SiteNoSitemapSubDirectoriesOption.Get();

AddOption(siteSourceDirectoryOption);
AddOption(outputDirectoryOption);
AddOption(batchFileOption);
AddOption(siteBuildDirectoryOption);
AddOption(diagramFormatsOption);
AddOption(environmentVariablesOption);
AddOption(siteContentSubDirectoriesOption);
AddOption(siteNoSitemapSubDirectoriesOption);

this.SetHandler(async (siteSourceDirectory, outputDirectory, batchFile, siteBuildDirectory, diagramFormats, environmentVariables) =>
this.SetHandler(async (siteSourceDirectory, outputDirectory, siteContentSubDirectories, batchFile, siteBuildDirectory, diagramFormats, environmentVariables, siteNoSitemapSubDirectories) =>
{
await Execute(siteSourceDirectory, outputDirectory, batchFile, siteBuildDirectory, diagramFormats, environmentVariables);
await Execute(siteSourceDirectory, outputDirectory, siteContentSubDirectories, batchFile, siteBuildDirectory, diagramFormats, environmentVariables, siteNoSitemapSubDirectories);
},
siteSourceDirectoryOption, outputDirectoryOption, batchFileOption, siteBuildDirectoryOption, diagramFormatsOption, environmentVariablesOption);
siteSourceDirectoryOption, outputDirectoryOption, siteContentSubDirectoriesOption, batchFileOption, siteBuildDirectoryOption, diagramFormatsOption, environmentVariablesOption, siteNoSitemapSubDirectoriesOption);
}

private static async Task<int> Execute(string siteSourceDirectory, string outputDirectory, string? batchFile = null, string? siteBuildDirectory = null, string[]? diagramFormats = null, string[]? environmentVariables = null)
private static async Task<int> Execute(string siteSourceDirectory, string outputDirectory, string[] siteContentSubDirectories, string ? batchFile = null, string? siteBuildDirectory = null, string[]? diagramFormats = null, string[]? environmentVariables = null, string[]? siteNoSitemapSubDirectories = null)
{

diagramFormats = diagramFormats?.Length > 0 ? diagramFormats : DiagramFormatsOption.GetAllDiagramFormats();
batchFile = batchFile ?? "build.bat";
siteBuildDirectory = siteBuildDirectory ?? "build";

try
{
Console.WriteLine($"'{COMMAND_NAME}' command is executing...");

ClearDirectory(outputDirectory, siteContentSubDirectories.Select(x => Path.Join(outputDirectory, x)).ToArray());

var sitemap = new
{
urlset = BuildDirectoryMap(outputDirectory, outputDirectory, diagramFormats.Select(x => $".{x}").ToArray())
urlset = BuildDirectoryMap(outputDirectory, outputDirectory, diagramFormats.Select(x => $".{x}").ToArray(), siteNoSitemapSubDirectories.Select(x => Path.Join(outputDirectory, x)).ToArray())
};

string json = JsonConvert.SerializeObject(sitemap, Formatting.Indented);
Expand All @@ -52,7 +60,7 @@ private static async Task<int> Execute(string siteSourceDirectory, string output

RunBatchFile(Path.Join(siteSourceDirectory, batchFile.Replace(siteSourceDirectory, "").TrimStart('\\')), environmentVariables);

CopyFilesRecursively(Path.Join(siteSourceDirectory, siteBuildDirectory.Replace(siteSourceDirectory, "").TrimStart('\\')), outputDirectory);
CopyFiles(Path.Join(siteSourceDirectory, siteBuildDirectory.Replace(siteSourceDirectory, "").TrimStart('\\')), outputDirectory);

Console.WriteLine($"'{COMMAND_NAME}' command completed.");
return 0;
Expand All @@ -64,24 +72,27 @@ private static async Task<int> Execute(string siteSourceDirectory, string output
}
}

private static List<object> BuildDirectoryMap(string directory, string rootPath, string[] fileExtensions)
private static List<object> BuildDirectoryMap(string directory, string rootPath, string[] fileExtensions, string[]? excludedPaths = null)
{
List<object> map = new List<object>();
foreach (string subDirectory in Directory.GetDirectories(directory))
{
string label = Path.GetFileName(subDirectory);
var directoryItem = new
{
label = label,
loc = subDirectory.Replace(rootPath, "").TrimStart('\\').Replace("\\", "/"),
urlset = BuildDirectoryMap(subDirectory, rootPath, fileExtensions),
types = new HashSet<string>(),
levelsOfDetails = new HashSet<string>(),
formats = new HashSet<string>()
};
if (excludedPaths?.Contains(subDirectory) == true)
continue;

AddFileDetails(subDirectory, fileExtensions, directoryItem.types, directoryItem.levelsOfDetails, directoryItem.formats);
map.Add(directoryItem);
string label = Path.GetFileName(subDirectory);
var directoryItem = new
{
label = label,
loc = subDirectory.Replace(rootPath, "").TrimStart('\\').Replace("\\", "/"),
urlset = BuildDirectoryMap(subDirectory, rootPath, fileExtensions, excludedPaths),
types = new HashSet<string>(),
levelsOfDetails = new HashSet<string>(),
formats = new HashSet<string>()
};

AddFileDetails(subDirectory, fileExtensions, directoryItem.types, directoryItem.levelsOfDetails, directoryItem.formats);
map.Add(directoryItem);
}
return map;
}
Expand Down Expand Up @@ -144,7 +155,25 @@ private static void RunBatchFile(string batchFile, string[]? environmentVariable
Directory.SetCurrentDirectory(currentDirectory);
}

static void CopyFilesRecursively(string sourcePath, string targetPath)
static public void ClearDirectory(string path, string[] excludedPaths)
{
foreach (var subDirectory in Directory.GetDirectories(path))
{
if (excludedPaths.Contains(subDirectory))
continue;

ClearDirectory(subDirectory, excludedPaths);
Directory.Delete(subDirectory, true);
}

var files = Directory.GetFiles(path);
foreach (var file in files)
{
File.Delete(file);
}
}

static void CopyFiles(string sourcePath, string targetPath)
{
// Check if the target directory exists, if not, create it.
if (!Directory.Exists(targetPath))
Expand All @@ -168,7 +197,7 @@ static void CopyFilesRecursively(string sourcePath, string targetPath)
{
Directory.CreateDirectory(Path.Combine(targetPath, dirName));
}
CopyFilesRecursively(directoryPath, Path.Combine(targetPath, dirName));
CopyFiles(directoryPath, Path.Combine(targetPath, dirName));
}
}

Expand Down
2 changes: 1 addition & 1 deletion C4InterFlow/Visualisation/ComponentDiagram.cs
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ protected override IEnumerable<Structure> Structures
foreach (var activity in Process.Activities)
{
var actor = activity.GetActorInstance();
if (actor != null && !_structures.Any(i => i.Alias != actor.Alias))
if (actor != null && !_structures.Any(i => i.Alias == actor.Alias))
{
_structures.Add(actor);
}
Expand Down
15 changes: 8 additions & 7 deletions C4InterFlow/Visualisation/ContainerDiagram.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ protected override IEnumerable<Structure> Structures
foreach (var activity in Process.Activities)
{
var actor = activity.GetActorInstance();
if (actor != null && !_structures.Any(i => i.Alias != actor.Alias))
if (actor != null && !_structures.Any(i => i.Alias == actor.Alias))
{
if(actor is Interface @interface)
{
Expand Down Expand Up @@ -247,11 +247,8 @@ private void PopulateRelationships(IList<Relationship> relationships, Structure
}
}

if (usesInterfaceOwner is Container)
{
newToScope = usesInterfaceOwner.Alias;
}
else if (usesInterfaceOwner is Component)

if (usesInterfaceOwner is Component)
{
var usesContainer = Utils.GetInstance<Structure>(((Component)usesInterfaceOwner).Container);
if (usesContainer != null)
Expand All @@ -260,13 +257,17 @@ private void PopulateRelationships(IList<Relationship> relationships, Structure
usesInterfaceOwner = usesContainer;
}
}
else if (usesInterfaceOwner is Container || usesInterfaceOwner is SoftwareSystem)
{
newToScope = usesInterfaceOwner.Alias;
}

var label = $"{usesInterface.Label}";

if (relationships.Where(x => x.From == (actor).Alias &&
x.To == usesInterfaceOwner.Alias &&
x.Label == label).FirstOrDefault() == null &&
(!(fromScope ?? string.Empty).Equals(newToScope)))
(!(newFromScope ?? string.Empty).Equals(newToScope)))
{


Expand Down
4 changes: 2 additions & 2 deletions C4InterFlow/Visualisation/ContextDiagram.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ protected override IEnumerable<Structure> Structures
foreach (var activity in Process.Activities)
{
var actor = activity.GetActorInstance();
if (actor != null && !_structures.Any(i => i.Alias != actor.Alias))
if (actor != null && !_structures.Any(i => i.Alias == actor.Alias))
{
_structures.Add(actor);
}
Expand Down Expand Up @@ -246,7 +246,7 @@ private void PopulateRelationships(IList<Relationship> relationships, Structure
x.To == usesInterfaceOwner.Alias &&
x.Label == label &&
x.Protocol == protocol).FirstOrDefault() == null &&
(!(fromScope ?? string.Empty).Equals(newToScope)))
(!(newFromScope ?? string.Empty).Equals(newToScope)))
{
relationships.Add(((actor) > usesInterfaceOwner)[
label,
Expand Down
2 changes: 1 addition & 1 deletion C4InterFlow/Visualisation/Plantuml/PlantumlSequenceFlow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ public static string ToPumlSequenceString(this Flow flow, SequenceDiagramStyle s
// TODO: Investigate why doing this in C4 sequence diagrams doesn't seem to work (out of memory java error when rendering)
if (style == SequenceDiagramStyle.PlantUML)
{
innerFlows.AddRange(flow.GetFlowsByType(flow, Flow.FlowType.Use, false));
innerFlows.AddRange(flow.GetFlowsByType(flow, Flow.FlowType.Use, true));
innerFlows.AddRange(flow.GetFlowsByType(flow, Flow.FlowType.Return, false));
innerFlows.AddRange(flow.GetFlowsByType(flow, Flow.FlowType.ThrowException, false));
if (innerFlows.Any())
Expand Down
1 change: 0 additions & 1 deletion Publishers/StaticSite/build.bat
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
@echo off
call npm install
call npm run build
pause
14 changes: 11 additions & 3 deletions Publishers/StaticSite/src/App.css
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* Spli- */
/* Split- */
.split-pane {
display: flex;
height: 100vh;
Expand All @@ -17,12 +17,20 @@
background-color: #ccc;
background-repeat: no-repeat;
background-position: 50%;
transition: width 0.1s ease;
}

.gutter:hover {
background-color: #aaa;
width: 5px !important; /* Increase width on hover and ensure it's applied */
}

.gutter.gutter-horizontal {
background-image: url('data:image/png;base64,...'); /* add a vertical line image or gradient for the splitter */
cursor: col-resize;
background-image: url('data:image/png;base64,...');
cursor: col-resize;
}


/* Tree View */
.tree-view,
.tree-view ul {
Expand Down
16 changes: 12 additions & 4 deletions Publishers/StaticSite/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@ const TreeView = ({ sitemap, onNodeSelect, levelOfDetail, setLevelOfDetail, type

// Controls Component
const Controls = ({ selectedNode, levelOfDetail, setLevelOfDetail, type, setType, format, setFormat }) => {
if (!selectedNode || !selectedNode.levelsOfDetails || !selectedNode.types || !selectedNode.formats) return null;
if (!selectedNode ||
!selectedNode.levelsOfDetails || !selectedNode.types || !selectedNode.formats ||
selectedNode.levelsOfDetails.length == 0 || selectedNode.types.length == 0 || selectedNode.formats.length == 0) return null;

return (
<div className="controls-container">
Expand Down Expand Up @@ -96,7 +98,14 @@ const DiagramView = ({ selectedNode, levelOfDetail, type, format }) => {
}
}, [selectedNode, levelOfDetail, type, format]);

if (!selectedNode || !selectedNode.levelsOfDetails || !selectedNode.types || !selectedNode.formats) return null;
if (!selectedNode ||
!selectedNode.levelsOfDetails || !selectedNode.types || !selectedNode.formats ||
selectedNode.levelsOfDetails.length == 0 || selectedNode.types.length == 0 || selectedNode.formats.length == 0)
return (
<div className="diagrams-container">
<p>No diagrams found for selected node</p>
</div>
);

const diagramUrl = `${selectedNode.loc}/${levelOfDetail} - ${type}.${format}`;

Expand All @@ -110,7 +119,6 @@ const DiagramView = ({ selectedNode, levelOfDetail, type, format }) => {

return (
<div className="diagrams-container">
<h2>Diagram</h2>
{renderDiagram()}
</div>
);
Expand Down Expand Up @@ -143,7 +151,7 @@ const App = () => {
}, []);

return (
<Split sizes={[25, 75]} minSize={100} expandToMin={false} gutterSize={5} gutterAlign="center" snapOffset={30} dragInterval={1} direction="horizontal" className="split-pane" gutterClassName="gutter">
<Split sizes={[25, 75]} minSize={100} expandToMin={false} gutterSize={3} gutterAlign="center" snapOffset={30} dragInterval={1} direction="horizontal" className="split-pane" gutterClassName="gutter">
<div className="pane pane-left">
<TreeView sitemap={sitemap} onNodeSelect={setSelectedNode} levelOfDetail={levelOfDetail} setLevelOfDetail={setLevelOfDetail} type={type} setType={setType} format={format} setFormat={setFormat} />
</div>
Expand Down

0 comments on commit 49d19a0

Please sign in to comment.