Skip to content

Commit cdf2a64

Browse files
committed
Fix SCIM pagination: item count, Load All button, and account-scope auth
- Use _find_list_key() for item count to skip "schemas" array (was showing 1 item) - Add _detect_has_more() supporting both has_more and SCIM totalResults pagination - Fix Load All for account-scope APIs by resolving account console credentials - Handle SCIM startIndex/count pagination in _load_all_worker - Clean up SCIM pagination fields from merged results Co-authored-by: Isaac
1 parent 53000de commit cdf2a64

3 files changed

Lines changed: 55 additions & 12 deletions

File tree

app.py

Lines changed: 53 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,26 @@ def _highlight_ini(text: str) -> str:
194194
}
195195

196196

197+
def _detect_has_more(data: Any) -> bool:
198+
"""Check whether a paginated response has more pages to fetch.
199+
200+
Handles two pagination styles:
201+
* ``has_more`` flag (standard Databricks APIs).
202+
* SCIM-style ``totalResults`` / ``startIndex`` / ``itemsPerPage``.
203+
"""
204+
if not isinstance(data, dict):
205+
return False
206+
if data.get("has_more"):
207+
return True
208+
# SCIM pagination: totalResults, startIndex, itemsPerPage
209+
total = data.get("totalResults")
210+
start = data.get("startIndex")
211+
per_page = data.get("itemsPerPage")
212+
if isinstance(total, int) and isinstance(start, int) and isinstance(per_page, int):
213+
return (start + per_page - 1) < total
214+
return False
215+
216+
197217
def _detect_next_page_token(data: Any) -> Optional[str]:
198218
"""Extract a ``next_page_token`` from a paginated API response.
199219
@@ -759,10 +779,9 @@ def build_response_panel(
759779
if isinstance(data, list):
760780
item_count = f" · {len(data)} items"
761781
elif isinstance(data, dict):
762-
for v in data.values():
763-
if isinstance(v, list):
764-
item_count = f" · {len(v)} items"
765-
break
782+
list_key = _find_list_key(data)
783+
if list_key:
784+
item_count = f" · {len(data[list_key])} items"
766785

767786
# CSV response: render as a scrollable HTML table
768787
csv_text = result.get("_csv")
@@ -2428,7 +2447,7 @@ def execute_api_call(n_clicks, endpoint, param_values, param_ids, body_text, tim
24282447

24292448
chips = extract_chips(endpoint.get("id", ""), result["data"]) if result["success"] else []
24302449
resp_data = result["data"] if isinstance(result["data"], dict) else {}
2431-
has_more = bool(resp_data.get("has_more"))
2450+
has_more = _detect_has_more(resp_data)
24322451
last_req = {
24332452
"path": path,
24342453
"method": method,
@@ -2439,6 +2458,7 @@ def execute_api_call(n_clicks, endpoint, param_values, param_ids, body_text, tim
24392458
"elapsed_ms": result["elapsed_ms"],
24402459
"status_code": result["status_code"],
24412460
"url": result.get("url", ""),
2461+
"is_account": is_account,
24422462
}
24432463
ep_id = endpoint.get("id", "")
24442464
new_cache = dict(cache or {})
@@ -2461,8 +2481,8 @@ def start_pagination(last_req):
24612481
if not last_req:
24622482
return {}, True
24632483
initial_data = last_req.get("initial_data", {})
2464-
# Skip auto-pagination when has_more is present — "Load All" handles those
2465-
if isinstance(initial_data, dict) and initial_data.get("has_more"):
2484+
# Skip auto-pagination when has_more is detected — "Load All" handles those
2485+
if isinstance(initial_data, dict) and _detect_has_more(initial_data):
24662486
return {"run_id": time.time()}, True
24672487
next_token = _detect_next_page_token(initial_data)
24682488
list_key = _find_list_key(initial_data) if next_token else None
@@ -2704,6 +2724,10 @@ def _load_all_worker(last_req, host, token, list_key, initial_data):
27042724
offset = len(items)
27052725
next_token = _detect_next_page_token(initial_data)
27062726
use_token = next_token is not None
2727+
# Detect SCIM-style pagination (startIndex / count / totalResults)
2728+
is_scim = "totalResults" in initial_data and "startIndex" in initial_data
2729+
scim_total = int(initial_data.get("totalResults", 0)) if is_scim else 0
2730+
scim_start = int(initial_data.get("startIndex", 1)) + int(initial_data.get("itemsPerPage", limit))
27072731
total_elapsed = last_req.get("elapsed_ms", 0)
27082732
pages = 1
27092733

@@ -2716,6 +2740,9 @@ def _load_all_worker(last_req, host, token, list_key, initial_data):
27162740
qp = dict(last_req.get("query_params") or {})
27172741
if use_token and next_token:
27182742
qp["page_token"] = next_token
2743+
elif is_scim:
2744+
qp["startIndex"] = str(scim_start)
2745+
qp["count"] = str(limit)
27192746
else:
27202747
qp["offset"] = str(offset)
27212748
qp["limit"] = str(limit)
@@ -2731,17 +2758,24 @@ def _load_all_worker(last_req, host, token, list_key, initial_data):
27312758

27322759
page_data = r["data"]
27332760
new_page_items = page_data.get(list_key, [])
2761+
if not new_page_items:
2762+
break
27342763
items.extend(new_page_items)
27352764
pages += 1
27362765
offset += limit
2766+
scim_start += len(new_page_items)
27372767
next_token = _detect_next_page_token(page_data)
27382768

27392769
state["items"] = items
27402770
state["pages"] = pages
27412771
state["total_items"] = len(items)
27422772
state["elapsed_ms"] = total_elapsed
27432773

2744-
if not page_data.get("has_more"):
2774+
# Determine whether more pages remain
2775+
if is_scim:
2776+
if len(items) >= scim_total:
2777+
break
2778+
elif not _detect_has_more(page_data):
27452779
break
27462780

27472781
state["running"] = False
@@ -2765,13 +2799,18 @@ def start_load_all(n_clicks, last_req, conn_config):
27652799
if not n_clicks or not last_req:
27662800
return NO
27672801
initial_data = last_req.get("initial_data", {})
2768-
if not isinstance(initial_data, dict) or not initial_data.get("has_more"):
2802+
if not isinstance(initial_data, dict) or not _detect_has_more(initial_data):
27692803
return NO
27702804
list_key = _find_list_key(initial_data)
27712805
if not list_key:
27722806
return NO
27732807

2774-
host, token = _resolve_conn(conn_config)
2808+
# Resolve host/token — account-scope APIs need the accounts console credentials
2809+
ws_host, ws_token = _resolve_conn(conn_config)
2810+
if last_req.get("is_account") and ws_host:
2811+
host, token = resolve_account_connection(conn_config, _accounts_host(ws_host))
2812+
else:
2813+
host, token = ws_host, ws_token
27752814
if not token or not host:
27762815
return NO
27772816

@@ -2856,6 +2895,10 @@ def poll_load_all(n_intervals, cache):
28562895
merged_data = {**initial_data, list_key: items}
28572896
merged_data.pop("has_more", None)
28582897
merged_data.pop("next_page_token", None)
2898+
# Clean up SCIM pagination fields in merged result
2899+
merged_data.pop("totalResults", None)
2900+
merged_data.pop("startIndex", None)
2901+
merged_data.pop("itemsPerPage", None)
28592902
merged_result = {
28602903
"status_code": last_req.get("status_code", 200),
28612904
"elapsed_ms": elapsed,

assets/style.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -733,7 +733,7 @@ body {
733733
.sp-load-all-btn {
734734
position: absolute;
735735
top: 8px;
736-
left: 220px;
736+
left: 232px;
737737
z-index: 5;
738738
background: rgba(0,212,255,0.1);
739739
border: 1px solid rgba(0,212,255,0.25);

version.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
533
1+
549

0 commit comments

Comments
 (0)