Somewhat coincidentally, there’s a new Storyteller 4.1.1 release up today that improves the Storyteller spec editor UI quite a bit. To use the techniques shown in this post, you’ll want to at least be on 4.1 (for some bug fixes to problems found in writing this blog post).
One of our goals with the Storyteller 4.0 release was to shorten the time and effort it takes to go from authoring or capturing specification text to a fully automated execution with backing code. As part of that, Joe McBride and I built in a new feature that lets you create or modify the specification language for Storyteller with markdown completely outside of the backing C# code.
Great, but how about a demonstration to make that a bit more concrete? I’m working on a Jasper feature today to effectively provide a form of content negotiation within the service bus to try to select the most efficient serialization format for a given message. At the moment we need to specify and worry about:
- What serialization formats are available?
- What is the preferred formats for the application in order?
- Are there any format preferences for the outgoing channel where the message is going to be sent?
- Did the user explicitly choose which serialization format to use for the message?
- If this message is a response to an original message sent from somewhere else, did the original sender specify its preferred list of serialization formats?
Okay, so back to Storyteller. Step #1 is to design the specification language I’ll need to describe the desired serialization selection logic to Storyteller Fixture’s and Grammar’s. That led to a markdown file like this that I added with the “New Fixture” link from the Storyteller UI:
# Serializer Selection ## AvailableSerializers ### The available serializers are {mimetypes} ## Preference ### The preferred serializer order is {mimetypes} ## SerializationChoice ### Outgoing Serialization Choice |table |content |channel |envelope |selection| |default|NULL |NULL |EMPTY |EMPTY | |header |Content Type|Channel Accepted Types|Envelope Accepted Types|Selection|
This is definitely the kind of scenario that lends itself to being expressed as a decision table, so I’ve described a Table grammar for the main inputs and the expected serialization format selection.
Now, without writing any additional C# code, I can switch to writing up acceptance tests for the new serialization selection logic. I think in this case it’s a little bit easier to go straight to the specification markdown file, so here’s the first specification as that:
# Serialization Selection Rules [SerializerSelection] |> AvailableSerializers text/xml; text/json; text/yaml |> Preference text/json; text/yaml |> SerializationChoice [rows] |content |channel |envelope |selection| |NULL |EMPTY |EMPTY |text/json| |NULL |text/xml, text/yaml |EMPTY |text/xml | |NULL |EMPTY |text/xml, text/yaml |text/xml | |text/xml |EMPTY |EMPTY |text/xml | |text/xml |text/json, text/other |text/yaml |text/xml | |text/other|EMPTY |EMPTY |NULL | |NULL |text/other, text/else |EMPTY |NULL | |NULL |text/other, text/json |EMPTY |text/json| |NULL |EMPTY |text/other |NULL | |NULL |EMPTY |text/other, text/json |text/json| |NULL |text/yaml |text/xml |text/xml |
In the Storyteller UI, this specification is rendered as this:
At this point, it’s time for me to write the backing Fixture code. Using the new Fixture & Grammar Explorer page in Storyteller 4, I can export a stubbed version of the Fixture code I’ll need to implement:
public class SerializerSelectionFixture : StoryTeller.Fixture { public void AvailableSerializers(string mimetypes) { throw new System.NotImplementedException(); } public void Preference(string mimetypes) { throw new System.NotImplementedException(); } [StoryTeller.Grammars.Tables.ExposeAsTable("Outgoing Serialization Choice")] public void SerializationChoice(string content, string channel, string envelope, string selection) { throw new System.NotImplementedException(); } }
That’s only Storyteller’s guess at what the matching code should be, but in this case it’s good enough with just one tweak to the “SerializationChoice” method you can see in the working code for the class above.
Now I’ve got a specification for the desired functionality and even a stub of the test harness. Time for coffee, standup, and then actually writing the real code and fleshing out the SerializerSelectionFixture class shown above. Back in a couple hours….
…which turned into a week or two of Storyteller bugfixes, but here’s the results of the specification as rendered in the results:
Cool, How can I do that? I have not understood the explanation yet, but it seems to me that I have begun to understand now. Thanks for his knowledge Lupacode