Skip to content

Feat/lab settings#7943

Draft
mskorokhodov wants to merge 9 commits intomainfrom
feat/lab-settings
Draft

Feat/lab settings#7943
mskorokhodov wants to merge 9 commits intomainfrom
feat/lab-settings

Conversation

@mskorokhodov
Copy link
Copy Markdown
Collaborator

Background

Description

Checklist

  • Input validation
  • Output encoding
  • Authentication management
  • Session management
  • Access control
  • Cryptographic practices
  • Error handling and logging
  • Data protection
  • Communication security
  • System configuration
  • Database security
  • File management
  • Memory management
  • Testing

@mskorokhodov mskorokhodov marked this pull request as draft April 2, 2026 12:14
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces Hive Router query plan rendering to the Hive Laboratory, featuring a new visual graph component and a text-based viewer. It also expands the laboratory's configuration options for fetch operations and subscription protocols. Feedback highlights the need to adhere to the repository's style guide by using fetchWithRetry for introspection, improving the retry logic to handle HTTP errors, securing unsafe JSON parsing in the UI, and optimizing performance in the graph rendering logic.

import { toast } from 'sonner';
import z from 'zod';
import { asyncInterval } from '@/lib/utils';
import { createRequestSignal } from './request';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Import fetchWithRetry to adhere to the repository style guide regarding network calls and built-in retries.

Suggested change
import { createRequestSignal } from './request';
import { createRequestSignal, fetchWithRetry } from './request';
References
  1. Avoid using fetch or node-fetch directly, as it does not have built-in retries. (link)

Comment on lines +105 to 120
const response = await fetch(requestUrl, {
signal: requestSignal,
method: introspectionRequest.method,
body:
introspectionRequest.method === 'POST'
? JSON.stringify({
query: introspectionRequest.query,
})
: undefined,
headers:
introspectionRequest.method === 'POST'
? {
'Content-Type': 'application/json',
}
: undefined,
}).then(r => r.json());
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Use fetchWithRetry instead of direct fetch to ensure introspection requests have built-in retries as required by the style guide. This also helps in handling transient network failures during schema polling.

        const response = await fetchWithRetry(
          requestUrl,
          {
            signal: requestSignal,
            method: introspectionRequest.method,
            body:
              introspectionRequest.method === 'POST'
                ? JSON.stringify({
                    query: introspectionRequest.query,
                  })
                : undefined,
            headers:
              introspectionRequest.method === 'POST'
                ? {
                    'Content-Type': 'application/json',
                  }
                : undefined,
          },
          props.settingsApi?.settings.fetch.retry,
        ).then(r => r.json());
References
  1. Avoid using fetch or node-fetch directly, as it does not have built-in retries. (link)

Comment on lines +14 to +32
export async function fetchWithRetry(
input: RequestInfo | URL,
init: RequestInit,
retryCount = 0,
): Promise<Response> {
let attempt = 0;

while (true) {
try {
return await fetch(input, init);
} catch (error) {
if (attempt >= retryCount) {
throw error;
}

attempt++;
}
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The fetchWithRetry implementation only retries on network errors (exceptions thrown by fetch). It should also consider retrying on transient HTTP error status codes (e.g., 502, 503, 504) by checking response.ok. Additionally, implementing a delay or exponential backoff between retries is recommended to avoid overwhelming the server during transient failures.

Comment on lines +194 to +204
const queryPlan = useMemo(() => {
const queryPlan =
JSON.parse((historyItem as LaboratoryHistoryRequest)?.response ?? '{}').extensions
?.queryPlan ?? {};

if (!queryPlan) {
return null;
}

return QueryPlanSchema.safeParse(queryPlan).success ? queryPlan : null;
}, [historyItem]);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Directly calling JSON.parse on the response string is unsafe. If the response is not valid JSON (e.g., a network error returning HTML or plain text), this will throw and potentially crash the component. Consider wrapping this in a try-catch block to handle parsing errors gracefully.

  const queryPlan = useMemo(() => {
    try {
      const queryPlan =
        JSON.parse((historyItem as LaboratoryHistoryRequest)?.response ?? '{}').extensions
          ?.queryPlan;

      if (!queryPlan) {
        return null;
      }

      return QueryPlanSchema.safeParse(queryPlan).success ? queryPlan : null;
    } catch {
      return null;
    }
  }, [historyItem]);

Comment on lines +322 to +336
const hasValidQueryPlan = useMemo(() => {
if (!historyItem) {
return false;
}

const queryPlan = JSON.parse(historyItem.response).extensions?.queryPlan;

if (!queryPlan) {
return false;
}

console.log(QueryPlanSchema.safeParse(queryPlan).error?.message);

return QueryPlanSchema.safeParse(queryPlan).success;
}, [historyItem?.response]);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The JSON.parse call is unsafe and could crash the UI if the response is not valid JSON. Also, ensure debug console.log statements are removed before merging.

  const hasValidQueryPlan = useMemo(() => {
    if (!historyItem) {
      return false;
    }

    try {
      const queryPlan = JSON.parse(historyItem.response).extensions?.queryPlan;
      return !!queryPlan && QueryPlanSchema.safeParse(queryPlan).success;
    } catch {
      return false;
    }
  }, [historyItem?.response]);

Comment on lines +348 to +355
ref={ref => {
if (ref && !nodeSizes[node.id]) {
setNodeSizes(prev => ({
...prev,
[node.id]: { width: ref.clientWidth, height: ref.clientHeight },
}));
}
}}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Updating state (setNodeSizes) within a ref callback inside a loop (nodes.map) can lead to performance issues, as it triggers a re-render for every node being measured. For large graphs, this will cause excessive churn. Consider using a ResizeObserver to measure elements or batching these updates in a useEffect hook after the initial render.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants