Fix/rest api openwebui integration - #262
Open
amirdhs wants to merge 5 commits into
Open
Conversation
Db::column_data_implode() embedded print_r($column_definitions) and the supplied values into the InvalidSql exception message. CalDAV::exception_handler() echoes that message to the client, so any REST request with an unknown filter name returned the complete table definition of the queried table. Reproduced with GET /groupdav.php/<user>/timesheet/?filters[nosuchthing]=1 which answered 500 with the full egw_timesheet schema (3832 bytes). The arrays are still logged via error_log() for debugging; only the exception message is now a single line. The same request now returns 76 bytes.
…lter() CalDAV::jsonIndex() converts filters[start] / filters[end] into a synthetic ['name' => 'time-range', 'attrs' => [...]] element stored under an integer key. The JSON/REST branch of infolog_groupdav::_report_filters() merged $options['filters'] verbatim into the column filter, so that array ended up as a column and produced "AND Array" in the SQL: GET /groupdav.php/<user>/infolog/?filters[start]=2026-07-01&filters[end]=2026-08-01 -> 500 Invalid SQL: ... AND Array ... Unknown column 'Array' in 'WHERE' Integer-keyed time-range elements are now converted with the existing _time_range_filter() helper, as the CalDAV/XML branch already did, and appended as a SQL fragment. Verified against real rows: a June window matches the tasks due in June, a July window matches none.
…ests
calendar_groupdav::_report_filters() had no Api\CalDAV::isJSON() branch, unlike
the addressbook, infolog and timesheet handlers. REST filters arrive as a plain
name => value array rather than as CalDAV XML filter elements, so every one of
them fell through to the "unknown filter --> ignored" default arm. filters[search]
and filters[linked] were therefore documented in doc/REST-CalDAV-CardDAV/Calendar.md
and advertised in doc/openapi/calendar.json, but silently returned unfiltered
results.
Added jsonReportFilters(), which maps:
- search -> query, either a free-text string matched against cal_title,
cal_description and cal_location, or an array of <db-column> => <value>
- linked -> query['egw_cal.cal_id'] via Api\Link::get_links(), table-qualified
because cal_id exists in egw_cal and egw_cal_user. sql_filter is not
usable here: calendar_bo::search() deliberately overwrites
$params['sql_filter'] from its own second argument.
- filters[start] / filters[end] keep flowing through the existing time-range case.
calendar_bo::search() accepts only one "query", so a free-text filters[search]
cannot be combined with filters[linked]; that combination now reports a clear
error instead of silently dropping one of them.
CalDAV/CardDAV XML request handling is unchanged.
…d SQL filter2col_filter()'s default arm prefixes any unknown filter name with "ts_", which turned two valid-looking requests into 500s that echoed the whole egw_timesheet schema back to the client: - filters[start] / filters[end]: CalDAV::jsonIndex() appends these as a synthetic ['name' => 'time-range', ...] element under an integer key. Timesheet has no time-range support, so the array became a "ts_0" column. Array- and integer-keyed filters now raise a JsParseException (422) explaining that timesheet does not support date-range filtering. - filters[order]: became "ts_order" instead of reaching propfind_generator(), so the documented ordering filter was unusable. It is now passed through, restricted to an optionally table-qualified column name plus an optional ASC/DESC. so_sql::search() already sanitises order_by via $sanitize_order_by, so this only turns a silently mangled sort into an explicit 422.
The descriptions in doc/openapi/*.json are what an OpenAPI tool-calling client hands to an LLM, so incorrect ones directly cause wrong requests. - filters[start] / filters[end] in calendar.json and infolog.json declared "type": "datetime", which is not a valid JSON Schema type. Clients that map OpenAPI parameters onto function-call schemas copy the type verbatim, so the parameter arrived unusable and date filters were never sent. Now "type": "string" with "format": "date". - nresults claimed to "Limit number of responses (only for sync-collection)". It now spells out the actual mechanism: send sync-token= together with nresults=N to get a bounded chunk plus more-results and a follow-up sync-token. - Documented filters[order], which the addressbook, infolog, timesheet and tracker handlers read but which appeared in no description, including each app's real default ordering. Not added to calendar.json, whose handler has no ordering support. - The filters[end] example read "filters[start]=2026-02-01". - calendar states its implicit -100/+365 day default window, and timesheet states that date-range filtering is unsupported and answers 422. - Accept is no longer "required": true. It is an in: header parameter, and tool executors that forward only path and query parameters force the model to emit a value that is then discarded. It stays in the spec as optional documentation.
amirdhs
force-pushed
the
fix/rest-api-openwebui-integration
branch
from
July 26, 2026 14:08
a8e8338 to
d907679
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fix REST/JSON collection filters: nresults, date ranges, sort order and a schema disclosure
Found while connecting the
groupdav.php/openapi.jsonREST API to an LLM tool-calling client(Open WebUI). Four independent bugs, all reproducible with plain
curl. Two return HTTP 500 ondocumented parameters, one discloses table schemas, and one makes it impossible to bound a response.
Each commit stands alone and can be dropped independently.
1.
nresultswas silently ignored without async-tokenCalDAV::jsonIndex()computed the limit but only forwarded it inside theif (isset($_GET['sync-token']))branch, andaddressbook,calendarandtimesheetadditionally required
$options['root']['name'] === 'sync-collection'. A REST client therefore hadno way to bound a collection listing and always received the entire collection serialized as
JsContact / JsEvent / JsTask objects.
Both gates are relaxed.
sync-collectionsemantics (more-resultsplus a newsync-token) areunchanged, and its forced ascending order still applies.
2.
addressbookandtimesheetdefaulted to oldest-firstDefaults were
egw_addressbook.contact_idandegw_timesheet.ts_id, both ascending. Combined with(1), a limited request returns the N oldest entries — a plausible-looking wrong answer rather than
a visible failure. Now
contact_modified DESC/ts_modified DESC.infologalready usedinfo_datemodified DESC;calendarhas no ordering support.Shipped in the same commit as (1) deliberately: a row cap without a sensible order is worse than no
cap at all.
3.
filters[start]/filters[end]returned HTTP 500 on infolog and timesheetCalDAV::jsonIndex()converts these into a synthetic['name' => 'time-range', 'attrs' => [...]]element under an integer key. The JSON branch ofinfolog_groupdav::_report_filters()merged$options['filters']verbatim into the column filter,so the array became a column:
It now goes through the existing
_time_range_filter()helper, as the CalDAV/XML branch alreadydid. Verified against real rows: a June window matches the tasks due in June, a July window matches
none, and a narrow window discriminates correctly.
Timesheet genuinely has no time-range support, so there the same element now raises a
JsParseException(422) with an explanatory message instead of building a bogusts_0column.doc/openapi/timesheet.jsondocuments this.4. Unknown filter names disclosed the full table schema
Db::column_data_implode()embeddedprint_r($column_definitions)and the supplied values in theInvalidSqlexception message, andCalDAV::exception_handler()echoes that message to the client:The arrays are still written to
error_log()for debugging; only the exception message is now asingle line. The same request returns 76 bytes.
Also included
calendarsilently ignoredfilters[search]andfilters[linked].calendar_groupdav::_report_filters()had noApi\CalDAV::isJSON()branch, unlike the other threehandlers, so REST filters (a plain
name => valuearray, not CalDAV XML elements) all fell throughto
unknown filter --> ignored, whiledoc/openapi/calendar.jsonadvertised both. AddedjsonReportFilters().filters[linked]usesquery['egw_cal.cal_id']rather thansql_filter, becausecalendar_bo::search()deliberately overwrites$params['sql_filter']from its own secondargument.
calendar_bo::search()accepts only one "query", so a free-textfilters[search]cannotbe combined with
filters[linked]; that combination now reports a clear error rather than droppingone silently.
filters[order]was unreachable ontimesheet. Its column mapper rewroteordertots_order, producing bug (4). Now passed through, restricted to an optionally table-qualifiedcolumn plus an optional direction.
so_sql::search()already sanitisesorder_by, so this onlyconverts a silently mangled sort into an explicit 422.
doc/openapi/*.jsoncorrections. These descriptions are what an LLM client actually sees, sowrong ones directly produce wrong requests:
filters[start]/filters[end]declared"type": "datetime", which is not a valid JSONSchema type. Clients that map OpenAPI parameters onto function schemas pass it through
verbatim, so the parameter arrives unusable and date filters are never sent. Now
"type": "string"with"format": "date".nresultswas described as "Limit number of responses (only for sync-collection)", activelydiscouraging clients from sending it.
filters[order]is now documented for the four apps whose handlers read it (notcalendar).filters[end]example readfilters[start]=2026-02-01.Acceptis no longerrequired: true— it is anin: headerparameter, and tool executors thatforward only
pathandqueryparameters force a value that is then discarded. It remains inthe spec as optional documentation.