# UbiFunctions

This command allows you to:

* Perform CRUD operations over UbiFunctions in Ubidots.
* Set up a local development environment for UbiFunctions.

Its subcommands are:

```bash
Usage: ubidots functions [OPTIONS] COMMAND [ARGS]...

 Tool for managing and deploying functions.

╭─ Options ───────────────────────────────────────────────────────────────────╮
│ --help          Show this message and exit.                                 │
╰─────────────────────────────────────────────────────────────────────────────╯
╭─ Commands ──────────────────────────────────────────────────────────────────╮
│ add       Adds a new function.                                              │
│ delete    Deletes a specific function using its id or label.                │
│ get       Retrieves a specific function using its id or label.              │
│ list      Lists all available functions.                                    │
│ update    Update a function.                                                │
╰─────────────────────────────────────────────────────────────────────────────╯
```

## Cloud operations

### Create a function <a href="#create-a-function" id="create-a-function"></a>

Create an UbiFunction in your Ubidots account.

```bash
 Usage: ubidots functions add [OPTIONS] NAME

╭─ Arguments ─────────────────────────────────────────────────────────────────╮
│     name      TEXT  The name of the function. [required]                    │
╰─────────────────────────────────────────────────────────────────────────────╯
╭─ Options ───────────────────────────────────────────────────────────────────╮
│ --label                       TEXT                   The label for the      │
│                                                      function.              │
│ --runtime                     [python3.9:lite|pytho  The runtime for the    │
│                               n3.9:base|python3.9:f  function.              │
│                               ull|python3.11:lite|p  [default:              │
│                               ython3.11:base|python  nodejs20.x:lite]       │
│                               3.11:full|nodejs20.x:                         │
│                               lite|nodejs20.x:base]                         │
│ --raw            --no-raw                            Flag to determine if   │
│                                                      the output should be   │
│                                                      in raw format.         │
│                                                      [default: no-raw]      │
│ --token                       TEXT                   Optional               │
│                                                      authentication token   │
│                                                      to invoke the          │
│                                                      function.              │
│ --methods                     TEXT                   The HTTP methods the   │
│                                                      function will respond  │
│                                                      to.                    │
│                                                      [default: GET]         │
│ --cors           --no-cors                           Flag to enable         │
│                                                      Cross-Origin Resource  │
│                                                      Sharing (CORS) for the │
│                                                      function.              │
│                                                      [default: no-cors]     │
│ --cron                        TEXT                   Cron expression to     │
│                                                      schedule the function  │
│                                                      for periodic           │
│                                                      execution.             │
│                                                      [default: * * * * *]   │
│ --environment                 TEXT                   environment in JSON    │
│                                                      format.                │
│                                                      [default: []]          │
│ --help                                               Show this message and  │
│                                                      exit.                  │
╰─────────────────────────────────────────────────────────────────────────────╯
```

### Get an UbiFunction <a href="#get-an-ubifunction" id="get-an-ubifunction"></a>

Retrieve an UbiFunction from Ubidots.

```bash
 Usage: ubidots functions get [OPTIONS]

╭─ Options ───────────────────────────────────────────────────────────────────╮
│ --id            TEXT  Unique identifier for the function. If both id        │                       and label are provided, the id takes precedence.      │
│ --label         TEXT  Descriptive label identifier for the function.
│                       Ignored if id is provided.                            │
│ --fields        TEXT  Comma-separated fields to process. e.g.               │
│                       field1,field2,field3                                  │
│                       [default: id,label,name]                              │
│ --help                Show this message and exit.                           │
╰─────────────────────────────────────────────────────────────────────────────╯
```

### Update an UbiFunction <a href="#update-an-ubifunction" id="update-an-ubifunction"></a>

Update an existing UbiFunction in Ubidots.

