loopable / api /routes_connected_tables.py
fsanyoto's picture
Deploy AIOS web (React glide grid + FastAPI slice)
051f280 verified
Raw
History Blame Contribute Delete
4.03 kB
"""routes_connected_tables.py β€” the SOURCE-NEUTRAL door to a connected grid (W31-T49, contract C4).
`GET /api/v1/connected-tables/{table_key}/rows` β€” one window over any grid whose rows come from a
connector and are read THROUGH the per-tenant DuckDB mirror rather than copied into the tenant
document. Odoo's four locked databases and its two line grains today; Meta Ads' entity and Insights
grids when `meta_relational` registers its specs (R2).
⭐ WHY THIS FILE EXISTS. The read-through window shipped in wave 30 inside `routes_odoo_tables.py`,
Odoo-named down to the URL β€” `/api/v1/odoo-tables/{key}/rows`. R2 puts a SECOND connector on the
same mirror, and a Meta campaign served from a URL with `odoo` in it is a name that lies to every
reader of a network tab, a log line and a bug report. So the capability gets a name that describes
what it IS.
β›”β›” IT IS AN ALIAS, NOT A REIMPLEMENTATION, AND THAT IS THE WHOLE DESIGN. T49's `done-when` is
*"the Odoo path behaves byte-identically, proven by an NC"*. There are two ways to make that true:
write a second handler and test that the two agree, or have ONE handler behind two URLs. Only the
second is true BY CONSTRUCTION β€” the first is true on the day it is written and drifts the first
time somebody fixes a bug in one of them. Every request here lands in
`routes_odoo_tables.odoo_table_rows`, unchanged, so the two doors cannot disagree without a line
being deleted. [[one-question-two-normalizers]]
⚠ THE OLD URL STAYS, and that is not politeness to a client we control. `CustomerGrid`'s bridge,
saved views and anything an operator has in a browser tab all name it; retiring it in the same wave
that introduces the new one would turn a rename into an outage for a feature that is already
half-proven (D-174). The Odoo-named route is kept as a deliberate alias with a stated retirement
condition rather than as a leftover: it goes when nothing in `web/src` names it.
β›” THE MOUNT IS THE POINT AND IT HAS BEEN MISSED BEFORE β€” three finished routers shipped 404-dead
behind green gates in wave 23, and wave 28 proved a MOUNTED router can still be uncallable
(`session.is_admin`, an attribute that does not exist, raising above the route's own `try`). So
`verify_meta` both enumerates `main.app.routes` for this path AND calls it.
"""
from fastapi import APIRouter, Depends, Query
from deps import Session, require_session
router = APIRouter(prefix="/api/v1")
@router.get("/connected-tables/{table_key}/rows")
def connected_table_rows(table_key: str,
offset: int = Query(default=0, ge=0),
limit: int = Query(default=0),
filters: str = Query(default=None),
filterConj: str = Query(default="and"),
sorts: str = Query(default=None),
search: str = Query(default=None),
session: Session = Depends(require_session)):
"""ONE WINDOW over a connected grid β€” `{fields, rows, total, totalUnfiltered, offset, limit,
limits, identity, recordsMutable}`, whichever connector the rows come from.
β›” EVERY GUARANTEE IS INHERITED, NOT RESTATED, because the body below is one call. `total` is a
`SELECT count(*)` and never `len(rows)`; the filter, sort and search resolve in SQL over the
whole table; anything that cannot be answered is reported in `limits` with a cause and a
recommendation (R6's second sentence); the permission wall is `_defn_or_refuse`; and the
cross-tenant mirror guard (D-169) refuses with `409 cross_tenant_store` before a cursor is
handed out. Documenting them again HERE would be a second statement of a contract that has one
home β€” and the day the two drifted, this file would be the one that lied.
"""
import routes_odoo_tables
return routes_odoo_tables.odoo_table_rows(
table_key, offset=offset, limit=limit, filters=filters, filterConj=filterConj,
sorts=sorts, search=search, session=session)