1. Prerequisites

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

  • Docker install with 4Gb of space

  • 16Gb of RAM minimal ,32 Gb recommended

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 no more logs are written on the output(It could continue to log but slowly).

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

If you are not accessing the server from localhost, there is a bug with authentication redirection. Your must use the following URL ,replacing SERVER_IP by the IP adress of your server :

http://SERVER_IP:89/auth/realms/dev/protocol/openid-connect/auth?response_type=code&client_id=opfab-client&redirect_uri=http://SERVER_IP:2002/ui/

After connection your shoud see the following screen

empty opfab screenshot

To stop the server , use :

docker-compose down &

3. Examples

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

All examples assume you connect to the server from localhost (if not, you will need to change the provided scripts)

3.1. Example 1 : Send and update a basic card

Go in directory "client/example1" and send a card :

curl -X POST http://localhost:2102/cards -H "Content-type:application/json" --data @card.json

or use provided script

./sendCard.sh card.json

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

{"count":1,"message":"All pushedCards were successfully handled"}

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",
	"publisherVersion" : "1",
	"process"  :"defaultProcess",
	"processId" : "hello-world-1",
	"state": "messageState",
	"recipient" : {
				"type" : "GROUP",
				"identity" : "TSO1"
			},
	"severity" : "INFORMATION",
	"startDate" : 1553186770681,
	"summary" : {"key" : "defaultProcess.summary"},
	"title" : {"key" : "defaultProcess.title"},
	"data" : {"message":"Hello World !!! That's my first message"}
}

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",
	"publisherVersion" : "1",
	"process"  :"defaultProcess",
	"processId" : "hello-world-1",
	"state": "messageState",
	"recipient" : {
				"type" : "GROUP",
				"identity" : "TSO1"
			},
	"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 processId

curl -s -X DELETE http://localhost:2102/cards/message-publisher_hello-world-1 -H "Content-type:application/json"

or use provided script:

./deleteCard.sh

3.2. Example 2 : Publish a new bundle

The way the card is display in the UI is defined via a Bundle containing templates and process description .

The bundle is structured as follow :

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

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/en/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 :

{
	"name":"message-publisher",
	"version":"2",
	"templates":["template"],
	"csses":["style"],
	"processes" : {
		"defaultProcess" : {
			"states":{
				"messageState" : {
					"details" : [{
						"title" : { "key" : "defaultProcess.title"},
						"templateName" : "template",
						"styles" : [ "style.css" ]
					}]
				}
			}
		}
	}
}

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

3.2.1. Package you bundle

Your bundle need to be package 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 authentified . To do that you need to get a token with the following commande :

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

or use the provided script

./getToken.sh

You should received JSON a reponse 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. The token will be valid for 10 hours , after you will need to ask for a new one.

3.2.3. Send the bundle

Replace the value "THE TOKEN" with the token in the sendBundle.sh script :

token=THE TOKEN
curl -s -X POST "http://localhost:2100/thirds" -H  "accept: application/json" -H  "Content-Type: multipart/form-data" -H "Authorization:Bearer $token" -F "file=@bundle.tar.gz;type=application/gzip"

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

./sendBundle.sh

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

{"name":"message-publisher","version":"2","templates":["template"],"csses":["style"],"i18nLabelKey":null,"processes":{"defaultProcess":{"statesData":{"messageState":{"detailsData":[{"title":{"key":"defaultProcess.title","parameters":null},"titleStyle":null,"templateName":"template","styles":null}],"actionsData":null,"details":[{"title":{"key":"defaultProcess.title","parameters":null},"titleStyle":null,"templateName":"template","styles":null}],"actions":null}},"states":{"messageState":{"detailsData":[{"title":{"key":"defaultProcess.title","parameters":null},"titleStyle":null,"templateName":"template","styles":null}],"actionsData":null,"details":[{"title":{"key":"defaultProcess.title","parameters":null},"titleStyle":null,"templateName":"template","styles":null}],"actions":null}}}},"medias":null,"menuEntries":null}

3.2.4. Send a card

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