```bash
 Usage: ubidots functions update [OPTIONS]

╭─ Options ───────────────────────────────────────────────────────────────────────────────────╮
│ --id                          TEXT                           Unique identifier for the      │
│                                                              function. If both id and label │                                                              are provided, the id takes     │
│                                                              precedence.                    │
│ --label                       TEXT                           Descriptive label              │
│                                                              identifier for the             │
│                                                              function. Ignored if id is
│                                                              provided.                      │
│ --new-label                   TEXT                           The label for the device.      │
│ --new-name                    TEXT                           The name of the device.        │
│ --runtime                     [python3.9:lite|python3.9:bas  The runtime for the function.  │
│                               e|python3.9:full|python3.11:l  [default: nodejs20.x:lite]     │
│                               ite|python3.11:base|python3.1                                 │
│                               1:full|nodejs20.x:lite|nodejs                                 │
│                               20.x:base]                                                    │
│ --raw            --no-raw                                    Flag to determine if the       │
│                                                              output should be in raw        │
│                                                              format.                        │
│                                                              [default: no-raw]              │
│ --token                       TEXT                           Optional authentication token  │
│                                                              to invoke the function.        │
│ --methods                     TEXT                           The HTTP methods the function  │
│                                                              will respond to.               │
│                                                              [default: GET]                 │
│ --cors           --no-cors                                   Flag to enable Cross-Origin    │
│                                                              Resource Sharing (CORS) for    │
│                                                              the function.                  │
│                                                              [default: no-cors]             │
│ --cron                        TEXT                           Cron expression to schedule    │
│                                                              the function for periodic      │
│                                                              execution.                     │
│                                                              [default: * * * * *]           │
│ --environment                 TEXT                           environment in JSON format.    │
│                                                              [default: []]                  │
│ --help                                                       Show this message and exit.    │
╰─────────────────────────────────────────────────────────────────────────────────────────────╯
```

### Delete an UbiFunction <a href="#delete-an-ubifunction" id="delete-an-ubifunction"></a>

Delete an UbiFunction from your Ubidots account.

```bash
 Usage: ubidots functions delete [OPTIONS]

╭─ Options ───────────────────────────────────────────────────────────────────────────────────╮
│ --id           TEXT  Unique identifier for the function. If both id and label are
│                      provided, the id takes precedence.                                     │
│ --label        TEXT  Descriptive label **identifier** for the function. Ignored if id is    │
│                      provided.                                                              │
│ --help               Show this message and exit.                                            │
╰─────────────────────────────────────────────────────────────────────────────────────────────╯
```

{% hint style="warning" %}
This command permanently deletes the UbiFunction, just like removing it through the platform, thus special care is advised.
{% endhint %}

### List all UbiFunctions

List all UbiFunctions on your account.

```bash
 Usage: ubidots functions list [OPTIONS]

╭─ Options ───────────────────────────────────────────────────────────────────────────────────────────╮
│ --fields           TEXT          Comma-separated fields to process * e.g. field1,field2,field3. *   │
│                                  Available fields: (url, id, label, name, isActive, createdAt,      │
│                                  serverless, triggers, environment, zipFileProperties).             │
│                                  [default: id,label,name]                                           │
│ --filter           TEXT          Filter results by attributes. e.g.                                 │
│                                  'key1=val1&key2__in=val20,val21' or                                │
│                                  key1=val1\&key2__in=val20,val21                                    │
│ --sort-by          TEXT          Attribute to sort the result set by.                               │
│ --page-size        INTEGER       Defines the page number to be retrieved.                           │
│ --page             INTEGER       Defines how many items per page are retrieved.                     │
│ --format           [table|json]  [default: table]                                                   │
│ --help                           Show this message and exit.                                        │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────╯
```

## Local development

