Skip to content

Instantly share code, notes, and snippets.

@adamchester
Last active March 13, 2016 09:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save adamchester/54aad247abbfe623d037 to your computer and use it in GitHub Desktop.
Save adamchester/54aad247abbfe623d037 to your computer and use it in GitHub Desktop.
Generate a Visual Studio DGML Graph from F#
[<AutoOpen>]
module Workflow =
type State = Initial | Draft | PendingApproval | Approved | Cancelled | Completed
type Action = Create | Cancel | SendForApproval | Approve | Reject | Complete
type Role = Creator | Approver | Completor
type Transition = { Action:Action; From:State; To:State; Roles:Role list }
module Transitions =
let create = { Action=Create; From=Initial; To=Draft; Roles=[ Creator ] }
let cancel = { Action=Cancel; From=Draft; To=Cancelled; Roles=[ Creator ] }
let sendForAppr = { Action=SendForApproval; From=Draft; To=PendingApproval; Roles=[ Creator ] }
let approve = { Action=Approve; From=PendingApproval; To=Approved; Roles=[ Approver ] }
let reject = { Action=Reject; From=PendingApproval; To=Draft; Roles=[ Approver ] }
let complete = { Action=Complete; From=Approved; To=Completed; Roles=[ Completor ] }
let all = [ create; sendForAppr; approve; reject; complete; cancel; ]
#r "System.Xml.Linq"
[<AutoOpen>]
module Dgml =
open System.Xml.Linq
let xn name = XName.Get(name, "")
let xns name ns = XName.Get(name, ns)
let xnd name = xns name "http://schemas.microsoft.com/vs/2009/dgml"
let attr name value = XAttribute(xn name, value) :> XObject
let elem name (children:XObject list) = XElement(xnd name, children) :> XObject
type Link = { Source:string; Target:string; Label:string; }
type Node = { Id:string; }
let nodeToElement (node:Node) = elem "Node" [ attr "Id" node.Id ]
let linkToElement (link:Link) = elem "Link" [ attr "Source" link.Source
attr "Target" link.Target
attr "Label" link.Label ]
let create (nodes: Node list) (links: Link list) =
XDocument(elem "DirectedGraph" [ elem "Nodes" (nodes |> List.map nodeToElement)
elem "Links" (links |> List.map linkToElement) ])
let stateToString (state:State) = sprintf "%A" state
let actionToString (action:Action) = sprintf "%A" action
let transitionToNodes (t:Transition) = [ { Id=stateToString t.From}; { Id=stateToString t.To } ]
let transitionsToNodes = Seq.collect transitionToNodes >> Seq.distinct >> Seq.toList
let transitionToLink t = { Source=stateToString t.From; Target=stateToString t.To; Label=actionToString t.Action }
let transitionsToLinks = Seq.map transitionToLink >> Seq.toList
Dgml.create (transitionsToNodes Transitions.all) (transitionsToLinks Transitions.all)

Executing this generates the following DGML XML:

  <DirectedGraph xmlns="http://schemas.microsoft.com/vs/2009/dgml">
  <Nodes>
    <Node Id="Initial" />
    <Node Id="Draft" />
    <Node Id="PendingApproval" />
    <Node Id="Approved" />
    <Node Id="Completed" />
    <Node Id="Cancelled" />
  </Nodes>
  <Links>
    <Link Source="Initial" Target="Draft" Label="Create" />
    <Link Source="Draft" Target="PendingApproval" Label="SendForApproval" />
    <Link Source="PendingApproval" Target="Approved" Label="Approve" />
    <Link Source="PendingApproval" Target="Draft" Label="Reject" />
    <Link Source="Approved" Target="Completed" Label="Complete" />
    <Link Source="Draft" Target="Cancelled" Label="Cancel" />
  </Links>
</DirectedGraph>

And, when saved to a .dgml file, visual studio renders as follows: image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment