Commit Graph
36 Commits
Author SHA1 Message Date
james.maes 777eb3b95b Merge branch 'develop' into feature/developer-panel 2026-10-12 16:03:18 -05:00
james.maes 7f21e46a2f Merge pull request #15 from QRun-IO/feature/ga-only-release-tags
ci: publish only GA tags (vX.Y.Z), never RC tags
2026-10-12 16:01:09 -05:00
james.maesandClaude Opus 5.5 dbdb5eb113 ci: publish only GA tags (vX.Y.Z), never RC tags
An RC tag matched /v.*/ and the orb republished it as the next RC.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 15:58:14 -05:00
james.maesandClaude Opus 5.5 c36a0e8c94 Merge develop into feature/developer-panel; open SQL in the Runner
Brings the snake_case rename (#7) and the app home and Runner (#9).
The developer panel's "open as a new cell" links now target the Runner
app (SqlWorkbenchRunnerAppMetaDataProducer.APP_NAME, prefixed), since
/app/sqlWorkbench is the home dashboard. Tests and README updated; the
changelog entry moves under 1.0.0-RC.3.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 15:31:22 -05:00
james.maes 72647623fa Merge pull request #9 from QRun-IO/feature/app-home-runner-nav
feat: app home dashboard, Runner nav entry, main tables only (1.0.0-RC.3)
2026-10-12 15:14:21 -05:00
james.maes f7b5ffd569 Merge branch 'develop' into feature/app-home-runner-nav 2026-10-12 15:12:19 -05:00
james.maes f42f76c59b Merge pull request #7 from QRun-IO/feature/snake-case-table-names
feat!: snake_case QQQ names with the sql_workbench_ prefix (for 1.0.0-RC.3)
2026-10-12 15:12:05 -05:00
james.maes b3913fca7f Merge branch 'develop' into feature/snake-case-table-names 2026-10-12 15:10:14 -05:00
james.maesandClaude Opus 5.5 68f559dfc8 fix: home and notebook record polish from the screenshot review
- Recent, failed and write runs sort by start time (not insert order).
- Activity by User has four columns (last run moves under the user), the
  host readability rule's maximum.
- No widget-permission throw inside the renderer: the middleware checks
  widget permissions, and host guards render every widget.
- Notebook record view: plain section titles (Cells, Shared With, Pinned
  Results, Versions, Runs) instead of join names; Runs newest first,
  Versions newest first; the Open in Runner widget is not a nested card.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 15:05:46 -05:00
james.maes 5be0693255 Merge pull request #8 from QRun-IO/chore/qrun-repo-standard
chore: align with the QRun qbit repository standard
2026-10-12 14:54:53 -05:00
james.maesandClaude Opus 5.5 18651d6712 chore: align with the QRun qbit repository standard
- `.editorconfig` (Kingsrook 3-space layout)
- `.gitattributes` (LF, CRLF for `*.cmd`)
- `.github/dependabot.yml` (Maven + Actions, grouped minor/patch, at most 3 open PRs, target `develop`)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 14:48:42 -05:00
james.maesandClaude Opus 5.5 6f2626b04f feat: app home dashboard, Runner nav entry, main tables only in the nav
Owner spec: the SQL Workbench app home is an information dashboard like
the other app homes, the notebook UI is its own nav entry ("Runner"), and
the nav lists only the main tables; sub-tables are relations on a notebook.

- Home: summary tiles and links, runs per day, recent notebooks, recent
  runs, slowest and failed queries, most-used notebooks, shared notebooks,
  committed writes, and (admins only) activity by user. Read through core
  actions in the viewer's session, so permissions and record locks apply;
  honest empty and no-permission states. Full width, inline styles only.
- Runner: sqlWorkbenchRunner child app showing the SQL_WORKBENCH widget
  (/app/sqlWorkbenchRunner?notebook=<id>).
- Nav: Runner, sql_workbench_notebook, sql_workbench_execution. Cells,
  versions, pinned results and shares stay standard tables (URL, API) as
  child lists on the notebook; new Runs child list (notebook -> execution
  join, locked to the viewer's runs) and an Open in Runner widget.
- SqlWorkbenchIntegrations: opt-in ESB and Quick Search hooks for the main
  tables, no dependency on either module.
- Tests: home widgets (H2 and Postgres), nav structure, child lists,
  hooks. README, CHANGELOG, UPGRADE, CLAUDE/AGENTS.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 14:45:38 -05:00
james.maesandClaude Opus 5.5 51c5a06859 feat!: snake_case QQQ names with the sql_workbench_ prefix (owner naming rule)
Owner rule: table names are lowercase snake_case, prefixed with the
qbit's name. Metadata only; the physical tables were already
sql_workbench_*, so no DDL.

- Tables, notebook PVS: sql_workbench_notebook, _cell, _notebook_version,
  _result_snapshot, _notebook_share, _execution (now equal to their
  backend table names)
- Enum PVS: sql_workbench_share_audience_type
- Permissions: sql_workbench.{hasAccess,write,share},
  sql_workbench_admin.hasAccess, <table>.read etc.
- Security key types: sql_workbench_user (all-access
  sql_workbench_all_access), sql_workbench_audience
- Joins: sql_workbench_notebook_join_sql_workbench_*
- tableNamePrefix / namespace normalized to snake_case, one "_"
- @Deprecated LEGACY_* constants with the pre-RC.3 names
- UPGRADE.md: permissions, security keys, stored references (audit_table,
  saved views/reports) with sample SQL; CHANGELOG 1.0.0-RC.3
- Test for the new names, legacy constants and prefix normalization

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 14:07:13 -05:00
james.maes c78737c359 Merge pull request #5 from QRun-IO/feature/3-standard-qqq-tables
feat(#3): workbench data as standard QQQ tables secured by record locks (1.0.0-RC.2)
2026-10-12 12:27:40 -05:00
james.maesandClaude Opus 5.5 ad8f9001a6 ci: rebuild now that QQQ 4.1.0-RC.4 is on Maven Central
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 12:25:22 -05:00
james.maesandClaude Opus 5.5 8346a5fe27 build: build against the released QQQ 4.1.0-RC.4
RC.4 lets partial edits of child rows pass joined record locks
(qqq#1073), which the table security matrix tests need.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 12:24:46 -05:00
james.maesandClaude Opus 5.5 e0d9312aff feat: SQL Workbench record developer panel
Add SqlWorkbenchDeveloperPanelProvider (id sqlWorkbench, order 450, icon
terminal), registered by SqlWorkbenchDeveloperPanelsMetaDataProducer via
DeveloperPanelsMetaData. For tables on an allowed RDBMS backend, and users
with the workbench access permission, it shows read-only SELECTs for the
record and its table in the backend's own table/column names (dialect
quoted identifiers, escaped primary-key literal), backend details, and
links that open each query in the workbench (?sql=&backend=, not run).

Build against QQQ 4.1.0-RC.4 (the developer panel contract).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 10:38:51 -05:00
james.maesandClaude Opus 5.5 663c209f4e fix(#3): gate record actions with a context-free permission checker
QQQ's UseTablePermissionCustomPermissionChecker needs a QContext instance
when available permissions are listed and threw a NullPointerException
there (seen in Website-Backend's permission-listing tests). The record
actions now use SqlWorkbenchPermissionChecker, which requires one named
session permission (share-table insert, notebook insert / edit). Test:
listing available permissions with no QContext.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 06:21:13 -05:00
james.maesandClaude Opus 5.5 f823c9a50b fix(#3): harden versioning, cascades and visibility sync; more security tests
From review of the standard-tables change:
- Versioning: the next number passes both currentVersion and the newest
  stored version, the currentVersion update is checked, and failures are
  logged (QQQ turns post-customizer exceptions into record warnings).
- Notebook delete cascade: a failed child delete aborts the delete.
- Share visibility sync checks its update.
- An admin ownership transfer resets ownerName to the new owner id.
- Shares are audited at record level, like the other tables.
- Tests: mixed insert/update batches, re-targeting rows into one's own
  notebook, planting ownership, a shared reader vs pinned results, and
  per-notebook children after denied deletes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 06:17:10 -05:00
james.maesandClaude Opus 5.5 ddd85ebe0d feat(#3): expose workbench data as standard QQQ tables secured by record locks
Notebooks, cells, versions, pinned results, shares and the execution
audit are now ordinary QQQ tables in the SQL Workbench app (nav children,
record views with child lists, edit, export, audits, developer mode)
instead of hidden admin-only tables.

- Record security locks (core-enforced, so every entry point): owner OR
  share audience for notebooks and their children (through joins), the
  audit by user; admins hold sqlWorkbenchAllAccess. Key types
  sqlWorkbenchUser / sqlWorkbenchAudience, applied to sessions by
  SqlWorkbenchSecurity.applyToSession (no keys without workbench access).
- New sqlWorkbenchNotebookShare (USER / GROUP / ALL); publishing writes an
  ALL share and visibility follows the shares.
- Per-table read/insert/edit/delete permissions, listed per role by the
  config (user / share / admin).
- Table customizers hold the notebook invariants for the notebook UI and
  the tables alike: owner from the session, cell validation and order,
  one version per change, share keys, append-only versions and audit
  (admins included), pinned results only from pinning, cascades.
- NotebookService and history run as the caller through the same actions;
  explicit owner/share checks stay as defense in depth.
- Record actions: Share Notebook, Duplicate Notebook, Restore Version.
- DDL: share table plus an idempotent backfill of library shares.
- Tests: TableSecurityMatrixTest (role x table x action over the v1 table
  API, core actions, export, PVS, record actions, notebook endpoints) and
  a PostgreSQL lock test. Docs: README, DESIGN, CHANGELOG.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 06:08:10 -05:00
james.maes 6c90ba12c0 Merge pull request #4 from QRun-IO/docs/adoption-checklist-link
docs(readme): link the QQQ adoption checklist
2026-10-12 06:04:07 -05:00
james.maesandClaude Opus 5.5 ea6e9ffec9 docs(readme): link the QQQ adoption checklist
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: James Maes <james@kof22.com>
2026-10-12 05:47:17 -05:00
james.maes 4f1e2305e1 Merge pull request #2 from QRun-IO/docs/adopting-in-another-app
docs: add Adopting in another app section and 1.0.0-RC.1 changelog entry
2026-10-12 05:28:47 -05:00
james.maesandClaude Opus 5.5 c2e754b146 docs: add Adopting in another app section and 1.0.0-RC.1 changelog entry
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 05:26:49 -05:00
james.maes 731225813d Merge pull request #1 from QRun-IO/feature/1-sql-workbench-1.0
feat: SQL Workbench qbit 1.0 backend
2026-10-11 13:52:05 -05:00
james.maesandClaude Opus 5.5 ec607016bd build: build against the released QQQ 4.1.0-RC.2
Re-pin the default-active qqq-snapshot profile from qqq-bom-pom
4.1.0-RC.1 to 4.1.0-RC.2 (now on Maven Central) and update the docs.
No SNAPSHOT dependencies.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 13:49:10 -05:00
james.maesandClaude Opus 5.5 5342c04a2a docs: warn that production backends need readOnlyCredentials
Without a least-privilege read-only role, read mode runs as the app
role and relies on the classifier (H2 / generic) or a READ ONLY
transaction that still allows side-effecting functions (Postgres).
Also records the security fixes in the CHANGELOG.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 13:40:16 -05:00
james.maesandClaude Opus 5.5 c43c135ca4 fix: keep credential jdbcUrl out of JSON; correct CHANGELOG coordinates
- SqlWorkbenchCredentials.getJdbcUrl is @JsonIgnore (a URL can carry a
  password); it is only read in code when deriving the connection.
- CHANGELOG names the artifact com.kingsrook.qbits:qbit-sql-workbench.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 13:39:55 -05:00
james.maesandClaude Opus 5.5 f67853d0e4 fix(export): neutralize CSV formula injection
Text cells starting with =, +, -, @, tab or CR are prefixed with a
quote so spreadsheets open them as text. Numbers are left alone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 13:39:54 -05:00
james.maesandClaude Opus 5.5 1ac98b25cb fix(engine): generic connection errors; never leave an audit row RUNNING
- A failure to open the target connection (driver messages can expose
  hosts, users, URLs) now answers "Could not connect to backend X; see
  the server log." with no sqlState, and logs the cause server-side.
  Schema introspection does the same.
- An unexpected exception inside a started run finishes its audit row
  as FAILED (generic message, detail logged) before it propagates.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 13:39:54 -05:00
james.maesandClaude Opus 5.5 24ce85f187 fix(sql): fail closed on MySQL executable comments and optimizer hints
MySQL runs the contents of /*! ... */ (and MariaDB /*M! ... */) and reads
/*+ ... */ hints, but the scanner skipped them as comments, so
"SELECT * FROM t /*! INTO OUTFILE '/tmp/x' */" classified as READ.

On the MySQL dialect the scanner now emits EXECUTABLE_COMMENT tokens for
these, and the classifier marks any statement containing one FORBIDDEN.
MySQL "--" also only starts a comment when whitespace follows ("1--1"
is arithmetic), so text after it is no longer hidden from the classifier.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 13:37:50 -05:00
james.maesandClaude Opus 5.5 52d2d6bc87 fix(engine): make client-chosen executionUuids single-use
Re-sending an in-flight executionUuid overwrote its registration, failed
the audit insert (UNIQUE), and the finally-unregister then removed the
original - escaping maxConcurrentExecutionsPerUser and cancellation.

- RunningStatements.register refuses an id already registered
  (putIfAbsent); unregister(uuid, registration) removes only its own.
- The engine refuses a client id that is running or already in the
  audit, before registering or recording anything
  (DuplicateExecutionException; the route provider answers 409).
- ExecutionAuditor.record() checks the insert result and fails loudly.
- Execution ids are lower-cased at the API edge (execute, rows, export,
  cancel, snapshots) and in engine.cancel.
- Tests for each, including 409 over HTTP.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 13:36:09 -05:00
james.maesandClaude Opus 5.5 ebef56f34b fix(security): guard the qbit's tables with a separate admin permission
The five tables used the same HAS_ACCESS_PERMISSION base (sqlWorkbench)
that every workbench user holds, so any user could read and write other
users' personal notebooks, cells, versions, pinned result snapshots and
the full execution audit through the generic /qqq/v1/table API.

- Tables now use adminPermissionBaseName (default sqlWorkbenchAdmin,
  validated to differ from the access base name).
- Cell, version and snapshot tables drop insert/update/delete
  capabilities, like the audit table.
- NotebookService and the history / snapshot audit lookups check the
  caller in code, then read and write as the system user (SystemUser,
  shared with ExecutionAuditor).
- Tests: a second ordinary user (and the owner) get permission denied
  from the table query executor on all five tables; the workbench API
  keeps working for ordinary users; admins can read everything.
- README / DESIGN document the admin permission.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 13:33:23 -05:00
james.maesandClaude Opus 5.5 c497e7ece8 feat: accept a client-chosen executionUuid
POST /execute takes an optional executionUuid (validated as a UUID) so
the UI can cancel a running statement before the response arrives.
Generated server-side when absent; the audit's unique key still
guarantees no reuse.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 10:14:01 -05:00
james.maesandClaude Opus 5.5 b8d33460bf feat: SQL Workbench qbit 1.0 backend
QQQ 4.1 QBit (com.kingsrook.qbits:sql-workbench) for a super-user SQL
notebook over any RDBMS backend of the instance:

- Dialect-aware scanner, statement splitter/classifier (READ,
  EXPLAIN_ANALYZE, WRITE, DDL, FORBIDDEN) and :named parameter binding.
- Execution engine: read-only sessions in a database READ ONLY
  transaction (Postgres, MySQL, SQLite query_only; classifier on H2),
  gated write mode (rolled-back dry run, single-use confirm token,
  row guard, hard cap, drift check), statement timeouts, row/byte caps,
  cancellation, per-user concurrency, clean connection return, and a
  fail-closed execution audit (audit updates deferred until the
  session connection is released).
- Dedicated read-only/write credentials per backend; backend read-only
  replica support.
- EXPLAIN / EXPLAIN ANALYZE (always rolled back) normalized to a plan
  tree; schema introspection with QQQ table/field -> SQL name mapping.
- Notebooks (personal/shared), cells, versions with retention, result
  snapshots.
- REST routes under /qqq/v1/sql-workbench (Javalin 7, JSON-only
  bodies), CSV/XLSX/JSON export.
- Tables, notebook PVS, SQL_WORKBENCH widget and sqlWorkbench app; no
  processes. PostgreSQL DDL in docs/ddl/postgres.sql.
- Tests: 83 unit (H2, SQLite, HTTP) + 6 Testcontainers Postgres
  integration tests; JaCoCo 94% instructions.
- CircleCI qqq-orb 0.6.8 config, Maven wrapper, CLAUDE.md/AGENTS.md,
  CHANGELOG.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 09:47:40 -05:00
james.maesandClaude Opus 5.5 13ec09cab3 docs: SQL Workbench qbit design and repository scaffold
Design for a super-user SQL notebook qbit: read-only by default with a
gated, audited write mode over any QQQ RDBMS backend; notebooks with
versions, parameters and snapshots; REST API under
/qqq/v1/sql-workbench; Next UI SQL_WORKBENCH widget. Adds license,
notice, policy files and CODEOWNERS like the sibling qbits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 09:47:22 -05:00