OperatorFabric Architecture

1. Introduction

OperatorFabric is a modular, extensible, industrial-strength and field-tested platform for use in electricity, water, and other utility operations.

  • System visualization and console integration

  • Precise alerting

  • Workflow scheduling

  • Historian

  • Scripting (ex: Python, JavaScript)

Workflow Scheduling could be addressed either as an internal module or through simplified and standardized (BPMN) integration with external workflow engines, we’re still weighing the pros and cons of the two options._

It is an open source project initiated by RTE with LF Energy.

The aim of this document is to describe the architecture of the solution, first by defining the business concepts it deals with and then showing how this translates into the technical architecture.

2. Business Architecture

OperatorFabric is based on the concept of cards, which contain data regarding events that are relevant for the operator. A third party tool publish cards and the cards are received on the screen of the operators. Depending on the type of the cards, the operator can send back information to the third party via an "action".

2.1. Business components

functional diagram

To do the job, the following business components are defined:

  • Card Publication : this component receive the cards from third party tools

  • Card Consultation : this component delivers the cards to the operators and provide access to all cards exchanged (archives)

  • Actions : this component receive the action from the operator and send it to the third party tool

  • Card rendering and process definition : this component store the information for card rendering(templates , internationalization , …​ ) and a light description of the process associate(states , actions, …​) . This configuration data can be provided either by an administrator or by a third party tool.

  • User Management : this component is used to manage users and groups

2.2. Business objects

The business objects can be represented as follow:

business objects diagram
  • Card : the core business object which contains the data to show to the user(or operator)

  • Publisher : the third party that publishes cards

  • User : the operator receiving cards and responding via actions

  • Group : a group of users

  • Process : the process the card is dealing with

  • State : the step in the process

  • Card Rendering : data for card rendering

  • Action : a list of possible actions for a specific state in a process

3. Technical Architecture

The architecture is based on micro-services. All business services are accessible via REST API.

functional diagram

3.1. Business components

We find here the business component seen before:

  • We have a "UI" component that store the static pages and the UI code that is downloaded by the browser. The UI is based an Angular and Handlebars for card templating.

  • The business component named "Card rendering and process definition" is at the technical level known as "Third service". This service receive card rendering and process definition as a bundle. The bundle is a tar.gz file containing

    • json process configuration file (containings states & actions)

    • templates for rendering

    • stylesheets

    • internationalization information

All business components are based on SpringBoot and packaged via Docker.

Spring WebFlux is used to provide the card in a fluid way.

3.2. Technical components

3.2.1. Registry

It is the central component where all services are registered. It serves as a reference point for the gateway and other services to find information about the running services instance and allow for local load balancing of accesses. It is implemented by Spring Cloud Netflix .

3.2.2. Gateway

It provides a filtered view of the APIS and static served pages for external access through browsers or other http compliant accesses. It provide the rooting and load balancing for accessing the micro-services from outside. It is implemented by Spring Cloud Gateway.

3.2.3. Configuration

A configuration service is not mandatory in a micro-services architecture but may allow for better sharing of common configuration and to dispatch global configuration changes to all services. It is implemented via Spring Cloud Config.

3.2.4. Broker

The broker is used to share information asynchronously across the whole services. It is implemented via RabbitMQ

3.2.5. Authentication

The architecture provide a default authentication service via KeyCloak but it can delegates it to an external provider. Authentication is done through the use of Oauth2, three flows are supported: implicit, authorization code and password.

3.2.6. Database

The cards are stored in a MongoDb database. The bundles are stored in a file system.

OperatorFabric Getting Started

4. 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

5. 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 &

6. 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)

6.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

6.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"}
}

6.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.

6.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

6.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.

6.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.

6.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.

6.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}

6.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.

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

6.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

6.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

7. Troubleshooting

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

7.1.1. Solution

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

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

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

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

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

7.3.1. Reason

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

7.3.2. Solution

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

7.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)"]}

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

7.4.2. Solution

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

7.5. My template is not used.

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

7.5.1. Solution

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

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

OperatorFabric Reference Documentation

The aim of this document is to:

  • Explain what OperatorFabric is about and define the concepts it relies on

  • Give a basic tour of its features from a user perspective

  • Describe the technical implementation that makes it possible

8. Introduction

To perform their duties, an operator has to interact with multiple applications (perform actions, watch for alerts, etc.), which can prove difficult if there are too many of them.

The idea is to aggregate all the notifications from all these applications into a single screen, and to allow the operator to act on them if needed.

Feed screen layout

These notifications are materialized by cards sorted in a feed according to their period of relevance and their severity. When a card is selected in the feed, the right-hand pane displays the details of the card: information about the state of the parent process instance in the third-party application that published it, available actions, etc.

In addition, the cards will also translate as events displayed on a timeline (its design is still under discussion) at the top of the screen. This view will be complimentary to the card feed in that it will allow the operator to see at a glance the status of processes for a given period, when the feed is more like a "To Do" list.

Part of the value of OperatorFabric is that it makes the integration very simple on the part of the third-party applications. To start publishing cards to users in an OperatorFabric instance, all they have to do is:

  • Register as a publisher through the "Thirds" service and provide a "bundle" containing handlebars templates defining how cards should be rendered, i18n info etc.

  • Publish cards as json containing card data through the card publication API

OperatorFabric will then:

  • Dispatch the cards to the appropriate users (by computing the actual users who should receive the card from the recipients rules defined in the card)

  • Take care of the rendering of the cards, displaying details, actions, inputs etc.

  • Display relevant information from the cards in the timeline

Another aim of OperatorFabric is to make cooperation easier by letting operators forward or send cards to other operators, for example:

  • If they need an input from another operator

  • If they can’t handle a given card for lack of time or because the necessary action is out of their scope

This will replace phone calls or emails, making cooperation more efficient and traceable.

For instance, operators might be interested in knowing why a given decision was made in the past: the cards detailing the decision process steps will be accessible through the Archives screen, showing how the operators reached this agreement.

9. A quick walk through the user interface

Work in progress

10. Technical overview

11. Thirds service

As stated above, third-party applications (or "thirds" for short) interact with OperatorFabric by sending cards. The Thirds service allows them to tell OperatorFabric

  • how these cards should be rendered

  • what actions should be made available to the operators regarding a given card

  • if several languages are supported, how cards should be translated

In addition, it lets third-party applications define additional menu entries for the navbar (for example linking back to the third-party application) that can be integrated either as iframe or external links.

11.1. Declaring a Third Party Service

This sections explains Third Party Service Configuration

The third party service configuration is declared using a bundle which is described below. Once this bundle fully created, it must be uploaded to the server which will apply this configuration into current for further web UI calls.

The way configuration is done is explained throughout examples before a more technical review of the configuration details. The following instructions describe tests to perform on OperatorFabric to understand how customization is working in it. The card data used here are send automatically using a script as describe here Cards sending script.

11.1.1. Requirements

Those examples are played in an environment where an OperatorFabric instance (all micro-services) is running along a MongoDB Database and a RabbitMQ instances.

11.1.2. Bundle

Third bundles customize the way third card details are displayed. Those tar.gz archives contain a descriptor file named config.json, eventually some css files, i18n files and handlebars templates to do so.

For didactic purposes, in this section, the third name is BUNDLE_TEST (to match the parameters used by the script). This bundle is localized for en and fr.

As detailed in the Third core service README the bundle contains at least a metadata file called config.json, a css folder, a media folder (not created in this example), an i18n folder and a template folder. All elements except the config.json file are optional.

The files of this example are organized as below:

bundle
├── config.json
├── css
│   └── bundleTest.css
├── i18n
│   ├── en.json
│   └── fr.json
└── template
    ├── en
    │   ├── template1.handlebars
    │   └── template2.handlebars
    └── fr
        ├── template1.handlebars
        └── template2.handlebars

To summarize, there are 5 directories and 8 files.

The config.json file

It’s a description file in json format. It lists the content of the bundle.

example

{
  "name": "BUNDLE_TEST",
  "version": "1",
  "csses": [
    "bundleTest"
  ],
  "i18nLabelKey": "third-name-in-menu-bar",
  "menuEntries": [
    {
      "id": "uid test 0",
      "url": "https://opfab.github.io/whatisopfab/",
      "label": "first-menu-entry"
    },
    {
      "id": "uid test 0",
      "url": "https://www.lfenergy.org/",
      "label": "b-menu-entry"
    },
    {
      "id": "uid test 1",
      "url": "https://github.com/opfab/opfab.github.io",
      "label": "the-other-menu-entry"
    }
  ],
  "processes" : {
      "simpleProcess" : {
        "start" : {
          "details" : [ {
            "title" : {
              "key" : "start.first.title"
            },
            "titleStyle" : "startTitle text-danger",
            "templateName" : "template1"
          } ],
          "actions" : {
            "finish" : {
              "type" : "URL",
              "url": "http://somewher.org/simpleProcess/finish",
              "lockAction" : true,
              "called" : false,
              "updateStateBeforeAction" : false,
              "hidden" : true,
              "buttonStyle" : "buttonClass",
              "label" : {
                "key" : "my.card.my.action.label"
              },
            }
          }
        },
        "end" : {
          "details" : [ {
            "title" : {
              "key" : "end.first.title"
            },
            "titleStyle" : "startTitle text-info",
            "templateName" : "template2",
            "styles" : [ "bundleTest.css" ]
          } ]
        }
      }
    }
}
  • name: third name;

  • version: enable the correct display, even the old ones as all versions are stored by the server. Your card has a version field that will be matched to third configuration for correct rendering ;

  • processes : list the available processes and their possible states; actions and templates are associated to states

  • css file template list as csses;

  • third name in the main bar menu as i18nLabelKey: optional, used if the third service add one or several entry in the OperatorFabric main menu bar,cf menu entries section for details;

  • extra menu entries as menuEntries: optional, see below for the declaration format of objects of this array, cf menu entries section for details.

The mandatory declarations are name and version attributes.

i18n

There are two ways of i18n for third service. The first one is done using l10n files which are located in the i18n folder, the second one throughout l10n name folder nested in the template folder or in the media folder.

The i18n folder contains one json file per l10n.

These localisation is used for integration of the third service into OperatorFabric, i.e. the label displayed for the third service, the label displayed for each tab of the details of the third card, the label of the actions in cards if any or the additional third entries in OperatorFabric(more on that at the chapter ????).

Template and Media folders

The template and the media folder must contain localized folder for the i18n of the card details. This is why in our example, as the bundle is localized for en and fr language, the template folder contains a en and a fr folder.

i18n file

If there is no i18n file or key is missing, the i18n key is displayed in OperatorFabric.

The choice of i18n keys is left to the Third service maintainer. The keys are referenced in the following places:

  • config.json file:

    • i18nLabelKey: key used for the label for the third service displayed in the main menu bar of OperatorFabric;

    • label of menu entry declaration: key used to l10n the menu entries declared by the Third party in the bundle;

  • card data: values of card title and card summary refer to i18n keys as well as key attribute in the card detail section of the card data.

example

So in this example the third service is named Bundle Test with BUNDLE_TEST technical name. The bundle provide an english and a french l10n.

The example bundle defined an new menu entry given access to 3 entries. The title and the summary have to be l10n, so needs to be the 2 tabs titles.

The name of the third service as displayed in the main menu bar of OperatorFabric. It will have the key "third-name-in-menu-bar". The english l10n will be Bundle Test and the french one will be Bundle de test.

A name for the three entries in the third entry menu. Their keys will be in order "first-menu-entry", "b-menu-entry" and "the-other-menu-entry" for an english l10n as Entry One, Entry Two and Entry Three and in french as Entrée une, Entrée deux and Entrée trois.

The title for the card and its summary. As the card used here are generated by the script of the cards-publication project we have to used the key declared there. So they are respectively process.title and process.summary with the following l10ns for english: Card Title and Card short description, and for french l10ns: Titre de la carte and Courte description de la carte.

A title for each (two of them) tab of the detail cards. As for card title and card summary, those keys are already defined by the test script. There are "process.detail.tab.first" and "process.detail.tab.second". For english l10n, the values are First Detail List and Second Detail List and for the french l10n, the values are Première liste de détails and Seconde liste de détails.

Here is the content of en.json

{
	"third-name-in-menu-bar":"Bundle Test",
		"first-menu-entry":"Entry One",
		"b-menu-entry":"Entry Two",
		"the-other-menu-entry":"Entry Three",
		"process":{
			"title":"Card Title",
			"summary":"Card short description",
			"detail":{
				"tab":{
					"first":"First Detail List",
					"second":"Second Detail List"
				}
			}
		}
}

Here the content of fr.json

{
	"third-name-in-menu-bar":"Bundle de test",
		"first-menu-entry":"Entrée une",
		"b-menu-entry":"Entrée deux",
		"the-other-menu-entry":"Entrée trois",
		"process":{
			"title":"Titre de la carte",
			"summary":"Courte description de la carte",
			"detail":{
				"tab":{
					"first":"Première liste de détails",
					"second":"Deuxième liste de détails"
				}
			}
		}
}

Once the bundle is correctly uploaded, the way to verify if the i18n have been correctly uploaded is to use the GET method of third api for i18n file.

The service is described here and can be used directly in the browser using the Swagger UI - Third get i18n. The locale language, the version of the bundle and the technical name of the third party are needed to get json in the response.

To verify if the french l10n data of the version 1 of the BUNDLE_TEST third party we could use the following command line curl -X GET "http://localhost:2100/thirds/BUNDLE_TEST/i18n?locale=fr&version=1" -H "accept: application/json". The service response with a 200 status and with the json corresponding to the defined fr.json file show below.

{
"third-name-in-menu-bar":"Bundle de test",
"first-menu-entry":"Entrée une",
"b-menu-entry":"Entrée deux",
"the-other-menu-entry":"Entrée trois",
"tests":{
	"title":"Titre de la carte",
	"summary":"Courte description de la carte",
	"detail":{
		"tab":{
			"first":"Première liste de détails",
			"second":"Deuxième liste de détails"
			}
		}
}
}

Those elements are declared in the config.json file of the bundle.

If there are several items to declare for a third service, a title for the third menu section need to be declared within the i18nLabelKey attribute, otherwise the first and only menu entry item is used to create an entry in the menu nav bar of OperatorFabric.

config.json declaration

This kind of objects contains the following attributes :

  • id: identifier of the entry menu in the UI;

  • url: url opening a new page in a tab in the browser;

  • label: it’s an i18n key used to l10n the entry in the UI.

Examples

In the following examples, only the part relative to menu entries in the config.json file is detailed, the other parts are omitted and represented with a '…'.

Single menu entry

{
	…
	"menuEntries":[{
			"id": "identifer-single-menu-entry",
			"url": "https://opfab.github.io",
			"label": "single-menu-entry-i18n-key"
		}],
}

Several menu entries

Here a sample with 3 menu entries.

{
	…
	"i18nLabelKey":"third-name-in-menu-navbar",
	"menuEntries": [{
			"id": "firstEntryIdentifier",
			"url": "https://opfab.github.io/whatisopfab/",
			"label": "first-menu-entry"
		},
		{
			"id": "secondEntryIdentifier",
			"url": "https://www.lfenergy.org/",
			"label": "second-menu-entry"
		} ,
		{
			"id": "thirdEntryIdentifier",
			"url": "https://opfab.github.io",
			"label": "third-menu-entry"
		}]
}
Processes and States

Processes and their states allows to match a Third Party service process specific state to a list of templates for card details and actions allowing specific card rendering for each state of the business process.

The purpose of this section is to display elements of third card data in a custom format.

Regarding the card detail customization, all the examples in this section will be based on the cards generated by the script existing in the Cards-Publication project. For the examples given here, this script is run with arguments detailed in the following command line:

$OPERATOR_FABRIC_HOME/services/core/cards-publication/src/main/bin/push_card_loop.sh --publisher BUNDLE_TEST --process tests

where:

  • $OPERATOR_FABRIC_HOME is the root folder of OperatorFabric where tests are performed;

  • BUNDLE_TEST is the name of the Third party;

  • tests is the name of the process referred by published cards.

configuration

The process entry in the configuration file is a dictionary of processes, each key maps to a process definition. A process definition is itself a dictionary of states, each key maps to a state definition. A state is defined by:

  • a list of details: details are a combination of an internationalized title (title), css class styling element (titleStyle) and a template reference

  • a dictionary of actions: actions are described in the bellow Actions section.

Actions
{
  "type" : "URL",
  "url": "http://somewher.org/simpleProcess/finish",
  "lockAction" : true,
  "called" : false,
  "updateStateBeforeAction" : false,
  "hidden" : true,
  "buttonStyle" : "buttonClass",
  "label" : {
    "key" : "my.card.my.action.label"
  }
}

An action aggregates both the mean to trigger action on the third party and data for an action button rendering:

  • type - mandatory: for now only URL type is supported:

    • URL: this action triggers a call to an external REST end point

  • url - mandatory: a template url for URL type action. this url may be injected with data before actions call, data are specified using curly brackets. Available parameters:

    • processInstance: the name/id of the process instance

    • process: the name of the process

    • state: the state name of the process

    • jwt: the jwt token of the user

    • data.[path]: a path to object in card data structure

  • hidden: if true, action won’t be visible on the card but will be available to templates

  • buttonStyle: css style classes to apply to the action button

  • label: an i18n key and parameters used to display a tooltip over the button

  • lockAction: not yet implemented

  • updateStateBeforeAction: not yet implemented

  • called: not yet implemented

For in depth information on the behavior needed for the third party rest endpoints refer to the Actions service reference.

Templates

For demonstration purposes, there will be two simple templates. For more advance feature go to the section detailing the handlebars templates in general and helpers available in OperatorFabric. As the card used in this example are created above, the bundle template folder needs to contain 2 templates: template1.handlebars and template2.handlebars.

examples of template (i18n versions)

/template/en/template1.handlers

<h2>Template Number One</h2>
<div class="bundle-test">'{{card.data.level1.level1Prop}}'</div>

/template/fr/template1.handlers

<h2>Patron numéro Un</h2>
<div class="bundle-test">'{{card.data.level1.level1Prop}}'</div>

Those templates display a l10n title and an line containing the value of the scope property card.level1.level1Prop which is This is a root property.

/template/en/template2.handelbars

