D2 Oracle
D2 has an API built on top of its AST for programmatically creating diagrams in Go.
This package is d2/d2oracle
.
This API is exercised heavily by Terrastruct to implement bidirectional edits. We have comprehensive test coverage of these functions. If there's any confusion from the docs, there's almost certainly a test that answers your question. (We're also happy to help, just file a GitHub issue!)
For a blog post detailing an example usage (building a SQL table diagram), see https://terrastruct.com/blog/post/generate-diagrams-programmatically/.
All functions in d2oracle
are pure: they do not mutate the original graph, they return a new one. If you are chaining calls, don't forget to use the resulting graph from the previous call.
Create
Create a shape or connection.
Parameters:
g
: D2 graphboardPath
: Path of the board to modify. Only applicable to multi-board diagrams; otherwise, pass innil
.key
: The ID of the shape or connection being created
Return:
newG
: The modified D2 graphnewKey
: The actual ID of the created shape or connection created
Everything specified in the given key
will be created. So
for example, if you create a connection between 2 shapes that don't exist, they will be
created in the same call. If you specify a nested object, it will create the parent
containers if they do not exist.
If you call Create
twice with the same shape ID, you will get an
error. If you call it twice with the same edge ID, you will create another edge.
newKey
is the ID of the object created. This doesn't always match the input key.
For shapes, there may be an ID collision. Create
appends a counter in this case.
Connection IDs include the index.
If you have a multi-board diagram like so:
Set
Set an attribute on a shape or connection.
Parameters:
g
: D2 graphboardPath
: Path of the board to modify. Only applicable to multi-board diagrams; otherwise, pass innil
.key
: The identifier for the attributetag
: The language tag. This is only non-nil when text values are set that can be different languages, e.g. a code snippet value.value
: Value being set
Return:
newG
: The modified D2 graph
The shape or connection that Set
is modifying must exist.
If the attribute is arleady set, it is overwritten.
Connections are targeted with an index.
To unset an attribute, just pass nil
.
If you do not pass an attribute and just give the ID of a shape or connection, it will set that object's primary value (usually label).
Delete
Delete a shape or connection.
Parameters:
g
: D2 graphboardPath
: Path of the board to modify. Only applicable to multi-board diagrams; otherwise, pass innil
.key
: The identifier for the shape or connection
Return:
newG
: The modified D2 graph
If you specify a container with children, those children will be deleted too.
Rename
Rename the ID of a shape or connection.
Note that the ID != label. If you want to change the label, use Set
.
Parameters:
g
: D2 graphboardPath
: Path of the board to modify. Only applicable to multi-board diagrams; otherwise, pass innil
.key
: The current identifier for the shape or connectionnewName
: The new identifier for the shape or connection
Return:
newG
: The modified D2 graph
Rename will rename all references of the given key.
Move
Move a given shape or connection to a different container.
If you give two keys of the same scope (e.g. "a" to "b"), it's the same as Rename
.
Parameters:
g
: D2 graphboardPath
: Path of the board to modify. Only applicable to multi-board diagrams; otherwise, pass innil
.key
: The current identifier for the shape or connectionnewKey
: The new identifier for the shape or connection
Return:
newG
: The modified D2 graph
You can move things out of containers, into container, or across containers
ID Deltas
For calls which can affect more than one ID, there is an API for getting a map of every single ID change. This can be hard to keep track of; for example, if you move a container with many descendants, the ID of all those descendants have changed, as well as all the connections that reference anything within the container.
When would you use this? If you are keeping state of D2 objects somewhere else other than the graph, e.g. in your own storage or writing back to somewhere, these calls should be used to keep track of all programmatic changes.
Delta methods:
- MoveIDDeltas
- DeleteIDDeltas
- RenameIDDeltas
Each of these have the same input parameters as their counterparts, and return a string to string map of ID changes.
Given this input D2 script:
deltas, err := MoveIDDeltas(g, nil, "x", "y.x")
deltas
: