Docs / Build Workflow

Sources

Sources son el paso uno de la confiabilidad del contenido

Los models dependen de aliases de source. Si el alias, el filename de credentials o la referencia al dataset están mal, cada model, query y visualization construida arriba falla. Configurá y validá runtime/sources.runtime.yml antes de escribir un solo model.

Si todavía no creaste tu service account de GCP, hacé eso primero. Mirá Acceso al Dataset de BigQuery.

Working example: dataset público de BigQuery

Working example usando el dataset público bigquery-public-data.thelook_ecommerce. Copialo como tu punto de partida; reemplazá project_id con tu propio billing project de GCP y credentials_file con el nombre del archivo JSON de tu service account (el archivo que dropeás en secrets/). El dataset es público, así que cualquiera con un service account de BigQuery funcionando lo puede consultar.

sources:
  ecommerce:
    name: The Look Ecommerce
    type: bigquery
    project_id: my-gcp-billing-project
    credentials_file: my-workspace-bq.json
    datasets:
      - bigquery-public-data.thelook_ecommerce

Campo por campo:

  • ecommerce: el alias que van a usar tus models de Malloy. Elegí algo corto y estable — cambiarlo después rompe cada model que lo referencia.
  • project_id: el proyecto de GCP que paga el costo de las queries. Este es tu billing project, no necesariamente donde viven los datos.
  • credentials_file: filename plano del JSON de service-account dropeado en la carpeta secrets/ del workspace. Pattern ^[A-Za-z0-9_-][A-Za-z0-9._-]*$ — sin separadores de path, sin punto inicial.
  • datasets: las ubicaciones de dataset que el runtime puede consultar. Acá los datos están en bigquery-public-data, un proyecto distinto al billing — eso es normal y esperado.

Working example: tu propio dataset privado

Cuando los datos viven en tu propio proyecto de GCP, project_id y el proyecto del dataset son el mismo:

sources:
  sales:
    name: Sales Data
    type: bigquery
    project_id: my-company-gcp-project
    credentials_file: my-workspace-bq.json
    datasets:
      - my-company-gcp-project.sales_warehouse

Podés definir múltiples sources en el mismo archivo:

sources:
  sales:
    name: Sales Data
    type: bigquery
    project_id: my-company-gcp-project
    credentials_file: my-workspace-bq.json
    datasets:
      - my-company-gcp-project.sales_warehouse
  marketing:
    name: Marketing Data
    type: bigquery
    project_id: my-company-gcp-project
    credentials_file: my-workspace-bq.json
    datasets:
      - my-company-gcp-project.marketing_events

Cada alias se vuelve referenciable de forma independiente en los models.

Working example: Postgres source

Los sources de Postgres separan coordenadas de red y credenciales: el dsn es una URI estándar de libpq (host, port, database, flags TLS o de pool) sin el user:password@; el user y password viven en un JSON dentro de secrets/ referenciado por credentials_file. La plataforma lee los dos, inyecta las credenciales en el DSN en memoria y lo pasa al driver de Postgres. runtime/sources.runtime.yml nunca contiene una password.

sources:
  warehouse:
    name: Internal Warehouse
    type: postgres
    dsn: "postgresql://pg.internal.example.com:5432/warehouse?sslmode=require"
    credentials_file: warehouse.json
    schemas:
      - reporting
      - public

Dropeá el JSON correspondiente en secrets/warehouse.json:

{
  "user": "looky_reader",
  "password": "..."
}

Campo por campo:

  • type: tiene que ser postgres.
  • name: label humano opcional que se muestra en la UI cuando el source aparece en herramientas de operator. Puramente cosmético.
  • dsn: URI libpq sin userinfo. Tiene que empezar con postgres:// o postgresql://; el regex del schema rechaza cualquier URI que contenga @ (es decir, user:password@). Cualquier parámetro de query-string libpq — sslmode, application_name, connect_timeout, target_session_attrs, channel_binding, gssencmode, … — pasa intacto.
  • credentials_file: filename plano de un JSON adentro de la carpeta secrets/ del workspace. Pattern ^[A-Za-z0-9_-][A-Za-z0-9._-]*$ — sin separadores de path, sin punto inicial. El JSON tiene que declarar {"user": "…", "password": "…"}.
  • schemas: array opcional que limita la introspección. Default a todos los schemas no-sistema (pg_catalog e information_schema quedan siempre excluidos).

