1. Prerequisites

To use OperatorFabric, you need a linux OS with the following:

  • Docker install with 4Gb of space

  • 4Gb of free RAM

2. Install and run server

To start OperatorFabric, you first need to clone the getting started git

git clone https://github.com/opfab/operatorfabric-getting-started.git

Launch the startserver.sh in the server directory. You need to wait for all the services to start (it usually takes one minute to start), it is done when the console prompt is available again.

Test the connection to the UI: to connect to OperatorFabric, open in a browser the following page: localhost:2002/ and use operator1_fr as login and test as password.

After connection, you should see the following screen

empty opfab screenshot

To stop the server (if you started it in background), use:

docker-compose down &

3. Examples

For each example, relevant files and scripts are in the directory client/exampleX.

All examples assume you connect to the server from localhost (otherwise change the provided scripts)

3.1. Example 1: Send and update a basic card

Go in the directory client/example1.

To receive the test cards it is necessary to configure a perimeter which you will have a closer look at in Example 5.

Configure the required perimeter by executing the provided script:

./setupPerimeter.sh perimeter.json

Send a card, using the provided script :

./sendCard.sh card.json

The result should be a 201 Http status.

See the result in the UI, you should see a card, if you click on it you’ll see the detail

detail card screenshot

3.1.1. Anatomy of the card :

A card is containing information regarding the publisher, the recipients, the process, the data to show…​

More information can be found in the Card Structure section of the reference documentation.

{
        "publisher" : "message-publisher",
        "processVersion" : "1",
        "process" :"defaultProcess",
        "processInstanceId" : "hello-world-1",
        "state" : "messageState",
        "groupRecipients": ["Dispatcher"],
        "severity" : "INFORMATION",
        "startDate" : 1553186770681,
        "summary" : {"key" : "defaultProcess.summary"},
        "title" : {"key" : "defaultProcess.title"},
        "data" : {"message" :"Hello World !!! That's my first message"}
}
If you open the json file of the card, you will see '${current_date_in_milliseconds_from_epoch}' for the field 'startDate'. We have used this so that the date of the card is the current day (or the next day in some other examples). Indeed, in the shell script that sends the card, you will see that we create an environment variable with the current date which is then injected into the json file.

3.1.2. Update the card

We can send a new version of the card (updateCard.json):

  • change the message, field data.message in the JSON File

  • the severity , field severity in the JSON File

{
        "publisher" : "message-publisher",
        "processVersion" : "1",
        "process"  :"defaultProcess",
        "processInstanceId" : "hello-world-1",
        "state" : "messageState",
        "groupRecipients": ["Dispatcher"],
        "severity" : "ALARM",
        "startDate" : 1553186770681,
        "summary" : {"key" : "defaultProcess.summary"},
        "title" : {"key" : "defaultProcess.title"},
        "data" : {"message" :":That's my second message"}
}

You can send the updated card with:

./sendCard.sh cardUpdate.json

The card should be updated on the UI.

3.1.3. Delete the card

You can delete the card using DELETE HTTP code with reference to publisher and processInstanceId

curl -s -X DELETE http://localhost:2102/cards/defaultProcess.hello-world-1

or use provided script:

./deleteCard.sh

3.2. Example 2: Publish a new bundle

The way the card is displayed on the UI is defined by a Bundle containing templates and a process description.

The bundle structure is the following:

├── css : stylesheets files
└── template : handlebar templates for detail card rendering
config.json : process description and global configuration
i18n.json : internationalization file

The bundle is provided in the bundle directory of example2. It contains a new version of the bundle used in example1.

We just change the template and the stylesheet instead of displaying:

Message :  The message

we display:

You received the following message

The message

If you look at the template file (template/template.handlebars):

<h2> You received the following message </h2>

{{card.data.message}}

