EliasDB Event Condition Action Language
=======================================

EliasDB supports a scripting language called [Event Condition Action Language (ECAL)](https://devt.de/krotik/ecal/) to enable rule based scripting functionality. ECAL provides [database trigger](https://en.wikipedia.org/wiki/Database_trigger) functionality for EliasDB.

ECAL was added for the following use-cases:
- Providing a way to manipulate data in response to events
- Enforce certain aspects of a database schema
- Providing back-end logic for web applications using EliasDB

The source of EliasDB comes with a game example which demonstrates some aspects of ECAL. See the documentation here: [TODO Link to documentation]

ECAL related config values:
--
These ECAL related config options are available in `eliasdb.config.json`:
| Configuration Option | Description |
| --- | --- |
| EnableECALScripts | Enable ECAL scripting. |
| ECALScriptFolder | Scripting folder for ECAL scripts. |
| ECALEntryScript | Entry script in the script folder. |
| ECALLogFile | File in which the logs should be written (use an empty string for stdout). |
| ECALLogLevel | Log level for the printed logs. |
| EnableECALDebugServer | Enable debugging and start the ECAL debug server. Note: Activating debugging will slow down the interpreter speed significantly! |
| ECALDebugServerHost | Host for the debug server. |
| ECALDebugServerPort | Port for the debug server. |

ECAL Debugging
--
If the debug server is enabled in the config file then it is possible to debug ECAL scripts with [VSCode](https://devt.de/krotik/ecal/src/master/ecal-support/README.md). The debugger supports break points and thread state inspection. It is also possible to restart and reload the scripts.

Using the `-ecal-console` parameter it is possible to open an interactive console into the server process. If used together with the debug server additional debug commands are available also there. Enter `?` to see the build-in documentation.

EliasDB specific events which can be handled:
--
The ECAL interpreter in EliasDB receives the following events:

Web Request | ECAL event kind | Event state contents | Description
-|-|-|-
/db/api/|`db.web.api`| bodyJSON, bodyString, header, method, path, pathList, query | Any web request to /db/api/... These endpoints are public and never require authentication.
/db/ecal/|`db.web.ecal`| bodyJSON, bodyString, header, method, path, pathList, query | Any web request to /db/ecal/... These endpoints are considered internal and require authentication if access control is enabled.
/db/sock/|`db.web.sock`| bodyJSON, bodyString, commID, header, method, path, pathList, query | Any web request to /db/sock/... These endpoints are used to initiate websocket connections.
-|`db.web.sock.data`| commID, data, header, method, path, pathList, query | An existing websocket connection received some JSON object data. If the close attribute of the object is set to true then the websocket connection is closed.

EliasDB can receive the following events from the ECAL interpreter:

ECAL event kind | Event state contents | Description
-|-|-
db.web.sock.msg | commID, payload, close | The payload is send to a client with an open websocket identified by the commID.


EliasDB Graph Event | ECAL event kind | Event state contents | Description
-|-|-|-
graph.EventNodeCreated | `db.node.created` | part, trans, node | A node was created.
graph.EventNodeUpdated | `db.node.updated` | part, trans, node, old_node | A node was updated.
graph.EventNodeDeleted | `db.node.deleted` | part, trans, node | A node was deleted.
graph.EventEdgeCreated | `db.edge.created` | part, trans, edge | An edge was created.
graph.EventEdgeUpdated | `db.edge.updated` | part, trans, edge, old_edge | An edge was updated.
graph.EventEdgeDeleted | `db.edge.deleted` | part, trans, edge | An edge was deleted.
graph.EventNodeStore | `db.node.store` | part, trans, node | A node is about to be stored (always overwriting existing values).
graph.EventNodeUpdate | `db.node.update` | part, trans, node | A node is about to be updated.
graph.EventNodeDelete | `db.node.delete` | part, trans, key, kind | A node is about to be deleted.
graph.EventEdgeStore | `db.edge.store` | part, trans, edge | An edge is about to be stored.
graph.EventEdgeDelete | `db.edge.delete` | part, trans, key, kind | An edge is about to be deleted.

Note: EliasDB will wait for the event cascade to be finished before performing the actual operation (e.g. inserting a node). If the event handling requires a time consuming operation then a new parallel event cascade can be started using `addEvent` with a scope:
```
addEvent("request", "foo.bar.xxx", {
   "payload" : 123
}, {
   "": true  # This scope allows all events
})
```

EliasDB specific functions:
--
The ECAL interpreter in EliasDB supports the following EliasDB specific functions:

#### `db.storeNode(partition, nodeMap, [transaction])`
Inserts or updates a node in EliasDB.

Parameter | Description
-|-
partition | Partition of the node
nodeMap | Node object as a map with at least a key and a kind attribute
transaction | Optional a transaction to group a set of changes

Example:
```
db.storeNode("main", {
  "key" : "foo",
  "kind" : "bar",
  "data" : 123,
})
```

#### `db.updateNode(partition, nodeMap, [transaction])`
Updates a node in EliasDB (only update the given values of the node).

Parameter | Description
-|-
partition | Partition of the node
nodeMap | Node object as a map with at least a key and a kind attribute
transaction | Optional a transaction to group a set of changes

Example:
```
db.updateNode("main", {
  "key" : "foo",
  "kind" : "bar",
  "data" : 123,
})
```

#### `db.removeNode(partition, nodeKey, nodeKind, [transaction])`
Removes a node in EliasDB.

Parameter | Description
-|-
partition | Partition of the node
nodeKey | Key attribute of the node to remove
nodeKind | Kind attribute of the node to remove
transaction | Optional a transaction to group a set of changes

Example:
```
db.removeNode("main", "foo", "bar")
```

#### `db.fetchNode(partition, nodeKey, nodeKind)`
Fetches a node in EliasDB.

Parameter | Description
-|-
partition | Partition of the node
nodeKey | Key attribute of the node to fetch
nodeKind | Kind attribute of the node to fetch

Example:
```
db.fetchNode("main", "foo", "bar")
```

#### `db.storeEdge(partition, edgeMap, [transaction])`
Inserts or updates an edge in EliasDB.

Parameter | Description
-|-
partition | Partition of the edge
edgeMap | Edge object as a map with at least the main attributes: key, kind, end1cascading, end1key, end1kind, end1role, end2cascading, end2key, end2kind, end2role
transaction | Optional a transaction to group a set of changes

Example:
```
db.storeEdge("main", {
  "key":           "123",
  "kind":          "myedges",
  "end1cascading": true,
  "end1key":       "foo",
  "end1kind":      "bar",
  "end1role":      "role1",
  "end2cascading": false,
  "end2key":       "key2",
  "end2kind":      "kind2",
  "end2role":      "role2",
})
```

#### `db.removeEdge(partition, edgeKey, edgeKind, [transaction])`
Removes an edge in EliasDB.

Parameter | Description
-|-
partition | Partition of the edge
edgeKey | Key attribute of the edge to remove
edgeKind | Kind attribute of the edge to remove
transaction | Optional a transaction to group a set of changes

Example:
```
db.removeEdge("main", "123", "myedges")
```

#### `db.fetchEdge(partition, edgeKey, edgeKind)`
Fetches an edge in EliasDB.

Parameter | Description
-|-
partition | Partition of the edge
edgeKey | Key attribute of the edge to fetch
edgeKind | Kind attribute of the edge to fetch

Example:
```
db.fetchEdge("main", "123", "myedges")
```

#### `db.traverse(partition, nodeKey, nodeKind, traversalSpec)`
Traverses an edge in EliasDB from a given node. Returns a list of nodes which were
reached and a list of edges which were followed.

Parameter | Description
-|-
partition | Partition of the node
nodeKey | Key attribute of the node to traverse from
nodeKind | Kind attribute of the node to traverse from
traversalSpec | Traversal spec

Example:
```
[nodes, edges] := db.traverse("main", "foo", "bar", "role1:myedges:role2:kind2")
```

#### `db.newTrans()`
Creates a new transaction for EliasDB.

Example:
```
trans := db.newTrans()
```

#### `db.newRollingTrans(n)`
Creates a new rolling transaction for EliasDB. A rolling transaction commits after n entries.

Parameter | Description
-|-
n | Rolling threshold (number of operations before rolling)

Example:
```
trans := db.newRollingTrans(5)
```

#### `db.commit(transaction)`
Commits an existing transaction for EliasDB.

Parameter | Description
-|-
transaction | Transaction to execute

Example:
```
db.commit(trans)
```

#### `db.query(partition, query)`
Run an EQL query.

Parameter | Description
-|-
partition | Partition to query
query | Query to execute

Example:
```
db.commit("main", "get bar")
```

#### `db.graphQL(partition, query, [variables], [operationName])`
Run a GraphQL query.

Parameter | Description
-|-
partition | Partition to query
query | Query to execute
variables | Map of variables for the query
operationName | Operation to execute (useful if the query defines more than a single operation)

Example:
```
db.graphQL("main", "query myquery($x: string) { bar(key:$x) { data }}", {
  "x": "foo",  
}, "myquery")
```

#### `db.raiseGraphEventHandled()`
When handling a graph event, notify the GraphManager of EliasDB that no further action is necessary. This creates a special error object and should not be used inside a `try` block. When using a `try` block this can be used inside an `except` or `otherwise` block.

Example:
```
sink mysink
  kindmatch [ "db.*.*" ],
{
  db.raiseGraphEventHandled()
}
```

#### `db.raiseWebEventHandled()`
When handling a web event, notify the web API of EliasDB that the web request was handled. This creates a special error object and should not be used inside a `try` block. When using a `try` block this can be used inside an `except` or `otherwise` block.

Example:
```
sink mysink
  kindmatch [ "web.*.*" ],
{
  db.raiseWebEventHandled({
    "status" : 200,
    "headers" : {
      "Date": "today"
    },
    "body" : {
      "mydata" : [1,2,3]
    }
  })
}
```
