Conversation
The April 2026 security update bumped swagger-ui 4.15.5 -> ^5.32.2 without migrating df-api-docs.component.ts to the v5 calling convention. v5 introduced breaking changes in bundle layout and internal call paths, causing every service's API docs to throw "TypeError: o is not a function" from inside the swagger-ui bundle. Pinning back to 4.15.5 restores the working UI as a stopgap. The patched DOMPurify XSS / Handlebars JS injection CVEs return — low practical risk for the admin UI (specs come from trusted backends), but a proper v5 migration is still owed. Rebuilt dist/ artifacts included.
The API Builder nav entries and breadcrumbs rendered raw transloco keys (nav.api-builder.nav, nav.api-connections.api-types.api-builder.*, and the missing api-types.header). Add the strings to en.json (source + runtime dist copy). Translations are runtime-loaded JSON, so no JS rebuild is required.
Major rework of the API Builder admin UI (df-api-builder.component.ts):
UX:
- Replace the cramped 2-column workbench with a Swagger-style master-detail:
API settings card (title, /api/v2/{base_path}, status, Save/Docs/Delete) +
a method-colored endpoint accordion. Click a row to edit inline; Add Endpoint
opens a new draft. Collapsed by default.
- Surface Endpoint Name/URL as primary fields; preserve user-entered identity
(don't overwrite path/label/description once edited).
- Autocomplete source-API/table pickers; hide non-data service types from the
source list; focus-to-show-full-list with deferred blur restore.
Behavior fixes:
- Preview/Test now execute (send dry_run:false + snake_case path_params); the
Test runner response is shown verbatim.
- Re-introspect the source when loading a saved endpoint so Fields/Relationships
render with saved selections (safe now that the CD-hang is fixed via trackBy).
- "Save the API first" hint when no API is saved yet.
Field/relationship renaming (output aliases):
- Per-field/relationship rename inputs -> flat alias map on the execution-plan
step; backend applies a key-remap on fetched rows.
camelCase interceptor safety (shared utilities):
- case.ts: treat execution_plan/response_mapping VALUES as verbatim blobs (their
inner keys are real field names + user aliases) — transform the envelope key
only, both directions. Same pattern as the existing requestBody exclusion.
- case.interceptor.ts: skip response transform for /api_builder/test so the
preview shows the real output keys the deployed endpoint returns.
- Add regression tests; existing case tests still pass.
Includes rebuilt dist/.
…crumb-header Fix/api types breadcrumb header
…ship builder) Workspace/relationship panel on the custom-API editor: pick backing services for the API and author cross-service relationships, with a live preview and per-type help text. Endpoint source picker is scoped to the API's workspace and refreshes live when it changes. camelCase interceptor support for API Builder fields (case.ts). Rebuilt production dist.
… date last modified
…ervice_request + transform recipe)
Self-contained adf-agents component (mirrors adf-alerts): agent CRUD with role + key TTL and revoke, approve/deny pending access requests, and an activity view (last-active + recent agent alerts). Top-level 'agents' route + nav label. Includes rebuilt dist. Claude-Session: https://claude.ai/code/session_01TAqkbJ6Wpa9CX2sydcFeZu
…eguard The global caseInterceptor converts /api responses to camelCase; the component read snake_case so every field was undefined (toggle stuck off, blank cells, edits not persisting). Rewrote to camelCase (alerts log stays snake — /_internal is not under /api). Toggle now reverts to server truth on error. Rebuilt dist. Claude-Session: https://claude.ai/code/session_01TAqkbJ6Wpa9CX2sydcFeZu
Drive the AI chat and AI Connections UIs off theme tokens instead of hardcoded white text, so they are legible in light mode. Dark mode is unchanged: the dark token values reproduce the original whites. - AI chat: theme tokens for messages, tool-result cards, session list, and the chat shell; show the tool CALL (name + arguments), not just the result; move "Act as role" beside New chat; tighten height so the input stays in view. - AI chat config: MCP servers + data services as selectable pickers. - AI Connections config: theme tokens for allowed-roles, model-picker, and test-connection; fix df-role-scope wildcard callout. - Hide API Builder / Alerts / Agents from the left nav (routes intact). - Drop em-dashes from shipped picker and test-connection copy.
Snapshot of in-progress work found uncommitted in the tree: a new API Builder preview component (+ specs), large reworks to the workspace and main builder components, and small tweaks to Agents, event-script details, welcome page, and the script editor. Committed to clean the tree ahead of the UI-hardening branch; not reviewed.
…solved toward main's --df-ai-* pattern; dist kept ours, rebuilt next)
…d once on body - styles.scss: --df-* token set, light on :root, dark under .dark-theme, dark values reproduce the pre-token hardcoded whites - DfThemeService now stamps .dark-theme on <body> (single source of truth); color-scheme: dark for native controls - component migration to tokens follows in stream1 sweep commits
…tion-chips AI Chat: follow-up suggestion chips from a trailing suggestions fence
…stimator AI Usage: task-based what-if cost estimator (estimate → budget verdict → run & compare)
The Health chip flagged nearly every service as "Needs attention" with "No API docs", but the API Docs page for those same services works. The noDocs rule read the service_doc_by_service_id relation, which is only populated when an admin uploads a *custom* OpenAPI override. Every service gets a spec generated from its type at request time, so on a healthy install that relation is empty for the whole catalog and the rule fired on all of it. Neither is the presence of a doc a health signal to begin with. Dropped noRateLimit for the same reason: rate limiting is a licensed feature and an install with no limits is the norm, so the rule warned on every service without saying anything about its health. Health now covers what actually makes a service unusable - no role grants access (danger) and explicitly deprecated (danger). That also takes the context down from three list reads to one.
Every detail route is keyed by id (/rate-limiting/7, /users/12, /api-keys/3 ...), and the breadcrumb is generated straight from the URL, so the page you are editing announced itself as a bare number. The one place that already had a name to show - the Rate Limiting table - was printing the raw service/role/user ids in its own columns too. - Breadcrumb: the last crumb of a dynamic route now takes its label from the record the route's resolver already fetched, so no extra request. Only the crumb that *is* the identifier is renamed (a trailing static segment like :id/scope is left alone), and the raw segment stays when nothing names the record - a paywalled or failed resolve. - Rate Limiting table: the user/service/role columns render the related record's name (already fetched via `related`), '-' when the limit is not scoped to one, instead of the foreign key. - Dynamic crumbs render the whole segment. Splitting on '-' and keeping the tail turned a service named "my-mysql-db" into "db" in the breadcrumb on API Docs, Schema and Scripting pages.
On Home the "N services allow broad read and write access" strip was drawn under the bottom of the four stat tiles. df-stat-tile sets height:100% so the tiles fill their grid row at equal height, but the app ships no global box-sizing reset, so the tiles compute under content-box: the 16px padding on each side plus the 1px borders are added *on top of* the row height, and every tile renders 34px taller than its own grid track. The overflow lands on whatever follows .tile-grid in the column - here, the exposure strip. Measured in Chromium: tile bottom 34px past the grid, 18px into the strip. box-sizing: border-box keeps the padding inside the 100%. Scoped to the tile rather than adding a global reset, which would move layout on every other page.
The committed dist/ predated the merges of #509 (chat suggestion chips) and #510 (task-based cost estimator), so neither feature was present in the served admin UI. Rebuilt with ng build (Angular 16.1, node 20) from develop @ 793c942; the new features' jest specs pass (36 tests across df-chat-message.component.spec.ts and task-estimate.spec.ts). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Health chip on the Services table hides its reasons behind a menu. That is fine for scanning a list and useless on the page where you would actually act on them, so the service edit page now opens with the same verdict spelled out: one row per failing rule, the consequence in plain words, and a deep link to the config that clears it. df-service-health-panel reuses DfServiceHealthService.derive() rather than restating the rules, so the page and the table can never disagree, and the context is already cached app-wide - opening a service costs no extra request. Placement and scope, given how far the service pages diverge below the fold: top of the page for every service type in edit mode (create has nothing to score yet), and never on the DreamFactory Platform APIs route, whose services are not role-governed the way a user service is. A passing service collapses to a single quiet line instead of a green wall, so the panel does not compete with the config form.
…alth"
"Healthy" was reporting on the role graph and nothing else, so a service
whose credentials had rotted still read healthy - the chip never opens a
connection. Nothing else re-checks either: df-core's ServiceHealthChecker
fires only on static::created and persists no result, so a password that
broke after setup was invisible everywhere in the admin UI.
Two changes, matching the two things that were conflated:
- The service page's panel now probes the service live, using the same
endpoints the API Docs page already checks (/_schema for a database, /
for file storage, from healthCheckEndpointsInfo keyed by the route
group). A dead connection is a danger finding that outranks anything
derived. A type with no mapped endpoint says it was not checked rather
than claiming health it cannot prove.
- The Services table column is renamed Access (services.access.chip.*),
because that is all it measures. A live probe costs one request per
service and the list would fire one per row on every visit, so the
probe stays on the page for a single service.
The failed-probe row carries the normalized message plus df-error-detail
for status, request and the DSN-scrubbed raw body. The probe requests
JSON rather than text so DF's { error: { message } } envelope parses at
all - the API Docs probe asks for text, which collapses every failure
into the generic "errors.http5xx" string.
…on screen Reverts the column back to Health and makes the name true, rather than renaming around the problem: the list now probes each service on the current page, so a database whose credentials broke reads "Action required" in the catalog, not just on its own page. Scope is the rendered page, never the catalog. The table pages server-side, so dataSource.data is exactly what is on screen: cost tracks page size, not install size. DfServiceProbeService caches each verdict by service name, so paging back and forth re-opens nothing, and the service page and the list now share one probe implementation. A chip with no failing rule says which of two things it means: "Healthy" only when a connection actually answered, "Not checked" for a type with no defined probe (scripting, remote, auth), "Checking…" while in flight. That distinction is the whole point - the old chip called all three Healthy. A failed probe becomes a cannotConnect rule via DfServiceHealthService.withProbe(), so the existing chip and its "why" menu render it with no special casing, and governance rules stay behind it in the list. Composed rather than overwritten: the role-graph context and the probe verdict land in either order.
The DreamFactory Platform APIs (system, files, logs, db, email, user) are reached with an admin session, not through a role grant, so the governance rule says nothing about them, and their route defines no service group, so there is nothing to probe either. The column could only ever read "Not checked" or a misleading "Action required". Hidden rather than filled: the same reason the service page's health panel already skips this route.
The page's copy was written against the light theme with fixed colours: the subhead and the "Pick a SQL service above" empty state are rgba(0,0,0,.6)/.5, i.e. black at low alpha, which all but disappears on the dark ground. Swapped for --df-text-muted, whose light value is rgba(0,0,0,.55) - so light mode is unchanged - and which carries rgba(255,255,255,.55) in dark and its own value in phosphor. Same swap for the other muted text on the page (stat labels, table sub-labels, the muted lock icon), and --df-danger / --df-accent for the drift and enforcement colours, which were dark red and dark purple on a dark background. Deliberately left: the rgba inside .badge-grey. That text sits on the badge's own #f5f5f5 fill, not the page, so tokenising it would put near-white text on near-white.
The three dialogs (table diff, snapshot history, OpenAPI) style themselves from inline literals written against the light theme: muted text as black-at-low-alpha, hairlines as rgba(0,0,0,.08), panels as #f5f5f5/#fafafa/#fff. In dark mode the text all but disappears and the panels are white slabs. Everything now rides the same tokens the rest of the admin UI uses, so all three themes repaint by construction: breaking -> --df-danger(-soft) potentially breaking-> --df-warning(-soft) additive -> --df-accent(-soft) cosmetic / neutral -> --df-surface-2 + --df-text-muted locked / active -> --df-success(-soft) OpenAPI draft blue -> --df-tint-data-bg/fg Severity border-lefts follow the same ladder. The status badges on the page itself move with them - with the badge fill tokenised, the grey badge's text can now be --df-text-muted too, which is what the earlier pass had to leave as a literal.
…and-record-labels Fix/service health and record labels
# Conflicts: # dist/index.html
…erates
A {table_name} / {procedure_name} / {function_name} token always rendered
as a <select>, populated by one lookup whose failure is swallowed into an
empty list. When that list came back empty the operation became unrunnable:
a dropdown holding nothing but its disabled placeholder, and no way to type
a value.
Empty is not rare. The lookup 404s or 403s on RBAC, returns nothing for a
service with no stored procedures, and 500s outright on the Laravel 13
bundle, where cache.serializable_classes=false breaks GET /{service}/_table
for every service.
usePicker() keeps the dropdown while options exist - and while the request
is still in flight, so the control does not flip out from under the user -
and falls back to the free-text input the template already carries once the
list resolves empty.
Also repairs the suite, which failed 3/3 on 7.7 before this change: the
component gained a MatSnackBar injection and a route-snapshot read that the
TestBed never provided, and the swagger-ui host throws while jsdom tears the
fixture down.
…typing Fix/api docs token typing
…atures Adds the three AI service types to SILVER_SERVICES so open-source admin UIs show them in the service picker with the standard upgrade paywall instead of omitting them entirely - matching how Active Directory, SAML, and other paid connectors are presented. Dist rebuilt from this branch head (includes the #513/#514 fixes). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Rebuild dist with suggestion chips and task cost estimator
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.
7.7.0 release cut. Highlights: the 7.7 admin UI wave — design system + command palette, API Docs & API Builder workspaces, Agents/Alerts pages, chat suggestion chips, task cost estimator, real service health probes — plus the Silver paywall catalog for the AI types and a current dist build.
After merge, tag 1.7.7 on the merge commit (df-commercial's version refresh consumes the tags). Full release notes: see the 7.7.0 draft.
🤖 Generated with Claude Code