feat(dav,obsidian): grupo dav completo (CardDAV/CalDAV client + split vcf/ics + import pipelines) + build_obsidian_graph + dav_list_calendars

Funciones reutilizables creadas esta sesion para el sistema self-hosted de contactos/calendario (Xandikos) y la app osint_web:
- grupo dav (infra): split_vcards, split_vevents_to_vcalendars, extract_or_make_uid, carddav_put_vcard, caldav_put_event, dav_list_resources, dav_get_resource, dav_list_calendars
- pipelines: import_vcf_to_carddav, import_ics_to_caldav
- obsidian: build_obsidian_graph (grafo agregado del vault)
This commit is contained in:
2026-06-12 00:43:59 +02:00
parent 4a0f0e9dc0
commit a76760edba
32 changed files with 2814 additions and 0 deletions
@@ -0,0 +1,75 @@
---
name: dav_get_resource
kind: function
lang: py
domain: infra
version: "1.0.0"
purity: impure
signature: "def dav_get_resource(base_url: str, username: str, password: str, resource_path: str, *, timeout_s: float = 20.0, verify_tls: bool = True) -> dict"
description: "Descarga (HTTP GET) el contenido de un recurso DAV individual (un VCARD o un VCALENDAR) con HTTP Basic auth. Construye el header Authorization: Basic base64(user:pass) a mano con stdlib. El resource_path puede ser un href absoluto (como los que devuelve dav_list_resources) o una URL completa. verify_tls=True por defecto. Maneja errores sin lanzar. Solo stdlib (urllib, base64, ssl). Probado contra Xandikos."
tags: [dav, carddav, caldav, get, download, http, infra]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: "error_go_core"
imports: [base64, ssl, urllib.error, urllib.request]
params:
- name: base_url
desc: "URL base del servidor DAV. Se ignora si resource_path ya es una URL absoluta."
- name: username
desc: "usuario para HTTP Basic auth (p.ej. 'enmanuel')."
- name: password
desc: "contrasena para HTTP Basic auth. Resolver desde pass con pass_get_secret, nunca hardcodear."
- name: resource_path
desc: "href absoluto (p.ej. '/enmanuel/contacts/addressbook/x.vcf') o URL completa del recurso a descargar. Acepta directamente los hrefs que devuelve dav_list_resources."
- name: timeout_s
desc: "timeout de la peticion HTTP en segundos. Default 20.0."
- name: verify_tls
desc: "si True (default) verifica el certificado TLS. No desactivar salvo entorno de prueba."
output: "dict. En exito: {status:'ok', http_status:int, text:str, url:str} donde text es el cuerpo del recurso (VCARD o VCALENDAR). En error (sin lanzar): {status:'error', error:str, http_status:int|None}."
tested: true
tests:
- "test_construye_request_get_con_auth"
- "test_resource_path_relativo_se_resuelve_con_base_url"
- "test_resource_path_absoluto_se_respeta"
- "test_devuelve_texto_del_recurso"
- "test_httperror_devuelve_status_error"
test_file_path: "python/functions/infra/dav_get_resource_test.py"
file_path: "python/functions/infra/dav_get_resource.py"
---
## Ejemplo
```python
import sys
sys.path.insert(0, "python/functions")
from infra.pass_get_secret import pass_get_secret
from infra.dav_list_resources import dav_list_resources
from infra.dav_get_resource import dav_get_resource
base = "https://dav-eedeb681c4ab89ab8e444ac9.organic-machine.com"
pw = pass_get_secret("dav/xandikos-enmanuel")["value"] # NO logear
listing = dav_list_resources(base, "enmanuel", pw, "/enmanuel/contacts/addressbook/")
first = listing["resources"][0]["href"]
res = dav_get_resource(base, "enmanuel", pw, first)
print(res["text"][:13]) # BEGIN:VCARD
```
## Cuando usarla
Cuando quieres leer el contenido de un recurso concreto cuyo href ya conoces
(por `dav_list_resources` o porque lo construyes tu). Util para hacer backup de
una coleccion (listar + get cada uno), validar que un import quedo bien escrito,
o comparar etags en un sync. Acepta directamente los hrefs del listing sin
reconstruir la URL.
## Gotchas
- Lectura remota real sobre TLS; password de `pass`, no se logea.
- Decodifica el cuerpo como UTF-8 con `errors='replace'`: bytes invalidos se
sustituyen por el caracter de reemplazo en vez de fallar.
- Si resource_path es relativo se concatena a base_url; si es absoluto
(http/https) se usa tal cual y base_url se ignora.
- `verify_tls=False` solo en pruebas; abre MITM.