In the stylesheet css/style.css we just change the color value to red (#ff0000):

h2{
        color:#ff0000;
        font-weight: bold;
}

The global configuration is defined in config.json :

{
        "id":"defaultProcess",
        "version":"2",
        "states":{
                "messageState" : {
                        "templateName" : "template",
                        "styles" : [ "style" ]
                }
        }
}

To keep the old bundle, we create a new version by setting version to 2.

3.2.1. Package your bundle

Your bundle needs to be packaged in a tar.gz file, a script is available:

./packageBundle.sh

A file name bundle.tar.gz will be created.

3.2.2. Get a Token

To send the bundle you need to be authenticated. To get a token you can source the provided script:

source ../getToken.sh

This will run the following command:

curl -s -X POST -d "username=admin&password=test&grant_type=password&client_id=opfab-client" http://localhost:2002/auth/token

This should return a JSON a response like this:

{"access_token":"eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJSbXFOVTNLN0x4ck5SRmtIVTJxcTZZcTEya1RDaXNtRkw5U2NwbkNPeDBjIn0.eyJqdGkiOiIzZDhlODY3MS1jMDhjLTQ3NDktOTQyOC1hZTdhOTE5OWRmNjIiLCJleHAiOjE1NzU1ODQ0NTYsIm5iZiI6MCwiaWF0IjoxNTc1NTQ4NDU2LCJpc3MiOiJodHRwOi8va2V5Y2xvYWs6ODA4MC9hdXRoL3JlYWxtcy9kZXYiLCJhdWQiOiJhY2NvdW50Iiwic3ViIjoiYTNhM2IxYTYtMWVlYi00NDI5LWE2OGItNWQ1YWI1YjNhMTI5IiwidHlwIjoiQmVhcmVyIiwiYXpwIjoib3BmYWItY2xpZW50IiwiYXV0aF90aW1lIjowLCJzZXNzaW9uX3N0YXRlIjoiODc3NzZjOTktYjA1MC00NmQxLTg5YjYtNDljYzIxNTQyMDBhIiwiYWNyIjoiMSIsInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJlbWFpbCBwcm9maWxlIiwic3ViIjoiYWRtaW4iLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsInByZWZlcnJlZF91c2VybmFtZSI6ImFkbWluIn0.XMLjdOJV-A-iZrtq7sobcvU9XtJVmKKv9Tnv921PjtvJ85CnHP-qXp2hYf5D8TXnn32lILVD3g8F9iXs0otMAbpA9j9Re2QPadwRnGNLIzmD5pLzjJ7c18PWZUVscbaqdP5PfVFA67-j-YmQBwxiys8psF8keJFvmg-ExOGh66lCayClceQaUUdxpeuKFDxOSkFVEJcVxdelFtrEbpoq0KNPtYk7vtoG74zO3KjNGrzLkSE_e4wR6MHVFrZVJwG9cEPd_dLGS-GmkYjB6lorXPyJJ9WYvig56CKDaFry3Vn8AjX_SFSgTB28WkWHYZknTwm9EKeRCsBQlU6MLe4Sng","expires_in":36000,"refresh_expires_in":1800,"refresh_token":"eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICIzZjdkZTM0OC05N2Q5LTRiOTUtYjViNi04MjExYTI3YjdlNzYifQ.eyJqdGkiOiJhZDY4ODQ4NS1hZGE0LTQwNWEtYjQ4MS1hNmNkMTM2YWY0YWYiLCJleHAiOjE1NzU1NTAyNTYsIm5iZiI6MCwiaWF0IjoxNTc1NTQ4NDU2LCJpc3MiOiJodHRwOi8va2V5Y2xvYWs6ODA4MC9hdXRoL3JlYWxtcy9kZXYiLCJhdWQiOiJodHRwOi8va2V5Y2xvYWs6ODA4MC9hdXRoL3JlYWxtcy9kZXYiLCJzdWIiOiJhM2EzYjFhNi0xZWViLTQ0MjktYTY4Yi01ZDVhYjViM2ExMjkiLCJ0eXAiOiJSZWZyZXNoIiwiYXpwIjoib3BmYWItY2xpZW50IiwiYXV0aF90aW1lIjowLCJzZXNzaW9uX3N0YXRlIjoiODc3NzZjOTktYjA1MC00NmQxLTg5YjYtNDljYzIxNTQyMDBhIiwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbIm9mZmxpbmVfYWNjZXNzIiwidW1hX2F1dGhvcml6YXRpb24iXX0sInJlc291cmNlX2FjY2VzcyI6eyJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJzY29wZSI6ImVtYWlsIHByb2ZpbGUifQ.sHskPtatqlU9Z8Sfq6yvzUP_L6y-Rv26oPpykyPgzmk","token_type":"bearer","not-before-policy":0,"session_state":"87776c99-b050-46d1-89b6-49cc2154200a","scope":"email profile"}

Your token is the access_token value in the JSON, which the script will export to a $token environment variable.

The sendBundle.sh script below will use this variable.

The token will be valid for 10 hours, after that you will need to ask for a new one.

3.2.3. Send the bundle

Executing the sendBundle.sh script will send the bundle.

You can now execute the script, it will send the bundle.

./sendBundle.sh

You should receive the following JSON in response, describing your bundle.

{"id":"defaultProcess","name":"process.name","version":"2","states":{"messageState":{"responseData":null,"acknowledgmentAllowed":"Always","color":null,"name":null,"description":null,"showDetailCardHeader":null,"userCard":null,"templateName":"template","styles":["style"],"type":null,"response":null}},"uiVisibility":null}

3.2.4. Send a card

You can send the following card to test your new bundle:

{
        "publisher" : "message-publisher",
        "processVersion" : "2",
        "process"  :"defaultProcess",
        "processInstanceId" : "hello-world-1",
        "state": "messageState",
        "groupRecipients": ["Dispatcher"],
        "severity" : "INFORMATION",
        "startDate" : 1553186770681,
        "summary" : {"key" : "defaultProcess.summary"},
        "title" : {"key" : "defaultProcess.title"},
        "data" : {"message":"Hello world in new version"}
}

To use the new bundle, we set processVersion to "2"

To send the card:

./sendCard.sh

You should see in the UI the detail card with the new template.

3.3. Example 3: Process with state

For this example, we will set the following process:

  • Step 1: A critical situation arises on the High Voltage grid

  • Step 2: The critical situation evolve

  • Step 3: The critical situation ends

To model this process in OperatorFabric, we will use a "Process" with "States", we will model this in the config.json of the bundle:

{
        "id":"criticalSituation",
        "name": "Critical situation process",
        "version":"1",
        "states":{
                "criticalSituation-begin" : {
                        "templateName" : "criticalSituationTemplate",
                        "styles" : [ "style" ],
                        "acknowledgmentAllowed": "Always"
                },
                "criticalSituation-update" : {
                        "templateName" : "criticalSituationTemplate",
                        "styles" : [ "style" ],
                        "acknowledgmentAllowed": "Always"
                },
                "criticalSituation-end" : {
                        "templateName" : "endCriticalSituationTemplate",
                        "styles" : [ "style" ],
                        "acknowledgmentAllowed": "Always"
                }
        }
}

You can see in the JSON we define a process name "criticalSituation" with 3 states: criticalSituation-begin, criticalSituation-update and criticalSituation-end. For each state we define a title for the card, and the template of stylesheet to use.

The title is a key which refers to the i18n.json file:

{
        "criticalSituation-begin":{
                "title":"CRITICAL SITUATION",
                "summary":" CRITICAL SITUATION ON THE GRID , SEE DETAIL FOR INSTRUCTION"
        },
        "criticalSituation-update":{
                "title":"CRITICAL SITUATION - UPDATE",
                "summary":" CRITICAL SITUATION ON THE GRID , SEE DETAIL FOR INSTRUCTION"
        },
        "criticalSituation-end":{
                "title":"CRITICAL SITUATION - END",
                "summary":" CRITICAL SITUATION ENDED"
        }

}

The templates can be found in the template directory.

Before sending cards it is necessary to configure the required perimeter by executing the provided script:

./setupPerimeter.sh perimeter.json

We can now send cards and simulate the process, first we send a card at the beginning of the critical situation:

{
        "publisher" : "alert-publisher",
        "processVersion" : "1",
        "process"  :"criticalSituation",
        "processInstanceId" : "alert1",
        "state": "criticalSituation-begin",
        "groupRecipients": ["Dispatcher"],
        "severity" : "ALARM",
        "startDate" : 1553186770681,
        "summary" : {"key" : "criticalSituation-begin.summary"},
        "title" : {"key" : "criticalSituation-begin.title"},
        "data" : {"instruction":"Critical situation on the grid : stop immediatly all maintenance on the grid"}
}

The card refers to the process "criticalSituation" as defined in the config.json, the state attribute is put to "criticalSituation-begin" which is the first step of the process, again as defined in the config.json. The card can be sent via the provided script :

./sendCard.sh card.json

Two other cards have been provided to continue the process

  • cardUpdate.json: the state is criticalSituation-update

  • cardEnd.json: the state is criticalSituation-end and severity set to "compliant"

You can send these cards:

./sendCard.sh cardUpdate.json
./sendCard.sh cardEnd.json

3.4. Example 4: Time Line

To view the card in the time line, you need to set times in the card using timeSpans attributes as in the following card:

 {
        "publisher" : "scheduledMaintenance-publisher",
        "processVersion" : "1",
        "process"  :"maintenanceProcess",
        "processInstanceId" : "maintenance-1",
        "state": "planned",
        "groupRecipients": ["Dispatcher"],
        "severity" : "INFORMATION",
        "startDate" : 1553186770681,
        "summary" : {"key" : "maintenanceProcess.summary"},
        "title" : {"key" : "maintenanceProcess.title"},
        "data" : {
                "operationDescription":"Maintenance operation on the International France England (IFA) High Voltage line ",
                "operationResponsible":"RTE",
                "contactPoint":"By Phone : +33 1 23 45 67 89 ",
                "operationStartingTime":"Wed 11 dec 2019 8pm",
                "operationEndTime":"Thu 12 dec 2019 10am",
                "comment":"Operation has no impact on service"
                },
        "timeSpans" : [
        {"start" : 1576080876779},
        {"start" : 1576104912066}
            ]
}

For this example, we use a new publisher called "scheduledMaintenance-publisher". You won’t need to post the corresponding bundle to the businessconfig service as it has been loaded in advance to be available out of the box (only for the getting started). If you want to take a look at its content you can find it under server/businessconfig-storage/scheduledMaintenance-publisher/1.

Before sending the provided cards, you need to set the correct time values as epoch (ms) in the json. For each value you set, you will have a dot on the timeline. In our example, the first dot represents the beginning of the maintenance operation, and the second the end of the maintenance operation.

For example cards the dates are calculated automatically in the provided sendCard.sh script.

It is possible to change dates values by editing the card’s json file. To get the dates in Epoch, you can use the following commands:

For the first date:

date -d "+ 60 minutes" +%s%N | cut -b1-13

And for the second

date -d "+ 120 minutes" +%s%N | cut -b1-13

Before sending cards it is necessary to configure the required perimeter by executing the provided script:

./setupPerimeter.sh perimeter.json

To send the card use the provided script in example4 directory

./sendCard.sh card.json

A second card (card2.json) is provided as example, as before you can eventually change times values in the json file and then send it

./sendCard.sh card2.json

This time the severity of the card is ALERT, you should see the dot in red on the timeline

example 4 screenshot

3.5. Example 5: Card routing mechanism

3.5.1. Card sent to a group

As we saw previously, if a card is sent to a group, then you need to be a member of the group and have the process / state of the card within the group’s perimeter to receive it.

3.5.2. Card sent to an entity

If a card is sent to an entity, then you must be a member of this entity and have the process / state of the card within the user’s perimeter. As the perimeters are attached to groups, the user must therefore be a member of a group attached to this perimeter.

Let’s send this card :

{
    "publisher" : "message-publisher",
    "processVersion" : "1",
    "entityRecipients" : ["ENTITY1_FR"],
    "process" :"defaultProcess",
    "processInstanceId" : "cardExample5",
    "state" : "messageState1",
    "severity" : "INFORMATION",
    "startDate" : 1553186770681,
    "summary" : {"key" : "defaultProcess.summary"},
    "title" : {"key" : "defaultProcess.title"},
    "data" : {"message" : "Hello World !!! Here is a message for ENTITY1_FR"}
}

Use the provided script :

./sendCard.sh cardSentToEntity.json

The result should be a 201 Http status.

Look at the result in the UI, you should not be able to see the card.

To receive the card you need to create a perimeter and to do it you need to be authenticated. To get a token you can source the provided script:

source ../getToken.sh

Now let’s create this perimeter :

{
  "id" : "getting-startedPerimeter",
  "process" : "defaultProcess",
  "stateRights" : [
    {
      "state" : "messageState1",
      "right" : "Receive"
    }
  ]
}

You can use this command line :

curl -X POST http://localhost:2103/perimeters -H "Content-type:application/json" -H "Authorization:Bearer $token" --data @perimeter.json

or use the provided script :

./createPerimeter.sh perimeter.json

The result should be a 201 Http status, and a json object such as:

{"id":"getting-startedPerimeter","process":"defaultProcess","stateRights":[{"state":"messageState","right":"Receive"}]}

Now let’s attach this perimeter to the Dispatcher group. You can use this command line :

curl -X PUT http://localhost:2103/perimeters/getting-startedPerimeter/groups -H "Content-type:application/json" -H "Authorization:Bearer $token" --data "[\"Dispatcher\"]"

or use the provided script :

./putPerimeterForGroup.sh

The result should be a 200 Http status.

Now, if you refresh the UI or send again the card, you should see the card.

3.5.3. Card sent to a group and an entity

If a card is sent to a group and an entity, then to receive the card the user must be both a member of this entity and a member of this group and have the process / state of the card within the group’s perimeter.

Let’s send this card (for ENTITY1_FR and Dispatcher group with process/state not in user’s perimeter) :

{
        "publisher" : "message-publisher",
        "processVersion" : "1",
        "entityRecipients" : ["ENTITY1_FR"],
        "process"  :"defaultProcess",
        "processInstanceId" : "cardExample5_1",
        "state": "messageState2",
        "groupRecipients": ["Dispatcher"],
        "severity" : "INFORMATION",
        "startDate" : 1553186770681,
        "summary" : {"key" : "defaultProcess.summary"},
        "title" : {"key" : "defaultProcess.title"},
        "data" : {"message" : "Hello World !!! Here is a message for ENTITY1_FR and group Dispatcher - process/state not in operator1_fr perimeter "}
}

Use the provided script :

./sendCard.sh cardSentToEntityAndGroup_1.json

The result should be a 201 Http status.

Look at the result in the UI, you should not be able to see the card.

Now let’s send this card (for ENTITY1_FR and Dispatcher group with process/state in user’s perimeter) :

{
        "publisher" : "message-publisher",
        "processVersion" : "1",
        "entityRecipients" : ["ENTITY1_FR"],
        "process"  :"defaultProcess",
        "processInstanceId" : "cardExample5_2",
        "state": "messageState",
        "groupRecipients": ["Dispatcher"],
        "severity" : "INFORMATION",
        "startDate" : 1553186770681,
        "summary" : {"key" : "defaultProcess.summary"},
        "title" : {"key" : "defaultProcess.title"},
        "data" : {"message" : "Hello World !!! Here is a message for ENTITY1_FR and group Planner - process/state in operator1_fr perimeter "}
}

Use the provided script :

./sendCard.sh cardSentToEntityAndGroup_2.json

The result should be a 201 Http status.

Look at the result in the UI, you should be able to see the card.

3.6. Example 6: Sending cards from the UI

3.6.1. Updating the perimeter

In the top right corner, left to your username, there is the button to send a new card.

example 6 screen shot

If you click on it now, you shouldn’t be able to send a card because none of the groups you’re part of has the right to send a card. So first we need to set up a new perimeter with the correct rights and associate it to one of your group.

{
        "id" : "example6-Perimeter",
        "process" : "defaultProcess",
        "stateRights" : [
                {
                        "state" : "messageState",
                        "right" : "ReceiveAndWrite"
                }
        ]
}

Use the provided script :

./setupPerimeter.sh perimeter.json

3.6.2. Creating the template to send a card

Now you need to update the bundle to include a template for the card sending screen. In the config.json file of the bundle, it is necessary to define the userCard object.

{
        "id":"defaultProcess",
        "name": "Message process",
        "version":"2",
        "states": {
                "messageState": {
                  "name": "Message",
                  "description": "Message",
                  "userCard" : {
                        "template" : "usercard_message",
                        "startDateVisible": false,
                        "endDateVisible" : false,
                        "lttdVisible" : false,
                        "expirationDateVisible" : false
                  },
                  "templateName": "message"
                }
        }
}

The bundle folder now contains usercard_message.handlebars:

<div class="opfab-textarea">
        <label> MESSAGE </label>
        <textarea id="message" name="message" placeholder="Write something.."
                style="width:100%">{{card.data.message}}</textarea>
</div>


<script>
    usercardTemplateGateway.getSpecificCardInformation = function () {
        const message = document.getElementById('message').value;
        const card = {
        summary : {key : "message.summary"},
                title : {key : "message.title"},
        data : {message: message},
        };
        if (message.length<1) return { valid:false , errorMsg:'You must provide a message'}
        return {
            valid: true,
            card: card
        };

    }

</script>

You can package it and send it to the server with the provided scripts :

./packageBundle.sh | ./sendBundle.sh

Now if you reload the page, you should be able to send a card. Don’t forget to send it to an entity you’re a part of to receive it. For instance Control Center FR North.

example 6 screen shot #2

3.6.3. Customize the card sending template

To go further you can check out the folder named bundle_updated to see a template where it is possible to add an object to the message similar to e-mails.

You can see the updated UI using this command line and refreshing the page to update the bundle.

./packageBundle_updated.sh | ./sendBundle.sh
example 6 screen shot #3

The object attribute is defined in the userCard template in the usercard_message.handlebars file.

        <div class="opfab-textarea">
            <label> Object </label>
            <textarea id="object" name="object" placeholder="Write something.."
                style="width:100%">{{card.data.object}}</textarea>
        </div>
        <br/>

        <div class="opfab-textarea">
            <label> MESSAGE </label>
            <textarea id="message" name="message" placeholder="Write something.."
                style="width:100%">{{card.data.message}}</textarea>
        </div>

<script>
    usercardTemplateGateway.getSpecificCardInformation = function () {
        const message = document.getElementById('message').value;
        const object = document.getElementById('object').value;
        const card = {
        summary : {key : "message.summary"},
                title : {key : "message.title"},
        data : {message: message, object: object},
        };
        if (message.length<1) return { valid:false , errorMsg:'You must provide a message'}
        return {
            valid: true,
            card: card
        };

    }

</script>

If you look at the message.handlebars file in the bundle, you will see how the template was changed to take the object attribute into account:

<h2><b>Object:</b> {{card.data.object}}</h2>

{{card.data.message}}

See the card documentation for further information on cards.

3.7. Example 7: Message card using "build-in" templates

Instead of coding your own templates for message cards or user cards, you can use opfab build-in templates if it suits your needs. In the following example, we are going to modify the templates of the previous bundle (the message card).

If you want to show only a simple message, you can use the message build-in template, to achieve this, first you have to put in your handlebar file message.handlebars:

<opfab-message-card> </opfab-message-card>
The build-in template supposes the message is stored in the card in field data.message.

Then you have to put in your handlebar file message_usercard.handlebars:

<opfab-message-usercard> </opfab-message-usercard>
The message will be stored in the field data.message of the card.

You can package the bundle and send it to the server with the provided scripts :

./packageBundle.sh | ./sendBundle.sh

Now if you reload the page, you should be able to create a card using build-in template.

example 7 screen shot #1

You can change the text header by providing the message-header attribute:

<opfab-message-card  message-header="a new header"> </opfab-message-card>

By using attributes you can set some parameters regarding recipients, see common attributes for user cards

You can see the updated UI using this command line and refreshing the page to update the bundle.

./packageBundle_updated.sh | ./sendBundle.sh
example 7 screen shot #2

3.8. Example 8: Question card using "build-in" templates

Another example of templating is a question/response use case. Opfab allows you to send a response which will be attached to the card. This mechanism can be illustrated via the build-in template for question card.

To show a question and see user responses, first you have to put in your handlebar file question.handlebars:

<opfab-question-card> </opfab-question-card>
The build-in template supposes the question is stored in the card in the field data.question.

Then you have to put in your handlebar file question_usercard.handlebars:

<opfab-question-usercard> </opfab-question-usercard>
The question will be stored in the field data.question of the card.

Like the message build-in template, you can set default attributes : common attributes for user cards

Use the provided script to add rights to send question card :

./setupPerimeter.sh perimeter.json

You can package the bundle and send it to the server with the provided scripts :

./packageBundle.sh | ./sendBundle.sh

Example for using a question card with the previous build-in template :

First, operator1_fr log in and create the question card :

example 8 screen shot #1

Then, operator3_fr log in, read the question card and answer to it :

example 8 screen shot #2

Finally, operator1_fr sees the answer of operator3_fr :

example 8 screen shot #3

4. Troubleshooting

4.1. My bundle is not loaded

The server send a {"status":"BAD_REQUEST","message":"unable to open submitted file","errors":["Error detected parsing the header"]}, despite correct http headers.

The uploaded bundle is corrupted. Test your bundle in a terminal (Linux solution).

Example for a bundle archive named MyBundleToTest.tar.gz giving the mentioned error when uploaded :

tar -tzf MyBundleToTest.tar.gz >/dev/null
tar: This does not look like a tar archive
tar: Skipping to next header
tar: Exiting with failure status due to previous errors

4.1.1. Solution

Extract content if possible and compress it to a correct compressed archive format.

4.2. I can’t upload my bundle

The server responds with a message like the following: {"status":"BAD_REQUEST","message":"unable to open submitted file","errors":["Input is not in the .gz format"]}

The bundle has been compressed using an unmanaged format.

4.2.1. Format verification

4.2.1.1. Linux solution

Command line example to verify the format of a bundle archive named MyBundleToTest.tar.gz(which gives the mentioned error when uploaded):

 tar -tzf MyBundleToTest.tar.gz >/dev/null

which should return in such case the following messages:

gzip: stdin: not in gzip format
tar: Child returned status 1
tar: Error is not recoverable: exiting now

4.2.2. Solution

Use tar.gz format for the archive compression. Shell command is tar -czvf MyBundleToTest.tar.gz config.json i18n.json template/ css/ for a bundle containing templates and css files.

4.3. My bundle is rejected due to internal structure

The server sends {"status":"BAD_REQUEST","message":"Incorrect inner file structure","errors":["$OPERATOR_FABRIC_INSTANCE_PATH/d91ba68c-de6b-4635-a8e8-b58 fff77dfd2/config.json (Aucun fichier ou dossier de ce type)"]}

Where $OPERATOR_FABRIC_INSTANCE_PATH is the folder where businessconfig files are stored server side.

4.3.1. Reason

The internal file structure of your bundle is incorrect. config.json file and folders need to be at the first level.

4.3.2. Solution

Add or organize the files and folders of the bundle to fit the Businessconfig bundle requirements.

4.4. My template is not used.

It need to be declared in the config.json of the bundle.

4.4.1. Solution

Add or verify the name of the templates declared in the config.json file of the bundle.

4.5. My value is not displayed in the detail template

There are several possibilities:

  • the path of the data used in the template is incorrect;

  • number of pair of { and } must be 2 (example : {{card.data.myValue}})