Execution engine

Elements: documentatiehub

Elements zijn Python bouwblokken binnen Dashview. Deze hub splitst de documentatie in twee detailreferenties: editor/filebeheer en runtime/execution.

1. End-to-end workflow

  1. Maak element aan met ID, naam, kind en entrypoint.
  2. Voeg code/files toe en stel requirements in.
  3. Save draft en publiceer een nieuwe version.
  4. Configureer runtime vars/secrets.
  5. Gebruik Test execute en controleer execution logs. Element-code kan logger.info(...), logger.error(...) en logger.exception(...) gebruiken om diagnostische meldingen aan die logs toe te voegen.
  6. Koppel element aan datasource connector of Autoflow nodes.
Elements overzicht
Hier beheer je de volledige lifecycle van tenant-specifieke Pythoncode.
Element detail
Een stabiel Element bestaat uit code, dependencybeheer en een gecontroleerde publishflow.

2. Template-marktplaats

Open Template-marktplaats in Administration → Elements om een bewerkbaar Element-draft te starten vanuit de platformcatalogus in Firestore, die aanvankelijk met 86 voorbeelden wordt gevuld. Zoek op provider of gebruikssituatie en filter op categorie en Elementsoort.

De catalogus wordt uit Firestore geladen wanneer je de marktplaats opent. Platformbeheerders kunnen daardoor templates bewerken of toevoegen zonder de frontend opnieuw te bouwen. Providerlogo's worden als gesaniteerde SVG-afbeeldingen bij iedere template opgeslagen. De gedeelde catalogus verandert de tenantisolatie niet: een keuze maakt alleen een niet-opgeslagen draft binnen je huidige tenant.

Klik op Template gebruiken om de configuratiewizard te openen. De wizard controleert welke tenantbrede runtimevariabelen en geheime waarden de template nodig heeft, herkent bestaande waarden en laat je ontbrekende of gewijzigde waarden invoeren. Variabelen moeten geldige JSON zijn; bestaande write-only secrets kun je leeg laten om ze te behouden. Na de controlestap slaat Dashview de configuratie op en opent het nieuwe Element-draft in de bestaande editor.

Databases en warehouses

Er zijn starters voor onder andere PostgreSQL, MySQL/MariaDB, Microsoft SQL Server, ClickHouse, BigQuery, Snowflake, Amazon RDS, Redshift, Oracle, MongoDB, Elasticsearch/OpenSearch, Redis, Databricks, Trino/Presto en DuckDB.

Zakelijke SaaS

Kant-en-klare integraties omvatten Salesforce, HubSpot, Stripe, Shopify, Dynamics 365 / Dataverse, ServiceNow, Jira, Confluence, GitHub, GitLab, Google Sheets, Excel / OneDrive, SharePoint en SFTP.

Streaming en observability

Start met Kafka / Confluent, Google Pub/Sub, Amazon Kinesis, Azure Event Hubs, Prometheus, Grafana Loki of OpenSearch-logzoekopdrachten.

API-, sync- en bestandspatronen

Voorbeelden omvatten API-key, Basic Auth, HMAC, cursorpaginering, rate-limitafhandeling, OAuth refresh, async jobs, incrementele cursors en CSV-, XLSX-, Parquet-, NDJSON- en veilige ZIP-inname.

Governed Dashview-patronen

Gebruik starters voor queryDataSources, multi-source joins, rowsRef, schemavalidatie, deduplicatie, PII-redactie, datasource-appends, Parquet-exports, RAG-lookups, goedkeuringstaken, notificaties, apps, widgets en triggers.

Alle Elementsoorten

De catalogus bevat starters voor data, stream_data, transformer, detection, action, app, llm_tool, widget, trigger en server.

Veilig gebruik

  • De wizard slaat ingevoerde waarden tenantbreed op. Ze zijn daardoor beschikbaar voor alle Elements binnen de huidige tenant; alleen gebruikers met Element-bewerkingsrechten kunnen dit doen.
  • Secretwaarden blijven write-only. Een al ingesteld secret wordt nooit teruggestuurd naar de browser en blijft behouden wanneer je het veld leeg laat.
  • Na de wizard opent alleen een nieuw, tenant-scoped draft in de bestaande Element-editor; het Element opslaan, publiceren en testen blijven handmatige acties.
  • LLM-tooltemplates staan standaard uit totdat een beheerder ze bewust inschakelt en publiceert.
  • Providerlogo's identificeren compatibele producten van derden. Ze betekenen geen goedkeuring, samenwerking of eigendom door Dashview.
  • Gebruik least-privilege accounts voor directe externe verbindingen en beperk requests, velden en rijen expliciet. Directe providercalls krijgen niet automatisch Dashview-datasourcerechten of forced filters.
  • Gebruik bij voorkeur de governed queryDataSources- en rowsRef-starters wanneer actuele gebruikerstoegang en forced filters behouden moeten blijven.

3. Visuele groepen voor Elements

Gebruik het veld Groep om Elements binnen je tenant visueel te ordenen. Groepen helpen beheerders om Elements sneller terug te vinden wanneer de lijst groeit.

4. Detailreferenties

Runtime & execution reference

Publish/versioning, runtime vars/secrets, test execute payloads en logvelden.

Open runtime reference →

Runtime builtin contracts

Per helper: signature, payload format, return shape, voorbeeldcode en scope-notities.

Open builtin contracts →

5. Integratie met datasources en flows

Elements als LLM-tools

  • Gebruik kind="llm_tool" wanneer een gepubliceerd Element als tenant-scoped dynamic tool beschikbaar moet zijn voor Dashbot of dashview-llm.
  • Een LLM-tool is geen globale tool: registratie, beschikbaarheid en metadata blijven binnen de tenant en het Element-configuratieoppervlak.
  • Gebruik gepubliceerde Element-versies. Draft-code is bedoeld voor authoring en testen, niet als stabiele tooldefinitie.
  • Autonome tooluitvoering loopt via de beveiligde keten dashview-llm → llm-tool-executor → dashview-be → elements-runner.
  • De uitvoering behoudt de effectieve tenant/user access context. Datasource helpers, rowsRef-retrieval en downstream calls mogen forced filters of gebruikersrechten niet omzeilen.

6. Quick start: Element naar dashboardwidget

Basisroute

  1. Maak een Element dat rows teruggeeft met def run(params):.
  2. Test het Element met een kleine params-payload en publiceer daarna een versie.
  3. Maak een datasource met type element en kies het gepubliceerde Element.
  4. Vul vaste input in via elementParams, bijvoorbeeld een default country of limit.
  5. Refresh of test de datasource zodat kolommen en voorbeeldrows zichtbaar zijn.
  6. Koppel een tabel-, chart- of KPI-widget aan die datasource en gebruik de returned columns zoals bij elke andere datasource.

Hoe filters terug naar het Element gaan

  • Dashboardfilters en form-widget velden worden eerst runtime filterwaarden op de pagina.
  • Een Element-datasource kan die waarden als params ontvangen wanneer Filters als params doorgeven aan staat. Deze instelling is zichtbaar voor livestream-Element-datasources en voor parquet-snapshot wanneer Snapshot bij elk verzoek is gekozen.
  • Gebruik allowedRequestParams / Toegestane doorgestuurde filtersleutels om expliciet te bepalen welke filterkeys naar het Element mogen.
  • Gebruik requestParamMap als de dashboardfilterkey anders heet dan de param die je Element verwacht.
  • In het Element lees je ze gewoon uit params, bijvoorbeeld params.get("country").
  • Tijdens parquet refresh lopen geneste datasource reads nog steeds via het data-worker-agent helperpad met de parquetrefresh tenant-system materialisatiecontext en normale tenantscope.

Mini-voorbeeld met filterinput

def run(params):
    country = params.get("country", "NL")
    limit = int(params.get("limit", 10))

    rows = load_sales_rows(country=country, limit=limit)
    return [{
        "country": row["country"],
        "customer": row["customer"],
        "revenue": row["revenue"],
    } for row in rows]

Voorbeeldflow: dashboardfilter country → Element-datasource request param countryparams.get("country") in het Element → widget rendert de nieuwe rows.

Houd filterdoorvoer expliciet. Gebruik allowlists en hernoem alleen bewust, zodat widgets geen onverwachte params naar Elements sturen.

7. Datasource helpers vanuit een Element

Hoe reads draaien

  • Element-code roept queryDataSources, query_datasources, dataSources.query of dataSources.query_rows aan.
  • De Element-subprocess stuurt een gestructureerde helperrequest naar de parent data-worker-agent. De parent draait de geneste dataquery met de huidige tenant, gebruiker, access context, SQL safety rules en forced filters.
  • Geef expliciete sources mee. Een source kan een datasource-ID zijn of een volledige source spec zoals {"type":"datasource","dataSourceId":"orders-ds","alias":"orders"}.
  • SQL-tabelnamen komen uit de source alias wanneer die is meegegeven, anders uit de DuckDB-veilige datasource-ID/tabelnaam die de worker exposeert.

Rows lezen

def run():
    sources = [{
        "alias": "orders",
        "type": "datasource",
        "dataSourceId": "orders-ds",
    }]
    result = queryDataSources("""
        SELECT customer, SUM(revenue) AS revenue
        FROM orders
        GROUP BY customer
        ORDER BY revenue DESC
        LIMIT 100
    """, sources=sources, maxRows=100)
    return result.rows

Helperresultaten in lokale DuckDB laden

Voor grotere chained reads of datapreparatie geef je asTable, table of tableName mee. De parent worker schrijft het geneste resultaat naar een parquet sidecar en de Element-runtime laadt het in de request-local DuckDB connection. Het helperresultaat heeft table, rowCount, columns, metadata en lege rows.

def run(duckdb):
    queryDataSources(
        "SELECT customer, revenue FROM orders",
        sources=[{
            "alias": "orders",
            "type": "datasource",
            "dataSourceId": "orders-ds",
        }],
        asTable="orders_local",
    )
    duckdb.execute("""
        CREATE TABLE output AS
        SELECT customer, revenue * 1.21 AS revenue_with_tax
        FROM orders_local
    """)
    return {"table": "output"}

Een GCS file wijzigen met DuckDB

Tenant-scoped GCS helpers kun je combineren met de lokale DuckDB connection. Download het bestand naar workspace, lees het met DuckDB, draai SQL-wijzigingen zoals UPDATE, exporteer een vervangend bestand en upload het terug naar hetzelfde tenant-scoped pad met overwrite=True.

import base64
from pathlib import Path

def run(duckdb, workspace):
    object_path = "datasets/orders.parquet"
    work = Path(workspace)
    input_path = work / "orders.parquet"
    output_path = work / "orders.updated.parquet"

    downloaded = downloadGcsFile(object_path, asBytes=True)
    input_path.write_bytes(base64.b64decode(downloaded["dataBase64"]))

    duckdb.execute(
        "CREATE TABLE orders AS SELECT * FROM read_parquet(?)",
        [str(input_path)]
    )
    duckdb.execute("""
        UPDATE orders
        SET status = 'archived'
        WHERE status = 'closed'
    """)
    duckdb.execute("COPY orders TO ? (FORMAT PARQUET)", [str(output_path)])

    return uploadGcsFile(
        object_path,
        dataBase64=base64.b64encode(output_path.read_bytes()).decode("ascii"),
        contentType="application/vnd.apache.parquet",
        overwrite=True,
    )

Terugschrijven naar hetzelfde pad vervangt het object. Gebruik een nieuw outputpad wanneer je review of bestandsversies voor vervanging nodig hebt.

Duurzame datasource writes