Working example: MySQL source

Los sources de MySQL siguen la misma separación que Postgres: el dsn es una URI mysql://host:port/database sin el user:password@, y el user y password viven en un JSON dentro de secrets/ referenciado por credentials_file. A diferencia de Postgres, un DSN de MySQL no lleva parámetros de query — Looky parsea el host, port y database de la URI y rechaza cualquier ?…. runtime/sources.runtime.yml nunca contiene una password.

sources:
  shop:
    name: Online Shop
    type: mysql
    dsn: "mysql://db.internal.example.com:3306/shop"
    credentials_file: shop.json
    schemas:
      - shop

Dropeá el JSON correspondiente en secrets/shop.json:

{
  "user": "looky_reader",
  "password": "..."
}

Campo por campo:

  • type: tiene que ser mysql.
  • name: label humano opcional que se muestra en la UI cuando el source aparece en herramientas de operator. Puramente cosmético.
  • dsn: URI mysql://host:port/database sin userinfo. El regex del schema rechaza cualquier URI que contenga @ (es decir, user:password@) o un query string con ?. El port es opcional y default a 3306.
  • credentials_file: filename plano de un JSON adentro de la carpeta secrets/ del workspace. Pattern ^[A-Za-z0-9_-][A-Za-z0-9._-]*$ — sin separadores de path, sin punto inicial. El JSON tiene que declarar {"user": "…", "password": "…"}.
  • schemas: array opcional de databases a exponer para introspección. Default a la database nombrada en el dsn.

Dos cosas específicas de MySQL que conviene saber: las conexiones no van encriptadas (todavía no se puede configurar TLS hacia MySQL), así que usalo sobre una red de confianza; y MySQL no tiene un tipo boolean real, así que las columnas que parecen boolean vuelven como números — agregá un cast(… as boolean) explícito en tu model cuando necesites un filtro true/false.

Matriz de campos por adapter

Referencia rápida de qué requiere cada adapter:

  • BigQuery — requeridos: type: bigquery, project_id, credentials_file; opcionales: location, datasets (requeridos en tiempo de introspección, no en tiempo de validation).
  • Postgres — requeridos: type: postgres, dsn (URI libpq sin userinfo), credentials_file (filename de un JSON en secrets/ con {"user","password"}); opcionales: schemas.
  • MySQL — requeridos: type: mysql, dsn (mysql://host:port/database, sin userinfo, sin query string), credentials_file (filename de un JSON en secrets/ con {"user","password"}); opcionales: schemas (default a la database del DSN).

Para la matriz de divergencias completa por adapter (auth, binding de parámetros, manejo de NULL, dryRun, introspección, caching), mirá la comparación de adapters de source.

Workflow de validación de sources

  1. Editá runtime/sources.runtime.yml desde el root de tu workspace.
  2. Corré:
    looky sources list
    looky sources diff
    looky validate
  3. Arreglá errores de alias, credentials o dataset antes de tocar models.

sources list muestra los aliases de runtime actualmente registrados. sources diff muestra qué cambiaría en el push. validate chequea consistencia estructural sobre todo el workspace.

Errores comunes en sources

  • Alias mismatch: el model referencia sales pero el runtime define ecommerce. Cada query usando ese alias falla.
  • Filename de credentials equivocado: credentials_file tiene que ser un filename plano dentro de secrets/ — sin slashes, sin path prefix.
  • Dataset fuera de la lista: si un model consulta una tabla cuyo dataset no está listado en datasets:, la query se rechaza en runtime.
  • Billing project equivocado: si project_id no tiene BigQuery Job User otorgado al service account, las queries fallan incluso si los datos son legibles.

Nunca hagas workaround de problemas de source dentro del model. Arreglá el alias y el runtime config una vez, y mantené los models limpios.