The Ubidots CLI enables you to set up a fully-featured local development environment for UbiFunctions, replicating runtimes and their included libraries. As specified in the [installing](https://dev.ubidots.com/sdks/cli/installing "mention") section, [Docker](https://docs.docker.com/desktop/install/linux/) is required for this.

{% hint style="info" %}
Before starting with local UbiFunctions development, remember to run the `ubidots config` command in order to properly set the environment's required configuration.
{% endhint %}

### Create a local UbiFunction

Create a new local UbiFunction with the given runtime, method and other settings.

```bash
 Usage: ubidots functions new [OPTIONS]

╭─ Options ───────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ --name                          TEXT                                        The name of the project folder.             │
│                                                                             [default: my_function]                      │
│ --runtime                       [python3.9:lite|python3.9:base|python3.9:f  The runtime for the function.               │
│                                 ull|python3.11:lite|python3.11:base|python  [default: nodejs20.x:lite]                  │
│                                 3.11:full|nodejs20.x:lite|nodejs20.x:base]                                              │
│ --cors             --no-cors                                                Flag to enable Cross-Origin Resource        │
│                                                                             Sharing (CORS) for the function.            │
│                                                                             [default: no-cors]                          │
│ --cron                          TEXT                                        Cron expression to schedule the function    │
│                                                                             for periodic execution.                     │
│                                                                             [default: * * * * *]                        │
│ --methods                       TEXT                                        The HTTP methods the function will respond  │
│                                                                             to.                                         │
│                                                                             [default: GET]                              │
│ --raw              --no-raw                                                 Flag to determine if the output should be   │
│                                                                             in raw format.                              │
│                                                                             [default: no-raw]                           │
│ --interactive  -i                                                           Enable interactive mode to select options   │
│                                                                             through prompts.                            │
│ --verbose      -v                                                           Enable verbose output.                      │
│ --help                                                                      Show this message and exit.                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
```

#### UbiFunction directory structure

Upon creating an UbiFunction, a directory named after it will be created in the current one. This directory will the following:

```
my-first-function/
└── main.<extension>
└── .manifest.yaml
```

#### `main` file

The `main` file is the entry point to the UbiFunction, that is, the starting point from where your logic will start to execute.

{% hint style="info" %}
The `main` file is mandatory, otherwise the UbiFunction won't work properly.
{% endhint %}

#### `manifest` file

The `.manifest.yaml` contains all the required configuration for UbiFunction. It looks like this:

```yaml
function:
  cron: '* * * * *'
  has_cors: false
  id: ''
  is_raw: false
  label: ''
  memory_size: 128
  methods:
  - POST
  payload: {}
  timeout: 10
  token: ''
  url: ''
globals:
  engine: DOCKER
project:
  created: 2024-10-29 16:57:10.690698
  language: python
  local_label: lambda_fn_my-first-function_ymRHeN5GKg
  main_file: main.py
  name: my-first-function
  runtime: python3.9:base
```

By editing this file, you can can manually change the UbiFunction's settings; however, it's recommended to use the designated options for each command to make changes.

### Start the local development environment <a href="#start-the-local-development-environment" id="start-the-local-development-environment"></a>

Start the local development environment in order to enable the UbiFunction's execution.

```bash
Usage: ubidots functions start [OPTIONS]

 Initialize the function container environment for execution.

╭─ Options ───────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ --cors         --no-cors             Flag to enable Cross-Origin Resource Sharing (CORS) for the function.              │
│                                      [default: no-cors]                                                                 │
│ --cron                      TEXT     Cron expression to schedule the function for periodic execution.                   │
│                                      [default: * * * * *]                                                               │
│ --methods                   TEXT     The HTTP methods the function will respond to. [default: GET]                      │
│ --raw          --no-raw              Flag to determine if the output should be in raw format. [default: no-raw]         │
│ --timeout                   INTEGER  Maximum time (in seconds) the function is allowed to run before being terminated.  │
│                                      [max: 300]                                                                         │
│                                      [default: 10]                                                                      │
│ --token                     TEXT     Optional authentication token to invoke the function.                              │
│ --verbose  -v                        Enable verbose output.                                                             │
│ --help                               Show this message and exit.                                                        │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
```

{% hint style="warning" %}
This command must be executed from the local UbiFunction's directory.
{% endhint %}

After executing, this command will output the following:

```bash
    ------------------
    Starting Function:
    ------------------
    Name: prueba-python-push
    Runtime: python3.9:base
    Local label: lambda_fn_prueba-python-push_Bk2L4H5bKp

    -------
    INPUTS:
    -------
    Raw: False
    Methods: GET
    Token:

http://172.18.0.2:8042/lambda_fn_prueba-python-push_Bk2L4H5bKp

> [DONE]: Function started successfully.
```

{% hint style="info" %}
The displayed URL is where the local UbiFunction is exposed.
{% endhint %}

### Push a local UbiFunction

Push a local UbiFunction to Ubidots cloud.

```bash
 Usage: ubidots functions push [OPTIONS]

 Update and synchronize your local function code with the remote server.

╭─ Options ────────────────────────────────────────────────────────────────────╮
│ --yes      -y        Confirm file overwrite without prompt.                  │
│ --verbose  -v        Enable verbose output.                                  │
│ --help               Show this message and exit.                             │
╰──────────────────────────────────────────────────────────────────────────────╯
```

{% hint style="warning" %}
This command must be executed from the local UbiFunction's directory.
{% endhint %}

After executing this command and if the UbiFunction doesn't exists yet in the Ubidots cloud, it will prompt you for creating it:

```bash
This function is not created. Would you like to create a new function and push it? [y/N]: y

> [DONE]: Function uploaded successfully.
```

On the other hand, if the function already exists in Ubidots, it will promp you for overwriting:

```bash
Are you sure you want to overwrite the remote files? [y/N]: y

> [DONE]: Function uploaded successfully.
```

### Pull an UbiFunction from Ubidots

This command is helpful when you've pushed a local UbiFunction to Ubidots, made further changes there, and now want to pull those updates back to your local environment.

```bash
 Usage: ubidots functions pull [OPTIONS]

 Retrieve and update your local function code with the latest changes from the
 remote server.

╭─ Options ────────────────────────────────────────────────────────────────────╮
│ --yes      -y        Confirm file overwrite without prompt.                  │
│ --verbose  -v        Enable verbose output.                                  │
│ --help               Show this message and exit.                             │
╰──────────────────────────────────────────────────────────────────────────────╯
```

{% hint style="warning" %}
Use this command with caution, as it will overwrite your local UbiFunction. Be sure to run it from within the local UbiFunction's directory.
{% endhint %}

## Example

### Create the local UbiFunction

This example creates a local UbiFunction named `my-first-function` with runtime `python3.9:lite`. Run the following command:

```bash
ubidots functions new --name my-first-function --runtime python3.9:base
```

It will output something like:

```bash
ubidots functions new --name my-first-function --runtime python3.9:lite

> [DONE]: Project 'my-first-function' created in '/home/inumaki/test-cli/my-first-function'.
```

Navigate to the UbiFunction's directory and open the `main.py` file with your preferred text editor. In the main function, locate the following lines:

```python
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']
```

Then add the following line just below `del args['device']`

```python
del args['_auth_token']
```

The code should now look like this:

```python
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']
    del args['_auth_token']
```

### Starting the UbiFunction

Now save the changes and go back to the terminal. Then run the following command to start the local UbiFunction:

```bash
ubidots functions start --methods POST
```

The output of that command will look like:

```bash
    ------------------
    Starting Function:
    ------------------
    Name: my-first-function
    Runtime: python3.9:base
    Local label: lambda_fn_my-first-function_OWWTAWxPGi

    -------
    INPUTS:
    -------
    Raw: False
    Methods: POST
    Token:

URL: http://172.18.0.2:8042/lambda_fn_my-first-function_OWWTAWxPGi

> [DONE]: Function started successfully.

```

The displayed URL is where the UbiFunction is exposed, thus you can perform HTTP request to it in order to trigger the UbiFunction's execution.

### Running the UbiFunction

Perform an HTTP POST request to the given URL by running:

```bash
curl -X POST <your-local-ubifunction-url> \
-H "Content-Type: application/json" \
-d '{
  "token": "<your-ubidots-token>",
  "device": "first-device-cli",
  "temperature": 78
}'
```

It shoud output the following:

```bash
curl -X POST <your-local-ubifunction-url> \
-H "Content-Type: application/json" \
-d '{
  "token": "<your-ubidots-token>",
  "device": "first-device-cli",
  "temperature": 78
}'
{"status":"Ok","result":{"temperature":[{"status_code":201}]}}
```

You can confirm proper execution by going to your Ubidots account and checking if there's a new device called `first-device-cli` containing a variable called `temperature` holding a value of `78`.

### Pushing the UbiFunction to Ubidots

After runing locally your UbiFunction and determining it's working as intended, you can deploy it to your production environment (Ubidots) by running:

```bash
ubidots functions push
Are you sure you want to overwrite the remote files? [y/N]: y

> [DONE]: Function uploaded successfully.


```

This will prompt you for overwriting the changes in Ubidots. Upon confirmation, the local changes will be pushed to Ubidots.

### Pulling the UbiFunction from Ubidots

If you need to make changes to the UbiFunction's production version and want to edit it from your local machine, you can pull the changes from Ubidots, make the necessary modifications locally, and then push the updates back to Ubidots. Start by pulling the changes by running:

```bash
ubidots functions pull
Are you sure you want to overwrite the local files? [y/N]: y

> [DONE]: Function downloaded successfully.

```

After that, you can use the `start`, `run` and `push` commands as described before.
