Telestion Message Type DSL Specification

This document specifies the YAML-based Domain-specific language (DSL) for the specification of JSON message types within the Telestion ecosystem.

1Prerequisites

Please refer to the Specification for more information

1.1JSON base types

For this documentation, we use the following base types / terminology:

A string is a text-based value.

A boolean is a value that can either be true or false.

A number is a value representing a numeric value.

null is a value representing the explicit absence of a value.

An object is a value that maps a string-based key to a value for any unique keys.

An array is a value that contains a list where each item is a value.

A value is either a string, a boolean, a number, null, an object or an array.

1.2YAML Base Types

A definition of the YAML expressions used within the DSL’s syntax.

BooleanValue
true
false
StringValue
any YAML syntax representing a string value
NumberValue
any YAML syntax representing a number value

1.3Transpiler

A transpiler generates code for a target language from the DSL defined in this document.

1.4Target language

A target language is a programming language for which you generate code representing the types defined with the DSL defined in this document.

Every Telestion project can have one or more target languages.

Note The most common target languages for Telestion projects are Java and TypeScript.

2Specification

2.1Types folder

The Types folder is a folder that contains type files. Its location is determined by the tooling used in the concrete Telestion Project.

2.2Type File

A Type File is any file within the Types folder with the file extension .types.yaml. Its contents are defined by TypeFileContent.

TypeFileContent must be a valid YAML file.

Any files fulfilling the Type File specification should get merged in interpretation if they are compatible. For example, any Interface defined in a.types.yaml should also be available in b.types.yaml as if it had been declared in b.types.yaml‘s InterfaceSpecifications. The same holds true for MessagesSpecification and PrimitiveSpecification. If types cannot be merged, the Transpiler should abort with an error.

2.3Interfaces

An Interface is a specification of an object‘s structure.

Every Interface has an InterfaceName, Interface properties, and optionally a set of Interface modifiers.

Example № 1interfaces:
  BaseMessage:
    type: string
  MyMessage:
    type:
      value: "my"
    numbers: double[]
InterfaceSpecifications
interfaces:an object containingInterfaceSpecificationlist
  1. Let InterfaceSpecifications be the interfaces property (an object) of the Type File‘s root object.
  2. Then InterfaceSpecificationlist contains the definitions of the interfaces defined by the current Type File.
InterfaceName
/^[A-Z][a-zA-Z0-9]+$/Message
  1. If any InterfaceName doesn’t match the specified pattern
    1. show a warning. The pattern is supposed to enable maximum cross-language support.

2.3.1Interface modifiers

Interface modifiers are essentially statements modifying how the Interface‘s definition should be interpreted.

Interface modifiers allow modifying interfaces by

Example № 2interfaces:
  BaseInterface:
    __abstract: true

  MyInterface:
    __extends: BaseInterface
    __description: "A nice little interface"
AbstractModifier
__abstract: BooleanValue
  1. Let the BooleanValue represent whether the current interface is abstract (default: false)
  2. If the BooleanValue is true
    1. forbid creating an object of this Interface‘s type, if possible within the target language
    2. forbid using this Interface‘s InterfaceName in the MessagesSpecification
ExtendsModifier
__extends: InterfaceName
  1. Let InterfaceName be any defined Interface‘s name.
  2. If the current Interface declares any property whose PropertyType is incompatible with the PropertyType of any Interface properties with the same name PropertyName in the Interface InterfaceName
    1. abort with an error message
  3. Then, the following statements are true for the current Interface:
    1. it contains all Interface properties from the Interface with the provided InterfaceName
    2. it contains all Interface properties declared by the current Interface
    3. for any Interface properties with the same PropertyName, the strictest type gets used.
    4. the current Interface extends the Interface with the passed InterfaceName.

For example, the following SimpleTypeSpecifiers would be compatible and thus, the OtherType Interface extends the BaseType Interface.

Example № 3interfaces:
  BaseType:
    prop: string | number
  OtherType:
    __extends: BaseType
    prop: number

