Skip to content

Conversation

@rubenfiszel
Copy link
Contributor

@rubenfiszel rubenfiszel commented Dec 23, 2025

mov-2025-12-23--00-10-01.mp4

@claude
Copy link
Contributor

claude bot commented Dec 23, 2025

Claude finished @rubenfiszel's task in 3m 2s —— View job


PR Review: Data Table Integrations for Raw Apps

I've completed a comprehensive review of this pull request which adds data table integration functionality to raw apps in Windmill.

Todo List

  • Analyze all changed files
  • Review code quality and best practices
  • Check for potential bugs or issues
  • Evaluate performance considerations
  • Assess security implications
  • Provide inline comments for specific issues
  • Write general observations
  • Add testing instructions

General Observations

What's Good ✅

  1. Well-structured architecture: The PR introduces a clean separation of concerns with dedicated components (RawAppDataTableDrawer, RawAppDataTableList, DefaultDatabaseSelector) and utility modules (dataTableRefUtils.ts, datatableUtils.svelte.ts).

  2. Proper Svelte 5 patterns: The code correctly uses Svelte 5 runes ($state, $derived, $effect) and follows the framework's best practices.

  3. Comprehensive AI integration: The core.ts additions for the AI chat include well-documented tools (get_datatables, exec_datatable_sql) with proper schema definitions using Zod.

  4. Good user experience: The new app creation modal provides a thoughtful setup flow with framework selection, data configuration, and optional AI integration.

  5. History management updated: The RawAppHistoryManager now includes data in snapshots, ensuring undo/redo works correctly with the new datatable configurations.

  6. Test helpers updated: The appEvalHelpers.ts file properly mocks the new datatable methods for testing.

Areas of Concern ⚠️

  1. SSR compatibility: Several components use window.innerWidth directly during initialization which will fail during server-side rendering (noted in inline comments).

  2. Magic numbers: There are hardcoded delays (500ms, 1000ms) for waiting on Monaco editor initialization. These are fragile and should ideally be event-based.

  3. Memory management: The cachedColDefs object in DBManagerContent.svelte grows indefinitely without eviction strategy.

  4. Security considerations: The exec_datatable_sql tool allows arbitrary SQL execution. While table creation is policy-controlled, destructive operations (DROP TABLE) are not restricted.

  5. Type safety: Some type assertions use internal Svelte types ($$prop_def) which may break in future versions.

Inline Comments Posted

I've added detailed inline comments on specific files:

  • datatableUtils.svelte.ts:12 - Resource pre-fetching pattern
  • RawAppDataTableDrawer.svelte:32 - SSR compatibility
  • DBManagerDrawer.svelte:78 - SSR compatibility
  • app/core.ts:749 - Security considerations for SQL execution
  • dataTableRefUtils.ts:32 - Input validation suggestion
  • apps_raw/add/+page.svelte:351 - Magic number documentation
  • DatatableCreationPolicy.svelte:21 - Potential race condition
  • DBManagerContent.svelte:128 - Memory management

Testing Instructions

To verify the changes in this PR:

Navigate to the Raw Apps section by going to the main menu and selecting "Apps" → "Raw Apps". Click "New App" to open the new app setup modal. The modal now has several new sections:

  1. Summary field: Enter a brief description of the app
  2. Framework selection: Choose between React 19, React 18, or Svelte 5 templates
  3. Data Configuration: This is the main new feature area:
    • If you have datatables configured in your workspace, you'll see a database selector
    • Schema options: Choose "None", "New" (creates a new schema), or "Existing" (select from available schemas)
    • Toggle for "Allow AI to create new tables"
    • Pre-whitelisted tables section where you can add tables the app should have access to
  4. AI Integration: If AI is configured, enter an optional prompt to start with AI assistance

Click the data table icon/button to open the drawer for adding table references. Test by adding references from different datatables and schemas. The drawer should show the table picker with proper filtering.

For existing apps, navigate to edit an existing raw app (Apps → Raw Apps → click on an app). In the sidebar, there should now be a "Data" section showing configured tables and the ability to add/remove them. The AI chat in APP mode should now have access to get_datatables and exec_datatable_sql tools.

@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Dec 23, 2025

Deploying windmill with  Cloudflare Pages  Cloudflare Pages

Latest commit: 90778c7
Status: ✅  Deploy successful!
Preview URL: https://06511456.windmill.pages.dev
Branch Preview URL: https://rf-datatablerawapps.windmill.pages.dev

View logs