{
	"publisher" : "message-publisher",
	"publisherVersion" : "2",
	"process"  :"defaultProcess",
	"processId" : "hello-world-1",
	"state": "messageState",
	"recipient" : {
				"type" : "GROUP",
				"identity" : "TSO1"
			},
	"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 publisherVersion to "2"

To send the card :

./sendCard.sh

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

3.2.5. Internationalization

If you switch langage to french in the UI (Settings menu on the top rigth of the screen), you should see the cards in french.

OperatorFabric will use templates define in repository "template/fr" and keys define in "i18n/fr.json".

3.3. Example 3 : Process with state

For this example , we will set the following process :

  • Step 1 : A critical situation arise on the HightVoltage grid

  • Step 2 : The critical situation evolve

  • Step 3 : The critical situation ended

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

{
  "name":"alert-publisher",
  "version":"1",
  "templates":["criticalSituationTemplate","endCriticalSituationTemplate"],
  "csses":["style"],
  "processes" : {
    "criticalSituation" : {
	  "states":{
		"criticalSituation-begin" : {
		  "details" : [{
		    "title" : { "key" : "criticalSituation-begin.title"},
			  "templateName" : "criticalSituationTemplate",
			  "styles" : [ "style.css" ]
			}]
		},
		"criticalSituation-update" : {
		  "details" : [{
		    "title" : { "key" : "criticalSituation-update.title"},
			  "templateName" : "criticalSituationTemplate",
			  "styles" : [ "style.css" ]
			}]
		},
		"criticalSituation-end" : {
		  "details" : [{
		    "title" : { "key" : "criticalSituation-end.title"},
			"templateName" : "endCriticalSituationTemplate",
			"styles" : [ "style.css" ]
		  }]
		}
	  }
	}
  }
}

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 an stylesheets to use .

The title is a key which refer to a i18n found in the coresponding i18n respository :

{
	"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.

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

{
	"publisher" : "alert-publisher",
	"publisherVersion" : "1",
	"process"  :"criticalSituation",
	"processId" : "alert1",
	"state": "criticalSituation-begin",
	"recipient" : {
				"type" : "GROUP",
				"identity" : "TSO1"
			},
	"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 refer 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 send via provided script :

./sendCard.sh card.json

Two other card have be provided to continu the process

  • cardUpdate.json : the state is criticalSituation-update

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

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",
	"publisherVersion" : "1",
	"process"  :"maintenanceProcess",
	"processId" : "maintenance-1",
	"state": "planned",
	"recipient" : {
				"type" : "GROUP",
				"identity" : "TSO1"
			},
	"severity" : "INFORMATION",
	"startDate" : 1553186770681,
	"summary" : {"key" : "maintenanceProcess.summary"},
	"title" : {"key" : "maintenanceProcess.title"},
	"data" : {
		"operationDescription":"Maintenance operation on the International France England (IFA) Hight Voltage line ",
		"operationResponsible":"RTE",
		"contactPoint":"By Phone : +33 1 23 45 67 89 ",
		"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 thirds 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/thirds-storage/scheduledMaintenance-publisher/1.

Before sending the card provide , you need to set the good time values as epoch (ms) (en.wikipedia.org/wiki/Epoch_(computing)) in the json . For each value you set , you will have a point in the timeline . In our example , the first point represent the beginning of the maintenance operation and the second the end of the maintenance operation.

To get the dates in epoch , you can use the following commandes

For the first date:

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

And for the second

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

To send the card use the provided script in example4 directory

./sendCard.sh card.json

A second card (card2.json) is provided as example, you need again to set times values in the json file and then send it

./sendCard.sh card2.json

This time the criticity of the card is ALERT, you should see the point in red in the timeline

example 4 screenshot

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

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 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 thirds 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 Third bundle requirements.

4.4. No template display

The server send 404 for requested template with a response like {"status":"NOT_FOUND","message":"The specified resource does not exist","errors":["$OPERATOR_FABRIC_INSTANCE_PATH/thirds-storage/BUNDLE_TEST/1/te mplate/fr/template1.handlebars (Aucun fichier ou dossier de ce type)"]}

4.4.1. Verification

The previous server response is return for a request like: http://localhost:2002/thirds/BUNDLE_TEST/templates/template1?locale=fr&version =1

The bundle is lacking localized folder and doesn’t contain the requested localization.

If you have access to the card-publication micro service source code you should list the content of $CARDS_PUBLICATION_PROJECT/build/docker-volume/third-storage

4.4.2. Solution

Either request another l10n or add the requested one to the bundle and re-upload it.

4.5. My template is not used.

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

4.5.1. Solution

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

4.6. 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 } has to follow those rules:

    • with no helper the good number is only 2 pairs;

    • with OperatorFabric helpers it’s 3 pairs of them.