-
Notifications
You must be signed in to change notification settings - Fork 0
feat: resolving issue #8 #9 and #10 #18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 3 commits
1d22447
993e9b1
a789ebd
cfccbae
a63c052
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,6 +4,7 @@ import instanceService from '../services/instanceService'; | |
| const useInstances = () => { | ||
| const [state, setState] = useState({ | ||
| instances: [], | ||
| allInstances: [], | ||
| loading: true, | ||
| error: null, | ||
| lastUpdate: null | ||
|
|
@@ -15,7 +16,11 @@ const useInstances = () => { | |
| }; | ||
| instanceService.addListener(handleUpdate); | ||
| const initialState = instanceService.getHealthyInstances(); | ||
| setState(initialState); | ||
| const allInstancesState = instanceService.getAllInstancesWithStatus(); | ||
| setState({ | ||
| ...initialState, | ||
| allInstances: allInstancesState.instances | ||
| }); | ||
|
Comment on lines
17
to
+23
|
||
| return () => { | ||
| instanceService.removeListener(handleUpdate); | ||
| }; | ||
|
|
@@ -26,6 +31,7 @@ const useInstances = () => { | |
|
|
||
| return { | ||
| instances: state.instances, | ||
| allInstances: state.allInstances, | ||
| loading: state.loading, | ||
| error: state.error, | ||
| lastUpdate: state.lastUpdate, | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,4 +1,4 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import React, { useState } from 'react'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import React, { useState, useEffect } from 'react'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { useNavigate } from 'react-router-dom'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import styled from 'styled-components'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { testConnection } from '../services/api'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -8,6 +8,8 @@ import ErrorMessage from '../components/common/ErrorMessage'; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import useInstances from '../hooks/useInstances'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { ArrowLeft, Play, Zap, RefreshCw } from 'lucide-react'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const ELIXIR_FI_INSTANCE_SUBSTRING = 'csc-tesk-noauth.rahtiapp.fi'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const PageContainer = styled.div` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| padding: 20px; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| background-color: #f8f9fa; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -196,11 +198,40 @@ const SubmitTask = () => { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| instances, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| allInstances, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| loading: instancesLoading, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| error: instancesError, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| refresh: refreshInstances | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } = useInstances(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Helper function to get status badge | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const getStatusBadge = (status) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| switch(status) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case 'healthy': | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return '✓'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case 'unhealthy': | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return '✗'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case 'unreachable': | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return '⚠'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| default: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return '?'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| useEffect(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (instances.length > 0 && !formData.tes_instance) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const elixirFiInstance = instances.find( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| instance => instance.url && instance.url.includes(ELIXIR_FI_INSTANCE_SUBSTRING) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (elixirFiInstance) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setFormData(prev => ({ ...prev, tes_instance: elixirFiInstance.url })); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setFormData(prev => ({ ...prev, tes_instance: instances[0].url })); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, [instances, formData.tes_instance]); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+212
to
+225
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (instances.length > 0 && !formData.tes_instance) { | |
| const elixirFiInstance = instances.find( | |
| instance => instance.url && instance.url.includes(ELIXIR_FI_INSTANCE_SUBSTRING) | |
| ); | |
| if (elixirFiInstance) { | |
| setFormData(prev => ({ ...prev, tes_instance: elixirFiInstance.url })); | |
| } else { | |
| setFormData(prev => ({ ...prev, tes_instance: instances[0].url })); | |
| } | |
| } | |
| }, [instances, formData.tes_instance]); | |
| if (instances.length === 0) { | |
| return; | |
| } | |
| setFormData(prev => { | |
| // If tes_instance is already set, do not override it | |
| if (prev.tes_instance) { | |
| return prev; | |
| } | |
| const elixirFiInstance = instances.find( | |
| instance => instance.url && instance.url.includes(ELIXIR_FI_INSTANCE_SUBSTRING) | |
| ); | |
| if (elixirFiInstance) { | |
| return { ...prev, tes_instance: elixirFiInstance.url }; | |
| } | |
| return { ...prev, tes_instance: instances[0].url }; | |
| }); | |
| }, [instances]); |
Copilot
AI
Jan 28, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Issue #8 requested that the Finland instance (https://csc-tesk-noauth.rahtiapp.fi/) should be prioritized as the default for demo tasks. However, the current implementation only finds the first healthy instance from the list without specifically prioritizing Finland. Since the instances come from the backend in the order defined in .tes_instances, and Finland is listed 7th (after CZ, CZ, CZ, DE, GR instances), a different instance will be selected if it's healthy first. Consider either: 1) Moving the Finland instance to the top of .tes_instances, or 2) Adding logic to specifically prefer instances with 'csc-tesk-noauth.rahtiapp.fi' in the URL when selecting the default.
| // Use first healthy instance as default for demos | |
| let defaultTesInstance = ''; | |
| if (instances.length > 0) { | |
| const healthyInstance = (allInstances.length > 0 ? allInstances : instances).find( | |
| instance => instance.status === 'healthy' | |
| ); | |
| // Prefer Finland instance for demos, fallback to first healthy instance | |
| let defaultTesInstance = ''; | |
| if (instances.length > 0) { | |
| const candidateInstances = (allInstances.length > 0 ? allInstances : instances); | |
| const preferredHost = 'csc-tesk-noauth.rahtiapp.fi'; | |
| // First, try to find a healthy Finland instance | |
| const preferredHealthyInstance = candidateInstances.find( | |
| instance => | |
| instance.status === 'healthy' && | |
| typeof instance.url === 'string' && | |
| instance.url.includes(preferredHost) | |
| ); | |
| // If none found, fall back to the first healthy instance | |
| const healthyInstance = preferredHealthyInstance || candidateInstances.find( | |
| instance => instance.status === 'healthy' | |
| ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
see my comment above about not prioritizing Finland per se.
Copilot
AI
Jan 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The sorting function checks if 'a.url' and 'b.url' exist before calling '.includes()', which prevents errors. However, if an instance has no URL, it will be treated as non-ELIXIR-FI. Consider whether instances without URLs should be sorted differently (e.g., to the end of the list) or filtered out entirely, as they likely cannot be used for task submission anyway.
Outdated
Copilot
AI
Jan 28, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using array index as the key prop is an anti-pattern in React when the list can be reordered (which happens here due to sorting). This can cause React to incorrectly reuse elements and lead to bugs with component state. Since instances have unique URLs, consider using instance.url as the key instead of index.
| key={index} | |
| key={instance.url} |
Copilot
AI
Jan 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The dropdown now displays unhealthy and unreachable instances alongside healthy ones. While users can see the status badges and labels, they can still select these non-functional instances. This could lead to task submission failures. Consider either:
- Disabling (making unselectable) unhealthy/unreachable instances in the dropdown
- Adding a warning when a user selects an unhealthy instance
- Only showing unhealthy instances as informational but preventing their selection for task submission
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new '/api/instances-with-status' endpoint is not included in the cache patterns in middleware_config.py. This endpoint performs real-time health checks on all TES instances which can be expensive (lines 38-40 show parallel HTTP requests to multiple endpoints). The endpoint should be added to the cache_patterns list to avoid repeated expensive operations, similar to '/api/instances' and '/api/tes_locations' which are already cached.