<h2>Second Template</h2>
<ul class="bundle-test-list">
	{{#each card.data.level1.level1Array}}
		<li class="bunle-test-list-item">{{this.level1ArrayProp}}</li>
	{{/each}}
</ul>

/template/fr/template2.handelbars

<h2>Second patron</h2>
<ul class="bundle-test-list">
	{{#each card.data.level1.level1Array}}
		<li class="bunle-test-list-item">{{this.level1ArrayProp}}</li>
	{{/each}}
</ul>

Those templates display also a l10n title and a list of numeric values from 1 to 3.

CSS

This folder contains regular css files. The file name must be declared in the config.json file in order to be used in the templates and applied to them.

Examples

As above, all parts of files irrelevant for our example are symbolised by a character.

Declaration of css files in config.json file

{
	…
	"csses":["bundleTest"]
	…
}

CSS Class used in ./template/en/template1.handlebars

	…
	<div class="bundle-test">'{{card.data.level1.level1Prop}}'</div>
	…

As seen above, the value of {{card.data.level1.level1Prop}} of a test card is This is a level1 property

Style declaration in ./css/bundleTest.css

.h2{
	color:#fd9312;
	font-weight: bold;
}

Expected result

Formatted root property
Upload

For this, the bundle is submitted to the OperatorFabric server using a POST http method as describe in the Third Service API documentation. It’s possible for test purposes to use the swagger documentation of the OperatorFabric using the following url https//:$OPERATOR_FABRIC_ROOT_URL:2100/swagger-ui.html#/thirds/uploadBundle

 where $OPERATOR_FABRIC_ROOT_URL is the url of the running OperatorFabric tested.

Example :

cd $BUNDLE_FOLDER
curl -X POST "http://localhost:2100/thirds" -H  "accept: application/json" -H  "Content-Type: multipart/form-data" -F "file=@bundle-test.tar.gz;type=application/gzip"

Where:

  • $BUNDLE_FOLDER is the folder containing the bundle archive to be uploaded.

  • bundle-test.tar.gz is the name of the uploaded bundle.

These command line should return a 200 http status response with the details of the content of the bundle in the response body such as :

{
  "mediasData": null,
  "menuEntriesData": [
    {
      "id": "uid test 0",
      "url": "https://opfab.github.io/whatisopfab/",
      "label": "first-menu-entry"
    },
    {
      "id": "uid test 0",
      "url": "https://www.lfenergy.org/",
      "label": "b-menu-entry"
    },
    {
      "id": "uid test 1",
      "url": "https://github.com/opfab/opfab.github.io",
      "label": "the-other-menu-entry"
    }
  ],
  "name": "BUNDLE_TEST",
  "version": "1",
  "csses": [
    "bundleTest"
  ],
  "i18nLabelKey": "third-name-in-menu-bar",
  "medias": null,
  "menuEntries": [
    {
      "id": "uid test 0",
      "url": "https://opfab.github.io/whatisopfab/",
      "label": "first-menu-entry"
    },
    {
      "id": "uid test 0",
      "url": "https://www.lfenergy.org/",
      "label": "b-menu-entry"
    },
    {
      "id": "uid test 1",
      "url": "https://github.com/opfab/opfab.github.io",
      "label": "the-other-menu-entry"
    }
  ],
  "processes" : {
        "simpleProcess" : {
          "start" : {
            "details" : [ {
              "title" : {
                "key" : "start.first.title"
              },
              "titleStyle" : "startTitle text-danger",
              "templateName" : "template1"
            } ],
            "actions" : {
              "finish" : {
                "type" : "URL",
                "url": "http://somewher.org/simpleProcess/finish",
                "lockAction" : true,
                "called" : false,
                "updateStateBeforeAction" : false,
                "hidden" : true,
                "buttonStyle" : "buttonClass",
                "label" : {
                  "key" : "my.card.my.action.label"
                },
              }
            }
          },
          "end" : {
            "details" : [ {
              "title" : {
                "key" : "end.first.title"
              },
              "titleStyle" : "startTitle text-info",
              "templateName" : "template2",
              "styles" : [ "bundleTest.css" ]
            } ]
          }
        }
      }
}

Otherwise please refer to the trouble shooting section to resolve the problem.

11.2. Bundle Technical overview

11.2.1. Resource serving

CSS

CSS 3 style sheet are supported, they allow custom styling of card template detail all css selector must be prefixed by the .detail.template parent selector

Internationalization

Internationalization (i18n) files are json file (JavaScript Object Notation). One file must be defined by module supported language. See the model section of the swagger generated documentation for data layout.

Sample json i18n file

{ "emergency":
  {
    "message": "Emergency situation happened on {{date}}. Cause : {{cause}}."
    "module":
      {
        "name": "Emergency Module",
        "description": "The emergency module managed ermergencies"
      }
  }
}

i18n messages may include parameters, these parameters are framed with double curly braces.

The bundled json files name must conform to the following pattern : [lang].json

ex:

fr.json
en.json
de.json
Media (notification sounds)

Supported media files type : * ogg * wav * mp3

Due to web nature of OperatorFabric, we encourage business modules provider to favor the lightest file formats (ogg,mp3, …​)

Templates

Templates are Handlebars template files. Templates are fuelled with a scope structure composed of

  • a card property (See card data model for more information)

  • a userContext :

    • login: user login

    • token: user jwt token

    • firstName: user first name

    • lastName: user last name

In addition to Handlebars basic syntax and helpers, OperatorFabric defines the following helpers :

numberFormat

formats a number parameter using developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Nu mberFormat[Intl.NumberFormat]. The locale used is the current user selected one, and options are passed as hash parameters (see Handlebars doc Literals section).

{{numberFormat card.data.price style="currency" currency="EUR"}}
dateFormat

formats the submitted parameters (millisecond since epoch) using mement.format. The locale used is the current user selected one, the format is "format" hash parameter (see Handlebars doc Literals section).

{{dateFormat card.data.birthday format="MMMM Do YYYY, h:mm:ss a"}}
slice

extracts a sub array from ann array

example:

<!--
{"array": ["foo","bar","baz"]}
-->
<ul>
{{#each (slice array 0 2)}}
  <li>{{this}}</li>
{{/each}}
</ul>

outputs:

<ul>
  <li>foo</li>
  <li>bar</li>
</ul>

and

<!--
{"array": ["foo","bar","baz"]}
-->
<ul>
{{#each (slice array 1)}}
  <li>{{this}}</li>
{{/each}}
</ul>

outputs:

<ul>
  <li>bar</li>
  <li>baz</li>
</ul>
now

outputs the current date in millisecond from epoch. The date is computed from application internal time service and thus may be different from the date that one can compute from javascript api which relies on the browsers' system time.

NB: Due to Handlebars limitation you must provide at least one argument to helpers otherwise, Handlebars will confuse a helper and a variable. In the bellow example, we simply pass an empty string.

example:

<div>{{now ""}}</div>
<br>
<div>{{dateFormat (now "") format="MMMM Do YYYY, h:mm:ss a"}}</div>

outputs

<div>1551454795179</div>
<br>
<div>mars 1er 2019, 4:39:55 pm</div>

for a local set to FR_fr

preserveSpace

preserves space in parameter string to avoid html standard space trimming.

{{preserveSpace card.data.businessId}}
bool

returns a boolean result value on an arithmetical operation (including object equality) or boolean operation.

Arguments: - v1: left value operand - op: operator (string value) - v2: right value operand

arithmetical operators:

  • ==

  • ===

  • !=

  • !==

  • <

  • >

  • >=

boolean operators:

  • &&

  • ||

examples:

{{#if (bool v1 '<' v2)}}
  v1 is strictly lower than v2
{{else}}
 V2 is lower or equal to v1
{{/if}}
math

returns the result of a mathematical operation.

arguments:

  • v1: left value operand

  • op: operator (string value)

  • v2: right value operand

arithmetical operators:

  • +

  • -

  • *

  • /

  • %

example:

{{math 1 '+' 2}}
split

splits a string into an array based on a split string.

example:

<ul>
{{#each (split 'my.example.string' '.')}}
  <li>{{this}}</li>
{{/each}}
</ul>

outputs

<ul>
  <li>my</li>
  <li>example</li>
  <li>string</li>
</ul>
action

outputs a card action button whose card action id is the concatenation of an arbitrary number of helper arguments

{{{action "PREREQUISITE_" id}}}
svg

outputs a svg tag with lazy loading, and missing image replacement message. The image url is the concatenation of an arbitrary number of helper arguments

{{{svg baseUri scheduledOpId "/" substation "/before/"
computationPhaseOrdinal}}}
i18n

outputs a i18n result from a key and some parameters. There are two ways of configuration :

  • Pass an object as sole argument. The object must contain a key field (string) and an optional parameter field (map of parameterKey ⇒ value)

    {{i18n card.data.i18nTitle}}
  • Pass a string key as sole argument and use hash parameters (see Handlebars doc Literals section) for i18n string parameters.

<!--
emergency.title=Emergency situation happened on {{date}}. Cause : {{cause}}.
-->
{{i18n "emergency.title" date="2018-06-14" cause="Broken Cofee Machine"}}

outputs

Emergency situation happened on 2018-06-14. Cause : Broken Cofee Machine
sort

sorts an array or some object’s properties (first argument) using an optional field name (second argument) to sort the collection on this fields natural order.

If there is no field argument provided :

  • for an array, the original order of the array is kept ;

  • for an object, the structure is sorted by the object field name.

<!--
users :

{"john": { "firstName": "John", "lastName": "Cleese"},
"graham": { "firstName": "Graham", "lastName": "Chapman"},
"terry": { "firstName": "Terry", "lastName": "Gilliam"},
"eric": { "firstName": "Eric", "lastName": "Idle"},
"terry": { "firstName": "Terry", "lastName": "Jones"},
"michael": { "firstName": "Michael", "lastName": "Palin"},
-->

<ul>
{{#each (sort users)}}
    <li>{{this.firstName}} {{this.lastName}}</li>
{{/each}}
</ul>

outputs :

<ul>
  <li>Eric Idle</li>
  <li>Graham Chapman</li>
  <li>John Cleese</li>
  <li>Michael Pallin</li>
  <li>Terry Gilliam</li>
  <li>Terry Jones</li>
</ul>

and

<ul>
{{#each (sort users "lastName")}}
    <li>{{this.firstName}} {{this.lastName</li>
{{/each}}
</ul>

outputs :

<ul>
  <li>Graham Chapman</li>
  <li>John Cleese</li>
  <li>Terry Gilliam</li>
  <li>Eric Idle</li>
  <li>Terry Jones</li>
  <li>Michael Pallin</li>
</ul>

12. OperatorFabric Users Service

The User service manages users and groups.

Users

represent account information for person destined to receive cards in the OperatorFabric instance.

Groups
  • represent set of users destined to receive collectively some cards.

  • can be used in a way to handle rights on card reception in OperatorFabric.

The user define here is an internal representation of the individual card recipient in OperatorFabric the authentication is leave to specific OAuth2 external service.
In the following commands the $token is an authentication token currently valid for the OAuth2 service used by the current OperatorFabric system.

12.1. Users and Groups

User service manager users and groups.

12.1.1. Users

Users are the individuals and mainly physical person who can log in OperatorFabric.

The access to this service has to be authorized, in the OAuth2 service used by the current OperatorFabric instance, at least to access User information and to manage Users. The membership of groups is stored in the user information.

Automation creation users

In case of an user does exist in a provided authentication service but he does not exist in the OperatorFabric instance, when he is authenticated and connected for the first time in the OperatorFabric instance, the user is automatically created in the system with no attached groups. The administration of the groups is dealt by the administrator manually. More details about automation creation user [here]

12.1.2. Groups

The notion of group is loose and can be used to simulate role in OperatorFabric. Groups are used to send cards to several users without name them specifically. The information about membership to a group is stored in the user information. The rules used to send cards are describe in the Card Publication Service.

Alternative way to manage groups

The standard way to handle groups in OperatorFabric instance is dealt on the user information. There is an alternative way to manage groups through the authentication token, the groups are defined by the administrator of the authentication service. [here] for more details to use this feature.

13. Cards Publication Service

The Cards Publication Service exposes a REST API through which third-party applications, or "publishers" can post cards to OperatorFabric. It then handles those cards:

  • Time-stamping them with a "publishDate"

  • Sending them to the message broker (RabbitMQ) to be delivered in real time to the appropriate operators

  • Persisting them to the database (MongoDB) for later consultation

13.1. Card Structure

Cards are modelized as Json objects. The technical design of card is describe in the swagger api description of Card Publication Service. A card correspond to the state of a Process in OperatorFabric.

13.1.1. Technical Information of the card

Those attributes are used by OperatorFabric to manage how cards are stored, to whom and when they’re sent.

Mandatory information

Below, the json technical key is in the '()' following the title.

Publisher (publisher)

Quite obviously it’s the Third party which publish the card. This information is used to look up for Presentation resources of the card.

Publisher Version (publisherVersion)

Refers the version of publisher third to use to render this card (i18n, title, summary, media and details). As through time, the presentation of a publisher card data changes, this changes are managed through publisherVersion in OperatorFabric. Each version is keep in the system in order to be able to display correctly old cards.

Process Identifier (processId)

It’s the way to identify the process to which the card is associated. A card represent a state of a process.

Start Date (startDate)

This attribute is a part of the card life management system. It’s indicate OperatorFabric the moment since the card can be displayed to the operators or main recipients, see Displaying rules.

Severity (severity)

The severity is a core principe of the OperatorFabric Card system. There are 4 severities available(Technical definition of OperatorFabric Card Severity). A color is associated in the GUI to each severity. Here the details about severity and their meaning for OperatorFabric:

  1. ALARM: represents a critical state of the associated process, need an action from the operator. In the Gui, the card is red;

  2. ACTION: the associated process need an action form operators in order to evolve correctly. In the Gui, an action is orange;

  3. COMPLIANT: the process related to the card is in a compliant status. In the UI, an action is green.;

  4. INFORMATION: give information to the operator. In the GUI, a information is blue.

Title (title)

This attribute is display as header of a card in the feed of the GUI. It’s the main User destined Information of a card. The value refer to an i18n value used to localized this information.

Summary (summary)

This attribute is display as a description of a card in the feed of the GUI, when the card is selected by the operator. It’s completing the information of the card title. The value refer to an i18n value used to localized this information.

Recipient (recipient)

Declares to whom the card is send. For more details about way recipient works see Displaying rules. Without recipient declaration a card is useless in OperatorFabric system.

Store information
Tags (tag)

Tags are intended to enable filter of cards in the feed of the GUI.

uid (uid)

Unique identifier of the card in the OperatorFabric system. This attribute can be send with card, but normally it’s manage by OperatorFabric system.

id (id)

State id of the associated process, determined by OperatorFabric can be set arbitrarily by the publisher.

Card Life Management Configuration

With this attributes OperatorFabric know when to display or hide cards, but also if an action is still available for a given card.

End Date (endDate)

Fixes the moment until when OperatorFabric displays the card. After the card is remove from the GUI feed, see Display Rules for some examples.

Last Time to Decide (lttd)

Fixes the moment until when a actions associated to the card are available. After then, the associated actions won’t be displayed or actionable.

Other technical attributes
Publish Date (publishDate)

Indicates when the card has been registered in OperatorFabric system. This is a technical information exclusively managed by OperatorFabric.

Deletion Date (`deletionDate°)

Indicates when the card has been removes from OperatorFabric system. Technical information manage by OperatorFabric.

13.1.2. User destined Information of the card

There are two kind of User destined information in a card. Some are restricted to the card format, others are defined by the publisher as long as there are encoded in json format.

Custom part
Data (data)

Determines where custom information is store. The content in this attribute, is purely publisher choice. This content, as long as it’s in json format can be used to display details. For the way the details are displayed see below.

13.1.3. Presentation Information of the card

Media (media)

Some cards can emit a sound when displayed in the feed of the GUI, the id of audio notification is indicated in this attribute.

details (details)

This attribute is a string of objects containing a title attribute which is i18n key and a template attribute which refers to a template name contained in the publisher bundle. The bundle in which those resources will be looked for is the one corresponding of the version declared in the card for the current publisher. If no resource is found, either because there is no bundle for the given version or there is no resource for the given key, then the corresponding key is displayed in the details section of the GUI.

example:

The TEST publisher has only a 0.1 version uploaded in the current OperatorFabric system. The details value is [{"title":{"key":"first.tab.title"},"template":"template0"}].

If the publisherVersion of the card is 2 then only the title key declared in the details array will be displays without any translation, i.e. the tab will contains TEST.2.first.tab.title and will be empty. If the l10n for the title is not available, then the tab title will be still TEST.2.first.tab.title but the template will be compute and the details section will display the template content.

TimeSpans (timeSpans)

When the simple startDate and endDate are not enough to characterize your process business times, you can add a list of TimeSpan to your card. TimeSpans are rendered in the timeline component as cluster bubbles are as lines depending on your parametrization of the span. This as no effect on the feed content

example 1:

to display the card two times in the timeline you can add two TimeSpan to your card:

{
	"publisher":"TSO1",
	"publisherVersion":"0.1",
	"processId":"process-000",
	"startDate":1546297200000,
	"severity":"INFORMATION",
	...
	"timeSpans" : [
        {"start" : 1546297200000},
        {"start" : 1546297500000}
    ]

}

In this sample, the card will be displayed twice in the time line. The card start date will be ignored.

example 2:

Instead of the default clustered view, you may want your card to be displayed as a line in the time line.

{
	"publisher":"TSO1",
	"publisherVersion":"0.1",
	"processId":"process-000",
	"startDate":1546297200000,
	"severity":"INFORMATION",
	...
	"timeSpans" : [
        {"start" : 1546297200000, "end" : 1546297500000}
    ]

}

13.2. Cards Examples

Before detailing the content of cards, let’s show you what cards look like through few examples of json.

13.2.1. Minimal Card

The OperatorFabric Card specification defines 8 mandatory attributes, but some optional attributes are needed for cards to be useful in OperatorFabric. Let’s clarify those point through few examples of minimal cards and what happens when they’re used as if.

Send to One User

The following card contains only the mandatory attributes.

{
	"publisher":"TSO1",
	"publisherVersion":"0.1",
	"processId":"process-000",
	"startDate":1546297200000,
	"severity":"INFORMATION",
	"title":{"key":"card.title.key"},
	"summary":{"key":"card.summary.key"},
	"recipient":{
		"type":"USER",
		"identity":"tso1-operator"
	}

}

This an information about the process process-000, send by the TSO1. The title and the summary refers to i18n keys defined in the associated i18n files of the publisher. This card is displayable since the first january of 2019 and should only be received by the user using the tso1-operator login.

Send to several users
Simple case

The following example is nearly the same as the previous one except for the recipient.

{
	"publisher":"TSO1",
	"publisherVersion":"0.1",
	"processId":"process-000",
	"startDate":1546297200000,
	"severity":"INFORMATION",
	"title":{"key":"card.title.key"},
	"summary":{"key":"card.summary.key"},
	"recipient":{
		"type":"GROUP",
		"identity":"TSO1"
	}

}

The recipient is here a group, the TSO1. So all users whoa are member of this group will receive the card.

Complex case

If this card need to be view by a user who is not in the TSO1 group, it’s possible to tune more precisely the definition of the recipient. If the tso2-operator needs to see also this card, the recipient definition could be(the following code details only the recipient part):

"recipient":{
	"type":"UNION",
	"recipients":[
		{ "type": "GROUP", "identity":"TSO1"},
		{ "type": "USER", "identity":"tso2-operator"}
		]
	}

So here, all the users of the TSO1 group will received the INFORMATION as should the tos2-operator user.

13.2.2. Regular Card

The previous cards were nearly empty regarding information carrying. In fact, cards are intended to contains more information than a title and a summary. The optional attribute data is here for that. This attribute is destined to contains any json object. The creator of the card is free to put any information needed as long as it’s in a json format.

Full of Hidden data

For this example we will use our previous example for the TSO1 group with a data attribute containing the definition of a json object containing two attributes: stringExample and numberExample.

{
	"publisher":"TSO1",
	"publisherVersion":"0.1",
	"processId":"process-000",
	"startDate":1546297200000,
	"severity":"INFORMATION",
	"title":{"key":"card.title.key"},
	"summary":{"key":"card.summary.key"},
	"recipient":{
		"type":"USER",
		"identity":"tso1-operator"
	},
	"data":{
		"stringExample":"This is a not so random string of characters.",
		"numberExample":123
		}

}

This card contains some data but when selected in the feed nothing more than the previous example of card happen because there is no rendering configuration.

Fully useful

When a card is selected in the feed (of the GUI), the data is displayed in the detail panel. The way details are formatted depends on template uploaded by Third parties as describe here. To have an effective example without to many actions to performed, the following example will use an already existing configuration.The one presents in the development version of OperatorFabric, for test purpose(TEST bundle).

At the card level, the attributes in the card telling OperatorFabric which template to use is the details attributes.

{
	"publisher":"TEST",
	"publisherVersion":"1",
	"processId":"process-000",
	"startDate":1546297200000,
	"severity":"INFORMATION",
	"title":{"key":"process.title"},
	"summary":{"key":"process.summary"},
	"recipient":{
		"type":"USER",
		"identity":"tso1-operator"
	},
	"data":{"rootProp":"Data displayed in the detail panel"},
	"details":[{"title":{"key":"process.detail.tab.first"}, "templateName":"template1"}]

}

So here a single custom data is defined and it’s rootProp. This attribute is used by the template called by the details attribute. This attribute contains an array of json object containing an i18n key and a template reference. Each of those object is a tab in the detail panel of the GUI. The template to used are defined and configured in the Third bundle upload into the server by the publisher.

13.2.3. Displaying Rules

Dates

Dates impact both the feed rendering and the timeline rendering.

In the feed cards are visible based on a collection of filters among which a time filter.

In the time line cards are visible based on a similar filter plus the time line renders the "position" in time of said cards. By default it groups cards at close time in bubbles whom color indicates severity and inner number indicates number of cards.

Start Date (startDate)

The card is only display after this date is reach by the current time. It’s a mandatory attributes for OperatorFabric cards.

example:

The current day is the 29 january of 2019.

A card with the following configuration "startDate":1548758040000, has a start date equals to the iso date: "2019-01-29T10:34:00Z". So the operator will see it appearing in it’s feed at 10h34 AM universal time. And if there is no endDate defines for it, it will stay in the feed indefinitely, so this card should be still visible the 30th january of 2019. Before "10h34 AM universal time", this card was not visible in the feed.

End Date (endDate)

This optional attribute, corresponds to the moment after which the card will be remove from the feed of the GUI.

example:

Imagine that the current day is still the 29 january of 2019.

The card we are looking after, has the same value for the startDate than in the previous example but has the following configuration for the endDate: "endDate":1548765240000. It’s corresponding to "2019-01-29T12:34:00Z" universal time.

So our card is present in the feed between "11h34" and "13h34". Before and after those hours, the card is not available.

Recipients

The attribute recipient of a card tells to whom it’s sent. It’s value format is an object named recipient that describe the rule for recipient computation.

The available types are:

  • GROUP

  • USER

  • UNION

  • DEADEND

The simplest way to determine the recipient is to assign the card to a user or a group as seen previously in "Minimal Card".

But it’s possible to combine groups and potentially users using UNION type to have a better control on whom should receive the card.

UNION

For example, if a card is destined to the operators of TSO1 and TSO2 and needs to be also seen by the admin, the recipient configuration looks like:

"recipient":{"type":"UNION",
	"recipients":[
		{"type":"GROUP","identity":"TSO1"},
		{"type":"GROUP","identity":"TSO2"},
		{"type":"USER","identity":"admin"}
		]
	}

14. Cards Consultation Service

The User Interface depends on the Cards Consultation service to be notified of new cards and to consult existing cards, both current and archived.

14.1. Archived Cards

14.1.1. Key concepts

Every time a card is published, in addition to being delivered to the users and persisted as a "current" card in MongoDB, it is also immediately persisted in the archived cards.

Archived cards are similar in structure to current cards, but they are managed differently. Current cards are uniquely identified by their id (made up of the publisher and the process id). That is because if a new card is published with id as an existing card, it will replace it in the card collection. This way, the current card reflects the current state of a process instance. In the archived cards collection however, both cards will be kept, so that the archived cards show all the states that a given process instance went through.

14.1.2. Archives screen in the UI

The Archives screen in the UI allows the users to query these archives with different filters. The layout of this screen is very similar to the Feed screen: the results are displayed in a (paginated) card list, and the user can display the associated card details by clicking a card in the list.

The results of these queries are limited to cards that the user is allowed to see, either because they are direct recipient of the card or because they belong to a group that is a recipient of the card.

14.1.3. Archive endpoints in the card-consultation API

This Archives screen relies on dedicated endpoints in the card-consultation API, as described here.

Deployment and Administration of OperatorFabric

The aim of this document is to explain how to configure and deploy OperatorFabric.

15. Deployment

For now OperatorFabric consist of Docker images available either by compiling the project or by using images releases from Dockerhub

Service images are all based on openjdk:8-jdk-alpine.

For simple one instance per service deployment, you can find a sample deployment as a docker-compose file here

To run OperatorFabric in development mode, see the development environment documentation.

16. RabbitMQ

16.1. Docker container

In development mode, the simplest way to deploy a RabbitMQ server is to create a RabbitMQ docker container. A docker-compose file is provided to allow quick setup of a convenient RabbitMQ server.

16.2. Server installation

This section is dedicated to production deployment of RabbitMQ. It is not complete and needs to be tailored to any specific production environment.

16.2.1. Download & Installation

Download and install RabbitMQ following the official procedure for the target environment

16.2.2. Used ports

If RabbitMQ may not bind to the following ports, it won’t start :

  • 4369: epmd, a peer discovery service used by RabbitMQ nodes and CLI tools

  • 5672, 5671: used by AMQP 0-9-1 and 1.0 clients without and with TLS

  • 25672: used for inter-node and CLI tools communication (Erlang distribution server port) and is allocated from a dynamic range (limited to a single port by default, computed as AMQP port + 20000). Unless external connections on these ports are really necessary (e.g. the cluster uses federation or CLI tools are used on machines outside the subnet), these ports should not be publicly exposed. See networking guide for details.

  • 35672-35682: used by CLI tools (Erlang distribution client ports) for communication with nodes and is allocated from a dynamic range (computed as server distribution port + 10000 through server distribution port + 10010). See networking guide for details.

  • 15672: HTTP API clients, management UI and rabbitmqadmin (only if the management plugin is enabled)

  • 61613, 61614: STOMP clients without and with TLS (only if the STOMP plugin is enabled)

  • 1883, 8883: (MQTT clients without and with TLS, if the MQTT plugin is enabled)

  • 15674: STOMP-over-WebSockets clients (only if the Web STOMP plugin is enabled)

  • 15675: MQTT-over-WebSockets clients (only if the Web MQTT plugin is enabled)

16.2.3. Production configuration

See the guide for production configuration guidelines

17. Configuration

OperatorFabric has multiple services to configure. These configurations are managed by a Configuration Service, of which they can be several instances to ensure availability.

See OperatorFabric Architecture for more information on the different services.

All services are SpringBoot applications and use jetty as an embedded servlet container. As such, they share some common configuration which is described in the following documentation:

17.1. Global configuration

Aside from the one for the configuration service, all configurations are gathered as resources in the configuration service (under /services/infra/config/src/main/docker/volume).

Note that a few things cannot be set in the configuration served by the configuration service (because its needed right at the application startup for example) and must be set in each services bootstrap configuration file. In this regard, Cloud Configuration service[Configuration] and Cloud Registry service[Registry] services have specific configuration but for other services, they all require the same minimal information:

  • mandatory service name(spring.application.name)

  • mandatory configuration name (spring.cloud.config.name)

  • mandatory configuration fetch from registry enabled (spring.cloud.config.discovery.enabled: true)

  • mandatory configuration service name in registry (spring.cloud.config.discovery.service-id)

  • mandatory eureka registration (eureka.client.register-with-eureka: true and eureka.client.fetch-registry: true)

  • mandatory eureka registry urls (eureka.client.service-url.defaultZone)

  • configuration fail fast (spring.cloud.config.fail-fast)

  • configuration fetch maximum retry (spring.cloud.config.retry.max-attempts)

In most situation, you do not need to change the default bootstrap configuration for those services. Make a copy of the default bootstrap (inside the jar or from the sources) and set eureka up (see above).

The different services also share common configuration you can setup in the config service backend. those common configuration may be set up in the backend application.yml file.

spring:
  rabbitmq:
    host: rabbitmq
    port: 5672
    username: guest
    password: guest
  data:
    mongodb:
      uris:
        - mongodb://root:password@mongodb:27017/operator-fabric?authSource=admin&authMode=scram-sha1
  security:
    oauth2:
      resourceserver:
        jwt:
          jwk-set-uri: http://authserver/auth/realms/dev/protocol/openid-connect/certs
eureka:
  client:
    service-url:
      defaultZone: http://registry:8080/eureka
    region: default
  registryFetchIntervalSeconds: 5

operatorfabric:
  security:
    oauth2:
      client-id: opfab-client
      client-secret: opfab-oauth-secret
    jwt:
      login-claim: preferred_username
      expire-claim: exp

In the above example you can see that we need to configure:

17.2. OperatorFabric Mongo configuration

We only use URI configuration for mongo through the usage of the spring.data.mongodb.uris, it allows us to share the same configuration behavior for simple or cluster configuration and with both spring classic and reactive mongo configuration. See mongo connection string for the complete URI syntax.

17.2.1. Define time to live for archived cards

By default, archived cards will remain stored in the database forever. It is possible to have them automatically removed after a specified duration by using the TTL index feature of mongoDB on their publishDate field.

For example, to have cards expire after 10 days (864000s), enter the following commands in the mongo shell:

use operator-fabric
db.archivedCards.createIndex( { "publishDate": 1 }, { expireAfterSeconds: 864000 } )
You cannot use createIndex() to change the value of expireAfterSeconds of an existing index. Instead use the collMod database command in conjunction with the index collection flag. Otherwise, to change the value of the option of an existing index, you must drop the index first and recreate.

17.3. OperatorFabric Specific configuration

Below are description of OperatorFabric specific configuration properties.

Note that other components may have specific configuration, see the relevant sub-sections.

17.3.1. Security Configuration

Common configuration

This concern is configured within the ${OF_HOME}/services/infra/config project into 3 distinct files:

  • application.yml;

  • web-ui.yml;

  • client-gateway.yml.

They are located into the following folders within the ${OF_HOME}/services/infra/config/java/src/main/docker/volume folder:

  • dev-configurations: to be use into dev mode;

  • docker-configurations: used by docker at run time.

EXAMPLE: the configuration for dev before compilation is done in the following files: - ${OF_HOME}/services/infra/config/java/src/main/docker/volume/dev-configurations/application.yml; - ${OF_HOME}/services/infra/config/java/src/main/docker/volume/dev-configurations/client-gateway.ym; - ${OF_HOME}/services/infra/config/java/src/main/docker/volume/dev-configurations/web-ui.yml.

For test purposes in dev mode, it’s possible to edit files located into `${OF_HOME}/services/infra/config/build/docker-volume/dev-configurations. Be aware that those changes will be lost after a new compilation or even a simple re-run of the project.

This configuration files follow the springframework configuration convention. Within a same file it’s possible to use SpEL ${} convention. For example, ${AnotheProperty} allows to re-use the value of AnotherProperty already declared. It helps to avoid typos or missing to report some changes. An example could be find below into the delegate-url property.

application.yml
name default mandatory? Description

operatorfabric.security.oauth2.client-id

null

yes

Oauth2 client id used by OperatorFabric may be specific for each service

operatorfabric.security.oauth2.client-secret

null

yes

Oauth2 client secret used by OperatorFabric may be specific for each service

operatorfabric.security.jwt.login-claim

sub

no

Jwt claim is used as user login or id

operatorfabric.security.jwt.expire-claim

exp

no

Jwt claim is used as token expiration timestamp

spring.security.oauth2.resourceserver.jwt.provider-url

null

yes

The keycloak instance url

spring.security.oauth2.resourceserver.jwt.provider-realm

null

yes

The realm name within the keycloak instance

example of application.yml

operatorfabric:
    security:
        oauth2:
            client-id: opfab-client
            client-secret: opfab-keycloak-secret
        jwt:
            login-claim: preferred_username
            expire-claim: exp

where operatorfabric.security.jwt.expire-claim could have been omitted because having the same value as the default one.

client-gateway.yml

In a configuration service client-gateway.yml file

  • The application must set up a route to the oauth server

    • /auth/token(?<path>.*): must match the oauth2 token entry point;

    • /auth/code/(?<params>.*): must match the auth entry point with specific query parameters like response_type=code&client_id=[client id]&${params};

    • /auth/check_token: must match token introspection entry point

  • The application must add request header for each request:

    • AddRequestHeader: Authorization, Basic: followed, separated with a space, by the OAuth2 client-id and the OAuth2 client-secret encoded in base64.

  • The application may set up CORS rules if api are to be accessed from browser outside of the deployment domain

Configuration example of the filters, for the docker dev keycloak:

spring.cloud.gateway.routes[0].filters:
        - RewritePath=/auth/token(?<path>.*), /auth/realms/dev/protocol/openid-connect/token$\{path}
        - RewritePath=/auth/code/(?<params>.*), /auth/realms/dev/protocol/openid-connect/auth?response_type=code&client_id=opfab-client&$\{params}
        - RewritePath=/auth/check_token, /auth/realms/dev/protocol/openid-connect/token/introspect
        - AddRequestHeader=Authorization, Basic b3BmYWItY2xpZW50Om9wZmFiLWtleWNsb2FrLXNlY3JldA==

where: - spring.cloud.gateway.routes.uri: is your keycloak instance - spring.cloud.gateway.routes[0] is the routes with id equals to auth; - /realms/dev: is the keycloak realm where opfab-client is defined. - b3BmYWItY2xpZW50Om9wZmFiLWtleWNsb2FrLXNlY3JldA==: is the base64 encoded string of opfab-client:opfab-keycloak-secret with opfab-client as client-id and opfab-keycloak-secret its client secret.

web-ui.yml

For OAuth2 security concerns into this file, there are two way to configure it, based on the Oauth2 chosen flow. There are two common properties:

  • operatorfabric.security.oauth2.flow.provider which corresponds to the OAuth2 provider.

  • operatorfabric.security.realm-url: which is the OAuth2 realm provider under which the OpertaroFabric client is declared.

  • operatorfabric.security.provider-url: which is the The keycloak server instance.

OAuth2 Flows and specific configuration

There are 3 OAuth2 Authentication flows available into OperatorFabric UI:

  • password grant: referred as PASSWORD mode flow;

  • code flow : referred as CODE mode flow;

  • implicit flow: referred as IMPLICIT mode flow.

OAuth2 IMPLICIT Flow

It had its own way of configuration. To enable IMPLICIT Flow authentication the following properties need to be set:

  • operatorfabric.security.oauth2.flow.mode to IMPLICIT;

  • operatorfabric.security.oauth2.flow.delegate-url with the URL of the OAuth2 leading to the .well-known/openid-configuration end-point used for authentication configuration.

Example of Keycloak configuration for OperatorFabric OAuth2
operatorfabric:
  keycloak:
    realm: dev
  security:
    oauth2:
      flow:
        mode: IMPLICIT
        provider: Opfab Keycloak
        delagate-url: http://localhost:89/auth/realms/${operatorfabric.keycloak.realm}

Within the delegate-url property the ${operatorfabric.keycloak.realm} refers to the value of the operatorfabric.keycloak.realm declared earlier, dev here. For keycloak instance used for dev purposes, this delegate-url correspond to the realm under which the client opfab-client is registred. The url look up by the implicit ui mechanism is localhost:89/auth/realms/dev/.well-known/openid-configuration.

Keycloak instance additional configuration for dev purposes

Once connected as Admin (login:_admin_,password:_admin_ ) on keycloak (localhost:89/auth/admin) The first thing is to enable the implicit flow for opfa-client. To do so select Dev in the upper select of the right panel then click on Clients. In the table, click on opfab-client in the column Client ID. Then in the Settings tabs turn on the Implicit Flow Enabled. In the same tab, modify localhost:2002/ui/ to localhost:2002/ and localhost:4200 to localhost:4200/.

OAuth2 PASSWORD or CODE Flows

These two modes share the same way of declaring the delegate URL. The default configuration for dev is set to CODE and work straight away.

  • operatorfabric.security.oauth2.flow.mode to PASSWORD or CODE;

  • operatorfabric.security.oauth2.flow.delegate-url with the URL of the OAuth2 leading to the protocol used for authentication.

Example of Keycloak Configuration For OperatorFabric OAuth2
operatorfabric:
  keycloak:
    realm: dev
  security:
    oauth2:
      flow:
        mode: CODE
        provider: Opfab Keycloak
        delagate-url: http://localhost:89/auth/realms/${operatorfabric.keycloak.realm}/protocol/openid-connect/auth?response_type=code&client_id=opfab-client

Within the delegate-url property the ${operatorfabric.keycloak.realm} refers to the value(also dev here) of the operatorfabric.keycloak.realm declared earlier. Here, the client-id value is opfab-client which is define as client under the realm named dev on the dev keycloak instance.

Using token
Get a token

Method: POST

Body arguments:

  • client_id: string constant=clientIdPassword;

  • grant_type: string constant=password;

  • username: string any value, must match an OperatorFabric registered user name;

  • password: string any value;

The following examples will be for admin user.

Curl

command:

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

example of expected result:

{"access_token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiIsImV4cC
I6MTU1MjY1OTczOCwiYXV0aG9yaXRpZXMiOlsiUk9MRV9BRE1JTiIsIlJPTEVfVVNFUiJdLCJqdGkiOi
IwMmQ4MmU4NS0xM2YwLTQ2NzgtOTc0ZC0xOGViMDYyMTVhNjUiLCJjbGllbnRfaWQiOiJjbGllbnRJZF
Bhc3N3b3JkIiwic2NvcGUiOlsicmVhZCIsInVzZXJfaW5mbyJdfQ.SDg-BEzzonIVXfVBnnfq0oMbs_0
rWVtFGAZzRHj7KPgaOXT3bUhQwPOgggZDO0lv2U1klwB94c8Cb6rErzd3yjJ8wcVcnFLO4KxrjYZZxdK
VAz0CkMKqng4kQeQm_1UShsQXGLl48ezbjXyJn6mAl0oS4ExeiVsx_kYGEdqjyb5CiNaAzyx0J-J5jVD
SJew1rj5EiSybuy83PZwhluhxq0D2zPK1OSzqiezsd5kX5V8XI4MipDhaAbPYroL94banZTn9RmmAKZC
AYVM-mmHbjk8mF89fL9rKf9EUNhxOG6GE0MDqB3LLLcyQ6sYUmpqdP5Z94IkAN-FpC7k93_-RDw","to
ken_type":"bearer","refresh_token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWI
iOiJhZG1pbiIsInNjb3BlIjpbInJlYWQiLCJ1c2VyX2luZm8iXSwiYXRpIjoiMDJkODJlODUtMTNmMC0
0Njc4LTk3NGQtMThlYjA2MjE1YTY1IiwiZXhwIjoxNTUyNzAxMTM4LCJhdXRob3JpdGllcyI6WyJST0x
FX0FETUlOIiwiUk9MRV9VU0VSIl0sImp0aSI6IjMwOWY2ZDllLWNmOGEtNDg0YS05ZjMxLWViOTAxYzk
4YTFkYSIsImNsaWVudF9pZCI6ImNsaWVudElkUGFzc3dvcmQifQ.jnZDt6TX2BvlmdT5JV-A7eHTJz_s
lC5fHrJFVI58ly6N7AUUfxebG_52pmuVHYULSKqTJXaLR866r-EnD4BJlzhk476FtgtVx1nazTpLFRLb
8qDCxeLrzClQBkzcxOt6VPxB3CD9QImx3bcsDwjkPxofUDmdg8AxZfGTu0PNbvO8TKLXEkeCztLFvSJM
GlN9zDzWhKxr49I-zPZg0XecgE9j4WITkFoDVwI-AfDJ3sGXDi5AN55Sz1j633QoqVjhtc0lO50WPVk5
YT7gU8HLj27EfX-6vjnGfNb8oeq189-NX100QHZM9Wgm79mIm4sRgwhpv-zzdDAkeb3uwIpb8g","exp
ires_in":1799,"scope":"read
user_info","jti":"02d82e85-13f0-4678-974d-18eb06215a65"}
Httpie
http --form POST http://localhost:2002/auth/token username=admin password=test
grant_type=password client_id=clientIdPassword

example of expected result:

.HTTP/1.1 200 OK
Cache-Control: no-store
Content-Type: application/json;charset=utf-8
Date: Fri, 15 Mar 2019 13:57:19 GMT
Pragma: no-cache
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
transfer-encoding: chunked

{
    "access_token":
"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiIsImV4cCI6MTU1MjY2MDAzOS
wiYXV0aG9yaXRpZXMiOlsiUk9MRV9BRE1JTiIsIlJPTEVfVVNFUiJdLCJqdGkiOiI2MjQzMDliMS03Yz
g3LTRjZGMtODQ0My0wMTI0NTE1Zjg3ZjgiLCJjbGllbnRfaWQiOiJjbGllbnRJZFBhc3N3b3JkIiwic2
NvcGUiOlsicmVhZCIsInVzZXJfaW5mbyJdfQ.VO4OZL7ycqNez0cHzM5WPuklr0r6SAOkUdUV2qFa5Bd
3PWx3DFHAHUxkfSX0-R4OO6iG2Zu7abzToAZNVLwk107LH_lWXOMQBriGx3d2aSgCf1yx_wI3lHDd8ST
8fxV7uNeolzywYavSpMGfgz9GXLzmnyeuPH4oy7eyPk9BwWVi0d7a_0d-EfhE1T8eaiDfymzzNXJ4Bge
8scPy-93HmWpqORtJaFq1qy4QgU28N2LgHFEEEWCSzfhYXH-LngTCP3-JSNcox1hI51XBWEqoeApKdfD
J6o4szR71SIFCBERxCH9TyUxsFywWL3e-YnXMiP2J08eB8O4YwhYQEFqB8Q",
    "expires_in": 1799,
    "jti": "624309b1-7c87-4cdc-8443-0124515f87f8",
    "refresh_token":
"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiIsInNjb3BlIjpbInJlYWQiLC
J1c2VyX2luZm8iXSwiYXRpIjoiNjI0MzA5YjEtN2M4Ny00Y2RjLTg0NDMtMDEyNDUxNWY4N2Y4IiwiZX
hwIjoxNTUyNzAxNDM5LCJhdXRob3JpdGllcyI6WyJST0xFX0FETUlOIiwiUk9MRV9VU0VSIl0sImp0aS
I6ImRiYzMxNTJiLTM4YTUtNGFmZC1hY2VmLWVkZTI4MjJkOTE3YyIsImNsaWVudF9pZCI6ImNsaWVudE
lkUGFzc3dvcmQifQ.Ezd8kbfNQHOOvUCNNN4UmOOkncHiT9QVEM63FiW1rq0uXDa3xfBGil8geM5MsP0
7Q2He-mynkFb8sGNDrAXTdO-8r5o4a60zWrktrMg2QH4icC1lyeZpiwZxe6675QpLpSeMlXt9PdYj-pb
14lrRookxXP5xMQuIMteZpbtby7LuuNAbNrjveZ1bZ4WMi7zltUzcYUuqHlP1AYPteGRrJVKXiuPpoDv
gwMsEk2SkgyyACI7SdZZs8IT9IGgSsIjjgTMQKzj8P6yYxNLUynEW4o5y1s2aAOV0xKrzkln9PchH9zN
qO-fkjTVRjy_LBXGq9zkn0ZeQ3BUe1GuthvGjaA",
    "scope": "read user_info",
    "token_type": "bearer"
}
Extract token

From the previous results, the data need to be considered to be authenticated by OperatorFabric services is the content of the "access_token" attribute of the body response.

Once this value extracted, it need to be passed at the end of the value of the http HEADER of type Authorization:Bearer. example from previous results:

Curl
Authorization:Bearer
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiIsImV4cCI6MTU1MjY1OTczOCw
iYXV0aG9yaXRpZXMiOlsiUk9MRV9BRE1JTiIsIlJPTEVfVVNFUiJdLCJqdGkiOiIwMmQ4MmU4NS0xM2Y
wLTQ2NzgtOTc0ZC0xOGViMDYyMTVhNjUiLCJjbGllbnRfaWQiOiJjbGllbnRJZFBhc3N3b3JkIiwic2N
vcGUiOlsicmVhZCIsInVzZXJfaW5mbyJdfQ.SDg-BEzzonIVXfVBnnfq0oMbs_0rWVtFGAZzRHj7KPga
OXT3bUhQwPOgggZDO0lv2U1klwB94c8Cb6rErzd3yjJ8wcVcnFLO4KxrjYZZxdKVAz0CkMKqng4kQeQm
_1UShsQXGLl48ezbjXyJn6mAl0oS4ExeiVsx_kYGEdqjyb5CiNaAzyx0J-J5jVDSJew1rj5EiSybuy83
PZwhluhxq0D2zPK1OSzqiezsd5kX5V8XI4MipDhaAbPYroL94banZTn9RmmAKZCAYVM-mmHbjk8mF89f
L9rKf9EUNhxOG6GE0MDqB3LLLcyQ6sYUmpqdP5Z94IkAN-FpC7k93_-RDw
Httpie
Authorization:Bearer
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiIsImV4cCI6MTU1MjY2MDAzOSw
iYXV0aG9yaXRpZXMiOlsiUk9MRV9BRE1JTiIsIlJPTEVfVVNFUiJdLCJqdGkiOiI2MjQzMDliMS03Yzg
3LTRjZGMtODQ0My0wMTI0NTE1Zjg3ZjgiLCJjbGllbnRfaWQiOiJjbGllbnRJZFBhc3N3b3JkIiwic2N
vcGUiOlsicmVhZCIsInVzZXJfaW5mbyJdfQ.VO4OZL7ycqNez0cHzM5WPuklr0r6SAOkUdUV2qFa5Bd3
PWx3DFHAHUxkfSX0-R4OO6iG2Zu7abzToAZNVLwk107LH_lWXOMQBriGx3d2aSgCf1yx_wI3lHDd8ST8
fxV7uNeolzywYavSpMGfgz9GXLzmnyeuPH4oy7eyPk9BwWVi0d7a_0d-EfhE1T8eaiDfymzzNXJ4Bge8
scPy-93HmWpqORtJaFq1qy4QgU28N2LgHFEEEWCSzfhYXH-LngTCP3-JSNcox1hI51XBWEqoeApKdfDJ
6o4szR71SIFCBERxCH9TyUxsFywWL3e-YnXMiP2J08eB8O4YwhYQEFqB8Q
Check a token
Curl

from previous example

curl -s -X POST -d
"token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiIsImV4cCI6MTU1MjY1
OTczOCwiYXV0aG9yaXRpZXMiOlsiUk9MRV9BRE1JTiIsIlJPTEVfVVNFUiJdLCJqdGkiOiIwMmQ4MmU4
NS0xM2YwLTQ2NzgtOTc0ZC0xOGViMDYyMTVhNjUiLCJjbGllbnRfaWQiOiJjbGllbnRJZFBhc3N3b3Jk
Iiwic2NvcGUiOlsicmVhZCIsInVzZXJfaW5mbyJdfQ.SDg-BEzzonIVXfVBnnfq0oMbs_0rWVtFGAZzR
Hj7KPgaOXT3bUhQwPOgggZDO0lv2U1klwB94c8Cb6rErzd3yjJ8wcVcnFLO4KxrjYZZxdKVAz0CkMKqn
g4kQeQm_1UShsQXGLl48ezbjXyJn6mAl0oS4ExeiVsx_kYGEdqjyb5CiNaAzyx0J-J5jVDSJew1rj5Ei
Sybuy83PZwhluhxq0D2zPK1OSzqiezsd5kX5V8XI4MipDhaAbPYroL94banZTn9RmmAKZCAYVM-mmHbj
k8mF89fL9rKf9EUNhxOG6GE0MDqB3LLLcyQ6sYUmpqdP5Z94IkAN-FpC7k93_-RDw"
http://localhost:2002/auth/check_token

which gives the following example of result:

{
    "sub":"admin",
    "scope":["read","user_info"],
    "active":true,"exp":1552659738,
    "authorities":["ROLE_ADMIN","ROLE_USER"],
    "jti":"02d82e85-13f0-4678-974d-18eb06215a65",
    "client_id":"clientIdPassword"
}
Httpie

from previous example:

http --form POST http://localhost:2002/auth/check_token
token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiIsImV4cCI6MTU1MjY2M
DAzOSwiYXV0aG9yaXRpZXMiOlsiUk9MRV9BRE1JTiIsIlJPTEVfVVNFUiJdLCJqdGkiOiI2MjQzMDliM
S03Yzg3LTRjZGMtODQ0My0wMTI0NTE1Zjg3ZjgiLCJjbGllbnRfaWQiOiJjbGllbnRJZFBhc3N3b3JkI
iwic2NvcGUiOlsicmVhZCIsInVzZXJfaW5mbyJdfQ.VO4OZL7ycqNez0cHzM5WPuklr0r6SAOkUdUV2q
Fa5Bd3PWx3DFHAHUxkfSX0-R4OO6iG2Zu7abzToAZNVLwk107LH_lWXOMQBriGx3d2aSgCf1yx_wI3lH
Dd8ST8fxV7uNeolzywYavSpMGfgz9GXLzmnyeuPH4oy7eyPk9BwWVi0d7a_0d-EfhE1T8eaiDfymzzNX
J4Bge8scPy-93HmWpqORtJaFq1qy4QgU28N2LgHFEEEWCSzfhYXH-LngTCP3-JSNcox1hI51XBWEqoeA
pKdfDJ6o4szR71SIFCBERxCH9TyUxsFywWL3e-YnXMiP2J08eB8O4YwhYQEFqB8Q

which gives the following example of result:

HTTP/1.1 200 OK
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Content-Type: application/json;charset=utf-8
Date: Fri, 15 Mar 2019 14:19:31 GMT
Expires: 0
Pragma: no-cache
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
transfer-encoding: chunked

{
    "active": true,
    "authorities": [
        "ROLE_ADMIN",
        "ROLE_USER"
    ],
    "client_id": "clientIdPassword",
    "exp": 1552660039,
    "jti": "624309b1-7c87-4cdc-8443-0124515f87f8",
    "scope": [
        "read",
        "user_info"
    ],
    "sub": "admin"
}

17.3.2. User creation

Setting automated user creation==. Creation user requires an user id. Given name and family name are optional.

name default mandatory? Description

operatorfabric.security.jwt.login-claim

sub

no

Jwt claim is used as an user login or id

operatorfabric.security.jwt.given-name-claim

given-name

no

Jwt claim is used to set the user’s given name

operatorfabric.security.jwt.family-name-claim

family-name

no

Jwt claim is used to set the user’s family name

17.3.3. Alternative way to manage groups

By default, Operator-Fabric manage groups through the user’s group. Another mode can be defined, the JWT mode. The groups come from the authentication token. The administrator of the authentication service has to set what claims define a group. In the Operator-Fabric configuration, the opfab administrator has to set properties to retrieve thoses groups.

name default mandatory? Description

operatorfabric.security.jwt.groups.mode

OPERATOR_FABRIC

no

Set the group mode, possible values JWT or OPERATOR_FABRIC

operatorfabric.security.jwt.groups.rolesClaim.rolesClaimStandard-claim.path

no

path in the JWT to retrieve the claim that defines a group

operatorfabric.security.jwt.groups.rolesClaim.rolesClaimStandardArray.path

no

path in the JWT to retrieve the claim that defines an array of groups

operatorfabric.security.jwt.groups.rolesClaim.rolesClaimStandardList.path

no

path in the JWT to retrieve the claim that defines a list of group

operatorfabric.security.jwt.groups.rolesClaim.rolesClaimStandardList.separator

no

set the separator value of the list of group

operatorfabric.security.jwt.groups.rolesClaim.rolesClaimCheckExistPath.path

no

path in the JWT to check if that path does exist, if it does, use the roleValue as a group

operatorfabric.security.jwt.groups.rolesClaim.rolesClaimCheckExistPath.roleValue

no

set the value of the group if the path exists

application.yml

operatorfabric:
  security:
    jwt:
      groups:
        mode: JWT # value possible JWT | OPERATOR_FABRIC
        rolesClaim:
          rolesClaimStandard:
            - path: "ATTR1"
            - path: "ATTR2"
          rolesClaimStandardArray:
            - path: "resource_access/opfab-client/roles"
          rolesClaimStandardList:
            - path: "roleFieldList"
              separator: ";"
          rolesClaimCheckExistPath:
            - path: "resource_access/AAA"
              roleValue: "roleAAA"
            - path: "resource_access/BBB"
              roleValue: "roleBBB"

JWT example

{
  "jti": "5ff87583-10bd-4946-8753-9d58171c8b7f",
  "exp": 1572979628,
  "nbf": 0,
  "iat": 1572961628,
  "iss": "http://localhost:89/auth/realms/dev",
  "aud": [
    "AAA",
    "BBB",
    "account"
  ],
  "sub": "example_user",
  "typ": "Bearer",
  "azp": "opfab-client",
  "auth_time": 0,
  "session_state": "960cbec4-fcb2-47f2-a155-975832e61300",
  "acr": "1",
  "realm_access": {
    "roles": [
      "offline_access",
      "uma_authorization"
    ]
  },
  "resource_access": {
    "AAA": {
      "roles": [
        "role_AAA"
      ]
    },
    "BBB": {
      "roles": [
        "role_BBB"
      ]
    },
    "opfab-client": {
      "roles": [
        "USER"
      ]
    },
    "account": {
      "roles": [
        "manage-account",
        "manage-account-links",
        "view-profile"
      ]
    }
  },
  "scope": "openid ATTR2 email ATTR1 profile roleFieldList",
  "email_verified": false,
  "name": "example_firtstname example_lastname",
  "ATTR2": "roleATTR2",
  "ATTR1": "roleATTR1",
  "preferred_username": "example_user",
  "given_name": "example_firtstname",
  "family_name": "example_lastname",
  "email": "example_user@mail.com",
  "roleFieldList": "roleA;roleB;roleC"
}

As the result, the group will be [ATTR1, ATTR2, roleA, roleB, roleC, USER, roleBBB, roleAAA]

17.4. Service-specific configuration

17.4.1. Actions Business service

OperatorFabric Actions Business service is a Spring Webflux application bootstrapped using SpringBoot.

In our spring micro service architecture this service depends on Eureka Registry.

Mandatory configuration, Profiles and default properties

The service has no mandatory configuration beside global configuration and usual bootstrap configuration.

For other configuration see:

Service specific properties
default properties

Note that you must provide a bootstrap file with a convenient registry configuration

bootstrap.yml

spring:
  application:
    name: actions
  cloud:
    config:
      name: actions
      fail-fast: true
      retry:
        max-attempts: 20
      discovery:
        service-id: config
        enabled: true

bootstrap-docker.yml

spring:
  application:
    name: actions
  cloud:
    config:
      name: actions
      failFast: true
      retry:
        maxAttempts: 20
      discovery:
        service-id: config
        enabled: true
#  level.root: debug
eureka:
  client:
    registerWithEureka: true
    fetchRegistry: true
    registryFetchIntervalSeconds: 5
    service-url:
      defaultZone: 'http://${REGISTRY_HOST}:${REGISTRY_PORT}/eureka/'

bootstrap-dev.yml

spring:
  application:
    name: actions
  cloud:
    config:
      name: actions
      fail-fast: true
      retry:
        max-attempts: 20
      discovery:
        service-id: config
        enabled: true
logging.level.root: debug
eureka:
  client:
    service-url:
      defaultZone: 'http://localhost:2001/eureka'

The bootstrap-docker.yml file is a replacement bootstrap file we use for our docker images configuration.

The bootstrap-dev.yml file is a replacement bootstrap file we use for our development environment

The above embedded configurations are the basic settings of the application:

  • it sets its service name

  • it sets the configuration name to use (which configuration file to retrieve)

  • it must set the registry service (example in bootstrap-docker.yml)

Sample development configuration
server:
  port: 2105
Sample docker image configuration
Specifying external configuration properties when launching a jar file

See Application Property Files on how to setup an external spring properties or yml file.

See Set the Active Spring Profiles for specifying alternate profile.

Specifying configuration properties when launching a docker image

Our docker image expects optional property file to be stored in the container /service-config folder. You can bind so docker volume to this path to make properties or yml available.

At time of writing, you cannot specify an alternate profile at runActions. The default profiles activated are docker and native.

Available environment variables for docker image
  • REGISTRY_HOST: Registry service host

  • REGISTRY_PORT: Registry service port

  • JAVA_OPTIONS: Additional java options

Specifying configuration properties when launching on Kubernetes

In progress

Available environment variables when launching on Kubernetes
  • REGISTRY_HOST: Registry service host

  • REGISTRY_PORT: Registry service port

  • JAVA_OPTIONS: Additional java options

17.4.2. Cards-Consultation Business service

OperatorFabric Cards-Consultation Business service is a Spring Webflux application bootstrapped using SpringBoot.

In our spring micro service architecture this service depends on Eureka Registry.

Mandatory configuration, Profiles and default properties

The service has no mandatory configuration beside global configuration and usual bootstrap configuration.

For other configuration see:

Service specific properties
default properties

Note that you must provide a bootstrap file with a convenient registry configuration

bootstrap.yml

spring:
  application:
    name: cards-consultation
  cloud:
    config:
      name: cards-consultation
      fail-fast: true
      retry:
        max-attempts: 20
      discovery:
        service-id: config
        enabled: true

bootstrap-docker.yml

spring:
  application:
    name: cards-consultation
  cloud:
    config:
      name: cards-consultation
      failFast: true
      retry:
        maxAttempts: 20
      discovery:
        service-id: config
        enabled: true
#  level.root: debug
eureka:
  client:
    registerWithEureka: true
    fetchRegistry: true
    registryFetchIntervalSeconds: 5
    service-url:
      defaultZone: 'http://${REGISTRY_HOST}:${REGISTRY_PORT}/eureka/'

bootstrap-dev.yml

spring:
  application:
    name: cards-consultation
  cloud:
    config:
      name: cards-consultation
      fail-fast: true
      retry:
        max-attempts: 20
      discovery:
        service-id: config
        enabled: true
#  level.root: debug
eureka:
  client:
    service-url:
      defaultZone: 'http://localhost:2001/eureka'

The bootstrap-docker.yml file is a replacement bootstrap file we use for our docker images configuration.

The bootstrap-dev.yml file is a replacement bootstrap file we use for our development environment

The above embedded configurations are the basic settings of the application:

  • it sets its service name

  • it sets the configuration name to use (which configuration file to retrieve)

  • it must set the registry service (example in bootstrap-docker.yml)

Sample development configuration
server:
  port: 2104
logging.level.root: DEBUG
Sample docker image configuration
Specifying external configuration properties when launching a jar file

See Application Property Files on how to setup an external spring properties or yml file.

See Set the Active Spring Profiles for specifying alternate profile.

Specifying configuration properties when launching a docker image

Our docker image expects optional property file to be stored in the container /service-config folder. You can bind so docker volume to this path to make properties or yml available.

At time of writing, you cannot specify an alternate profile at runCards-Consultation. The default profiles activated are docker and native.

Available environment variables for docker image
  • REGISTRY_HOST: Registry service host

  • REGISTRY_PORT: Registry service port

  • JAVA_OPTIONS: Additional java options

Specifying configuration properties when launching on Kubernetes

In progress

Available environment variables when launching on Kubernetes
  • REGISTRY_HOST: Registry service host

  • REGISTRY_PORT: Registry service port

  • JAVA_OPTIONS: Additional java options

17.4.3. Cards-Publication Business service

OperatorFabric Cards-Publication Business service is a Spring Webflux application bootstrapped using SpringBoot.

In our spring micro service architecture this service depends on Eureka Registry.

Mandatory configuration, Profiles and default properties

The service has no mandatory configuration beside global configuration and usual bootstrap configuration.

For other configuration see:

Service specific properties
name default mandatory? Description

operatorfabric.card-write.window.size

1000

no

card input window size to fill before flush

operatorfabric.card-write.window.timeout

500

no

card input window millis wait time before flush

operatorfabric.card-notification.window.size

100

no

card notification window size to fill before flush

operatorfabric.card-notification.window.timeout

1000

no

card notification window millis wait time before flush

default properties

Note that you must provide a bootstrap file with a convenient registry configuration

bootstrap.yml

spring:
  application:
    name: cards-publication
  cloud:
    config:
      name: cards-publication
      fail-fast: true
      retry:
        max-attempts: 20
      discovery:
        service-id: config
        enabled: true

bootstrap-docker.yml

spring:
  application:
    name: cards-publication
  cloud:
    config:
      name: cards-publication
      failFast: true
      retry:
        maxAttempts: 20
      discovery:
        service-id: config
        enabled: true
#  level.root: debug
eureka:
  client:
    registerWithEureka: true
    fetchRegistry: true
    registryFetchIntervalSeconds: 5
    service-url:
      defaultZone: 'http://${REGISTRY_HOST}:${REGISTRY_PORT}/eureka/'

bootstrap-dev.yml

spring:
  application:
    name: cards-publication
  cloud:
    config:
      name: cards-publication
      fail-fast: true
      retry:
        max-attempts: 20
      discovery:
        service-id: config
        enabled: true
eureka:
  client:
    service-url:
      defaultZone: 'http://localhost:2001/eureka'

The bootstrap-docker.yml file is a replacement bootstrap file we use for our docker images configuration.

The bootstrap-dev.yml file is a replacement bootstrap file we use for our development environment

The above embedded configurations are the basic settings of the application:

  • it sets its service name

  • it sets the configuration name to use (which configuration file to retrieve)

  • it must set the registry service (example in bootstrap-docker.yml)

Sample development configuration
server:
  port: 2102
Sample docker image configuration
Specifying external configuration properties when launching a jar file

See Application Property Files on how to setup an external spring properties or yml file.

See Set the Active Spring Profiles for specifying alternate profile.

Specifying configuration properties when launching a docker image

Our docker image expects optional property file to be stored in the container /service-config folder. You can bind so docker volume to this path to make properties or yml available.

At time of writing, you cannot specify an alternate profile at runCards-Publication. The default profiles activated are docker and native.

Available environment variables for docker image
  • REGISTRY_HOST: Registry service host

  • REGISTRY_PORT: Registry service port

  • JAVA_OPTIONS: Additional java options

Specifying configuration properties when launching on Kubernetes

In progress

Available environment variables when launching on Kubernetes
  • REGISTRY_HOST: Registry service host

  • REGISTRY_PORT: Registry service port

  • JAVA_OPTIONS: Additional java options

17.4.4. Cloud Gateway service

OperatorFabric Client Gateway service is a Spring Cloud Gateway application bootstrapped using SpringBoot.

In our spring micro service architecture the gateway service depends on Eureka Registry.

Mandatory configuration, Profiles and default properties

To get a working gateway service, there a few mandatory configuration properties:

In a configuration service client-gateway.yml file

  • The application must set up a route to the oauth server

    • /auth/token(?<path>.*): must match the oauth2 token entry point;

    • /auth/code/(?<params>.*): must match the auth entry point with specific query parameters like response_type=code&client_id=[client id]&${params};

    • /auth/check_token: must match token introspection entry point

  • The application must add request header for each request:

    • AddRequestHeader: Authorization, Basic: followed, separated with a space, by the OAuth2 client-id and the OAuth2 client-secret encoded in base64.

  • The application may set up CORS rules if api are to be accessed from browser outside of the deployment domain

Configuration example of the filters, for the docker dev keycloak:

spring.cloud.gateway.routes[0].filters:
        - RewritePath=/auth/token(?<path>.*), /auth/realms/dev/protocol/openid-connect/token$\{path}
        - RewritePath=/auth/code/(?<params>.*), /auth/realms/dev/protocol/openid-connect/auth?response_type=code&client_id=opfab-client&$\{params}
        - RewritePath=/auth/check_token, /auth/realms/dev/protocol/openid-connect/token/introspect
        - AddRequestHeader=Authorization, Basic b3BmYWItY2xpZW50Om9wZmFiLWtleWNsb2FrLXNlY3JldA==

where: - spring.cloud.gateway.routes.uri: is your keycloak instance - spring.cloud.gateway.routes[0] is the routes with id equals to auth; - /realms/dev: is the keycloak realm where opfab-client is defined. - b3BmYWItY2xpZW50Om9wZmFiLWtleWNsb2FrLXNlY3JldA==: is the base64 encoded string of opfab-client:opfab-keycloak-secret with opfab-client as client-id and opfab-keycloak-secret its client secret.

For other configuration see spring cloud gateway documentation.

Service specific properties
name default mandatory? Description

operatorfabric.gateway.configs

null

no

an array of string for each allowed config entries

default properties

Note that you must provide a bootstrap file with a convenient registry configuration

bootstrap.yml

spring:
  application:
    name: client-gateway
  cloud:
    config:
      name: client-gateway
      fail-fast: true
      retry:
        max-attempts: 20
      discovery:
        service-id: config
        enabled: true

bootstrap-docker.yml

spring:
  application:
    name: client-gateway
  cloud:
    config:
      name: client-gateway
      failFast: true
      retry:
        maxAttempts: 20
      discovery:
        service-id: config
        enabled: true
eureka:
  client:
    registerWithEureka: true
    fetchRegistry: true
    registryFetchIntervalSeconds: 5
    service-url:
      defaultZone: 'http://${REGISTRY_HOST}:${REGISTRY_PORT}/eureka/'

bootstrap-dev.yml

spring:
  application:
    name: client-gateway
  cloud:
    config:
      name: client-gateway
      failFast: true
      retry:
        max-attempts: 20
      discovery:
        service-id: config
        enabled: true
eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: 'http://localhost:2001/eureka/'

The bootstrap-docker.yml file is a replacement bootstrap file we use for our docker images configuration.

The bootstrap-dev.yml file is a replacement bootstrap file we use for our development environment

The above embedded configurations are the basic settings of the application:

  • it sets its service name

  • it sets the configuration name to use (which configuration file to retrieve)

  • it must set the registry service (example in bootstrap-docker.yml)

Sample developpement configuration
server:
  port: 2002
spring:
  cloud:
    gateway:
      routes:
      - id: auth
        uri: http://localhost:89
        predicates:
        - Path=/auth/**
        filters:
        - RewritePath=/auth/token(?<path>.*), /auth/realms/dev/protocol/openid-connect/token$\{path}
        - RewritePath=/auth/code/(?<params>.*), /auth/realms/dev/protocol/openid-connect/auth?response_type=code&client_id=opfab-client&$\{params}
        - RewritePath=/auth/check_token, /auth/realms/dev/protocol/openid-connect/token/introspect
        - AddRequestHeader=Authorization, Basic b3BmYWItY2xpZW50Om9wZmFiLWtleWNsb2FrLXNlY3JldA==
# WARNING : THIS CORS CONFIGURATION SHOULD NOT BE USED IN PRODUCTION
      globalcors:
        corsConfigurations:
          '[/**]':
            allowedOrigins: "*"
            allowedMethods: "*"
            allowedHeaders: "Authorization, Content-Type"
operatorfabric.gateway.configs:
  - web-ui.json
#logging.level.root: debug
Sample docker image configuration
spring:
  cloud:
    gateway:
      routes:
      - id: auth
        uri: http://keycloak:8080
        predicates:
        - Path=/auth/**
        filters:
        - RewritePath=/auth/token(?<path>.*), /auth/realms/dev/protocol/openid-connect/token$\{path}
        - RewritePath=/auth/code/(?<params>.*), /auth/realms/dev/protocol/openid-connect/auth?response_type=code&client_id=opfab-client&$\{params}
        - RewritePath=/auth/check_token, /auth/realms/dev/protocol/openid-connect/token/introspect
        - AddRequestHeader=Authorization, Basic b3BmYWItY2xpZW50Om9wZmFiLWtleWNsb2FrLXNlY3JldA==
# WARNING : THIS CORS CONFIGURATION SHOULD NOT BE USED IN PRODUCTION
        globalcors:
         corsConfigurations:
          '[/**]':
            allowedOrigins: "*"
            allowedMethods: "*"
            allowedHeaders: "Authorization, Content-Type"
operatorfabric.gateway.configs:
  - web-ui.json
Specifying external configuration properties when lauching a jar file

See Application Property Files on how to setup an external spring properties or yml file.

See Set the Active Spring Profiles for specifying alternate profile.

Specifying configuration properties when lauching a docker image

Our docker image expects optional property file to be stored in the container /service-config folder. You can bind so docker volume to this path to make properties or yml available.

At time of writting, you cannot specify an alternate profile at runtime. The default profiles activated are docker and native.

Available environment variables for docker image
  • REGISTRY_HOST: Registry service host

  • REGISTRY_PORT: Registry service port

  • JAVA_OPTIONS: Additional java options

Specifying configuration properties when lauching on Kubernetes

In progress

Available environment variables when launching on Kubernetes
  • REGISTRY_HOST: Registry service host

  • REGISTRY_PORT: Registry service port

  • JAVA_OPTIONS: Additional java options

17.4.5. Cloud Configuration service

OperatorFabric Configuration service is a Spring Cloud Config application bootstrapped using SpringBoot.

In our spring micro service architecture Configuration instances are the first services to bootstrap, thus they are configured in a specific way contrary to other services.

Mandatory configuration, Profiles and default properties

To get a working configuration service, there a few mandatory configuration properties:

  • in a bootstrap file:

    • The application must have a set name (spring.application.name)

  • in an application property file

    • The application must have a configured rabbitmq service for configuration change notification (spring.rabbitmq.*)

    • The application must have a configured eureka server (eureka.client.*)

For other configuration see spring cloud config documentation.

Note that it is mandatory to define an environment repository for the service. Dev and Docker profile uses a file system backend which is not suitable for production and redundancy. You should prefer the usage of Git or Vault for production.

default properties

It is preferable not to change the following bootstrap.yml file.

bootstrap.yml

spring:
  application:
    name: config

application.yml

management:
  endpoints:
    web:
      exposure:
        include: '*'

The above embedded configurations are the basic settings of the application: * it sets its name as a service (config) * it exposes management endpoints

dev profile

dev is the profile we use internally for developpement we make it available for external developers so that they don’t need extensive configuration to get a jar working in the development environment

server:
  port: 2000
spring:
#  level.root: debug
  profiles:
    active: [native,dev]
  cloud:
    config:
      server:
        native:
          search-locations:
           - file:./build/dev-data/dev-configurations
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest
eureka:
  client:
    region: default
    service-url:
      defaultZone: http://localhost:2001/eureka
    registryFetchIntervalSeconds: 5
  • it exposes the service on port 2000 (defaults to 8080)

  • it activates the dev and native profile (native is mandatory for serving config from file system)

  • it configures rabbitmq for dispatching config change messages to other services

  • it configures registration in eureka discovery service

docker profile

docker is the profile we use in our docker images

spring:
  profiles:
    active: [native]
  cloud:
    config:
      server:
        native:
          search-locations:
           - file:/service-config
  rabbitmq:
    host: ${RABBITMQ_HOST}
    port: ${RABBITMQ_PORT}
    username: ${RABBITMQ_USER}
    password: ${RABBITMQ_PASSWORD}
eureka:
  client:
    region: default
    service-url:
      defaultZone: http://${REGISTRY_HOST}:${REGISTRY_PORT}/eureka
    registryFetchIntervalSeconds: 5
  • it activates the native profile (native is mandatory for serving config from file system)

  • it configures rabbitmq for dispatching config change messages to other services

  • it configures registration in eureka discovery service

Specifying external configuration properties when lauching a jar file

See Application Property Files on how to setup an external spring properties or yml file.

Seehttps://docs.spring.io/spring-boot/docs/2.1.2.RELEASE//reference/htmlsingle/#howto-set-active-spring-profiles[Set the Active Spring Profiles] for specifying alternate profile.

Specifying configuration properties when lauching a docker image

Our docker image expects optional property file to be stored in the container /service-config folder. You can bind so docker volume to this path to make properties or yml available.

At time of writting, you cannot specify an alternate profile at runtime. The default profiles activated are docker and native.

Available environment variables for docker image
  • RABBITMQ_HOST: Rabbitmq host name

  • RABBITMQ_PORT: Rabbitmq host port

  • RABBITMQ_USER: Rabbitmq user name used for authentication service

  • RABBITMQ_PASSWORD: Rabbitmq user password used for authentication service

  • REGISTRY_HOST: Registry (eureka) host name

  • REGISTRY_PORT: Registry (eureka) host port

  • JAVA_OPTIONS: Additional java options

Specifying configuration properties when lauching on Kubernetes

In progress

Available environment variables when launching on Kubernetes
  • RABBITMQ_HOST: Rabbitmq host name

  • RABBITMQ_PORT: Rabbitmq host port

  • RABBITMQ_USER: Rabbitmq user name used for authentication service

  • RABBITMQ_PASSWORD: Rabbitmq user password used for authentication service

  • REGISTRY_HOST: Registry (eureka) host name

  • REGISTRY_PORT: Registry (eureka) host port

  • JAVA_OPTIONS: Additional java options

17.4.6. Cloud Registry service

OperatorFabric Registry service is a Spring Eureka application bootstrapped using SpringBoot.

In our spring micro service architecture Eureka Registry instances are the second services to bootstrap, its configuration relies on the Cloud Configuration service.

Mandatory configuration, Profiles and default properties

To get a working registry service, there a few mandatory configuration properties:

  • In a bootstrap file

    • The application must have a set name (spring.application.name)

    • The application must have a set configuration name (spring.cloud.config.name)

    • The application must have a set configuration server (spring.cloud.config.uri)

    • The application may have a retry/failfast configuration for config access (spring.cloud.config.failfast, spring.cloud.config.retry)

  • In a configuration service registry.yml file

    • The application may deactivate eureka registration to avoid self registration (deactivated by default in internal application.yml file)

    • The application must set the Eureka instance hostname (eureka.instance.hostname)

    • The application must set the Eureka default zone (eureka.lient.serviceUrl.defaultZone)

For other configuration see spring cloud netflix documentation.

default properties

It is preferable not to change the following bootstrap.yml file.

bootstrap.yml

spring:
  application:
    name: registry
  cloud.config:
    name: registry
    fail-fast: true

bootstrap-docker.yml

spring:
  application:
    name: registry
  cloud.config:
    name: registry
    uri: http://${CONFIG_HOST}:${CONFIG_PORT}
    failFast: true
    retry:
      maxAttempts: 50

bootstrap-dev.yml

spring:
  application:
    name: registry
  cloud.config:
    name: registry
    uri: http://localhost:2000

application.yml

eureka:
  client:
    register-with-eureka: false
    fetch-registry: false

The bootstrap-docker.yml file is a replacement bootstrap file we use for our docker images configuration.

The bootstrap-dev.yml file is a replacement bootstrap file we use for our developpement environment

The above embedded configurations are the basic settings of the application: * it sets its name as a service (config) * it sets the configuration name to use (which configuration file to retrieve) * it must set the configuration service (example in bootstrap-docker.yml)

dev profile

dev is the profile we use internally for developpement we make it available for external developers so that they don’t need extensive configuration to get a jar working in the development environment

server:
  port: 2001
eureka:
  instance.hostname: localhost
  client.serviceUrl.defaultZone: http://localhost:2001/eureka/
Sample development configuration
server:
  port: 2001
eureka:
  instance.hostname: localhost
  client.serviceUrl.defaultZone: http://localhost:2001/eureka/
Sample docker image configuration
eureka:
  instance.hostname: registry
  client:
    serviceUrl.defaultZone: http://registry:8080/eureka/
    registerWithEureka: false
    fetchRegistry: false
    server:
      waitTimeInMsWhenSyncEmpty: 0
Specifying external configuration properties when lauching a jar file

See Application Property Files on how to setup an external spring properties or yml file.

See Set the Active Spring Profiles for specifying alternate profile.

Specifying configuration properties when lauching a docker image

Our docker image expects optional property file to be stored in the container /service-config folder. You can bind so docker volume to this path to make properties or yml available.

At time of writting, you cannot specify an alternate profile at runtime. The default profiles activated are docker and native.

Available environment variables for docker image
  • CONFIG_HOST: Configuration service host

  • CONFIG_PORT: Configuration service port

  • JAVA_OPTIONS: Additional java options

Specifying configuration properties when lauching on Kubernetes

In progress

Available environment variables when launching on Kubernetes
  • CONFIG_HOST: Configuration service host

  • CONFIG_PORT: Configuration service port

  • JAVA_OPTIONS: Additional java options

17.4.7. Thirds Business service

OperatorFabric Thirds Business service is a Spring MVC application bootstrapped using SpringBoot.

In our spring micro service architecture this service depends on Eureka Registry.

Mandatory configuration, Profiles and default properties

The service has no mandatory configuration beside global configuration and usual bootstrap configuration.

For other configuration see:

Service specific properties
name default mandatory? Description

operatorfabric.thirds.storage.path

null

no

File path to data storage folder

default properties

Note that you must provide a bootstrap file with a convenient registry configuration

bootstrap.yml

spring:
  application:
    name: thirds
  cloud:
    config:
      name: thirds
      fail-fast: true
      retry:
        max-attempts: 20
      discovery:
        service-id: config
        enabled: true

bootstrap-docker.yml

spring:
  application:
    name: thirds
  cloud:
    config:
      name: thirds
      failFast: true
      retry:
        maxAttempts: 20
      discovery:
        service-id: config
        enabled: true
#  level.root: debug
eureka:
  client:
    registerWithEureka: true
    fetchRegistry: true
    registryFetchIntervalSeconds: 5
    service-url:
      defaultZone: 'http://${REGISTRY_HOST}:${REGISTRY_PORT}/eureka/'

bootstrap-dev.yml

spring:
  application:
    name: thirds
  cloud:
    config:
      name: thirds
      fail-fast: true
      retry:
        max-attempts: 20
      discovery:
        service-id: config
        enabled: true
eureka:
  client:
    service-url:
      defaultZone: 'http://localhost:2001/eureka'

The bootstrap-docker.yml file is a replacement bootstrap file we use for our docker images configuration.

The bootstrap-dev.yml file is a replacement bootstrap file we use for our development environment

The above embedded configurations are the basic settings of the application:

  • it sets its service name

  • it sets the configuration name to use (which configuration file to retrieve)

  • it must set the registry service (example in bootstrap-docker.yml)

Sample development configuration
server:
  port: 2100
operatorfabric.thirds:
  storage:
    path: "./services/core/thirds/build/docker-volume/thirds-storage"
Sample docker image configuration
operatorfabric.thirds:
  storage:
    path: "/thirds-storage"
Specifying external configuration properties when launching a jar file

See Application Property Files on how to setup an external spring properties or yml file.

See Set the Active Spring Profiles for specifying alternate profile.

Specifying configuration properties when launching a docker image

Our docker image expects optional property file to be stored in the container /service-config folder. You can bind so docker volume to this path to make properties or yml available.

At time of writing, you cannot specify an alternate profile at runThirds. The default profiles activated are docker and native.

Available environment variables for docker image
  • REGISTRY_HOST: Registry service host

  • REGISTRY_PORT: Registry service port

  • JAVA_OPTIONS: Additional java options

Specifying configuration properties when launching on Kubernetes

In progress

Available environment variables when launching on Kubernetes
  • REGISTRY_HOST: Registry service host

  • REGISTRY_PORT: Registry service port

  • JAVA_OPTIONS: Additional java options

17.4.8. Users Business service

OperatorFabric Users Business service is a Spring MVC application bootstrapped using SpringBoot.

In our spring micro service architecture this service depends on Eureka Registry.

Mandatory configuration, Profiles and default properties

The service has no mandatory configuration beside global configuration and usual bootstrap configuration.

For other configuration see:

Service specific properties
name default mandatory? Description

operatorfabric.users.default.users

null

no

Array of user objects to create upon startup if they don’t exist

operatorfabric.users.default.user-settings

null

no

Array of user settings objects to create upon startup if they don’t exist

operatorfabric.users.default.groups

null

no

Array of group objects to create upon startup if they don’t exist

default properties

Note that you must provide a bootstrap file with a convenient registry configuration

bootstrap.yml

spring:
  application:
    name: users
  cloud:
    config:
      name: users
      fail-fast: true
      retry:
        max-attempts: 20
      discovery:
        service-id: config
        enabled: true

bootstrap-docker.yml

spring:
  application:
    name: users
  cloud:
    config:
      name: users
      failFast: true
      retry:
        maxAttempts: 20
      discovery:
        service-id: config
        enabled: true
#  level.root: debug
eureka:
  client:
    registerWithEureka: true
    fetchRegistry: true
    registryFetchIntervalSeconds: 5
    service-url:
      defaultZone: 'http://${REGISTRY_HOST}:${REGISTRY_PORT}/eureka/'

bootstrap-dev.yml

spring:
  application:
    name: users
  cloud:
    config:
      name: users
      fail-fast: true
      retry:
        max-attempts: 20
      discovery:
        service-id: config
        enabled: true
eureka:
  client:
    service-url:
      defaultZone: 'http://localhost:2001/eureka'

The bootstrap-docker.yml file is a replacement bootstrap file we use for our docker images configuration.

The bootstrap-dev.yml file is a replacement bootstrap file we use for our development environment

The above embedded configurations are the basic settings of the application:

  • it sets its service name

  • it sets the configuration name to use (which configuration file to retrieve)

  • it must set the registry service (example in bootstrap-docker.yml)

Sample development configuration
server:
  port: 2103
operatorfabric.users.default:
  users:
    - login: admin
      groups: ["ADMIN"]
    - login: rte-operator
      groups: ["RTE","ADMIN","TRANS"]
    - login: tso1-operator
      groups: ["TSO1","TRANS"]
    - login: tso2-operator
      groups: ["TSO2", "TRANS"]
  groups:
    - name: ADMIN
      description: The admin group
    - name: RTE
      description: RTE TSO Group
    - name: TSO1
      description: TSO 1 Group
    - name: TSO2
      description: TSO 2 Group
    - name: TRANS
      description: Transnationnal Group
  user-settings:
    - login: rte-operator
      description: Da Operator Rulez
#logging.level.root: DEBUG
Sample docker image configuration
operatorfabric.users.default:
  users:
  - login: admin
    groups: ["ADMIN"]
  - login: rte-operator
    groups: ["RTE","ADMIN","TRANS"]
  - login: tso1-operator
    groups: ["TSO1","TRANS"]
  - login: tso2-operator
    groups: ["TSO2", "TRANS"]
  groups:
  - name: ADMIN
    description: The admin group
  - name: RTE
    description: RTE TSO Group
  - name: TSO1
    description: TSO 1 Group
  - name: TSO2
    description: TSO 2 Group
  - name: TRANS
    description: Transnationnal Group
Specifying external configuration properties when launching a jar file

See Application Property Files on how to setup an external spring properties or yml file.

See Set the Active Spring Profiles for specifying alternate profile.

Specifying configuration properties when launching a docker image

Our docker image expects optional property file to be stored in the container /service-config folder. You can bind so docker volume to this path to make properties or yml available.

At time of writing, you cannot specify an alternate profile at runUsers. The default profiles activated are docker and native.

Available environment variables for docker image
  • REGISTRY_HOST: Registry service host

  • REGISTRY_PORT: Registry service port

  • JAVA_OPTIONS: Additional java options

Specifying configuration properties when launching on Kubernetes

In progress

Available environment variables when launching on Kubernetes
  • REGISTRY_HOST: Registry service host

  • REGISTRY_PORT: Registry service port

  • JAVA_OPTIONS: Additional java options

17.4.9. Web UI

OperatorFabric Web UI service is a Spring MVC application bootstrapped using SpringBoot. Its sole purpose is to serve the Angular SPA to browsers.

In our spring micro service architecture this service depends on Eureka Registry.

Mandatory configuration, Profiles and default properties

The service has no mandatory configuration beside global configuration and usual bootstrap configuration.

For other configuration see: NONE * Spring Boot documentation. * Spring MVC documentation.

Service specific properties
name default mandatory? Description

operatorfabric.security.realm-url

yes

The realm name in keycloak server settings page. This is used for the log out process to know which realm should be affected.

operatorfabric.security.provider-url

yes

The keycloak server instance

operatorfabric.security.logout-url

yes

The keycloak logout URL. Is a composition of: - Your keycloak instance and the auth keyword (ex: www.keycloakurl.com/auth), but we also support domains without auth (ex: www.keycloakurl.com/customPath) - The realm name (Ex: dev) - The redirect URL (redirect_uri): The redirect URL after success authentification

operatorfabric.security.oauth2.flow.mode

PASSWORD

no

authentication mode, awailable options:

  • CODE: Authorization Code Flow;

  • PASSWORD: Direct Password Flow (fallback);

  • IMPLICIT: Implicit Flow.

operatorfabric.security.oauth2.flow.provider

null

no

provider name to display on log in button

operatorfabric.security.oauth2.flow.delegate-url

null

no

Url to redirect the browser to for authentication. Mandatory with:

  • CODE flow: must be the url with protocol choice and version as query parameters;

  • IMPLICIT flow: must be the url part before .well-known/openid-configuration (for example in dev configuration it’s localhost:89/auth/realms/dev).

operatorfabric.feed.subscription.timeout

60000

no

Milliseconds between card subscription renewal

operatorfabric.feed.card.time.display

BUSINESS

no

card time display mode in the feed. Values :

  • BUSINESS: displays card with entire business period. It the fallback if the set value is none of the values listed here;

  • BUSINESS_START: displays card with business start date;

  • PUBLICATION: displays card with publication date;

  • LTTD: displays card with lttd date;

  • NONE: nothing displayed.

operatorfabric.feed.timeline.hide

false

no

If set to true, the time line is not loaded in the feed screen

operatorfabric.feed.timeFilter.followClockTick

false

no

If set to true, the time filter on the feed will shift to reflect elapsed time

operatorfabric.feed.notify

false

no

If set to true, new cards are notified in the OS through web-push notifications

operatorfabric.i18n.supported.locales

no

List of supported locales (Only fr and en so far)

operatorfabric.i10n.supported.time-zones

no

List of supported time zones, for instance 'Europe/Paris'. Values should be taken from the TZ database.

operatorfabric.navbar.thirdmenus.type

BOTH

no

Defines how thirdparty menu links are displayed in the navigation bar and how they open. Possible values:

  • TAB: Only a text link is displayed, and clicking it opens the link in a new tab.

  • IFRAME: Only a text link is displayed, and clicking it opens the link in an iframe in the main content zone below the navigation bar.

  • BOTH: Both a text link and a little arrow icon are displayed. Clicking the text link opens the link in an iframe while clicking the icon opens in a new tab.

operatorfabric.time.pulse

5000

no

Duration between two ticks of the internal state of the User Interface

operatorfabric.archive.filters.page.size

no

The page size of archive filters

operatorfabric.archive.filters.page.first

no

The first page start of archiving module

operatorfabric.archive.filters.process.list

no

List of processes to choose from in the corresponding filter in archives

operatorfabric.archive.filters.tags.list

no

List of tags to choose from in the corresponding filter in archives

operatorfabric.settings.tags.hide

no

Control if you want to show or hide the tags filter in settings and feed page

operatorfabric.settings.infos.disable

no

Control if we want to disable/enable editing user email, description in the settings page

operatorfabric.settings.infos.email

false

no

Control if we want to hide(true) or display(false or not specified) the user email in the settings page

operatorfabric.settings.infos.description

false

no

Control if we want to hide(true) or display(false or not specified) the user description in the settings page

operatorfabric.settings.infos.language

false

no

Control if we want to hide(true) or display(false or not specified) the language in the settings page

operatorfabric.settings.infos.timezone

false

no

Control if we want to hide(true) or display(false or not specified) the timezone in the settings page

operatorfabric.settings.infos.timeformat

false

no

Control if we want to hide(true) or display(false or not specified) the timeformat in the settings page

operatorfabric.settings.infos.dateformat

false

no

Control if we want to hide(true) or display(false or not specified) the dateformat in the settings page

operatorfabric.settings.infos.datetimeformat

false

no

Control if we want to hide(true) or display(false or not specified) the datetimeformat in the settings page

operatorfabric.settings.infos.tags

false

no

Control if we want to hide(true) or display(false or not specified) the tags in the settings page

operatorfabric.settings.about

operatorfabric:
name: 'OperatorFabric'
version: ${currentVersion}
rank: 0

no

Declares application names and their version into web-ui about section.
Each entry is a free key value followed by its name (a string of characters), its version(a string of characters) and its rank of declaration(a number) cf the default value where the declared key is operatorfabric'.
For this default value, with 'OperatorFabric' as name and 0 as rank, the value of ${currentVersion} is the version of the current release. 0.16.1-RELEASE for example.

operatorfabric.logo.base64

medium OperatorFabric icon

no

The encoding result of converting the svg logo to Base64, use this online tool to encode your svg. If it is not set, a medium (32px) OperatorFabric icon is displayed.

operatorfabric.logo.height

32

no

The height of the logo (in px) (only taken into account if operatorfabric.logo.base64 is set).

operatorfabric.logo.width

150

no

The width of the logo (in px) (only taken into account if operatorfabric.logo.base64 is set).

operatorfabric.logo.limitSize

true

no

If it is true, the height limit is 32(px) and the width limit is 200(px), it means that if the height is over than 32, it will be set to 32, if the width is over than 200, it is set to 200. If it is false, no limit restriction for the height and the width.

operatorfabric.title

OperatorFabric

no

Title of the application, displayed on the browser

User Settings default values

name

default

mandatory?

Description

operatorfabric.settings.timeZone

no

Default user time zone for users (use

operatorfabric.settings.timeFormat

LT

no

Default user time format (moment)

operatorfabric.settings.dateFormat

LL

no

Default user date format (moment)

operatorfabric.settings.dateTimeFormat

LL LT

no

Default user date format (moment)

operatorfabric.settings.locale

en

no

Default user locale (use en if not set)

operatorfabric.settings.default-tags

no

Default user list of filtered in tags

operatorfabric.settings.timeLineDefaultClusteringFormats.dateInsideTooltipsWeek

no

operatorfabric.settings.timeLineDefaultClusteringFormats.dateInsideTooltipsWeek

ddd DD MMM HH

Default format use on date inside tooltips for time’s unit lower than hour

operatorfabric.settings.timeLineDefaultClusteringFormats.dateInsideTooltipsMonth

ddd DD MMM YYYY

Default format use on date inside tooltips for other case

operatorfabric.settings.timeLineDefaultClusteringFormats.dateOnDay

ddd DD MMM

Default format use for display a day and month informations with day name, number, and month

operatorfabric.settings.timeLineDefaultClusteringFormats.dateOnWeek

DD/MM/YY

Default format use for display a week information with day number, month number and two last year’s number

operatorfabric.settings.timeLineDefaultClusteringFormats.dateOnMonth

MMM YY

Default format use for display a month information with month name and two last year’s number

operatorfabric.settings.timeLineDefaultClusteringFormats.dateOnYear

YYYY

Default format use for display a year information

operatorfabric.settings.timeLineDefaultClusteringFormats.titleDateInsideTooltips

DD/MM

Default format use on first part of title inside tooltips showing day number and month number of its date

operatorfabric.settings.timeLineDefaultClusteringFormats.titleHourInsideTooltips

HH:mm

Default format use on second part of title inside tooltips showing hours and minutes of its date

operatorfabric.settings.timeLineDefaultClusteringFormats.dateOnDayNewYear

DD MMM YY

Default format use for display first year’s day on a day configuration display day number, month name and two last year’s number

operatorfabric.settings.timeLineDefaultClusteringFormats.realTimeBarFormat

DD/MM/YY HH:mm

Default format use on real time bar date, show minutes, hours, day number, month number and two last year’s number

operatorfabric.settings.timeLineDefaultClusteringFormats.dateSimplifliedOnDayNewYear

D MMM YY

Default format use on date configuration for first year’s day, show simplified day number, month name and last two year’s number

operatorfabric.settings.timeLineDefaultClusteringFormats.dateSimplifliedOnDay

D MMM

Default format use on date configuration, show simplified day number and month name

operatorfabric.settings.timeLineDefaultClusteringFormats.hoursOnly

HH

Default format for show only hours

operatorfabric.settings.timeLineDefaultClusteringFormats.minutesOnly

mm

Default format for show only minutes

operatorfabric.settings.timeLineDefaultClusteringFormats.secondedsOnly

ss

Default format for show only seconds

operatorfabric.settings.timeLineDefaultClusteringFormats.weekNumberOnly

ww

Default format for show only week number

default properties

Note that you must provide a bootstrap file with a convenient registry configuration

bootstrap.yml

spring:
  application:
    name: web-ui
  cloud:
    config:
      name: web-ui
      fail-fast: true
      retry:
        max-attempts: 20
      discovery:
        service-id: config
        enabled: true

bootstrap-docker.yml

spring:
  application:
    name: web-ui
  cloud:
    config:
      name: web-ui
      failFast: true
      retry:
        maxAttempts: 20
      discovery:
        service-id: config
        enabled: true
#  level.root: debug
eureka:
  client:
    registerWithEureka: true
    fetchRegistry: true
    registryFetchIntervalSeconds: 5
    service-url:
      defaultZone: 'http://${REGISTRY_HOST}:${REGISTRY_PORT}/eureka/'

bootstrap-dev.yml

spring:
  application:
    name: web-ui
  cloud:
    config:
      name: web-ui
      fail-fast: true
      retry:
        max-attempts: 20
      discovery:
        service-id: config
        enabled: true
eureka:
  client:
    service-url:
      defaultZone: 'http://localhost:2001/eureka'

The bootstrap-docker.yml file is a replacement bootstrap file we use for our docker images configuration.

The bootstrap-dev.yml file is a replacement bootstrap file we use for our development environment

The above embedded configurations are the basic settings of the application:

  • it sets its service name

  • it sets the configuration name to use (which configuration file to retrieve)

  • it must set the registry service (example in bootstrap-docker.yml)

Sample development configuration
server:
  port: 2200
operatorfabric:
  security:
    provider-url: http://localhost:89
    provider-realm: dev
    logout-url: ${operatorfabric.security.provider-url}/auth/realms/${operatorfabric.security.provider-realm}/protocol/openid-connect/logout?redirect_uri=http://localhost:2002/ui/
    oauth2:
      flow:
        mode: CODE
        provider: Opfab Keycloak
        delagate-url: ${operatorfabric.security.provider-url}/auth/realms/${operatorfabric.security.provider-realm}/protocol/openid-connect/auth?response_type=code&client_id=opfab-client
  feed:
    subscription:
      timeout: 600000
    card:
      time:
        display: BUSINESS
    timeline:
      domains:
        - "TR"
        - "J"
        - "7D"
        - "W"
        - "M"
        - "Y"
    notify: false
  archive:
    filters:
      page.size:
        - "10"
      page.first:
        - "0"
      publisher.list:
        - value: "TEST"
          label: "Test Publisher"
        - value: "TEST2"
          label: "Test Publisher 2"
      process.list:
            - process
            - someOtherProcess
      tags.list:
        - value: "tag1"
          label: "Label for tag 1"
        - value: "tag2"
          label: "Label for tag 2"
  i10n.supported.time-zones:
    - value: "Europe/Paris"
      label: "Headquarters timezone"
    - value: "Australia/Melbourne"
      label: "Down Under"
    - Europe/London
    - Europe/Dublin
    - Europe/Brussel
    - Europe/Berlin
    - Europe/Rome
    - Europe/Madrid
    - Europe/Lisbon
    - Europe/Amsterdam
    - Europe/Athens
    - Pacific/Samoa
  i18n.supported.locales:
    - en
    - fr
  settings:
    locale: en
    infos:
      description: true
    about:
      firstapplication:
        name: First application
        version: v12.34.56
        rank: 1
      keycloack:
        name: Keycloak
        version: 6.0.1
        rank: 2
      lastapplication:
          name: Wonderful Solution
          version: 0.1.2-RELEASE
  time:
    pulse: 5000
Sample docker image configuration
server:
  port: 2200
operatorfabric:
  security:
    provider-url: http://localhost:89
    provider-realm: dev
    logout-url: ${operatorfabric.security.provider-url}/auth/realms/${operatorfabric.security.provider-realm}/protocol/openid-connect/logout?redirect_uri=http://localhost:2002/ui/
    oauth2:
      flow:
        mode: CODE
        provider: Opfab Keycloak
        delagate-url: ${operatorfabric.security.provider-url}/auth/realms/${operatorfabric.security.provider-realm}/protocol/openid-connect/auth?response_type=code&client_id=opfab-client
  feed:
    subscription:
      timeout: 600000
    card:
      time:
        display: BUSINESS
    timeline:
      domains:
        - "TR"
        - "J"
        - "7D"
        - "W"
        - "M"
        - "Y"
    notify: false
  archive:
    filters:
      page.size:
        - "10"
      page.first:
        - "0"
      publisher.list:
        - value: "TEST"
          label: "Test Publisher"
        - value: "TEST2"
          label: "Test Publisher 2"
      process.list:
            - process
            - someOtherProcess
      tags.list:
        - value: "tag1"
          label: "Label for tag 1"
        - value: "tag2"
          label: "Label for tag 2"
  i10n.supported.time-zones:
    - value: "Europe/Paris"
      label: "Headquarters timezone"
    - value: "Australia/Melbourne"
      label: "Down Under"
    - Europe/London
    - Europe/Dublin
    - Europe/Brussel
    - Europe/Berlin
    - Europe/Rome
    - Europe/Madrid
    - Europe/Lisbon
    - Europe/Amsterdam
    - Europe/Athens
    - Pacific/Samoa
  i18n.supported.locales:
    - en
    - fr
  settings:
    locale: en
    infos:
      description: true
    about:
      firstapplication:
        name: First application
        version: v12.34.56
        rank: 1
      keycloack:
        name: Keycloak
        version: 6.0.1
        rank: 2
      lastapplication:
          name: Wonderful Solution
          version: 0.1.2-RELEASE
  time:
    pulse: 5000
Specifying external configuration properties when lauching a jar file

See Application Property Files on how to setup an external spring properties or yml file.

See Set the Active Spring Profiles for specifying alternate profile.

Specifying configuration properties when lauching a docker image

Our docker image expects optional property file to be stored in the container /service-config folder. You can bind so docker volume to this path to make properties or yml available.

At time of writing, you cannot specify an alternate profile at runCards-Publication. The default profiles activated are docker and native.

Available environment variables for docker image
  • REGISTRY_HOST: Registry service host

  • REGISTRY_PORT: Registry service port

  • JAVA_OPTIONS: Additional java options

Specifying configuration properties when lauching on Kubernetes

In progress

Available environment variables when launching on Kubernetes
  • REGISTRY_HOST: Registry service host

  • REGISTRY_PORT: Registry service port

  • JAVA_OPTIONS: Additional java options

17.5. Users and Groups Administration

A new operator call John Doe, who has OAuth granted right to connect ot current OperatorFabric instance, need to receive cards within current OperatorFabric instance. As a user of OperatorFabric, he needs to be added to the system with a login (john-doe-operator), his firstName (John) and his lastName (Doe). operator As there is no Administration GUI for the moment, it must be performed through command line, as specify here.

17.5.1. List all users

First of all, list the users (who are the recipients in OperatorFabric) of the system with the following commands:

Httpie

http http://localhost:2103/users "Authorization:Bearer $token" "Content-Type:application/type"

cURL

curl -v http://localhost:2103/users -H "Authorization:Bearer $token" -H "Content-Type:application/type"

response

HTTP/1.1 200 OK

[
    {
        "firstName": null,
        "groups": [
            "ADMIN"
        ],
        "lastName": null,
        "login": "admin"
    },
    {
        "firstName": null,
        "groups": [
            "RTE",
            "ADMIN",
            "CORESO",
            "TRANS",
            "TEST"
        ],
        "lastName": null,
        "login": "rte-operator"
    },
    {
        "firstName": null,
        "groups": [
            "ELIA"
        ],
        "lastName": null,
        "login": "elia-operator"
    },
    {
        "firstName": null,
        "groups": [
            "CORESO"
        ],
        "lastName": null,
        "login": "coreso-operator"
    },
    {
        "firstName": null,
        "groups": [
            "TSO1",
            "TRANS",
            "TEST"
        ],
        "lastName": null,
        "login": "tso1-operator"
    },
    {
        "firstName": null,
        "groups": [
            "TSO2",
            "TRANS"
        ],
        "lastName": null,
        "login": "tso2-operator"
    },
]

17.5.2. Create a new User

We are sure that no John-doe-operator exists in our OperatorFabric instance. We can add him in our OperatorFabric instance using the following command use httpie:

echo '{"login":"john-doe-operator","firstName":"Jahne","lastName":"Doe"}' | http POST http://localhost:2103/users "Authorization:Bearer $token" "Content-Type:application/json"

Or here cURL:

curl -X POST http://localhost:2103/users -H "Authorization:Bearer $token" -H "Content-Type:application/json" --data '{"login":"john-doe-operator","firstName":"Jahne","lastName":"Doe"}'

response

HTTP/1.1 200 OK

{
    "firstName": "Jahne",
    "lastName": "Doe",
    "login": "john-doe-operator"
}

17.5.3. Fetch user details

It’s always a good thing to verify if all the information has been correctly recorded in the system:

with httpie:

http -b http://localhost:2103/users/john-doe-operator "Authorization:Bearer $token" "Content-Type:application/json"

or with cURL:

curl http://localhost:2103/users/john-doe-operator -H "Authorization:Bearer $token" -H "Content-Type:application/json"

response

HTTP/1.1 200 OK

{
    "firstName": "Jahne",
    "groups": [],
    "lastName": "Doe",
    "login": "john-doe-operator"
}

17.5.4. Update user details

As shown by this result, the firstName of the new operator has been misspelled.We need to update the existing user with john-doe-operator login. To correct this mistake, the following commands can be used :

with httpie:

echo '{"login":"john-doe-operator","lastName":"Doe","firstName":"John"}' | http PUT http://localhost:2103/users/john-doe-operator "Authorization:Bearer $token" "Content-Type:application/json"

or with cURL:

curl -X PUT http://localhost:2103/users/john-doe-operator -H "Authorization:Bearer $token" -H "Content-Type:application/json" --data '{"login":"john-doe-operator","firstName":"John","lastName":"Doe"}'

response

HTTP/1.1 200 OK

{
    "firstName": "John",
    "lastName": "Doe",
    "login": "john-doe-operator"
}

17.5.5. List groups

This operator is the first member of a new group operator called the OPERATORS, which doesn’t exist for the moment in the system. As shown when we lists the groups existing in the server.

Httpie

http http://localhost:2103/groups "Authorization:Bearer $token" "Content-Type:application/type"

cURL

curl http://localhost:2103/groups -H "Authorization:Bearer $token" -H "Content-Type:application/json"

response

HTTP/1.1 200 OK

[
    {
        "description": "The admin group",
        "name": "ADMIN"
    },
    {
        "description": "RTE TSO Group",
        "name": "RTE"
    },
    {
        "description": "ELIA TSO group",
        "name": "ELIA"
    },
    {
        "description": "CORESO Group",
        "name": "CORESO"
    },
    {
        "description": "TSO 1 Group",
        "name": "TSO1"
    },
    {
        "description": "TSO 2 Group",
        "name": "TSO2"
    },
    {
        "description": "Transnationnal Group",
        "name": "TRANS"
    }
]

17.5.6. Create a new Group

Firstly, the group called OPERATORS has to be added to the system using the following command:

using httpie:

echo '{"name":"OPERATORS","decription":"This is the brand new  group of operator"}' | http POST http://localhost:2103/groups "Authorization:Bearer $token" "Content-Type:application/json"

using cURL:

curl -X POST http://localhost:2103/groups -H "Authorization:Bearer $token" -H "Content-Type:application/json" --data '{"name":"OPERATORS","decription":"This is the brand new  group of operator"}'

response

HTTP/1.1 200 OK

{
    "description": null,
    "name": "OPERATORS"
}

17.5.7. Fetch details of a given group

The result return seems strange, to verify if it’s the correct answer by displaying the details of the group called OPERATORS, use the following command:

using httpie:

http http://localhost:2103/groups/OPERATORS "Authorization:Bearer $token" "Content-Type:application/json"

using cURL:

curl http://localhost:2103/groups/OPERATORS -H "Authorization:Bearer $token" -H "Content-Type:application/json"

response

HTTP/1.1 200 OK

{
    "description": null,
    "name": "OPERATORS"
}

17.5.8. Update details of a group

The description is really null. After verification, in our first command used to create the group, the attribute for the description is misspelled. Using the following command to update the group, with the correct spelling, the new group of operator gets a proper description:

with httpie:

echo '{"name":"OPERATORS","description":"This is the brand new  group of operator"}' | http -b PUT http://localhost:2103/groups/OPERATORS "Authorization:Bearer $token" "Content-Type:application/json"

with cURL:

curl -X PUT http://localhost:2103/groups/OPERATORS -H "Authorization:Bearer $token" -H "Content-Type:application/json" --data '{"name":"OPERATORS","description":"This is the brand new  group of operator"}'

response

{
    "description": "This is the brand new  group of operator",
    "name": "OPERATORS"
}

17.5.9. Add a user to a group

As both new group and new user are correct it’s time to make the user member of the group. To achieve this, use the following command:

with httpie:

echo '["john-doe-operator"]' | http PATCH http://localhost:2103/groups/OPERATORS/users "Authorization:Bearer $token" "Content-Type:application/json"

with cURL:

curl -X PATCH http://localhost:2103/groups/OPERATORS/users -H "Authorization:Bearer $token" -H "Content-Type:application/json" --data '["john-doe-operator"]'

response

HTTP/1.1 200 OK

Let’s verify that the changes are correctly recorded by fetching the :

http http://localhost:2103/users/john-doe-operator "Authorization:Bearer $token" "Content-Type:application/json"

with cURL

curl http://localhost:2103/users/john-doe-operator -H "Authorization:Bearer $token" -H "Content-Type:application/json"

response

HTTP/1.1 200 OK

{
    "firstName": "John",
    "groups": ["OPERATORS"],
    "lastName": "Doe",
    "login": "john-doe-operator"
}

It’s now possible to send cards either specifically to john-doe-operator or more generally to the OPERATORS group.

17.5.10. Remove a user from a Group

When John Doe is no longer in charge of hypervising cards for OPERATORS group, this group has to be removed from his login by using the following command:

with httpie:

http DELETE http://localhost:2103/groups/OPERATORS/users/john-doe-operator "Authorization:Bearer $token" "Content-Type:application/json"

with cURL:

curl -X DELETE -H "Authorization:Bearer $token" -H "Content-Type:application/json" http://localhost:2103/groups/OPERATORS/users/john-doe-operator

response

HTTP/1.1 200 OK

{
	"login":"john-doe-operator","
	firstName":"John",
	"lastName":"Doe",
	"groups":[]
}

A last command to verify that OPERATORS is no longer linked to john-doe-operator:

with httpie:

http http://localhost:2103/users/john-doe-operator "Authorization:Bearer $token" "Content-Type:application/json"

with cURL:

curl http://localhost:2103/users/john-doe-operator -H "Authorization:Bearer $token" -H "Content-Type:application/json"

response

HTTP/1.1 200 OK

{
    "firstName": "John",
    "groups": [],
    "lastName": "Doe",
    "login": "coreso-operator"

}

18. Service port table

By default all service built artifacts are configured with server.port set to 8080

If you run the services using bootRun Gradle task or the provided docker-compose files (see [prj]/src/main/docker) the ports used are

Service bootRun port docker-compose mapping docker-compose debug mapping

registry

2001

2001

2001

gateway

2002

2002

2002

thirds

2100

2100

2100

time

2101

2101

2101

cards-publication

2102

2102

2102

users

2103

2103

2103

cards-consultation

2104

2104

2104

config

4000

4000

4000

registry

4001

4001

4001

gateway

4002

4002

4002

thirds

4100

4100

4100

time

4101

4101

4101

cards-publication

4102

4102

4102

users

4103

4103

4103

cards-consultation

4103

4103

4103

Complete port table

Port Forwards to

89

KeyCloak

89

KeyCloak api port

2000

config

8080

Configuration service http (REST)

2001

registry

8080

Registry service http (REST)

2002

gateway

8080

Gateway service http (REST+html)

2100

thirds

8080

Third party management service http (REST)

2101

time

8080

Time management service http (REST)

2102

cards-publication

8080

card publication service http (REST)

2103

users

8080

Users management service http (REST)

2104

cards-consultation

8080

card consultation service http (REST)

2105

actions

8080

actions (REST)

2200

web-ui

8080

card consultation service http (REST)

4000

config

5005

java debug port

4001

registry

5005

java debug port

4002

gateway

5005

java debug port

4100

thirds

5005

java debug port

4101

time

5005

java debug port

4102

cards-publication

5005

java debug port

4103

users

5005

java debug port

4104

cards-consultation

5005

java debug port

4105

actions

5005

java debug port

4200

web-ui

5005

java debug port

27017

mongo

27017

mongo api port

5672

rabbitmq

5672

amqp api port

15672

rabbitmq

15672

rabbitmq api port

19. Restricted operations (administration)

Some operations are restricted to users with the ADMIN role, either because they are administration operations with the potential to impact the OperatorFabric instance as a whole, or because they give access to information that should be private to a user.

Below is a quick recap of these restricted operations.

Users Service

Any action (read, create/update or delete) regarding a single user’s data (their personal info such as their first and last name, as well as their settings) can be performed either by the user in question or by a user with the ADMIN role.

Any action on a list of users or on the groups (if authorization is managed in OperatorFabric) can only be performed by a user with the ADMIN role.

Thirds Service

Any write (create, update or delete) action on bundles can only be performed by a user with the ADMIN role. As such, administrators are responsible for the quality and security of the provided bundles. In particular, as it is possible to use scripts in templates, they should perform a security check to make sure that there is no XSS risk.

Time Service (for demonstration purposes only)

Any user can get the current state of simulated time but acting on it (changing the speed of time flow, setting the reference for simulated time or resetting it entirely) requires the ADMIN role.

The ADMIN role doesn’t grant any special privileges when it comes to card consultation (be they current or archived), so an user with the ADMIN role will only see cards that have been addressed to them or to one of their groups), just like any other user.

Development environment

The aim of this document is to provide all the necessary information to developers who would like to start working on OperatorFabric. It will walk you through setting up the necessary tooling to be able to launch OperatorFabric in development mode, describe the structure of the project and point out useful tools (Gradle tasks, scripts, etc.) for development purposes.

20. Requirements

This section describes the projects requirements regardless of installation options. Please see [Getting Started] below for details on:
  • setting up a development environment with these prerequisites

  • building and running OperatorFabric

20.1. Tools and libraries

  • Gradle 6

  • Java 8.0

  • Maven 3.5.3

  • Docker

  • Docker Compose with 2.1+ file format support

  • Chrome (needed for UI tests in build)

the current Jdk used for the project is Java 8.0.242-zulu.
It is highly recommended to use sdkman and nvm to manage tools versions.

Once you have installed sdkman and nvm, you can source the following script to set up your development environment (appropriate versions of Gradle, Java, Maven and project variables set):

Set up development environment (using sdkman and nvm)
source bin/load_environment_light.sh

20.2. Software

  • RabbitMQ 3.7.6 +: AMQP messaging layer allows inter service communication

  • MongoDB 4.0 +: Card persistent storage

RabbitMQ is required for :

  • Time change push

  • Card AMQP push

  • Multiple service sync

MongoDB is required for :

  • Current Card storage

  • Archived Card storage

  • User Storage

Installing MongoDB and RabbitMQ is not necessary as preconfigured MongoDB and RabbitMQ are available in the form of docker-compose configuration files at src/main/docker

20.3. Browser support

We currently use Firefox (63.0.3). Automatic tests for the UI rely on Chrome (73.0.3683.86).

21. Setting up your development environment

The steps below assume that you have installed and are using sdkman and nvm to manage tool versions ( for java, gradle, node and npm).

There are several ways to get started with OperatorFabric. Please look into the section that best fits your needs.

If you encounter any issue, see Troubleshooting below. In particular, a command that hangs then fails is often a proxy issue.

The following steps describe how to launch MongoDB, RabbitMQ and SonarQube using Docker, build OperatorFabric using gradle and run it using the run_all.sh script.

21.1. Clone repository

git clone https://github.com/opfab/operatorfabric-core.git
cd operatorfabric-core

21.2. Set up your environment (environment variables & appropriate versions of gradle, maven, etc…)

source bin/load_environment_light.sh
From now on, you can use environment variable $OF_HOME to go back to the home repository of OperatorFabric.

21.3. Deploy dockerized MongoDB, RabbitMQ and SonarQube

MongoDB, RabbitMQ and SonarQube are needed for the tests to be run so the build can be done.

A docker-compose file with properly configured containers is available there.

The docker-compose can be run in detached mode:

cd src/main/docker/test-quality-environment/
docker-compose up -d

21.4. Build OperatorFabric with Gradle

Using the wrapper in order to ensure building the project the same way from one machine to another.

To only compile and package the jars:

cd $OF_HOME
./gradlew assemble

To launch the Unit Test, compile and package the jars:

cd $OF_HOME
./gradlew build

21.5. Run OperatorFabric Services using the run_all.sh script

bin/run_all.sh start
See bin/run_all.sh -h for details.

21.6. Check services status

bin/run_all.sh status

21.7. Log into the UI

URL: localhost:2002/ui/

login: admin

password: test

It might take a little while for the UI to load even after all services are running.
Don’t forget the final slash in the URL or you will get an error.

21.8. Push cards to the feed

You can check that you see cards into the feed by running the push_card_loop.sh script.

services/core/cards-publication/src/main/bin/push_card_loop.sh

22. User Interface

This project was partially generated with Angular CLI version 6.0.8.

In the following document the variable declared as OF_HOME is the root folder of the operatorfabric-core project.
CLI

stands for Command Line Interface

SPA

stands for Single Page Application

OS

stands for Operating System

22.1. Run

22.1.1. Linux

After launching docker containers, use the following command line $OF_HOME/bin/run_all.sh start to run the application. Once the whole application is ready, you should have the following output in your terminal:

##########################################################
Starting client-gateway-cloud-service, debug port: 5008

##########################################################
pid file: $OF_HOME/services/infra/client-gateway/build/PIDFILE
Started with pid: 7479

##########################################################
Starting users-business-service, debug port: 5009

##########################################################
pid file: $OF_HOME/services/core/users/build/PIDFILE
Started with pid: 7483

##########################################################
Starting time-business-service, debug port: 5010

##########################################################
pid file: $OF_HOME/services/core/time/build/PIDFILE
Started with pid: 7488

##########################################################
Starting cards-consultation-business-service, debug port: 5011

##########################################################
pid file: $OF_HOME/services/core/cards-consultation/build/PIDFILE
Started with pid: 7493

##########################################################
Starting cards-publication-business-service, debug port: 5012

##########################################################
pid file: $OF_HOME/services/core/cards-publication/build/PIDFILE

Wait a moment before trying to connect to the`SPA`, leaving time for the`client-gateway` to boot up completely.

The SPA, on a local machine, is available at the following Url: localhost:2002/ui/.

To log in you need to use a valid user. Currently you need to use a connection pair define in $OPERATOR_FABRIC_HOME/services/infra/auth/src/main/java/org/lfenergy/operatorfabric/auth/config/WebSecurityConfiguration.java. It could be admin with test, for example.

To test the reception of cards, you can use the following script to create dummy cards:

$OF_HOME/services/core/cards-publication/src/main/bin/push_cards_loop.sh

Once logged in, with that script running in the background, you should be able to see some cards displayed in localhost:2002/ui/feed.

22.2. Build

Run ng build to build the project. The build artifacts will be stored in :

$OPERATOR_FABRIC_CORE_HOME/services/web/web-ui/build/src/generated/resources/static

22.3. Test

22.3.1. Standalone tests

Run in the $OF_HOME/ui/main directory the command ng test --watch=false to execute the unit tests based on Jasmine using Karma to drive the browser.

22.3.2. Test during UI development

  1. if the RabbitMQ and MongoDB docker containers are not not running, launch them;

  2. set your environment variables with . $OF_HOME/bin/load_environment_light.sh;

  3. run the micro services using the same command as earlier: $OF_HOME/bin/run_all.sh start;

  4. if needed, enable a card-operation test flow using the script $OF_HOME/service/core/cards-publication/src/main/bin/push_cards_loop.sh;

  5. launch an angular server with the command: ng serve;

  6. test your changes in your browser using this url: localhost:4200 which leads to localhost:4200/#/feed.

23. Environment variables

These variables are loaded by bin/load_environment_light.sh bin/load_environment_ramdisk.sh

  • OF_HOME: OperatorFabric root dir

  • OF_CORE: OperatorFabric business services subroot dir

  • OF_INFRA: OperatorFabric infrastructure services subroot dir

  • OF_CLIENT: OperatorFabric client data definition subroot dir

  • OF_TOOLS: OperatorFabric tooling libraries subroot dir

Additionally, you may want to configure the following variables

  • Docker build proxy configuration (used to configure alpine apk proxy settings)

    • APK_PROXY_URI

    • APK_PROXY_HTTPS_URI

    • APK_PROXY_USER

    • APK_PROXY_PASSWORD

24. Project Structure

24.1. Tree View

project
├──bin
│   └─ travis
├──client
│   ├──cards (cards-client-data)
│   ├──src
│   ├──time (time-client-data)
│   └──users (users-client-data)
├──services
│   ├──core
│   │   ├──cards-consultation (cards-consultation-business-service)
│   │   ├──cards-publication (cards-publication-business-service)
│   │   ├──src
│   │   ├──thirds (third-party-business-service)
│   │   ├──time (time-business-service)
│   │   └──users (users-business-service)
│   ├──infra
│   │   ├──client-gateway (client-gateway-cloud-service)
│   │   ├──config (configuration-cloud-service)
│   │   └──registry (registry-cloud-service)
│   └──web
│       └──web-ui
├──src
|   ├──docs
|   │   ├──asciidoc
|   │   └──modelio
|   └──main
|       ├──docker
|       └──headers
├──tools
│   ├──generic
│   │   ├──test-utilities
│   │   └──utilities
│   ├── spring
│   │   ├──spring-amqp-time-utilities
│   │   ├──spring-mongo-utilities
│   │   ├──spring-oauth2-utilities
│   │   ├──spring-test-utilities
│   │   └──spring-utilities
│   └──swagger-spring-generators
└─ui

24.2. Content Details

24.3. Conventions regarding project structure and configuration

Sub-projects must conform to a few rules in order for the configured Gradle tasks to work:

24.3.1. Java

[sub-project]/src/main/java

contains java source code

[sub-project]/src/test/java

contains java tests source code

[sub-project]/src/main/resources

contains resource files

[sub-project]/src/test/resources

contains test resource files

24.3.2. Modeling

Core services projects declaring REST APIS that use Swagger for their definition must declare two files:

[sub-project]/src/main/modeling/swagger.yaml

Swagger API definition

[sub-project]/src/main/modeling/config.json

Swagger generator configuration

24.3.3. Docker

Services project all have docker image generated in their build cycle (See Gradle Tasks).

Per project configuration :

  • docker file : [sub-project]/src/main/docker/Dockerfile

  • docker-compose file : [sub-project]/src/main/docker/docker-compose.yml

  • runtime data : [sub-project]/src/main/docker/volume is copied to [sub-project]/build/docker-volume/ by task copyWorkingDir. The latest can then be mounted as volume in docker containers.

25. Development tools

25.1. Scripts (bin and CICD)

bin/build_all.sh

builds all artifacts as gradle is not able to manage inter project dependencies

bin/clean_all.sh

remove IDE data (project configuration, build output directory) - idea, vsc

bin/load_environment_light.sh

sets up environment when sourced (java version, gradle version, maven version, node version)

bin/load_environment_ramdisk.sh

sets up environment and links build subdirectories to a ramdisk when sourced at ~/tmp

bin/run_all.sh

runs all all services (see below)

bin/setup_dockerized_environment.sh

generate docker images for all services

25.1.1. load_environment_ramdisk.sh

There are prerequisites before sourcing load_environment_ramdisk.sh:

  • Logged user needs sudo rights for mount

  • System needs to have enough free ram

Never ever run a gradle clean or ./gradlew clean to avoid deleting those links.

25.1.2. run_all.sh

Please see run_all.sh -h usage before running.

Prerequisites

  • mongo running on port 27017 with user "root" and password "password" (See src/main/docker/mongodb/docker-compose.yml for a pre configured instance).

  • rabbitmq running on port 5672 with user "guest" and password "guest" (See src/main/docker/rabbitmq/docker-compose.yml for a pre configured instance).

Ports configuration

Port

2000

config

Configuration service http (REST)

2001

registry

Registry service http (REST)

2002

gateway

Gateway service http (REST+html)

2100

thirds

Third party management service http (REST)

2101

time

Time management service http (REST)

2102

cards-publication

card publication service http (REST)

2103

users

Users management service http (REST)

2104

cards-consultation

card consultation service http (REST)

4000

config

java debug port

4001

registry

java debug port

4002

gateway

java debug port

4100

thirds

java debug port

4101

time

java debug port

4102

cards-publication

java debug port

4103

users

java debug port

4103

cards-consultation

java debug port

25.1.3. setup_dockerized_environment.sh

Please see setup_dockerized_environment.sh -h usage before running.

Builds all sub-projects, generate docker images and volumes for docker-compose.

25.2. Gradle Tasks

In this section only custom tasks are described. For more information on tasks, refer to the output of the "tasks" gradle task and to gradle and plugins official documentation.

25.2.1. Services

Common tasks for all sub-projects
  • Test tasks

    • unitTest: runs unit tests

  • Other:

    • copyWorkingDir: copies [sub-project]/src/main/docker/volume to [sub-project]/build/

    • copyDependencies: copy dependencies to build/libs

Core
  • Swagger Generator tasks

    • debugSwaggerOperations: generate swagger code from /src/main/modeling/config.json to build/swagger-analyse

    • swaggerHelp: display help regarding swagger configuration options for java

Thirds Service
  • Test tasks

    • prepareTestDataDir: prepare directory (build/test-data) for test data

    • compressBundle1Data, compressBundle2Data: generate tar.gz third party configuration data for tests in build/test-data

    • prepareDevDataDir: prepare directory (build/dev-data) for bootRun task

    • createDevData: prepare data in build/test-data for running bootRun task during development

  • Other tasks

    • copyCompileClasspathDependencies: copy compile classpath dependencies, catching lombok that must be sent for sonarqube

infra/config
  • Test tasks

    • createDevData: prepare data in build/test-data for running bootRun task during development

tools/generic
  • Test tasks

    • prepareTestData: copy test data from src/test/data/simple to build/test-data/

    • compressTestArchive: compress the contents of /src/test/data/archive to /build/test-data/archive.tar.gz

25.2.2. Gradle Plugins

In addition to these custom tasks and standard Gradle tasks, OperatorFabric uses several Gradle plugins, among which:

26. Useful recipes

26.1. Running sub-project from jar file

  • gradle :[sub-projectPath]:bootJar

  • or java -jar [sub-projectPath]/build/libs/[sub-project].jar

26.2. Overriding properties when running from jar file

  • java -jar [sub-projectPath]/build/libs/[sub-project].jar –spring.config.additional-location=file:[filepath] NB : properties may be set using ".properties" file or ".yml" file. See Spring Boot configuration for more info.

  • Generic property list extract :

    • server.port (defaults to 8080) : embedded server port

  • :services:core:third-party-service properties list extract :

    • operatorfabric.thirds.storage.path (defaults to "") : where to save/load OperatorFabric Third Party data

26.3. Generating docker images

To Generate all docker images run bin/setup_dockerized_environment.sh.

INFORMATION: If you work behind a proxy you need to specify the following properties to configure alpine apk package manager:

  • apk.proxy.uri: proxy http uri ex: "http://somewhere:3128[somewhere:3128]" (defaults to blank)

  • apk.proxy.httpsuri: proxy http uri ex: "http://somewhere:3128[somewhere:3128]" (defaults to apk.proxy.uri value)

  • apk.proxy.user: proxy user

  • apk.proxy.password: proxy unescaped password

Alternatively, you may configure the following environment variables :

  • APK_PROXY_URI

  • APK_PROXY_HTTPS_URI

  • APK_PROXY_USER

  • APK_PROXY_PASSWORD

27. Troubleshooting

Proxy error when running third-party docker-compose

Error message
Pulling rabbitmq (rabbitmq:3-management)...
ERROR: Get https://registry-1.docker.io/v2/: Proxy Authentication Required
Possible causes & resolution

When running docker-compose files using third-party images(such as rabbitmq, mongodb etc.) the first time, docker will need to pull these images from their repositories. If the docker proxy isn’t set properly, you will see the above message.

To set the proxy, follow these steps from the docker documentation.

If your proxy needs authentication, add your user and password as follows:

HTTP_PROXY=http://user:password@proxy.example.com:80/
The password should be URL-encoded.

Gradle Metaspace error

Gradle task (for example gradle build) fails with the following error:

Error message
* What went wrong:
Metaspace
Possible causes & resolution

Issue with the Gradle daemon. Stopping the daemon using gradle --stop and re-launching the build should solve this issue.

Java version not available when setting up environment
When sourcing the load_environment_light script to set up your environment, you might get the following error message:

Error message
Stop! java 8.0.192-zulu is not available. Possible causes:
 * 8.0.192-zulu is an invalid version
 * java binaries are incompatible with Linux64
 * java has not been released yet

Select the next available version and update load_environment_light accordingly before sourcing it again.

Possible causes & resolution

The java version currently listed in the script might have been deprecated (for security reasons) or might not be available for your operating system (for example, 8.0.192-zulu wasn’t available for Ubuntu).

Run sdk list java to find out which versions are available. You will get this kind of output:

================================================================================
Available Java Versions
================================================================================
     13.ea.16-open       9.0.4-open          1.0.0-rc-11-grl
     12.0.0-zulu         8.0.202-zulu        1.0.0-rc-10-grl
     12.0.0-open         8.0.202-amzn        1.0.0-rc-9-grl
     12.0.0-librca       8.0.202.j9-adpt     1.0.0-rc-8-grl
     11.0.2-zulu         8.0.202.hs-adpt
     11.0.2-open         8.0.202-zulufx
     11.0.2-amzn         8.0.202-librca
     11.0.2.j9-adpt      8.0.201-oracle
     11.0.2.hs-adpt  > + 8.0.192-zulu
     11.0.2-zulufx       7.0.211-zulu
     11.0.2-librca       6.0.119-zulu
     11.0.2-sapmchn      1.0.0-rc-15-grl
     10.0.2-zulu         1.0.0-rc-14-grl
     10.0.2-open         1.0.0-rc-13-grl
     9.0.7-zulu          1.0.0-rc-12-grl

================================================================================
+ - local version
* - installed
> - currently in use
================================================================================

BUILD FAILED with message Execution failed for task ':ui:main-user-interface:npmInstall'.

Error message
FAILURE: Build failed with an exception.

    What went wrong:
    Execution failed for task ':ui:main-user-interface:npmInstall'.
Possible causes & resolution

A sudo has been used before the ./gradlew assemble.

Don’t use sudo to build OperatorFabric otherwise unexpected problems could arise.

28. Keycloak Configuration

The configuration needed for development purposes is automatically loaded from the dev-realms.json file. However, the steps below describe how they can be reproduced from scratch on a blank Keycloak instance in case you want to add to it.

The Keycloak Management interface is available here: [host]:89/auth/admin Default credentials are admin/admin.

28.1. Add Realm

  • Click top left down arrow next to Master

  • Add Realm

  • Name it dev (or whatever)

28.2. Setup at least one client (or best one per service)

28.2.1. Create client

  • Click Clients in left menu

  • Click Create Button

  • Set client ID to "opfab-client" (or whatever)

  • Select Openid-Connect Protocol

  • Enable Authorization

  • Access Type to Confidential

  • save

28.2.2. Add a Role to Client

  • In client view, click Roles tab

  • Click Add button

  • create a USER role (or whatever)

  • save == create a Mapper

Used to map the user name to a field that suits services

  • name it sub

  • set mapper type to User Property

  • set Property to username

  • set Token claim name to sub

  • enable add to access token

  • save

28.3. Create Users

  • Click Users in left menu

  • Click Add User button

  • Set username to admin

  • Save

  • Select Role Mappings tab

  • Select "opfab-client" in client roles combo (or whatever id you formerly chose)

  • Select USER as assigned role (or whatever role you formerly created)

  • Select Credentials tab

  • set password and confirmation to "test" *

repeat process for other users: rte-operator, tso1-operator, tso2-operator

28.3.1. Development-specific configuration

To facilitate development, in the configuration file provided in the git (dev-realms.json) ,session are set to have a duration of 10 hours (36000 seconds) and SSL is not required. These parameters should not be used in production.

The following parameters are set : accessTokenLifespan : 36000 ssoSessionMaxLifespan : 36000 accessCodeLifespan" : 36000 accessCodeLifespanUserAction : 36000 sslRequired : none

OperatorFabric Community

29. Introduction

The aim of this document is to present the OperatorFabric community, its code of conduct and to welcome contributors!

30. Contributing

Thank you for your interest !

We can’t stress enough that feedback, discussions, questions and contributions on OperatorFabric are very much appreciated. However, because the project is still in its early stages, we’re not fully equipped for any of it yet, so please bear with us while the contribution process and tooling are sorted out.

30.1. Code of Conduct

This project and everyone participating in it is governed by the OperatorFabric Code of Conduct. By participating, you are expected to uphold this code. Please report unacceptable behavior to opfab_AT_lists.lfenergy.org.

30.2. License and Developer Certificate of Origin

OperatorFabric is an open source project licensed under the Mozilla Public License 2.0. By contributing to OperatorFabric, you accept and agree to the terms and conditions for your present and future contributions submitted to OperatorFabric.

The project also uses a mechanism known as a Developer Certificate of Origin (DCO) to ensure that we are legally allowed to distribute all the code and assets for the project. A DCO is a legally binding statement that asserts that you are the author of your contribution, and that you wish to allow OperatorFabric to use your work.

Contributors sign-off that they adhere to these requirements by adding a Signed-off-by line to commit messages. All commits to any repository of the OperatorFabric organization have to be signed-off like this:

This is my commit message.

Signed-off-by: John Doe <john.doe@email-provider.com>

You can write it manually but Git has a -s command line option to append it automatically to your commit message:

$ git commit -s -m 'This is my commit message'

Note that in the future a check will be performed during the integration, making sure all commits in the Pull Request contain a valid Signed-off-by line.

30.3. How Can I Contribute ?

These processes and templates have been adapted from the ones defined by the PowSyBl project.

30.3.1. Reporting Bugs and Suggesting Enhancements

Work in Progress

30.3.2. Contributing Code

Code Guidelines
  • We don’t mention specific authors by name in each file (in Javadoc or in the documentation for example), so has not to have to maintain these mentions (since this information is tracked by git anyway).

  • If you are working on a JIRA issue:

    • Please name your branch with the corresponding JIRA issue ID: OC-XXX

    • Start all your commit messages with [OC-XXX]

      This allows the branch, PR and commits to be directly accessible from the JIRA issue.

  • Before submitting your PR, please rebase your branch against the current master to minimize conflicts when merging.

    git checkout OC-XXX
    git fetch
    git rebase origin/master
  • Before submitting your PR, please squash/fix your commits so as to reduce the number of commits and comment them accordingly. In the end, the division of changes into commits should make the PR easier to understand and review.

  • Feel free to add a copyright header (on top of the existing ones) to files you create or amend. See src/main/headers for examples.

Contribution Process

Code Contribution is tracked as GitHub Pull Requests. Crafting a good pull request takes time and energy and we will help as much as we can, but be prepared to follow our iterative process. The iterative process has several goals:

  • Maintain the quality of the project

  • Fix issues that are important to users

  • Allow everyone from the community to take part in the discussions, to reach the best possible solution

  • Make the review by OperatorFabric maintainers as efficient as possible

Please follow these steps to have your contribution considered by the maintainers:

  1. Fill in all the required information in the template.

  2. Request a GitHub review by one of the core developers

  3. Take their review into account or discuss the requested changes. Please don’t take criticism personally, it is normal to iterate on this step several times.

  4. Repeat step 3 until the pull request is merged!

Continuous integration is set up to run on all branches automatically and will often report problems, so don’t worry about getting everything perfect on the first try (SonarCloud Analysis is a notorious problem source). Until you add a reviewer, you can trigger as many builds as you want by amending your commits. The status checks enforce the following:

  • All tests in the test suite pass

  • Checkstyle and SonarCloud report no violations (coming soon)

  • The code coverage is high enough (currently about 85%) (coming soon)

30.4. Project Governance

30.4.1. Project Owner

OperatorFabric is part of the LF Energy Foundation, a project of the Linux Foundation that supports open source innovation projects within the energy and electricity sectors.

30.4.2. Committers

Committers are contributors who have made several valuable contributions to the project and are now relied upon to both write code directly to the repository and screen the contributions of others. In many cases they are programmers but it is also possible that they contribute in a different role. Typically, a committer will focus on a specific aspect of the project, and will bring a level of expertise and understanding that earns them the respect of the community and the project owner.

30.4.3. Technical Steering Committee

The Technical Steering Committee (TSC) is composed of voting members elected by the active Committers as described in the project’s Technical Charter.

OperatorFabric TSC voting members are:

Boris Dolley will chair the TSC, with Hanae Safi as his deputy.

30.4.4. Contributors

Contributors include anyone in the technical community that contributes code, documentation, or other technical artifacts to the project.

Anyone can become a contributor. There is no expectation of commitment to the project, no specific skill requirements and no selection process. To become a contributor, a community member simply has to perform one or more actions that are beneficial to the project.

30.5. Communication channels

In addition to GitHub we have set up:

30.5.1. Website: opfab.org

Our website contains all the documentation and resources we’re currently working on. Here is what we aim to provide:

  • Architecture documentation

  • REST API documentation

  • Reference documentation for each component

  • Javadoc/Compodoc for each component

  • Tutorials and QuickStart guides and videos

This documentation is our priority right now so future contributors can quickly find their way around the project. Needless to say, it’s a work in progress so feel free to tell us what you feel is missing or what type of documentation you would be interested in as a contributor.

We also use this website to broadcast any news we have about the project so don’t hesitate to subscribe to the RSS feed on the home page to be informed of any update.

30.5.2. Spectrum Community : spectrum.chat/opfab

If you would like to join the discussions regarding OperatorFabric, please join our community on Spectrum!

Regarding issue tracking, our Jira platform should be open soon.

30.5.3. LF Energy Mailing Lists

Several mailing lists have been created by LF Energy for the project, please feel free to subscribe to the ones you could be interested in:

And if you’re interested in LF Energy in general: LF Energy General Discussion

30.5.4. JIRA

31. Contributor Covenant Code of Conduct

31.1. Our Pledge

In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socioeconomic status, nationality, personal appearance, race, religion, or sexual identity and orientation.

31.2. Our Standards

Examples of behavior that contributes to creating a positive environment include:

  • Using welcoming and inclusive language

  • Being respectful of differing viewpoints and experiences

  • Gracefully accepting constructive criticism

  • Focusing on what is best for the community

  • Showing empathy towards other community members

Examples of unacceptable behavior by participants include:

  • The use of sexualized language or imagery and unwelcome sexual attention or advances

  • Trolling, insulting/derogatory comments, and personal or political attacks

  • Public or private harassment

  • Publishing others' private information, such as a physical or electronic address, without explicit permission

  • Other conduct which could reasonably be considered inappropriate in a professional setting

31.3. Our Responsibilities

Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.

Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.

31.4. Scope

This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.

31.5. Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at powsybl.ddl@rte-france.com. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.

Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project’s leadership.

31.6. Attribution

This Code of Conduct is adapted from the Contributor Covenant, version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html

For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq

OperatorFabric CICD

32. Introduction

The aim of this document is to describe our CICD pipeline and our release process.

33. Release process

33.1. Version numbers

Version numbers X.Y.Z.label should be understood like this:

  • X: Major version, a major version adds new features and breaks compatibility with previous major and minor versions.

  • Y: Minor version, a minor version adds new features and does not break compatibility with previous minor versions for the same major version.

  • Z: Patch, a patch version only contains bug fixes of current minor version

  • Label: pre-release or build metadata:

    • SNAPSHOT: a version whose development is in progress, nightly builds only deliver SNAPSHOT versions

    • RELEASE: a version whose development is finished

33.2. Releasing Version

To release a version we use some Travis dedicated jobs. These jobs are triggered by specific commit keywords and rely on the VERSION file at the root of this repository to know which version is being produced. It is thus crucial to double-check the content of this file before any push (triggering the Travis jobs) is made.

Before releasing a version, you need to prepare the release.

Considering a version X.X.X.SNAPSHOT.

33.2.1. In the source repository (operatorfabric-core)

  1. Use the ./CICD/prepare_release_version.sh script to automatically perform all the necessary changes:

    ./CICD/prepare_release_version.sh

    This will perform the following changes:

    • Replace X.X.X.SNAPSHOT with X.X.X.RELEASE in *.adoc, swagger.yaml files and the VERSION file at the root operator-fabric folder

    • Update the revision date in *.adoc files (by default, the new revision date is today, but see the script usage for details)

    • Change the version from W.W.W.RELEASE (previous release) to X.X.X.RELEASE in the deploy docker-compose file

      The VERSION file is particularly important as the CI/CD pipeline tasks (building the documentation and the docker images for example) are based on it.
  2. Commit with the template message:

    [RELEASE] X.X.X.RELEASE (ci_docker,ci_documentation)

    The commit comment leverages these three keywords to trigger the delivery of documentation and docker images, so you should check that they are correctly spelt.

    • ci_docker: triggers the build and upload of versioned docker images to dockerhub

    • ci_documentation: triggers the build and upload of the current documentation

  3. Tag the commit with the version

    git tag X.X.X.RELEASE
  4. Push the commit

    git push
  5. Push the tag

    git push origin X.X.X.RELEASE
  6. Check that the build is correctly triggered

    You can check the status of the build job triggered by the commit on Travis CI. The build job should have the following three stages:

    Running build status page screenshot

    Wait for the build to complete (around 30 minutes).

  7. Check that the X.X.X.RELEASE images have been generated and pushed to DockerHub.

  8. Check that the latest images have been updated on DockerHub.

  9. Check that the documentation has been generated and pushed to the GitHub pages website (check version and revision date at the top of document)

    You won’t be able to access it from the homepage until you have updated the _version.yml file in the opfab.github.io repository, so instead you can just type opfab.github.io/documentation/X.X.X.SNAPSHOT/developer_guide/ in your browser.
  10. Check that the tag was correctly pushed to GitHub and is visible under the releases page for the repository.

33.2.2. Checking deploy docker-compose

The deploy docker-compose file should always rely on the latest RELEASE version available on DockerHub. Once the CI pipeline triggered by the previous steps has completed successfully, and you can see X.X.X.RELEASE images for all services on DockerHub, you should:

  1. Remove your locally built X.X.X.RELEASE images if any

  2. Run each of the two docker-compose files to make sure they pull the images from DockerHub and behave as intended.

33.2.3. In the documentation repository (opfab.github.io)

  1. In the _data/versions.yml file:

    • Change:

      - name: X.X.X
        fullname: X.X.X.SNAPSHOT
        badges:
        - name: SNAPSHOT
          style: badge-snapshot

      to:

      - name: X.X.X
        fullname: X.X.X.RELEASE
        badges:
        - name: RELEASE
          style: badge-release
        - name: CURRENT
          style: badge-current
    • Then remove the following lines from the previous release version:

        - name: CURRENT
          style: badge-current
  2. Commit and push changes

33.2.4. In Jira

  1. Set all concerned tickets (US, BUG, FR) and set fix version to X.X.X.RELEASE

  2. In the "Releases" screen release X.X.X.RELEASE version

33.3. Advertising the new release (opfab.github.io and mailing list)

  1. Click the appropriate version from JIRA the release list to get the release notes (click "Release notes" under the version name at the top) listing new features, fixed bugs etc…​

    Release notes link
  2. Take this text and use it to create a short post on the opfab.github.io repository (under _posts) announcing the release. This post should highlight new features and necessary upgrade actions if any.

  3. Send the same content as an email to the opfab-announce@lists.lfenergy.org mailing list.

33.4. Preparing next version

You should wait for all the tasks associated with creating the X.X.X.RELEASE version to finish and make sure that they’ve had the expected output before starting the preparation of the next version. This is because any committed/pushed changes preparing the new version will make rolling back or correcting any mistake on the release more complicated.

To prepare a next version you simply need to increment the version after a release (see Version numbers).

33.4.1. In the source repository (operatorfabric-core)

  1. Use the ./CICD/prepare_snapshot_version.sh script to automatically perform all the necessary changes:

    ./CICD/prepare_snapshot_version.sh --version Y.Y.Y.SNAPSHOT

    This will perform the following changes:

    • Replace all occurrences of X.X.X.RELEASE by Y.Y.Y.SNAPSHOT EXCEPT in the deploy docker-compose file (src/main/docker/deploy/docker-compose.yml). The files concerned are *.adoc, swagger.yaml files and the VERSION file at the root operatorfabric-core folder.

      The VERSION file is particularly important as the CI/CD pipeline tasks (building the documentation and the docker images for example) are based on it.
      If no --version parameter is provided to the script, the new version will be the next minor version.
  2. Add a Y.Y.Y.RELEASE.adoc file (blank except for copyright and title) under src/docs/asciidoc/release_notes to initialize the release notes for next version.

  3. Commit and push changes with the following message:

    [PREPARE] next version: Y.Y.Y.SNAPSHOT

33.4.2. In the documentation repository (opfab.github.io)

  1. In the _data/versions.yml file, add the Y.Y.Y.SNAPSHOT version

    - name: Y.Y.Y
      fullname: Y.Y.Y.SNAPSHOT
      badges:
      - name: SNAPSHOT
        style: badge-snapshot
  2. Commit and push changes.

33.4.3. In Jira

  1. In the "Releases" screen create a Y.Y.Y.RELEASE version.

33.4.4. Checking DockerHub the following day

A CRON Travis job runs daily (the time of the day may vary) and generates the documentation and docker snapshot images. After you have finished switching to the Y.Y.Y.SNAPSHOT version, you should wait for such a job to run and check that the snapshot images are correctly generated and pushed on DockerHub.

34. CICD Scripts

CICD/prepare_release_version.sh

performs the changes necessary to tag a RELEASE version

CICD/prepare_snapshot_version.sh

performs the changes necessary to tag a SNAPSHOT version