introduce StringWriter and generate properly indented Flowchart

This commit is contained in:
Michael Hoennig 2024-02-25 11:58:45 +01:00
parent f45f88ba77
commit b4d6930fbe
3 changed files with 111 additions and 33 deletions

View File

@ -1,7 +1,5 @@
package net.hostsharing.hsadminng.rbac.rbacdef; package net.hostsharing.hsadminng.rbac.rbacdef;
import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity;
import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity;
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity; import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@ -16,11 +14,11 @@ public class RbacViewMermaidFlowchart {
public static final String HOSTSHARING_ORANGE = "#dd4901"; public static final String HOSTSHARING_ORANGE = "#dd4901";
public static final String HOSTSHARING_LIGHTBLUE = "#99bcdb"; public static final String HOSTSHARING_LIGHTBLUE = "#99bcdb";
private final RbacView rbacDef; private final RbacView rbacDef;
private final StringBuilder flowchart = new StringBuilder(); private final StringWriter flowchart = new StringWriter();
public RbacViewMermaidFlowchart(final RbacView rbacDef) { public RbacViewMermaidFlowchart(final RbacView rbacDef) {
this.rbacDef = rbacDef; this.rbacDef = rbacDef;
flowchart.append(""" flowchart.writeLn("""
%%{init:{'flowchart':{'htmlLabels':false}}}%% %%{init:{'flowchart':{'htmlLabels':false}}}%%
flowchart TB flowchart TB
"""); """);
@ -36,54 +34,63 @@ public class RbacViewMermaidFlowchart {
private void renderEntitySubgraph(final RbacView.EntityAlias entity) { private void renderEntitySubgraph(final RbacView.EntityAlias entity) {
final var color = rbacDef.isMainEntityAlias(entity) ? HOSTSHARING_ORANGE : HOSTSHARING_LIGHTBLUE; final var color = rbacDef.isMainEntityAlias(entity) ? HOSTSHARING_ORANGE : HOSTSHARING_LIGHTBLUE;
flowchart.append(""" flowchart.writeLn("""
subgraph %{aliasName}["`**%{aliasName}**`"] subgraph %{aliasName}["`**%{aliasName}**`"]
direction TB direction TB
style %{aliasName} fill:%{color},stroke:darkblue,stroke-width:8px style %{aliasName} fill:%{color},stroke:darkblue,stroke-width:8px
""" """
.replace("%{aliasName}", entity.aliasName()) .replace("%{aliasName}", entity.aliasName())
.replace("%{color}", color )); .replace("%{color}", color ));
rbacDef.getEntityAliases().values().stream() flowchart.indented( () -> {
.filter(e -> e.aliasName().startsWith(entity.aliasName() + ".")) rbacDef.getEntityAliases().values().stream()
.forEach(this::renderEntitySubgraph); .filter(e -> e.aliasName().startsWith(entity.aliasName() + "."))
.forEach(this::renderEntitySubgraph);
wrapOutputInSubgraph(entity.aliasName() + ":roles", color, wrapOutputInSubgraph(entity.aliasName() + ":roles", color,
rbacDef.getRoleDefs().stream() rbacDef.getRoleDefs().stream()
.filter(r -> r.getEntityAlias() == entity) .filter(r -> r.getEntityAlias() == entity)
.map(r -> " " + roleDef(r)) .map(r -> " " + roleDef(r))
.collect(joining("\n")));
wrapOutputInSubgraph(entity.aliasName() + ":permissions", color,
rbacDef.getPermDefs().stream()
.filter(p -> p.getEntityAlias() == entity)
.map(p -> " " + permDef(p) )
.collect(joining("\n"))); .collect(joining("\n")));
if (rbacDef.isMainEntityAlias(entity) && rbacDef.getEntityAliasProxy() != null ) { wrapOutputInSubgraph(entity.aliasName() + ":permissions", color,
renderEntitySubgraph(rbacDef.getEntityAliasProxy()); rbacDef.getPermDefs().stream()
} .filter(p -> p.getEntityAlias() == entity)
.map(p -> " " + permDef(p) )
.collect(joining("\n")));
flowchart.append("end\n\n"); if (rbacDef.isMainEntityAlias(entity) && rbacDef.getEntityAliasProxy() != null ) {
renderEntitySubgraph(rbacDef.getEntityAliasProxy());
}
});
flowchart.chopEmptyLines();
flowchart.writeLn("end");
flowchart.writeLn();
} }
private void wrapOutputInSubgraph(final String name, final String color, final String content) { private void wrapOutputInSubgraph(final String name, final String color, final String content) {
if (!StringUtils.isEmpty(content)) { if (!StringUtils.isEmpty(content)) {
flowchart.append("subgraph " + name + "[ ]\n"); flowchart.emptyLine();
flowchart.append("style %{aliasName} fill: %{color}\n\n" flowchart.writeLn("subgraph " + name + "[ ]\n");
.replace("%{aliasName}", name) flowchart.indented(() -> {
.replace("%{color}", color)); flowchart.writeLn("style %{aliasName} fill: %{color}"
flowchart.append(content); .replace("%{aliasName}", name)
flowchart.append("\nend\n\n"); .replace("%{color}", color));
flowchart.writeLn();
flowchart.writeLn(content);
});
flowchart.chopEmptyLines();
flowchart.writeLn("end");
flowchart.writeLn();
} }
} }
private void renderGrants() { private void renderGrants() {
rbacDef.getGrantDefs() rbacDef.getGrantDefs()
.forEach(g -> { .forEach(g -> {
flowchart.append(grantDef(g) + "\n" ); flowchart.writeLn(grantDef(g) + "\n");
}); });
} }
@ -141,8 +148,8 @@ public class RbacViewMermaidFlowchart {
} }
public static void main(String[] args) throws IOException { public static void main(String[] args) throws IOException {
new RbacViewMermaidFlowchart(HsOfficeBankAccountEntity.rbac()).generateToMarkdownFile(); // new RbacViewMermaidFlowchart(HsOfficeBankAccountEntity.rbac()).generateToMarkdownFile();
new RbacViewMermaidFlowchart(HsOfficeRelationshipEntity.rbac()).generateToMarkdownFile(); new RbacViewMermaidFlowchart(HsOfficeRelationshipEntity.rbac()).generateToMarkdownFile();
new RbacViewMermaidFlowchart(HsOfficeDebitorEntity.rbac()).generateToMarkdownFile(); // new RbacViewMermaidFlowchart(HsOfficeDebitorEntity.rbac()).generateToMarkdownFile();
} }
} }

