Skip to content

Commit

Permalink
Fix missing escaping in mermaid diagram (#28)
Browse files Browse the repository at this point in the history
* incorrect old behavior

* escape inputs/outputs

* bump version

* fix doctest string

* fix string escape

* update formatting of custom field dict value

* clean-up
  • Loading branch information
hannahilea authored Sep 19, 2023
1 parent 1d600a2 commit 9a46537
Show file tree
Hide file tree
Showing 8 changed files with 114 additions and 37 deletions.
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "TransformSpecifications"
uuid = "b92eaff8-8912-4b32-ad24-14d7c692d780"
authors = ["Beacon Biosignals, Inc"]
version = "0.4.0"
version = "0.5.0"

[deps]
Legolas = "741b9549-f6ed-4911-9fbf-4a1c0c97f0cd"
Expand Down
9 changes: 5 additions & 4 deletions src/mermaid.jl
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,11 @@ end
_ltab_spaces(str; n::Int=2) = repeat(" ", n) * str
_mermaid_key(key) = uppercase(string(key))
_field_node_name(field, prefix, step_key) = string(_mermaid_key(step_key), prefix, field)
mermaid_encode(str) = replace(str, "\"" => "#quot;")

function _mermaid_subgraph(node_key::String, display_name::String=node_key;
contents::Vector{String}=String[], direction="RL")
return ["subgraph $(node_key)[$(display_name)]",
return ["subgraph $(node_key)[\"$(mermaid_encode(display_name))\"]",
" direction $direction",
map(_ltab_spaces, contents)...,
"end"]
Expand All @@ -116,10 +117,10 @@ function _mermaid_subgraph_from_dag_step(step::DAGStep)
type_str = last(split(string(type), "}(:"; limit=2))
fieldstr = replace(type_str, ")" => "",
" => " => "::",
", :" => ",\n ")
"$(node_name){{\"$fieldname:\n $fieldstr\"}}"
", :" => "\n ")
"$(node_name){{\"`$fieldname\n *$(mermaid_encode(fieldstr))*`\"}}"
else
"$(node_name){{\"$fieldname::$(type_string(type))\"}}"
"$(node_name){{\"$fieldname::$(mermaid_encode(type_string(type)))\"}}"
end
return [node_contents,
"class $(node_name) classSpecField"]
Expand Down
20 changes: 10 additions & 10 deletions src/nothrow_dag.jl
Original file line number Diff line number Diff line change
Expand Up @@ -61,51 +61,51 @@ is_input_assembler(::Any) = false
##### `NoThrowDAG`
#####

# Saved as a const since we rely on it in two places (in the below docstring and in the docsd)
# Saved as a const since we rely on it in two places (in the below docstring and in the docs)
const DOCTEST_OUTPUT_nothrowdag_ex1 = """
flowchart
%% Define steps (nodes)
subgraph OUTERLEVEL["` `"]
direction LR
subgraph STEP_A[Step a]
subgraph STEP_A["Step a"]
direction TB
subgraph STEP_A_InputSchema[Input: ExampleOneVarSchemaV1]
subgraph STEP_A_InputSchema["Input: ExampleOneVarSchemaV1"]
direction RL
STEP_A_InputSchemavar{{"var::String"}}
class STEP_A_InputSchemavar classSpecField
end
subgraph STEP_A_OutputSchema[Output: ExampleOneVarSchemaV1]
subgraph STEP_A_OutputSchema["Output: ExampleOneVarSchemaV1"]
direction RL
STEP_A_OutputSchemavar{{"var::String"}}
class STEP_A_OutputSchemavar classSpecField
end
STEP_A_InputSchema:::classSpec -- fn_a --> STEP_A_OutputSchema:::classSpec
end
subgraph STEP_B[Step b]
subgraph STEP_B["Step b"]
direction TB
subgraph STEP_B_InputSchema[Input: ExampleOneVarSchemaV1]
subgraph STEP_B_InputSchema["Input: ExampleOneVarSchemaV1"]
direction RL
STEP_B_InputSchemavar{{"var::String"}}
class STEP_B_InputSchemavar classSpecField
end
subgraph STEP_B_OutputSchema[Output: ExampleOneVarSchemaV1]
subgraph STEP_B_OutputSchema["Output: ExampleOneVarSchemaV1"]
direction RL
STEP_B_OutputSchemavar{{"var::String"}}
class STEP_B_OutputSchemavar classSpecField
end
STEP_B_InputSchema:::classSpec -- fn_b --> STEP_B_OutputSchema:::classSpec
end
subgraph STEP_C[Step c]
subgraph STEP_C["Step c"]
direction TB
subgraph STEP_C_InputSchema[Input: ExampleTwoVarSchemaV1]
subgraph STEP_C_InputSchema["Input: ExampleTwoVarSchemaV1"]
direction RL
STEP_C_InputSchemavar1{{"var1::String"}}
class STEP_C_InputSchemavar1 classSpecField
STEP_C_InputSchemavar2{{"var2::String"}}
class STEP_C_InputSchemavar2 classSpecField
end
subgraph STEP_C_OutputSchema[Output: ExampleOneVarSchemaV1]
subgraph STEP_C_OutputSchema["Output: ExampleOneVarSchemaV1"]
direction RL
STEP_C_OutputSchemavar{{"var::String"}}
class STEP_C_OutputSchemavar classSpecField
Expand Down
27 changes: 27 additions & 0 deletions test/mermaid.jl
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ end # module
@version SchemaRadV1 begin
foo::Union{String,Missing}
list::Vector{Int}
d::Val{Symbol("""\"hi1232}{{}:yj,;./[]]""")}
end
@schema "schema-yay" SchemaYay
Expand Down Expand Up @@ -80,3 +81,29 @@ end
@test type_string(RobustImportsTest.A.X) == "X"
end
end
@testset "`mermaidify` handles escapes" begin
# Verifies types that are known to require string-escaping in order to render correctly
# Used to test that quotes are escaped correctly for mermaid diagram
struct Duckling
x::Val{Symbol("""\"hi12,32}{{}:y;./[]]""")}
end

