Skip to content

Application Management

The Cloud API provides methods for creating, retrieving, and updating Doover applications. Applications define deployable packages that can run on devices or as cloud tasks.

Application Class

The Application class represents a Doover application configuration.

Attributes

AttributeTypeDescription
keystrUnique identifier for the application
namestrApplication name (required)
display_namestrHuman-readable display name (required)
typeTypeApplication type (device, processor, task, integration)
visibilityVisibilityVisibility level (core, public, private, internal)
allow_manyboolWhether multiple instances can be deployed
descriptionstrShort description
long_descriptionstrDetailed description (can reference a file path)
depends_onlist[Object]List of dependency application keys
owner_orgObjectOrganization that owns the application
code_repoObjectCode repository reference
repo_branchstrRepository branch (default: "main")
image_namestrDocker image name
build_argsstrDocker build arguments
container_registry_profileObjectContainer registry configuration
config_schemadictConfiguration schema for the application
staging_configdictStaging environment configuration overrides
base_pathPathLocal filesystem path to application

Properties

PropertyTypeDescription
src_directoryPathPath to the source code directory

Application Types

from pydoover.cloud.api.application import Type

Type.device       # "DEV" - Device application
Type.processor    # "PRO" - Processor application
Type.task         # "TAS" - Task application
Type.integration  # "INT" - Integration application

Visibility Levels

from pydoover.cloud.api.application import Visibility

Visibility.core      # "COR" - Core system application
Visibility.public    # "PUB" - Publicly visible
Visibility.private   # "PRI" - Private to organization
Visibility.internal  # "INT" - Internal use only

Retrieving Applications

List All Applications

from pydoover.cloud.api import Client

client = Client()

# Get all available applications
apps = client.get_applications()
for app in apps:
    print(f"{app['name']}: {app['display_name']}")

Get Specific Application

app = client.get_application("app-key-here")
print(f"Application: {app.display_name}")
print(f"Type: {app.type}")
print(f"Description: {app.description}")

Creating Applications

From Configuration File

Applications are typically defined in a doover_config.json file:

{
    "my-app": {
        "name": "my-app",
        "display_name": "My Application",
        "type": "DEV",
        "visibility": "PRI",
        "description": "A sample application",
        "long_description": "README.md",
        "owner_org_key": "org-uuid",
        "code_repo_key": "repo-uuid",
        "repo_branch": "main",
        "config_schema": {
            "type": "object",
            "properties": {
                "interval": {
                    "type": "integer",
                    "default": 60
                }
            }
        }
    }
}

Load and create the application:

from pathlib import Path
from pydoover.cloud.api import Client
from pydoover.cloud.api.application import Application
import json

client = Client()

# Load configuration
app_dir = Path("/path/to/app")
config_path = app_dir / "doover_config.json"

with open(config_path) as f:
    config = json.load(f)

# Create Application instance
app = Application.from_config(config["my-app"], app_base=app_dir)

# Create on the server
app_key = client.create_application(app)
print(f"Created application with key: {app_key}")

Programmatic Creation

from pathlib import Path
from pydoover.cloud.api import Client
from pydoover.cloud.api.application import Application, Type, Visibility
from pydoover.cloud.api.object import Object

client = Client()

app = Application(
    key=None,  # Will be assigned on creation
    name="sensor-monitor",
    display_name="Sensor Monitor",
    app_type=Type.device,
    visibility=Visibility.private,
    allow_many=True,
    description="Monitors sensor data",
    long_description="Detailed description of the sensor monitoring application.",
    depends_on=[],
    owner_org=Object(key="your-org-uuid"),
    code_repo=Object(key="your-repo-uuid"),
    repo_branch="main",
    image_name="sensor-monitor",
    build_args="",
    container_registry_profile=Object(key=None),
    config_schema={
        "type": "object",
        "properties": {
            "poll_interval": {"type": "integer", "default": 30}
        }
    },
    staging_config={},
    app_base=Path("/path/to/app")
)

app_key = client.create_application(app)
print(f"Created: {app_key}")

Updating Applications

# Get existing application
app = client.get_application("app-key")

# Modify properties
app.description = "Updated description"
app.config_schema["properties"]["new_setting"] = {
    "type": "string",
    "default": "value"
}

# Update on server
client.update_application(app)

Staging Environment

Use the is_staging flag to deploy to staging:

# Create in staging environment
app_key = client.create_application(app, is_staging=True)

# Update in staging environment
client.update_application(app, is_staging=True)

The staging_config dictionary can override main configuration values for staging deployments.

Deployment Data

Applications can include deployment data (Docker compose files, configurations):

# Application structure:
# /path/to/app/
#   doover_config.json
#   deployment/
#     docker-compose.yml
#     config/
#       settings.yaml

app = Application.from_config(config, app_base=Path("/path/to/app"))

