# Connector Functions

Devices rarely speak the protocol you wish they did. A meter answers DLMS, a legacy gateway needs a Telnet
command, a sensor posts a binary frame nobody else understands. A **connector function** is your own
JavaScript, running inside the platform, that translates between OpenGate and that reality.

No middleware to deploy, no service to keep alive: you `POST` the script, and OpenGate runs it at the
moment the data or the operation passes through.

## What one looks like

A connector function is a JSON document with a `javascript` field holding the code, plus the metadata that
tells OpenGate **when** to run it:

```json
{
  "name": "CollectionInfoConnectorFunction",
  "description": "Connector function for collected data",
  "operationalStatus": "DISABLED",
  "type": "COLLECTION",
  "southCriterias": [
    "http://collected/data",
    "mqtt://iot/collected"
  ],
  "javascript": "... javascript code ..."
}
```

Each function belongs to exactly one **channel**, and its `name` must be unique within that channel.

## The three types

The type answers *which direction is this translating?*

```mermaid
flowchart TB
    APP["Back-office application"] -->|"launches an operation"| REQ["REQUEST"]
    REQ -->|"speaks the device protocol"| DEV["Device"]
    DEV -->|"answers the operation"| RES["RESPONSE"]
    DEV -->|"pushes data"| COL["COLLECTION"]
    RES -->|"operation result"| OUT["Operation updated"]
    COL -->|"data points"| STO["Platform storage"]

    classDef cf fill:#addcf8,stroke:#2b7cb8,color:#000,font-weight:bold
    class REQ,RES,COL cf
```

The three blue boxes are the connector functions: your JavaScript, at the point where each translation
happens.

| Type | Runs when | Must return |
|---|---|---|
| **`REQUEST`** | The platform has an operation to send to the device | Nothing is required. Return `null`, or omit the `return`, and the operation stays open until a response arrives. Return the `response` object and the operation finishes right there |
| **`RESPONSE`** | Something arrives from the device at a south URI, answering an operation | The OpenGate standard **response** object. Return nothing and no operation update happens |
| **`COLLECTION`** | Something arrives from the device at a south URI, carrying data | The OpenGate standard **collection** object. Return nothing and nothing is collected |

The [core JavaScript API](core_javascript_api/) gives you the `response` and `collection` objects to build
those returns without assembling JSON by hand.

## Criteria: how OpenGate picks your function

| Criteria | Used by | Meaning |
|---|---|---|
| `northCriterias` | `REQUEST` only, mandatory | Matches the operation coming from the platform. Two functions cannot share the same list |
| `southCriterias` | `RESPONSE` and `COLLECTION`, mandatory | One or more URIs the device talks to. **Each URI can belong to only one connector function** |

A `REQUEST` function also needs `operationName`, which must be an operation type you are allowed to use,
and must leave `southCriterias` unset. `RESPONSE` and `COLLECTION` functions are the mirror image: south
criteria set, `northCriteria` and `operationName` unset.

South criteria are URIs carrying the protocol, and the accepted set is configurable:

```
https://   mqtts://   wss://   snmps://   dlms://   coaps://
```

## When it runs, and on which devices

`operationalStatus` is what keeps a half-written function from touching your fleet:

| Value | OpenGate runs the function |
|---|---|
| `DISABLED` | Never |
| `TEST` | Only on devices whose operational status is `TEST` |
| `PRODUCTION` | Only on devices whose operational status is `PRODUCTION` |

The natural order is therefore: create it `DISABLED`, move it to `TEST` against a test device, and only
then to `PRODUCTION`.

## Chaining functions

A function can hand over to another when it finishes, using `cf.response` and `cf.collection`. Only these
hand-offs are honoured — anything else is silently ignored:

| From | Can invoke |
|---|---|
| `REQUEST` | `RESPONSE`, `COLLECTION`, or both |
| `RESPONSE` | `COLLECTION` |
| `COLLECTION` | Nothing |

That is what lets a single device message both close an operation and collect the readings it carried. See
[Concatenated Connector Functions](core_javascript_api/concatenated/).

## Where to go next

| To | Read |
|---|---|
| Write the script: what it receives, what it must return | [Core JavaScript API](core_javascript_api/) |
| Talk a specific protocol: DLMS, SNMP, SSH, MQTT… | [Protocol APIs](protocol_apis/) |
| Clone a ready-made function instead of writing one | [Catalog API](catalog_api/) |
| Debug a function that is already running | [Debugging](../../debugging/) |

## Managing them over REST

```bash
/north/v80/connectorFunctions/provision/organizations/{organizationName}/channels/{channelName}
```

| Action | Profiles allowed |
|---|---|
| `CREATE`, `UPDATE`, `DELETE` | `admin`, `admin_domain`, `super_admin_domain`, `root` |
| `GET`, list | Any profile |

Once created, `identifier` and `type` are **immutable**. Everything else can be updated.

Create a `COLLECTION` function in a channel:

```bash
curl -X POST 'https://api.opengate.es/north/v80/connectorFunctions/provision/organizations/{organizationName}/channels/{channelName}' \
  -H 'X-ApiKey: <your-api-key>' \
  -H 'Content-Type: application/json' \
  -d @connector-function.json
```

A valid request returns `201` with a `Location` header carrying the identifier of the new function.

List the connector functions of a channel:

```bash
curl 'https://api.opengate.es/north/v80/connectorFunctions/provision/organizations/{organizationName}/channels/{channelName}' \
  -H 'X-ApiKey: <your-api-key>'
```

{{% notice style="info" title="The REST API barely reads your JavaScript" icon="lightbulb" %}}
Creating or updating a connector function performs only minimal JavaScript parsing. A script that is
syntactically odd but parseable will be accepted and fail at runtime, which is why `TEST` status and
[debugging](../../debugging/) matter.
{{% /notice %}}

{{% children sort="weight" %}}

## API specification

{{< openapi src="connectorfunctions.yaml" >}}
