# Coding an UbiFunction

## Runtimes

UbiFunctions accept [NodeJS ](https://nodejs.org/en/blog/release/v10.0.0/)or [Python](https://www.python.org/downloads/release/python-370/), which means you can use these languages to write the  required logic to extract, transform, and analyze data.&#x20;

## Entrypoint

Regardless of the chosen runtime, the UbiFunction must define a *main* function. This function serves as the entrypoint, meaning it is the first function called when a request is made to the UbiFunction.

## UbiFunction arguments

UbiFunctions require a JSON payload to be sent in the body of the HTTP request. This payload is passed to the `main` function as a dictionary object named `args`. The `args` object contains all the parameters sent in the request, making them accessible within your function for further usage.

For UbiFunctions configured with the **GET** method, the `args` object instead contains the **query parameters** passed in the URL.

Additionally, when an `X-Auth-Token` header is included in the request, its value is automatically injected into `args` as `args["token"]`, regardless of whether the request is made via `GET` or `POST`.&#x20;

{% tabs %}
{% tab title="NodeJS" %}

```javascript
async function main(args) {
  var ubidots_token = args?.token;
  var device_label = args.device;
}
```

{% endtab %}

{% tab title="Python" %}

```python
def main(args):
    token = args.get('token', None)
    device = args.get('device', None)
```

{% endtab %}
{% endtabs %}

| Object       | Description                                                                                                     |
| ------------ | --------------------------------------------------------------------------------------------------------------- |
| `args`       | Any payload sent to the function will be contained by the `args` object.                                        |
| `args.token` | When an `x-auth-token` header is included in an HTTP invokation request, the `args.token` key will be included. |

### Behavior of the \`args\` object according to HTTP methods

{% tabs %}
{% tab title="GET" %}

```http
GET /prv/your_user/function_name?device=my_device&limit=10 HTTP/1.1
Host: parse.ubidots.com
X-Auth-Token: BBUS-abcdef0987654321

// Args within the UbiFunction
{
   "device": "my_device",
   "limit": "10",
   "token": "BBUS-abcdef0987654321\
}
```

{% endtab %}

{% tab title="POST " %}

```http
POST /prv/your_user/function_name HTTP/1.1
Host: parse.ubidots.com
Content-Type: application/json
X-Auth-Token: BBFF-1234567890abcdef

{
  "temperature": 22.5,
  "location": "lab"
}


// Args within the UbiFunction
//{
//  "temperature": 22.5,
//  "location": "lab",
//  "token": "BBFF-1234567890abcdef"
//}

```

{% endtab %}
{% endtabs %}

## Code

By default, every new UbiFunction has a sample code that uses input data (`token`, `device`, and a `variable value`) to make a request to Ubidots API. Here are the default examples for your reference:

{% tabs %}
{% tab title="NodeJS" %}

```javascript
const axios = require('axios');

// Main function - runs every time the function is executed.
// "args" is a dictionary containing both the URL params and the HTTP body (for POST requests).
async function main(args) {

  // Grab the token and device label from URL parameters, then erase them from the args dictionary
  var ubidots_token = args.token;
  var device_label = args.device;
  delete args['token'];
  delete args['device'];

  // Use the remaining parameters as payload
  var payload = args;

  // Log the payload to the console, for debugging purposes. You may access the function's logs using
  // the option in the above header.
  console.log(payload);

  // Send the payload to Ubidots
  var response = await ubidots_request(ubidots_token, device_label, payload);

  // Log Ubidots response to the console
  console.log(response);

  // Pass Ubidots' API response as the function's reponse
  return response;
}

// This function builds an HTTP POST request to Ubidots
async function ubidots_request(token, label, body) {
  let config = {
    method: 'post',
    url: 'https://industrial.api.ubidots.com/api/v1.6/devices/' + label,
    data: body,
    headers: {
      'Content-Type': 'application/json',
      'X-Auth-Token': token
    }
  }
  const response = await axios.request(config);
  return response.data;
}

```

{% endtab %}

{% tab title="Python" %}

```python
import requests
import time

BASE_URL = "https://industrial.api.ubidots.com"

REQUESTS_FUNCTIONS = {"get": requests.get, "post": requests.post}

def main(args):
    '''
    Main function - runs every time the function is executed.
    "args" is a dictionary containing both the URL params and the HTTP body (for POST requests).
    '''
    token = args.get('token', None)
    device = args.get('device', None)

    if token is None or device is None:
        print("[ERROR] Please send your Ubidots Token and device label to update in your args")
        return {"status": "error"}
    
    del args['token']
    del args['device']

    # Log the payload to the console, for debugging purposes. You may access the function's logs using
    # the option in the above header.

    print("[INFO] Payload to send: {}".format(args))

    # Use the remaining parameters as payload
    req = update_device(device, args, token)

    # Prints the request result

    print("[INFO] Request result:")
    print(req.text)

    return {"status": "Ok", "result": req.json()}

def update_device(device, payload, token):
    """
    updates a variable with a single dot
    """

    url = "{}/api/v1.6/devices/{}".format(BASE_URL, device)
    headers = {"X-Auth-Token": token, "Content-Type": "application/json"}

    req = create_request(url, headers, payload, attempts=5, request_type="post")
    
    return req

def create_request(url, headers, data, attempts, request_type):
    """
    Function to create a request to the server
    """

    request_func = REQUESTS_FUNCTIONS.get(request_type)

    kwargs = {"url": url, "headers": headers}

    if request_type == "post":
        kwargs["json"] = data

    try:
        req = request_func(**kwargs)
        print("[INFO] Request result: {}".format(req.text))
        status_code = req.status_code
        time.sleep(1)

        while status_code >= 400 and attempts < 5:
            req = request_func(**kwargs)
            print("[INFO] Request result: {}".format(req.text))
            status_code = req.status_code
            attempts += 1
            time.sleep(1)

        return req
    except Exception as e:
        print("[ERROR] There was an error with the request, details:")
        print(e)
        return None

```

{% endtab %}
{% endtabs %}

## Output Response

The output of an UbiFunction must be a JSON object. By default, the status code will be `200`, but this could be changed using [Raw Functions](https://dev.ubidots.com/ubifunctions/advanced/raw-functions).

{% tabs %}
{% tab title="NodeJS" %}

```javascript
function main(args) {
    return {'temperature' : 56}
}
```

{% endtab %}

{% tab title="Python" %}

```python
def main():
    return {'temperature' : 56}
```

{% endtab %}
{% endtabs %}
