Orchestration layer around the jrunner Java JDBC CLI, replacing the previous shell-based sync system in .archive/pre-rewrite. Includes the FastAPI + Jinja web frontend, per-driver adapters (DB2, MSSQL, PG), wizard-driven module creation with editable dest types and source-sourced table/column descriptions, watermark/hook CRUD, and the engine that runs modules end-to-end. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
101 lines
4.7 KiB
Python
101 lines
4.7 KiB
Python
"""HTTP client for Pipekit API."""
|
|
|
|
import requests
|
|
from requests.auth import HTTPBasicAuth
|
|
|
|
|
|
class PipekitClient:
|
|
def __init__(self, base_url: str = "http://localhost:8100",
|
|
username: str = "admin", password: str = "pipekit"):
|
|
self.base_url = base_url.rstrip("/")
|
|
self.auth = HTTPBasicAuth(username, password)
|
|
|
|
def _get(self, path: str, params: dict = None) -> dict | list:
|
|
r = requests.get(f"{self.base_url}{path}", auth=self.auth, params=params)
|
|
r.raise_for_status()
|
|
return r.json()
|
|
|
|
def _post(self, path: str, json: dict = None) -> dict:
|
|
r = requests.post(f"{self.base_url}{path}", auth=self.auth, json=json)
|
|
r.raise_for_status()
|
|
return r.json()
|
|
|
|
def _put(self, path: str, json: dict = None) -> dict:
|
|
r = requests.put(f"{self.base_url}{path}", auth=self.auth, json=json)
|
|
r.raise_for_status()
|
|
return r.json()
|
|
|
|
def _delete(self, path: str) -> dict:
|
|
r = requests.delete(f"{self.base_url}{path}", auth=self.auth)
|
|
r.raise_for_status()
|
|
return r.json()
|
|
|
|
# Connections
|
|
def list_connections(self): return self._get("/connections")
|
|
def create_connection(self, data): return self._post("/connections", data)
|
|
def get_connection(self, id): return self._get(f"/connections/{id}")
|
|
def update_connection(self, id, data): return self._put(f"/connections/{id}", data)
|
|
def delete_connection(self, id): return self._delete(f"/connections/{id}")
|
|
def test_connection(self, id): return self._post(f"/connections/{id}/test")
|
|
|
|
# Introspection
|
|
def list_tables(self, conn_id, schema=None):
|
|
params = {"schema": schema} if schema else None
|
|
return self._get(f"/connections/{conn_id}/tables", params)
|
|
def list_columns(self, conn_id, schema, table):
|
|
return self._get(f"/connections/{conn_id}/tables/{schema}.{table}/columns")
|
|
def propose_module(self, conn_id, schema, table, dest_schema=None,
|
|
linked_server=None, linked_db=None):
|
|
params = {}
|
|
if dest_schema: params["dest_schema"] = dest_schema
|
|
if linked_server: params["linked_server"] = linked_server
|
|
if linked_db: params["linked_db"] = linked_db
|
|
return self._get(f"/connections/{conn_id}/tables/{schema}.{table}/propose", params or None)
|
|
|
|
# Modules
|
|
def list_modules(self): return self._get("/modules")
|
|
def create_module(self, data): return self._post("/modules", data)
|
|
def get_module(self, id): return self._get(f"/modules/{id}")
|
|
def update_module(self, id, data): return self._put(f"/modules/{id}", data)
|
|
def delete_module(self, id): return self._delete(f"/modules/{id}")
|
|
def preview_module(self, id): return self._get(f"/modules/{id}/preview")
|
|
def run_module(self, id): return self._post(f"/modules/{id}/run")
|
|
def run_module_stream(self, id):
|
|
"""Stream sync output. Yields lines, final line starts with __DONE__ or __ERROR__."""
|
|
r = requests.post(f"{self.base_url}/modules/{id}/run/stream",
|
|
auth=self.auth, stream=True)
|
|
r.raise_for_status()
|
|
for line in r.iter_lines(decode_unicode=True):
|
|
if line.startswith("data: "):
|
|
yield line[6:]
|
|
def module_history(self, id): return self._get(f"/modules/{id}/history")
|
|
|
|
# Hooks
|
|
def list_hooks(self, module_id): return self._get(f"/modules/{module_id}/hooks")
|
|
def create_hook(self, data): return self._post("/hooks", data)
|
|
def delete_hook(self, id): return self._delete(f"/hooks/{id}")
|
|
|
|
# Groups
|
|
def list_groups(self): return self._get("/groups")
|
|
def create_group(self, data): return self._post("/groups", data)
|
|
def get_group(self, id): return self._get(f"/groups/{id}")
|
|
def delete_group(self, id): return self._delete(f"/groups/{id}")
|
|
def add_group_member(self, group_id, data): return self._post(f"/groups/{group_id}/members", data)
|
|
def remove_group_member(self, member_id): return self._delete(f"/groups/members/{member_id}")
|
|
def run_group(self, id): return self._post(f"/groups/{id}/run")
|
|
|
|
# Runs
|
|
def list_runs(self, limit=50): return self._get("/runs", {"limit": limit})
|
|
def get_run(self, id): return self._get(f"/runs/{id}")
|
|
|
|
# Schedules
|
|
def list_schedules(self): return self._get("/schedules")
|
|
def create_schedule(self, data): return self._post("/schedules", data)
|
|
def update_schedule(self, id, data): return self._put(f"/schedules/{id}", data)
|
|
def delete_schedule(self, id): return self._delete(f"/schedules/{id}")
|
|
|
|
# Drivers
|
|
def list_drivers(self): return self._get("/drivers")
|
|
def create_driver(self, data): return self._post("/drivers", data)
|
|
def delete_driver(self, id): return self._delete(f"/drivers/{id}")
|