Saltar a contenido

Primeros pasos

Crea un plugin de negocio con API REST y pantalla de listado en el panel panel, sin escribir React.

Cuándo usar esta guía

  • Es tu primer plugin en Cortex/HIVE.
  • Necesitas un listado con columnas y rutas REST bajo /api/v1.
  • Quieres UI declarativa con ResourceBuilder en lugar de JSON manual.

Estructura mínima

plugins/mi-modulo/
  pyproject.toml
  cortex_plugin_mi_modulo/
    __init__.py
    plugin.py          # API REST + create_plugin()
    resources.py       # register_resources()
    fixtures/            # opcional en desarrollo

Paso 1 — Paquete y entry point

En pyproject.toml:

[project]
name = "cortex-plugin-mi-modulo"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = [
    "cortex-core",
    "cortex-framework",
    "fastapi>=0.115",
]

[tool.uv.sources]
cortex-core = { workspace = true }
cortex-framework = { workspace = true }

[project.entry-points."cortex.plugins"]
mi-modulo = "cortex_plugin_mi_modulo:create_plugin"

Añade "plugins/mi-modulo" al workspace en el pyproject.toml raíz y ejecuta uv sync --all-packages.

Paso 2 — Plugin REST

plugin.py expone el contrato obligatorio del SPI:

from cortex_core.plugin import PluginDescriptor
from cortex_framework.api.list_response import list_endpoint
from fastapi import APIRouter, Request

class MiModuloPlugin:
    descriptor = PluginDescriptor("mi-modulo", "Mi módulo", "0.1.0")

    def api_router(self) -> APIRouter:
        router = APIRouter(prefix="/mi-modulo", tags=["mi-modulo"])

        @router.get("/items")
        async def list_items(request: Request) -> dict:
            items = [...]  # tu lógica
            return list_endpoint(items, request)

        return router

    def resource_paths(self) -> list[str]:
        return ["mi-modulo"]

def create_plugin() -> MiModuloPlugin:
    return MiModuloPlugin()

Referencia completa: plugins/clients/cortex_plugin_clients/plugin.py.

Paso 3 — UI con ResourceBuilder

resources.py registra manifest, dashboards y formularios:

from cortex_framework.ui import ResourceBuilder, register_resource
from cortex_framework.ui.registry import ResourceRegistry

def register_resources(registry: ResourceRegistry) -> None:
    def configure(builder: ResourceBuilder) -> None:
        (
            builder.id("items")
            .title("Ítems")
            .api_base("/api/v1/mi-modulo")
            .list_path("/api/v1/mi-modulo/items")
            .module_path("items")
            .nav_group("catalog", nav_sort=20)
            .pages(list=True, create=False, edit=False, view=False)
            .table(
                lambda t: t.columns(
                    lambda c: c.text("name", "Nombre", sortable=True)
                )
            )
        )

    register_resource(registry, "panel", "mi-modulo", configure)

Reexporta el hook en __init__.py:

from cortex_plugin_mi_modulo.plugin import create_plugin
from cortex_plugin_mi_modulo.resources import register_resources

__all__ = ["create_plugin", "register_resources"]

Ejemplo real: plugins/clients/cortex_plugin_clients/resources.py.

Paso 4 — Activar el plugin

export CORTEX_ENABLED_PLUGINS=...,mi-modulo
uv sync --all-packages
uv run cortex-api

En otra terminal:

npm run dev:web-shadcn   # http://localhost:5175

Paso 5 — Verificar

Comprobación Cómo
API GET http://localhost:8000/api/v1/mi-modulo/items + header X-Tenant-Id: default
OpenAPI http://localhost:8000/api/docs
UI Panel panel → módulo mi-modulo en la navegación
Manifest GET /api/v1/panels/panel/modules/mi-modulo/manifest

Flujo bootstrap → pantalla

sequenceDiagram
  participant P as Plugin Python
  participant API as FastAPI bootstrap
  participant Shell as web-shadcn
  participant W as Widgets

  P->>API: register_resources
  Shell->>API: GET panels/panel/modules/.../manifest
  Shell->>API: GET ui/dashboards/{id}
  Shell->>W: render data-table
  W->>API: GET /api/v1/mi-modulo/items

Errores frecuentes

Síntoma Causa habitual
Plugin no aparece en OpenAPI Falta en CORTEX_ENABLED_PLUGINS o error en uv sync
Módulo no en navegación panel_id incorrecto o hook no reexportado en __init__.py
Tabla vacía o 404 list_path no coincide con la ruta del APIRouter
register_dashboards ignorado Si existe register_resources, el loader no ejecuta register_dashboards

Siguiente paso

Profundiza en el ciclo de vida del plugin y en Recursos para páginas create/edit/view.