Fetch all JSONHTL notes from a remote notes server using the keys endpoint and per-key retrieval.
Input: base_url (str) — Base URL of the notes server (e.g. http://localhost:8021).
Output: dict[str, dict] — Mapping of note key to validated JSONHTL document.
Failure mode: Raises RuntimeError or ValueError on any HTTP, parsing, or structural validation error (fail-fast, no silent recovery).
import json
from urllib import request, error, parse
def fetch_all_notes_from_server(base_url):
if not isinstance(base_url, str) or not base_url.strip():
raise ValueError('base_url must be a non-empty string')
base_url = base_url.rstrip('/')
# Step 1: POST / with {"op": "keys"} to retrieve list of note keys
payload = json.dumps({'op': 'keys'}).encode('utf-8')
req = request.Request(
base_url + '/',
data=payload,
headers={'Content-Type': 'application/json'},
method='POST'
)
try:
with request.urlopen(req) as response:
status = getattr(response, 'status', None)
if status is not None and status != 200:
raise RuntimeError(f'HTTP POST / failed with status {status}')
raw = response.read()
except error.URLError as e:
raise RuntimeError(f'Failed to retrieve note keys: {e}') from e
try:
keys = json.loads(raw.decode('utf-8'))
except Exception as e:
raise RuntimeError(f'Failed to parse keys response JSON: {e}') from e
if not isinstance(keys, list):
raise ValueError('Keys response must be a JSON list of strings')
validated = {}
for key in keys:
if not isinstance(key, str) or not key:
raise ValueError(f'Invalid note key returned by server: {key!r}')
encoded_key = parse.quote(key, safe='')
url = base_url + '/' + encoded_key
try:
with request.urlopen(url) as response:
status = getattr(response, 'status', None)
if status is not None and status != 200:
raise RuntimeError(f'HTTP GET {key!r} failed with status {status}')
raw_doc = response.read()
except error.URLError as e:
raise RuntimeError(f'Failed to fetch document {key!r}: {e}') from e
try:
doc = json.loads(raw_doc.decode('utf-8'))
except Exception as e:
raise RuntimeError(f'Failed to parse JSON for document {key!r}: {e}') from e
if not isinstance(doc, dict):
raise ValueError(f'Document for key {key!r} is not an object')
title = doc.get('title')
content = doc.get('content')
if not isinstance(title, str):
raise ValueError(f'Document {key!r} missing valid title string')
if not isinstance(content, list):
raise ValueError(f'Document {key!r} missing valid content list')
validated[key] = doc
return validated