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.
true |
false |
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.
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[]
- Let InterfaceSpecifications be the
interfaces
property (an object) of the Type File‘s root object. - Then InterfaceSpecificationlist contains the definitions of the interfaces defined by the current Type File.
- Let InterfaceSpecification be a property of the InterfaceSpecifications object
- Let InterfaceName be the key of the InterfaceSpecifications property
- Let InterfaceDetails be the properties of an object.
- If any InterfaceName doesn’t match the specified pattern
- 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
- making them abstract (disallowing creation of instances of the Interface)
- adding a description to the Interface
- extending another Interface and inheriting its Interface properties
Example № 2interfaces:
BaseInterface:
__abstract: true
MyInterface:
__extends: BaseInterface
__description: "A nice little interface"
- Let the BooleanValue represent whether the current interface is abstract (default: false)
- If the BooleanValue is true
- forbid creating an object of this Interface‘s type, if possible within the target language
- forbid using this Interface‘s InterfaceName in the MessagesSpecification
- Let InterfaceName be any defined Interface‘s name.
- 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
- abort with an error message
- Then, the following statements are true for the current Interface:
- it contains all Interface properties from the Interface with the provided InterfaceName
- it contains all Interface properties declared by the current Interface
- for any Interface properties with the same PropertyName, the strictest type gets used.
- 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"
- Let StringValue be any string
- 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.
- Let InterfaceProperties be the collection of properties in an InterfaceDetails object where the keys don’t correspond to any InterfaceModifier.
- Then Propertylist represents all properties of any object of the Interface‘s type.
- Any object of the Interface‘s type must contain a property with the key PropertyName with a value of the type PropertyType.
- If any PropertyName doesn’t match the specified pattern
- show a warning. The pattern is supposed to enable maximum cross-language support.
2.4Message types
- Let InterfaceName be a property of the InterfaceSpecifications object with an array value.
- If InterfaceName isn’t specified in any InterfaceSpecifications in the Types folder
- abort compilation with an error
- 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.
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.- Then the Primitive PrimitiveName is defined by its PrimitiveLanguageMapEntrylist.
- Let TargetLanguageName be an identifier of a Target language.
- 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.
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?[])[]
- Let raw be equal to the StringValue‘s string value.
- Let specifier be raw with all whitespaces removed.
- Then specifier is a SimpleTypeSpecifierNode.
- Then the resulting type is equivalent to the type specified by the SimpleTypeSpecifierNode
- Then the resulting type can be the type specified by either the first or the second SimpleTypeSpecifierNode.
- Then the resulting type is an array whose values are of the type specified by the SimpleTypeSpecifierNode
- Then the resulting type can be the type specified by the SimpleTypeSpecifierNode or null.
- If no Interface with the given InterfaceName is defined
- abort with an error.
- Then the resulting type is an object with the structure specified by the InterfaceDetails of the Interface with the corresponding InterfaceName.
- If no Primitive with the given PrimitiveName is defined
- abort with an error.
- Then the resulting type is the target language’s type defined by the Primitive with the given PrimitiveName.
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.
- 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)
- abort with an error
- Then any value of the type represented by the ComplexTypeSpecifier must be equal to at least one Value from the Valuelist array
- Then the StringValue is a text description of the type represented by the ComplexTypeSpecifier authored in Markdown.
- Then the NumberValue represents the minimum valid value of the type specified by the ComplexTypeSpecifier.
- 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.
§Index
- array
- boolean
- BooleanValue
- Complex type specifier
- ComplexTypeSpecifierType
- Interface
- Interface modifiers
- Interface properties
- InterfaceDetails
- InterfaceModifiers
- null
- number
- NumberValue
- object
- Primitive
- Primitive
- PrimitiveSpecification
- PropertyType
- Simple type specifier
- SimpleTypeSpecifierNode
- string
- StringValue
- target language
- TargetLanguageName
- transpiler
- Type File
- Type Specifier
- TypeFileContent
- Types folder
- TypeSpecifier
- value
- Value