# Create with deployment data included
app_key = client.create_application(app)  # Automatically includes deployment/ folder

The deployment/ directory is zipped and uploaded as base64-encoded data.

Saving Configuration

Save application configuration back to disk:

app = client.get_application("app-key")

# Set base path if not already set
app.base_path = Path("/path/to/app")

# Save to doover_config.json
app.save_to_disk()

Configuration Schema

The config_schema defines what configuration options are available when deploying the application:

config_schema = {
    "type": "object",
    "properties": {
        "poll_interval": {
            "type": "integer",
            "default": 60,
            "description": "Data polling interval in seconds"
        },
        "api_key": {
            "type": "string",
            "description": "API key for external service"
        },
        "enabled_features": {
            "type": "array",
            "items": {"type": "string"},
            "default": ["logging", "metrics"]
        }
    },
    "required": ["api_key"]
}

Complete Example

from pathlib import Path
import json
from pydoover.cloud.api import Client
from pydoover.cloud.api.application import Application, Type, Visibility
from pydoover.cloud.api.object import Object

client = Client()

# Define application
app = Application(
    key=None,
    name="temperature-logger",
    display_name="Temperature Logger",
    app_type=Type.device,
    visibility=Visibility.private,
    allow_many=True,
    description="Logs temperature readings from connected sensors",
    long_description="""
# Temperature Logger

This application reads temperature data from connected sensors and logs
the readings to the Doover platform.

## Features
- Multi-sensor support
- Configurable polling intervals
- Automatic unit conversion
- Alert thresholds
    """,
    depends_on=[],
    owner_org=Object(key="org-uuid"),
    code_repo=Object(key="repo-uuid"),
    repo_branch="main",
    image_name="temperature-logger",
    build_args="",
    container_registry_profile=Object(key=None),
    config_schema={
        "type": "object",
        "properties": {
            "poll_interval": {
                "type": "integer",
                "default": 30,
                "minimum": 5,
                "description": "Sensor polling interval in seconds"
            },
            "temperature_unit": {
                "type": "string",
                "enum": ["celsius", "fahrenheit"],
                "default": "celsius"
            },
            "alert_threshold": {
                "type": "number",
                "default": 50.0,
                "description": "Temperature threshold for alerts"
            }
        }
    },
    staging_config={
        "poll_interval": 5  # Faster polling in staging
    },
    app_base=Path("/home/user/apps/temperature-logger")
)

# Create application
try:
    app_key = client.create_application(app)
    print(f"Created application: {app_key}")
except Exception as e:
    print(f"Failed to create application: {e}")

# Later, update the application
app = client.get_application(app_key)
app.description = "Updated: Logs temperature readings with enhanced alerting"
app.config_schema["properties"]["alert_email"] = {
    "type": "string",
    "format": "email",
    "description": "Email address for alerts"
}

client.update_application(app)
print("Application updated successfully")

# List all applications
print("\nAll applications:")
for app_data in client.get_applications():
    print(f"  - {app_data['name']}: {app_data.get('description', 'No description')}")

Tunnel Management

The Cloud API also provides methods for managing remote access tunnels to devices.

Listing Tunnels

# Get all tunnels for an agent
tunnels = client.get_tunnels(agent_id="agent-uuid")
for tunnel in tunnels:
    print(f"Tunnel: {tunnel['id']} - {tunnel.get('status')}")

# Get with choices (for UI selection)
tunnels = client.get_tunnels(agent_id="agent-uuid", show_choices=True)

Creating Tunnels

tunnel = client.create_tunnel(
    agent_id="agent-uuid",
    target_address="192.168.1.100",
    target_port=8080,
    protocol="http"
)
print(f"Created tunnel: {tunnel['id']}")

Managing Tunnel State

tunnel_id = "tunnel-uuid"

# Activate tunnel
client.activate_tunnel(tunnel_id)

# Deactivate tunnel
client.deactivate_tunnel(tunnel_id)

# Update tunnel configuration
client.patch_tunnel(tunnel_id, target_port=8443)

# Delete tunnel
client.delete_tunnel(tunnel_id)

Legacy Tunnel Endpoints

# Create ngrok-style endpoints
urls = client.create_tunnel_endpoints(
    agent_id="agent-uuid",
    endpoint_type="http",
    amount=2
)
print(f"Created endpoints: {urls}")

# Get existing endpoints
endpoints = client.get_tunnel_endpoints(
    agent_id="agent-uuid",
    endpoint_type="http"
)

Error Handling

from pydoover.cloud.api import Client, NotFound, HTTPException

client = Client()

try:
    app = client.get_application("nonexistent-key")
except NotFound:
    print("Application not found")
except HTTPException as e:
    print(f"API error: {e}")

try:
    client.create_application(app)
except HTTPException as e:
    if "already exists" in str(e):
        print("Application already exists, updating instead")
        client.update_application(app)
    else:
        raise