# obviously this function doesn't yield a _valid_ dag, but as far as
# mermaid is concerned, it doesn't have to be valid
func(x) = x
steps = [DAGStep("step_a", nothing, NoThrowTransform(NamedTuple{(:rad,)})),
DAGStep("step_b", nothing, NoThrowTransform(Nothing, Duckling, func))]
dag = NoThrowDAG(steps)

test_str = ("```mermaid\n$(mermaidify(dag))\n```\n")
ref_test_file = joinpath(pkgdir(TransformSpecifications), "test", "reference_tests",
"mermaid_escape.md")
ref_str = read(ref_test_file, String)
@test isequal(ref_str, test_str)

# If this test fails because the generated output is intentionally different,
# update the reference by doing
# write(ref_test_file, test_str)
end
21 changes: 13 additions & 8 deletions test/reference_tests/mermaid_custom.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,36 @@ flowchart
%% Define steps (nodes)
subgraph OUTERLEVEL["` `"]
direction LR
subgraph STEP_A[Step a]
subgraph STEP_A["Step a"]
direction TB
subgraph STEP_A_InputSchema[Input: SchemaRadV1]
subgraph STEP_A_InputSchema["Input: SchemaRadV1"]
direction RL
STEP_A_InputSchemalist{{"list::Vector{Int64}"}}
class STEP_A_InputSchemalist classSpecField
STEP_A_InputSchemad{{"d::Val{Symbol(#quot;\#quot;hi1232}{{}:yj,;./[]]#quot;)}"}}
class STEP_A_InputSchemad classSpecField
STEP_A_InputSchemafoo{{"foo::Union{Missing, String}"}}
class STEP_A_InputSchemafoo classSpecField
end
class STEP_A_InputSchema classSpec
end
subgraph STEP_B[Step b]
subgraph STEP_B["Step b"]
direction TB
subgraph STEP_B_InputSchema[Input: SchemaRadV1]
subgraph STEP_B_InputSchema["Input: SchemaRadV1"]
direction RL
STEP_B_InputSchemalist{{"list::Vector{Int64}"}}
class STEP_B_InputSchemalist classSpecField
STEP_B_InputSchemad{{"d::Val{Symbol(#quot;\#quot;hi1232}{{}:yj,;./[]]#quot;)}"}}
class STEP_B_InputSchemad classSpecField
STEP_B_InputSchemafoo{{"foo::Union{Missing, String}"}}
class STEP_B_InputSchemafoo classSpecField
end
subgraph STEP_B_OutputSchema[Output: SchemaYayV1]
subgraph STEP_B_OutputSchema["Output: SchemaYayV1"]
direction RL
STEP_B_OutputSchemarad{{"rad:
list::Vector{Int64},
foo::Union{Missing, String}"}}
STEP_B_OutputSchemarad{{"`rad
*list::Vector{Int64}
d::Val{Symbol(#quot;\#quot;hi1232}{{}:yj,;./[]]#quot;}
foo::Union{Missing, String}*`"}}
class STEP_B_OutputSchemarad classSpecField
end
STEP_B_InputSchema:::classSpec -- make_rad --> STEP_B_OutputSchema:::classSpec
Expand Down
40 changes: 40 additions & 0 deletions test/reference_tests/mermaid_escape.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
```mermaid
flowchart
%% Define steps (nodes)
subgraph OUTERLEVEL["` `"]
direction LR
subgraph STEP_A["Step a"]
direction TB
subgraph STEP_A_InputSchema["Input: NamedTuple{(:rad,)}"]
direction RL
STEP_A_InputSchemarad{{"rad::Any"}}
class STEP_A_InputSchemarad classSpecField
end
class STEP_A_InputSchema classSpec
end
subgraph STEP_B["Step b"]
direction TB
subgraph STEP_B_InputSchema["Input: Nothing"]
direction RL
end
subgraph STEP_B_OutputSchema["Output: Duckling"]
direction RL
STEP_B_OutputSchemax{{"x::Val{Symbol(#quot;\#quot;hi12,32}{{}:y;./[]]#quot;)}"}}
class STEP_B_OutputSchemax classSpecField
end
STEP_B_InputSchema:::classSpec -- func --> STEP_B_OutputSchema:::classSpec
end
%% Link steps (edges)
STEP_A:::classStep -..-> STEP_B:::classStep
end
OUTERLEVEL:::classOuter ~~~ OUTERLEVEL:::classOuter
%% Styling definitions
classDef classOuter fill:#cbd7e2,stroke:#000,stroke-width:0px;
classDef classStep fill:#eeedff,stroke:#000,stroke-width:2px;
classDef classSpec fill:#f8f7ff,stroke:#000,stroke-width:1px;
classDef classSpecField fill:#fff,stroke:#000,stroke-width:1px;
```
14 changes: 9 additions & 5 deletions test/reference_tests/mermaid_legolas.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,31 @@ flowchart
%% Define steps (nodes)
subgraph OUTERLEVEL["` `"]
direction LR
subgraph STEP_A[Step a]
subgraph STEP_A["Step a"]
direction TB
subgraph STEP_A_InputSchema[Input: SchemaRadV1]
subgraph STEP_A_InputSchema["Input: SchemaRadV1"]
direction RL
STEP_A_InputSchemalist{{"list::Vector{Int64}"}}
class STEP_A_InputSchemalist classSpecField
STEP_A_InputSchemad{{"d::Val{Symbol(#quot;\#quot;hi1232}{{}:yj,;./[]]#quot;)}"}}
class STEP_A_InputSchemad classSpecField
STEP_A_InputSchemafoo{{"foo::Union{Missing, String}"}}
class STEP_A_InputSchemafoo classSpecField
end
class STEP_A_InputSchema classSpec
end
subgraph STEP_B[Step b]
subgraph STEP_B["Step b"]
direction TB
subgraph STEP_B_InputSchema[Input: SchemaRadV1]
subgraph STEP_B_InputSchema["Input: SchemaRadV1"]
direction RL
STEP_B_InputSchemalist{{"list::Vector{Int64}"}}
class STEP_B_InputSchemalist classSpecField
STEP_B_InputSchemad{{"d::Val{Symbol(#quot;\#quot;hi1232}{{}:yj,;./[]]#quot;)}"}}
class STEP_B_InputSchemad classSpecField
STEP_B_InputSchemafoo{{"foo::Union{Missing, String}"}}
class STEP_B_InputSchemafoo classSpecField
end
subgraph STEP_B_OutputSchema[Output: SchemaYayV1]
subgraph STEP_B_OutputSchema["Output: SchemaYayV1"]
direction RL
STEP_B_OutputSchemarad{{"rad::SchemaRadV1"}}
class STEP_B_OutputSchemarad classSpecField
Expand Down
18 changes: 9 additions & 9 deletions test/reference_tests/mermaid_nothrowdag.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@ flowchart
%% Define steps (nodes)
subgraph OUTERLEVEL["` `"]
direction LR
subgraph STEP_A[Step a]
subgraph STEP_A["Step a"]
direction TB
subgraph STEP_A_InputSchema[Input: SchemaFooV1]
subgraph STEP_A_InputSchema["Input: SchemaFooV1"]
direction RL
STEP_A_InputSchemalist{{"list::Vector{Int64}"}}
class STEP_A_InputSchemalist classSpecField
STEP_A_InputSchemafoo{{"foo::String"}}
class STEP_A_InputSchemafoo classSpecField
end
subgraph STEP_A_OutputSchema[Output: SchemaBarV1]
subgraph STEP_A_OutputSchema["Output: SchemaBarV1"]
direction RL
STEP_A_OutputSchemavar1{{"var1::String"}}
class STEP_A_OutputSchemavar1 classSpecField
Expand All @@ -22,16 +22,16 @@ subgraph STEP_A[Step a]
end
STEP_A_InputSchema:::classSpec -- fn_step_a --> STEP_A_OutputSchema:::classSpec
end
subgraph STEP_B[Step b]
subgraph STEP_B["Step b"]
direction TB
subgraph STEP_B_InputSchema[Input: SchemaFooV1]
subgraph STEP_B_InputSchema["Input: SchemaFooV1"]
direction RL
STEP_B_InputSchemalist{{"list::Vector{Int64}"}}
class STEP_B_InputSchemalist classSpecField
STEP_B_InputSchemafoo{{"foo::String"}}
class STEP_B_InputSchemafoo classSpecField
end
subgraph STEP_B_OutputSchema[Output: SchemaFooV1]
subgraph STEP_B_OutputSchema["Output: SchemaFooV1"]
direction RL
STEP_B_OutputSchemalist{{"list::Vector{Int64}"}}
class STEP_B_OutputSchemalist classSpecField
Expand All @@ -40,16 +40,16 @@ subgraph STEP_B[Step b]
end
STEP_B_InputSchema:::classSpec -- fn_step_b --> STEP_B_OutputSchema:::classSpec
end
subgraph STEP_C[Step c]
subgraph STEP_C["Step c"]
direction TB
subgraph STEP_C_InputSchema[Input: SchemaBarV1]
subgraph STEP_C_InputSchema["Input: SchemaBarV1"]
direction RL
STEP_C_InputSchemavar1{{"var1::String"}}
class STEP_C_InputSchemavar1 classSpecField
STEP_C_InputSchemavar2{{"var2::String"}}
class STEP_C_InputSchemavar2 classSpecField
end
subgraph STEP_C_OutputSchema[Output: SchemaFooV1]
subgraph STEP_C_OutputSchema["Output: SchemaFooV1"]
direction RL
STEP_C_OutputSchemalist{{"list::Vector{Int64}"}}
class STEP_C_OutputSchemalist classSpecField
Expand Down

2 comments on commit 9a46537

@hannahilea
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration pull request created: JuliaRegistries/General/91699

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v0.5.0 -m "<description of version>" 9a4653743eff3daed165c7ef808e4baa3d1dd8a2
git push origin v0.5.0

Please sign in to comment.