Gebruik appendDataSourceRows of dataSources.appendRows wanneer een Element JSON rows moet appenden naar een uplink/parquet-backed datasource. De write wordt gecontroleerd tegen de effectieve request access context voordat de worker hem naar uplink stuurt.

def run():
    result = appendDataSourceRows(
        "element-events",
        [{"event": "refresh_complete", "count": 42}],
        merge=True,
        meta={"source": "element"},
    )
    return {"inserted": result["inserted"]}

Resultaat en limieten

  • Normale read-results zijn dict-like en hebben rows, columns, rowCount, .to_pandas() en .to_dataframe().
  • maxRows geldt nog steeds voor geneste reads, inclusief asTable-materialisatie.
  • Gebruik rowsRef, inline of file source specs wanneer je worker-produced artifacts via de helper chaint.
  • Helpercalls vergroten geen tenant-, user-, datasource- of forced-filter-scope.
Weet je de exacte datasource-ID, alias of kolommen niet zeker, controleer eerst de datasource schema's in de admin in plaats van te gokken.

8. Return, yield en memorygebruik

return: standaard output

  • Gebruik return rows voor normale Elements, Element-datasources en kleine tot middelgrote snapshots.
  • De return value moet JSON-serialiseerbaar zijn. Voor Element-datasources is dat normaal een lijst met dicts of een tabelreferentie zoals {"table": "output"}.
  • Return geen wrapper zoals {"rows": rows, "meta": ...} als de downstream datasource een pure rows-array verwacht.

yield: rows stuk voor stuk produceren

  • Gebruik yield row wanneer je rows kunt produceren zonder eerst alles in een lijst te verzamelen.
  • Een generator-resultaat wordt door de runner als streambare row-output behandeld en naar een tijdelijk row-artifact geschreven.
  • Gebruik in dezelfde entrypoint niet tegelijk yield en return rows; kies één outputmodus.

Memoryrichtlijnen voor grote datasets

  • Vermijd patronen zoals rows = list(iterator) als de bron groot kan worden. Je laadt dan de volledige dataset in het geheugen van de Element-runtime.
  • Filter, map en aggregeer per row of per kleine batch wanneer dat kan, en yield daarna de outputrow.
  • Gebruik queryDataSources(..., maxRows=...) of datasourcefilters om de input bewust te begrenzen wanneer je alleen een sample of top-N nodig hebt.
  • Gebruik .to_pandas() alleen als je echt een DataFrame nodig hebt; die stap materialiseert alle opgehaalde rows opnieuw in memory.
  • Voor live datasource updates gebruik je appendRows, replaceRows en emitProgress; yield is voor het eindresultaat van de entrypoint, niet voor progress-events.

Vermijd volledige materialisatie

def run(params):
    # Minder goed: alle rows eerst in memory verzamelen.
    rows = list(load_external_rows(params))
    return [normalize(row) for row in rows]

Produceer rows incrementeel

def run(params):
    # Beter voor grote bronnen: per row verwerken.
    for row in load_external_rows(params):
        yield normalize(row)

9. Code voorbeelden

Voorbeeld A: run(params)

def run(params):
    limit = int(params.get("limit", 5))
    country = params.get("country", "NL")
    rows = []
    for i in range(limit):
        rows.append({
            "rank": i + 1,
            "country": country,
            "value": (i + 1) * 100
        })
    return rows

Voorbeeld B: parameters verwerken

def run(params):
    label = params.get("label", "demo")
    multiplier = int(params.get("multiplier", 2))
    values = params.get("values") or [1, 2, 3]
    return [{
        "label": label,
        "value": value,
        "score": value * multiplier
    } for value in values]

Voorbeeld C: runtime variabele gebruiken

def run(params):
    cursor = int(getVar("sync_cursor", 0) or 0)
    batch_size = int(params.get("batchSize", 3))
    rows = []
    for index in range(batch_size):
        rows.append({"cursor": cursor + index, "source": "element"})
    setVar("sync_cursor", cursor + batch_size)
    return rows