* Pass a getter function that returns the workspace to create a reactive dependency.
*/
export function createDatatablesResource(getWorkspace: () => string | undefined) {
return resource.pre<string[]>([() => getWorkspace() ?? ''], async () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential Issue: Using resource.pre without proper error handling could leave users without feedback on persistent failures. Consider adding retry logic or user-facing error states.

Also, the empty dependency array [() => getWorkspace() ?? ''] in line 12 may not correctly trigger re-fetches when the workspace changes. The resource.pre API typically expects the callback to be called once initially, but the reactive dependency should be more explicit.

Suggested change
return resource.pre<string[]>([() => getWorkspace() ?? ''], async () => {
return resource<string[]>([() => getWorkspace() ?? ''], async () => {

let dbManagerContent: DBManagerContent | undefined = $state()
let hasReplResult = $state(false)
let isRefreshing = $state(false)
let windowWidth = $state(window.innerWidth)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consistency Issue: Using $state(window.innerWidth) during SSR will fail since window is not defined. While this component is likely client-only, it's good practice to handle this:

Suggested change
let windowWidth = $state(window.innerWidth)
let windowWidth = $state(0)

And then set the actual value after mount or via the svelte:window binding which already exists at line 150.

dbManagerContent?.clearReplResult()
}
let windowWidth = $state(window.innerWidth)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same SSR Issue: Accessing window.innerWidth during module initialization will fail during SSR.

Suggested change
let windowWidth = $state(window.innerWidth)
let windowWidth = $state(0)

fn: async ({ args, helpers, toolId, toolCallbacks }) => {
const parsedArgs = getExecDatatableSqlSchema().parse(args)

// Enforce datatable creation policy when new_table is specified
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Security Consideration: The exec_datatable_sql tool allows executing arbitrary SQL queries. While there is policy enforcement for table creation, consider:

  1. Adding rate limiting to prevent abuse
  2. Logging SQL executions for audit purposes
  3. Consider restricting DDL operations (DROP TABLE, ALTER TABLE) to prevent accidental data loss

The current implementation only restricts table creation but doesn't prevent destructive operations like DROP TABLE.

* Parse a string ref into a DataTableRef object
* Format: <datatableName>/<schema>:<table> or <datatableName>/<table> (for public schema)
*/
export function parseDataTableRef(ref: string): DataTableRef {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good Practice: The string parsing logic is clean, but consider adding input validation. Malformed refs could cause unexpected behavior:

Suggested change
export function parseDataTableRef(ref: string): DataTableRef {
export function parseDataTableRef(ref: string): DataTableRef {
if (!ref || typeof ref !== 'string') {
throw new Error('Invalid data table reference: ref must be a non-empty string')
}
const slashIdx = ref.indexOf('/')

}
aiChatManager.instructions = initialPrompt.trim()
aiChatManager.sendRequest()
}, 500)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Magic Number: The 500ms delay for triggering AI is arbitrary. Consider:

  1. Using a proper event-based approach to know when the editor is initialized
  2. Making this configurable or at least defining it as a constant with documentation
const AI_TRIGGER_DELAY_MS = 500 // Delay to ensure editor is initialized

aiChatManager.datatableCreationPolicy.enabled &&
!aiChatManager.datatableCreationPolicy.datatable
) {
aiChatManager.datatableCreationPolicy.datatable = datatables.current[0]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential Race Condition: The $effect that auto-selects the first datatable runs on every change to datatables.current.length. If datatables load asynchronously, this could overwrite a user's manual selection. Consider tracking whether the user has made a manual selection:

let userHasSelected = $state(false)

$effect(() => {
    if (
        datatables.current.length > 0 &&
        aiChatManager.datatableCreationPolicy.enabled &&
        !aiChatManager.datatableCreationPolicy.datatable &&
        !userHasSelected
    ) {
        aiChatManager.datatableCreationPolicy.datatable = datatables.current[0]
    }
})

hasReplResult = !!replResultData
})
let cachedColDefs: Record<string, TableMetadata> = {}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Memory Consideration: The cachedColDefs object grows indefinitely during a session. Consider implementing a cache eviction strategy (LRU cache) or size limit to prevent memory issues in long-running sessions with many tables.

const MAX_CACHED_TABLES = 100

async function getColDefs(tableKey: string): Promise<TableMetadata> {
    // ... cache refresh logic ...
    
    // Evict oldest entries if cache is too large
    const keys = Object.keys(cachedColDefs)
    if (keys.length > MAX_CACHED_TABLES) {
        delete cachedColDefs[keys[0]]
    }
    // ...
}

@rubenfiszel rubenfiszel merged commit 6a67869 into main Dec 23, 2025
151 of 152 checks passed
@rubenfiszel rubenfiszel deleted the rf/dataTableRawApps branch December 23, 2025 00:17
@github-actions github-actions bot locked and limited conversation to collaborators Dec 23, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants