Message Composition

Introduction

The Message Blocks guide introduced the various block models, but additional information is required to validate a message composition, especially when you take ContainerMessageBlocks into consideration.

Backus–Naur form (BNF) is typically used to describe the grammar of programming languages. However, a BNF-like description of the MedChat message composition helps visualize the validation rules enforced by the system. BNF consists of a collection of production rules. Each rule has a name and a definition which describes the pattern or patterns that match the rule. This guide introduces some of these rules and concludes with a comprehensive specification of valid message compositions.

In our BNF-like descriptions below, many of the production rule names tend to correspond closely with model types. Ideally the type system would enforce every one of the rules. However, keep in mind that the production rules represent a business/composition validation rule, not necessarily a class hierarchy. For example, the MedChat model defines a single ContainerMessageBlock, but the BNF notation describes FormContainerMessageBlock, InputChildContainerMessageBlock, and ResponseContainerMessageBlock. These three production rules represent different composition validation that is applied based on where the ContainerMessageBlock appears in the model.

The following conventions are used in the notation:

* means 0 or more
+ means 1 or more
? means 0 or 1
| means or
CAPS indicates a property
    When followed by =, the property is further described by another production rule or a static value
    Otherwise, it is a primitive property

Getting Started

At the top level of the Message model, we have a Body property that contains an array of MessageBlocks. The simplest example would be a message with a single TextMessageBlock:

{
    ...
    "Body": [
        {
            "Id": "OptionalId",
            "Type": "TextMessageBlock",
            "Text": "Hello world!"
        }
    ]
    ...
}

To validate the composition of this example, we need three of our production rules:

MessageBody           -> StandardMessageBlock+ | SubmittedFormMessageBlock;
StandardMessageBlock  -> TextMessageBlock | ...;
TextMessageBlock      -> ID?, TYPE="TextMessageBlock", TEXT;

The first production rule means we expect either at least one StandardMessageBlock OR a single SubmittedFormMessageBlock.

📘

Note

The SubmittedFormMessageBlock production rule is discussed later. It is a reserved production that can only be generated by the system after a user submits responses to form inputs.

In our example, a TextMessageBlock is one of the alternatives matched by the StandardMessageBlock rule. TextMessageBlocks have an optional Id property and a required Text property.

Requesting Input

MedChat supports many input controls in the body of a message. Here is an example message that prompts the user with a single text input:

{
    ...
    "Body": [
        {
            "Id": "SomeId",
            "Type": "TextInputMessageBlock",
            "Label": "What is your favorite color?",
            "IsPrivate": false,
            "Validators": [
                {
                    "Type": "RequiredInputMessageBlockValidator"
                },
                {
                    "Type": "TextLengthInputMessageBlockValidator",
                    "MaxLength": 50
                }
            ]
        }
    ]
    ...
}

📘

Note

IDs are required on input controls.

This example builds on the StandardMessageBlock production rule and introduces several new rules:

StandardMessageBlock                  -> TextMessageBlock
                                      |  TextInputMessageBlock
                                      |  ...;

TextInputMessageBlock                 -> ID, TYPE="TextInputMessageBlock", LABEL?, PLACEHOLDER?, IS_PRIVATE, VALIDATORS?=TextValidator*;

TextValidator                         -> RequiredInputMessageBlockValidator
                                      |  TextLengthInputMessageBlockValidator;
                                      |  FormatInputMessageBlockValidator;

RequiredInputMessageBlockValidator    -> TYPE="RequiredInputMessageBlockValidator";
TextLengthInputMessageBlockValidator  -> TYPE="TextLengthInputMessageBlockValidator", MIN_LENGTH?, MAX_LENGTH?;
FormatInputMessageBlockValidator      -> TYPE="FormatInputMessageBlockValidator", REGEX?, INPUT_FORMAT=InputFormat;

InputFormat                           -> "Alpha" | "AlphaNumeric" | "Numeric" | "Custom";

The example chose to include the optional label, but omitted the optional placeholder. We also chose two of the available TextValidators so our input requires a response and the response is limited to a maximum of 50 characters.

Production rules for additional input message block types are described in the full Composition Rules section.

Input Responses

When a user responds to form inputs, the system generates a new message using the reserved SubmittedFormMessageBlock and SubmittedInputMessageBlock types. The response message to the previous example would look like this:

{
    ...
    "Body": [
        {
            "Id": "<generated ID>"
            "Type": "SubmittedFormMessageBlock",
            "RequestMessageId": "<The ID of the original message containing the form>",
            "ReadOnlyForm": [
                 {
                    "Id": "SomeId",
                    "Type": "SubmittedInputMessageBlock",
                    "Label": "What is your favorite color?",
                    "Values": [ "<The user's answer>" ],
                    "IsPrivate": false,
                    "InputMessageBlockConfig": {
                        "Type": "SimpleInputMessageBlockConfig",
                        "InputMessageBlockType": "TextInputMessageBlock"
                    }
                }
            ]
        }
    ]
    ...
}

The new applicable production rules are as follows:

SubmittedFormMessageBlock      -> ID, TYPE="SubmittedFormMessageBlock", REQUEST_MESSAGE_ID, READ_ONLY_FORM=ResponseMessageBlock+;

ResponseMessageBlock           -> SubmittedInputMessageBlock
                               |  TextMessageBlock
                               |  ...;

SubmittedInputMessageBlock     -> ID, TYPE="SubmittedInputMessageBlock", LABEL?, VALUE*, IS_PRIVATE, INPUT_MESSAGE_BLOCK_CONFIG=InputMessageBlockConfig;

InputMessageBlockConfig        -> SimpleInputMessageBlockConfig;
SimpleInputMessageBlockConfig  -> TYPE="SimpleInputMessageBlockConfig", INPUT_MESSAGE_BLOCK_TYPE;

The structure of the SubmittedFormMessageBlock.ReadOnlyForm property matches the original message body except that all input controls are replaced with SubmittedInputMessageBlocks.

The Id, Label, and IsPrivate properties of the SubmittedInputMessageBlock are carried forward from the original input message block. The Values property is always an array of strings. Whether it may contain 0, 1, or more strings is dependent on the type and configuration of the original input control. Lastly, the InputMessageBlockConfig property records the type of the original input control.

Composition Rules

The current comprehensive specification for message composition is expressed below. The message and bot frameworks are expanding rapidly and will continue to support a wider range of controls.

MessageBody                             -> StandardMessageBlock+ | SubmittedFormMessageBlock;

SubmittedFormMessageBlock               -> ID, TYPE="SubmittedFormMessageBlock", REQUEST_MESSAGE_ID, READ_ONLY_FORM=ResponseMessageBlock+;

StandardMessageBlock                    -> TextMessageBlock
                                        |  TextInputMessageBlock
                                        |  ParagraphInputMessageBlock
                                        |  DropDownInputMessageBlock
                                        |  MultiChoiceInputMessageBlock
                                        |  FileUploadInputMessageBlock
                                        |  ScaleInputMessageBlock
                                        |  FormContainerMessageBlock;

ResponseMessageBlock                    -> SubmittedInputMessageBlock
                                        |  TextMessageBlock
                                        |  ResponseContainerMessageBlock;

FormContainerMessageBlock               -> ID?, TYPE="ContainerMessageBlock", ORIENTATION=ContainerOrientation, BLOCKS=StandardMessageBlock+;
ResponseContainerMessageBlock           -> ID?, TYPE="ContainerMessageBlock", ORIENTATION=ContainerOrientation, BLOCKS=ResponseMessageBlock+;

TextMessageBlock                        -> ID?, TYPE="TextMessageBlock", TEXT;

TextInputMessageBlock                   -> ID, TYPE="TextInputMessageBlock", LABEL?, PLACEHOLDER?, IS_PRIVATE, VALIDATORS?=TextValidator*;
ParagraphInputMessageBlock              -> ID, TYPE="ParagraphInputMessageBlock", LABEL?, PLACEHOLDER?, IS_PRIVATE, VALIDATORS?=TextValidator*;
DropDownInputMessageBlock               -> ID, TYPE="DropDownInputMessageBlock", LABEL?, PLACEHOLDER?, IS_PRIVATE, IS_MULTI_SELECT_ENABLED, ALLOW_CUSTOM_INPUT, OPTIONS=DropDownOption*, VALIDATORS?=DropDownValidator*;
MultiChoiceInputMessageBlock            -> ID, TYPE="MultiChoiceInputMessageBlock", LABEL?, IS_PRIVATE, IS_MULTI_SELECT_ENABLED, DISPLAY_MODE=MultiChoiceDisplayMode, OPTIONS=MultiChoiceOption*, VALIDATORS?=MultiChoiceValidator*;
FileUploadInputMessageBlock             -> ID, TYPE="FileUploadInputMessageBlock", LABEL?, VALIDATORS?=FileUploadValidator*;
ScaleInputMessageBlock                  -> ID, TYPE="ScaleInputMessageBlock", LABEL?, MIN_VALUE, MAX_VALUE, MIN_LABEL?, MAX_LABEL?, STEP, VALIDATORS?=ScaleValidator*;
DatePickerInputMessageBlock             -> ID, TYPE="DatePickerInputMessageBlock", LABEL?, PLACEHOLDER?, IS_PRIVATE, PICKER_TYPE, DATE_FORMAT_OPTION, DAYS_IN_THE_PAST_LIMIT, DAYS_IN_THE_FUTURE_LIMIT, VALIDATORS?=DatePickerInputMessageBlockValidator*;
GenderInputMessageBlock                 -> ID, TYPE="GenderInputMessageBlock", LABEL?, PLACEHOLDER?, IS_PRIVATE, OPTIONS, VALIDATORS?=RequiredInputMessageBlockValidator*;
PhoneNumberInputMessageBlock            -> ID, TYPE="PhoneNumberInputMessageBlock", LABEL?, PLACEHOLDER?, IS_PRIVATE, ALLOW_INTERNATIONAL, VALIDATORS?=RequiredInputMessageBlockValidator*;

SubmittedInputMessageBlock              -> ID, TYPE="SubmittedInputMessageBlock", LABEL?, VALUE*, IS_PRIVATE, INPUT_MESSAGE_BLOCK_CONFIG=InputMessageBlockConfig;

DropDownOption                          -> VALUE, LABEL?;
MultiChoiceOption                       -> VALUE, LABEL?, DESCRIPTION?;

TextValidator                           -> RequiredInputMessageBlockValidator
                                        |  TextLengthInputMessageBlockValidator
                                        |  FormatInputMessageBlockValidator;

DropDownValidator                       -> RequiredInputMessageBlockValidator
                                        |  SelectionSizeInputMessageBlockValidator
                                        |  TextLengthInputMessageBlockValidator
                                        |  FormatInputMessageBlockValidator;

MultiChoiceValidator                    -> RequiredInputMessageBlockValidator
                                        |  SelectionSizeInputMessageBlockValidator;

FileUploadValidator                     -> RequiredInputMessageBlockValidator;

ScaleValidator                          -> RequiredInputMessageBlockValidator;

DatePickerInputMessageBlockValidator    -> RequiredInputMessageBlockValidator;

RequiredInputMessageBlockValidator      -> TYPE="RequiredInputMessageBlockValidator";
TextLengthInputMessageBlockValidator    -> TYPE="TextLengthInputMessageBlockValidator", MIN_LENGTH?, MAX_LENGTH?;
FormatInputMessageBlockValidator        -> TYPE="FormatInputMessageBlockValidator", REGEX?, INPUT_FORMAT=InputFormat;
SelectionSizeInputMessageBlockValidator -> TYPE="SelectionSizeInputMessageBlockValidator", MIN_SELECTABLE?, MAX_SELECTABLE?;

MultiChoiceDisplayMode                  -> "List" | "Buttons";
InputFormat                             -> "Alpha" | "AlphaNumeric" | "Numeric" | "Custom";

InputMessageBlockConfig                 -> SimpleInputMessageBlockConfig;
SimpleInputMessageBlockConfig           -> TYPE="SimpleInputMessageBlockConfig", INPUT_MESSAGE_BLOCK_TYPE;