In the next example, however, the SimpleTypeSpecifier boolean is incompatible to the SimpleTypeSpecifier string | number in BaseType, so the Transpiler should abort with an error:

Counter Example № 4interfaces:
  BaseType:
    prop: string | number
  OtherType:
    __extends: BaseType
    prop: boolean # incompatible with "string | number"
DescriptionModifier
__description: StringValue
  1. Let StringValue be any string
  2. Then StringValue represents a text description of the current Interface authored in Markdown

2.3.2Interface properties

Interface properties are the properties of an object of the Interface‘s type.

InterfaceProperties
  1. Let InterfaceProperties be the collection of properties in an InterfaceDetails object where the keys don’t correspond to any InterfaceModifier.
  2. Then Propertylist represents all properties of any object of the Interface‘s type.
Property
  1. Any object of the Interface‘s type must contain a property with the key PropertyName with a value of the type PropertyType.
PropertyName
/^[a-z][a-z0-9]*$/
  1. If any PropertyName doesn’t match the specified pattern
    1. show a warning. The pattern is supposed to enable maximum cross-language support.

2.4Message types

MessagesSpecification
messages:an array ofInterfaceNamelist
  1. Let InterfaceName be a property of the InterfaceSpecifications object with an array value.
  2. If InterfaceName isn’t specified in any InterfaceSpecifications in the Types folder
    1. abort compilation with an error
  3. Then every message must be of one of the types specified by the InterfaceDetails of the corresponding InterfaceName.

2.5Primitives

A Primitive is a type that cannot be further specified within the DSL.

A Primitive is defined by a unique PrimitiveName.

Any Primitive must get mapped to a type in every target language for which a Transpiler gets run.

Note A Primitive within the DSL needn’t necessarily be a “primitive” in a target language. For example, you can define a date primitive that can get mapped to a DateTime instance in Java. In this case, you couldn’t define how a date gets represented any better within the DSL (making it a Primitive), but it wouldn’t be a “primitive” value in Java.
PrimitiveSpecification
primitives:an object containingPrimitivelist
PrimitiveName
/^[a-z][a-z_0-9]*$/
  1. Then the Primitive PrimitiveName is defined by its PrimitiveLanguageMapEntrylist.
PrimitiveLanguageMapEntry
  1. Let TargetLanguageName be an identifier of a Target language.
  2. Then StringValue contains the current Primitive‘s type in that Target language. It is up to the Transpiler to interpret that value and specify what it needs.
TargetLanguageName
/^[a-z][a-zA-Z0-9]*$/
Note While this specification doesn’t define any required Target language (this depends on the individual project and the Transpiler it uses), the most commonly used TargetLanguageName values are java, json, and typescript.
Example № 5primitives:
  double:
    java: double
    json: number
    typescript: number
  string:
    java: String
    json: string
    typescript: string
  json:
    java: com.fasterxml.jackson.databind.JsonNode
    json: '["number","string","boolean","object","array", "null"]'
    typescript: any

2.6Type specifiers

A Type Specifier is a representation of a value‘s type.

2.6.1Simple type specifier

A Simple type specifier is a representation of a value‘s type as a string.

Example № 6string[]
(string | number)[]?
(string[] | number | MyInterface?[])[]
Note To make it easier to work with the string-based SimpleTypeSpecifier, we provide an ANTLR 4 grammar to generate parsers for the SimpleTypeSpecifierNode: Download the ANTLR 4 grammar file
SimpleTypeSpecifier
  1. Let raw be equal to the StringValue‘s string value.
  2. Let specifier be raw with all whitespaces removed.
  3. Then specifier is a SimpleTypeSpecifierNode.
Note When parsing a SimpleTypeSpecifierNode, the types of SimpleTypeSpecifierNode take precedence in the order declared above. I.e., ParenthesizedTypeSpecifier takes precedence over UnionTypeSpecifier which takes precedence over ArrayTypeSpecifier, and so on. Use the first one that matches.
ParenthesizedTypeSpecifier
  1. Then the resulting type is equivalent to the type specified by the SimpleTypeSpecifierNode