View File

@ -0,0 +1,71 @@
package net.hostsharing.hsadminng.rbac.rbacdef;
import org.apache.commons.lang3.StringUtils;
import static java.util.Arrays.stream;
import static java.util.stream.Collectors.joining;
public class StringWriter {
private final StringBuilder string = new StringBuilder();
private int indentLevel = 0;
void writeLn(final String text) {
string.append( indented(text));
writeLn();
}
void writeLn() {
string.append( "\n");
}
private String indented(final String text) {
if ( indentLevel == 0) {
return text.trim();
}
final var indentation = StringUtils.repeat(" ", indentLevel);
final var indented = stream(text.split("\n"))
.map(line -> line.trim().isBlank() ? "" : indentation + line.trim())
.collect(joining("\n"));
return indented;
}
void indent() {
++indentLevel;
}
void unindent() {
--indentLevel;
}
void indented(final Runnable indented) {
indent();
indented.run();
unindent();
}
boolean chopTail(final String tail) {
if (string.toString().endsWith(tail)) {
string.setLength(string.length() - tail.length());
return true;
}
return false;
}
void chopEmptyLines() {
while (string.toString().endsWith("\n\n")) {
string.setLength(string.length() - 1);
};
}
void emptyLine() {
if (!string.toString().endsWith("\n\n")) {
writeLn();
}
}
@Override
public String toString() {
return string.toString();
}
}

View File

@ -27,7 +27,7 @@ class TestCustomerEntityTest {
end end
subgraph contact:permissions[ ] subgraph contact:permissions[ ]
style contact:permissions fill: #dd4901 style contact:permissions fill: #dd4901
perm:contact:*{{contact:*}} perm:contact:*{{contact:*}}
perm:contact:add-package{{contact:add-package}} perm:contact:add-package{{contact:add-package}}