#!/usr/bin/env python3 """ Generic Grist API wrapper using Vault for token retrieval. Usage: python3 vault_grist_api.py [JSON_PAYLOAD] Example: python3 vault_grist_api.py GET /api/docs/wmBGUbgBveCdeY8fZ6T6eL/tables/Intune/records """ import sys import json import urllib.request import urllib.error VAULT_URL = "https://beavault.beawit.net:8200" GRIST_URL = "https://grist.beawit.net" def vault_auth(): """Authenticate to Vault and return client token.""" auth_data = json.dumps({ "role_id": "75d2dcfb-9c65-7f60-59b4-eee8c7f8dc0e", "secret_id": "6202b465-2f25-547c-ec07-f47cfc4dda3e" }).encode() req = urllib.request.Request( f"{VAULT_URL}/v1/auth/approle/login", data=auth_data, headers={"Content-Type": "application/json"}, method="POST" ) with urllib.request.urlopen(req) as resp: data = json.loads(resp.read()) return data["auth"]["client_token"] def get_grist_token(vault_token): """Retrieve Grist API token from Vault.""" req = urllib.request.Request( f"{VAULT_URL}/v1/kv/data/api/integration/grist", headers={"X-Vault-Token": vault_token} ) with urllib.request.urlopen(req) as resp: data = json.loads(resp.read()) return data["data"]["data"]["token"] def grist_api_call(method, api_path, payload=None): """Make authenticated Grist API call.""" # Step 1: Vault auth vault_token = vault_auth() # Step 2: Get Grist token grist_token = get_grist_token(vault_token) # Step 3: Make API call url = f"{GRIST_URL}{api_path}" headers = { "Authorization": f"Bearer {grist_token}", "Content-Type": "application/json" } if method == "GET": req = urllib.request.Request(url, headers=headers) with urllib.request.urlopen(req) as resp: return json.loads(resp.read()) elif method == "PATCH": data = json.dumps(payload).encode() if payload else None req = urllib.request.Request(url, data=data, headers=headers, method="PATCH") with urllib.request.urlopen(req) as resp: return resp.read().decode() else: raise ValueError(f"Unsupported method: {method}") def main(): if len(sys.argv) < 3: print("Usage: python3 vault_grist_api.py [JSON_PAYLOAD_FILE]") sys.exit(1) method = sys.argv[1].upper() api_path = sys.argv[2] payload = None if len(sys.argv) > 3: with open(sys.argv[3]) as f: payload = json.load(f) result = grist_api_call(method, api_path, payload) print(json.dumps(result, indent=2)) if __name__ == "__main__": main()