UnionTypeSpecifier
  1. Then the resulting type can be the type specified by either the first or the second SimpleTypeSpecifierNode.
ArrayTypeSpecifier
  1. Then the resulting type is an array whose values are of the type specified by the SimpleTypeSpecifierNode
Note As the UnionTypeSpecifier takes precedence, the SimpleTypeSpecifierNode in an ArrayTypeSpecifier can’t be a UnionTypeSpecifier.
NullableTypeSpecifier
  1. Then the resulting type can be the type specified by the SimpleTypeSpecifierNode or null.
InterfaceTypeSpecifier
  1. If no Interface with the given InterfaceName is defined
    1. abort with an error.
  2. Then the resulting type is an object with the structure specified by the InterfaceDetails of the Interface with the corresponding InterfaceName.
PrimitiveTypeSpecifier
  1. If no Primitive with the given PrimitiveName is defined
    1. abort with an error.
  2. Then the resulting type is the target language’s type defined by the Primitive with the given PrimitiveName.
Note We evaluated using the GraphQL syntax for compatibility, but found it to be incompatible with YAML as a value starting with [ for an array would have resulted in interpretation as an array instead of the required StringValue.

2.6.2Complex type specifier

A Complex type specifier is an object representing a type that allows for specifications for more details for that type than a Simple type specifier.

ComplexTypeSpecifier
  1. If no ComplexTypeSpecifierTypeopt (or SimpleTypeSpecifier) exists for the current property in the current Interface or any Interface that the current Interface extends (directly or indirectly)
    1. abort with an error
ComplexTypeSpecifierValue
value:an array ofValuelist
  1. Then any value of the type represented by the ComplexTypeSpecifier must be equal to at least one Value from the Valuelist array
ComplexTypeSpecifierDescription
description:StringValue
  1. Then the StringValue is a text description of the type represented by the ComplexTypeSpecifier authored in Markdown.
ComplexTypeSpecifierMinimumValue
  1. Then the NumberValue represents the minimum valid value of the type specified by the ComplexTypeSpecifier.
ComplexTypeSpecifierMaximumValue
  1. Then the NumberValue represents the maximum valid value of the type specified by the ComplexTypeSpecifier.
Example № 7interfaces:
  MyInterface:
    someProperty:
      type: double
      min: 3
      description: |
        This is a multiline description.

        I can use `Markdown` here.

AAbout this specification

A.1Authors

Thanks to everyone who contributed to this specification:

A.2Generated with / special thanks to

This specification website was generated using Spec Markdown.

Learn more about Spec Markdown

§Index

  1. array
  2. boolean
  3. BooleanValue
  4. Complex type specifier
  5. ComplexTypeSpecifierType
  6. Interface
  7. Interface modifiers
  8. Interface properties
  9. InterfaceDetails
  10. InterfaceModifiers
  11. null
  12. number
  13. NumberValue
  14. object
  15. Primitive
  16. Primitive
  17. PrimitiveSpecification
  18. PropertyType
  19. Simple type specifier
  20. SimpleTypeSpecifierNode
  21. string
  22. StringValue
  23. target language
  24. TargetLanguageName
  25. transpiler
  26. Type File
  27. Type Specifier
  28. TypeFileContent
  29. Types folder
  30. TypeSpecifier
  31. value
  32. Value
  1. 1Prerequisites
    1. 1.1JSON base types
    2. 1.2YAML Base Types
    3. 1.3Transpiler
    4. 1.4Target language
  2. 2Specification
    1. 2.1Types folder
    2. 2.2Type File
    3. 2.3Interfaces
      1. 2.3.1Interface modifiers
      2. 2.3.2Interface properties
    4. 2.4Message types
    5. 2.5Primitives
    6. 2.6Type specifiers
      1. 2.6.1Simple type specifier
      2. 2.6.2Complex type specifier
  3. AAbout this specification
    1. A.1Authors
    2. A.2Generated with / special thanks to
  4. §Index