Skip to content

Conversation

@diegoimbert
Copy link
Contributor

@diegoimbert diegoimbert commented Oct 21, 2025

(Waiting for the redesign PR to get merged into main)
image

@diegoimbert diegoimbert changed the base branch from main to glm/frontend/update-overall-design October 21, 2025 15:28
@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Oct 21, 2025

Deploying windmill with  Cloudflare Pages  Cloudflare Pages

Latest commit: 7b5cd2f
Status: ✅  Deploy successful!
Preview URL: https://71a5158f.windmill.pages.dev
Branch Preview URL: https://di-sync-rt-button.windmill.pages.dev

View logs

Base automatically changed from glm/frontend/update-overall-design to main October 22, 2025 15:56
@diegoimbert diegoimbert marked this pull request as ready for review October 23, 2025 09:25
@claude
Copy link
Contributor

claude bot commented Oct 23, 2025

Claude finished @diegoimbert's task —— View job


Code Review Complete ✓

Overview

This PR adds a convenient "Sync resource types with Hub" button to the resource connection UI, replacing manual instructions with a one-click synchronization. The implementation is clean and follows Svelte 5 best practices well.

Summary of Changes

  • AppConnectInner.svelte: Added a button for superadmins to sync resource types directly from the UI
  • svelte5Utils.svelte.ts: Enhanced usePromise utility to support lazy loading with an 'idle' state

Positive Aspects ✅

1. Excellent Use of Svelte 5 Runes

The code properly uses $state for reactive variables and leverages the usePromise utility effectively. The implementation follows the runes-based reactivity model correctly.

2. Good UX Improvement

Replacing verbose manual instructions with a single button significantly improves the user experience. The loading state and error handling provide good feedback.

3. Proper Permission Gating

The button is correctly gated behind $superadmin, ensuring only authorized users can trigger the sync operation.

4. Clean State Management

The promise-based approach with usePromise provides a clean pattern for handling async operations with loading/error states.


Issues & Recommendations

🔴 Critical: Security - Hardcoded Workspace

Location: AppConnectInner.svelte:522-526

let jobUuid = await JobService.runScriptByPath({
    workspace: 'admins',  // ⚠️ Hardcoded
    path: 'u/admin/hub_sync',
    requestBody: {}
})
await pollJobResult(jobUuid, 'admins')  // ⚠️ Hardcoded

Issue: The code hardcodes the 'admins' workspace, which could:

  1. Fail if the workspace doesn't exist or is named differently
  2. Cause security issues if users can manipulate this to run scripts in workspaces they don't have access to
  3. Break in non-standard deployments

Recommendation:

  • Add error handling to check if the workspace/script exists before attempting to run it
  • Consider making the workspace configurable or checking for its existence
  • Add try-catch around the entire operation with user-friendly error messages
let hubRtSync = usePromise(
    async () => {
        try {
            let jobUuid = await JobService.runScriptByPath({
                workspace: 'admins',
                path: 'u/admin/hub_sync',
                requestBody: {}
            })
            await pollJobResult(jobUuid, 'admins')
            connectsManual = undefined
            await loadResourceTypes()
            connects = undefined
            await loadConnects()
            sendUserToast('Hub resource types sync completed')
        } catch (error) {
            // More specific error message
            throw new Error(`Failed to sync resource types: ${error?.message || 'Unknown error'}`)
        }
    },
    { loadInit: false }
)

🟡 Medium: Type Safety Issue

Location: svelte5Utils.svelte.ts:28-35

export type UsePromiseResult<T> = (
    | { status: 'loading'; value?: undefined; error?: undefined }
    | { status: 'error'; error: any; value?: undefined }  // ⚠️ 'any' type
    | { status: 'ok'; value: T; error?: undefined }
    | { status: 'idle'; value?: undefined; error?: undefined }
) & {
    refresh: () => void
    clear: () => void
}

Issue: The error type is any, which bypasses TypeScript's type safety.

Recommendation: Use a more specific error type:

| { status: 'error'; error: Error | unknown; value?: undefined }

🟡 Medium: UI/UX Considerations

Location: AppConnectInner.svelte:651-657

Issues:

  1. No success confirmation beyond toast: After sync completes, users might not see immediate visual feedback that new resource types are available
  2. Error display truncation: JSON.stringify(hubRtSync.error) might produce very long error messages that break the UI
  3. Button placement: The button appears after the grid, which might not be immediately visible if there are many resource types

Recommendations:

{#if $superadmin}
    <div class="flex flex-col gap-2">
        <Button
            loading={hubRtSync.status === 'loading'}
            onClick={() => hubRtSync.refresh()}
            wrapperClasses="mt-6"
            disabled={hubRtSync.status === 'loading'}
        >
            Sync resource types with Hub
        </Button>
        {#if hubRtSync.status === 'error'}
            <span class="text-red-400 dark:text-red-500 text-xs">
                Error syncing resource types: {hubRtSync.error?.message || 'Unknown error'}
            </span>
        {/if}
        {#if hubRtSync.status === 'ok'}
            <span class="text-green-600 dark:text-green-400 text-xs">
                ✓ Resource types synced successfully
            </span>
        {/if}
    </div>
{/if}

🟢 Minor: Code Organization

Location: AppConnectInner.svelte:519-534

Issue: The hubRtSync promise definition is quite long and placed in the middle of variable declarations, making it harder to read.

Recommendation: Consider extracting this into a separate function:

async function syncResourceTypesWithHub() {
    let jobUuid = await JobService.runScriptByPath({
        workspace: 'admins',
        path: 'u/admin/hub_sync',
        requestBody: {}
    })
    await pollJobResult(jobUuid, 'admins')
    connectsManual = undefined
    await loadResourceTypes()
    connects = undefined
    await loadConnects()
    sendUserToast('Hub resource types sync completed')
}

let hubRtSync = usePromise(syncResourceTypesWithHub, { loadInit: false })

🟢 Minor: Svelte 5 Best Practice

Location: AppConnectInner.svelte:653

Current:

onClick={() => hubRtSync.refresh()}

Issue: Using onClick prop instead of the Svelte 5-recommended direct event handler.

Recommendation (Svelte 5 style):

onclick={() => hubRtSync.refresh()}

However, I notice this is using the Button component which may expose onClick as a prop, so this might be intentional. If the Button component API uses onClick, then this is fine.


🟢 Minor: Missing Loading State Management

Issue: While the button shows loading state, the component doesn't disable user interaction with resource type buttons during sync, which could lead to confusing behavior.

Recommendation: Consider adding a disabled state to the resource type buttons while sync is in progress, or show a blocking overlay.


Performance Considerations ⚡

Good:

  • The loadInit: false option prevents unnecessary API calls on component mount
  • State updates are properly batched (setting multiple values to undefined, then reloading)

Could Improve:

  • Sequential reloading: The sync operation reloads loadResourceTypes() and loadConnects() sequentially. Consider if these could be parallelized:
await Promise.all([loadResourceTypes(), loadConnects()])

Security Review 🔒

Concerns:

  1. Workspace hardcoding (mentioned above) - potential security risk if not properly validated on backend
  2. No rate limiting visible - superadmins could spam the sync button, potentially causing backend load
  3. Job execution: Relies on backend script at u/admin/hub_sync - ensure this script has proper authorization checks

Good:

  • Properly gated behind $superadmin check
  • Uses existing JobService API which should have proper auth

Testing Instructions 📋

To test this feature, log in as a superadmin user and navigate to the resource connection page (typically accessed when creating a new resource or connecting to an API). At the bottom of the "Others" section showing manual resource types, you should see a new "Sync resource types with Hub" button. Click this button to trigger the synchronization - the button will show a loading spinner while the sync runs. Once complete, the resource type list should refresh automatically with any new types from the Hub. If few resource types (less than 10) are displayed initially, a warning message will appear indicating that resource types haven't been synced yet, prompting you to use the new sync button.


Summary

This is a solid PR that improves the UX significantly. The main concern is the hardcoded workspace reference which should have proper error handling. The other issues are minor improvements that would enhance robustness and user experience. Overall, the code follows Svelte 5 best practices and integrates well with the existing codebase.

Recommendation: Address the critical hardcoded workspace issue before merging, and consider the UX improvements for better user feedback.


let hubRtSync = usePromise(
async () => {
let jobUuid = await JobService.runScriptByPath({
Copy link
Contributor

Choose a reason for hiding this comment

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

The hub sync function uses hard-coded workspace admins and script path. Consider making these configurable constants.

</Button>
{#if hubRtSync.status === 'error'}
<span class="text-red-400 dark:text-red-500 text-xs">
Error syncing resource types : {JSON.stringify(hubRtSync.error)}
Copy link
Contributor

Choose a reason for hiding this comment

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

Display of sync errors uses JSON.stringify; consider sanitizing or formatting error messages for better UX.

Suggested change
Error syncing resource types : {JSON.stringify(hubRtSync.error)}
Error syncing resource types : {hubRtSync.error?.message || String(hubRtSync.error)}

let hubRtSync = usePromise(
async () => {
let jobUuid = await JobService.runScriptByPath({
workspace: 'admins',
Copy link
Contributor

Choose a reason for hiding this comment

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

🔴 Security/Reliability Concern: Hardcoded workspace name 'admins' could fail if:

  1. The workspace doesn't exist or has a different name in some deployments
  2. The hub_sync script doesn't exist at this path

Recommendation: Add error handling with try-catch and provide clear error messages to users if the workspace or script is missing.

</Button>
{#if hubRtSync.status === 'error'}
<span class="text-red-400 dark:text-red-500 text-xs">
Error syncing resource types : {JSON.stringify(hubRtSync.error)}
Copy link
Contributor

Choose a reason for hiding this comment

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

UX Enhancement: Consider using hubRtSync.error?.message instead of JSON.stringify() to display cleaner error messages. JSON.stringify could produce very long strings that break the UI layout.


export type UsePromiseResult<T> = (
| { status: 'loading'; value?: undefined; error?: undefined }
| { status: 'error'; error: any; value?: undefined }
Copy link
Contributor

Choose a reason for hiding this comment

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

Type Safety: Consider changing error: any to error: Error | unknown for better type safety.

})
await pollJobResult(jobUuid, 'admins')
connectsManual = undefined
await loadResourceTypes()
Copy link
Contributor

Choose a reason for hiding this comment

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

Performance: Consider parallelizing these reload operations:

await Promise.all([loadResourceTypes(), loadConnects()])

This would complete faster than sequential execution.

@rubenfiszel rubenfiszel merged commit 8eaef7b into main Oct 23, 2025
112 checks passed
@rubenfiszel rubenfiszel deleted the di/sync-rt-button branch October 23, 2025 09:50
@github-actions github-actions bot locked and limited conversation to collaborators Oct 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