Skip to content

feat: resolving issue #8 #9 and #10#18

Open
keshxvdayal wants to merge 5 commits intomainfrom
feature/issue-8-9-10
Open

feat: resolving issue #8 #9 and #10#18
keshxvdayal wants to merge 5 commits intomainfrom
feature/issue-8-9-10

Conversation

@keshxvdayal
Copy link
Collaborator

@keshxvdayal keshxvdayal commented Jan 25, 2026

Implementation Screenshot:
Screenshot 2026-01-25 at 2 22 43 PM

Summary by Sourcery

Set a healthy TES instance as the default for demo task submissions and streamline the demo and submission UX by removing alert pop-ups and clarifying connection test messaging.

New Features:

  • Automatically select the ELIXIR-FI TES instance by default for demo task submissions when available, falling back to the first available instance.

Bug Fixes:

  • Ensure demo task forms are populated without disruptive alert pop-ups, and navigate directly to the tasks view after submission without success alerts.

Enhancements:

  • Prioritize the ELIXIR-FI TES instance at the top of the TES instance dropdown to highlight the recommended endpoint.
  • Simplify connection test success and failure messages by removing emoji-style prefixes while retaining diagnostic details.

Copilot AI review requested due to automatic review settings January 25, 2026 08:53
@sourcery-ai
Copy link

sourcery-ai bot commented Jan 25, 2026

Reviewer's Guide

Sets the ELIXIR-FI TES instance as the default and prioritizes it in the dropdown, removes demo and submission alert pop-ups in favor of direct navigation/inline behavior, and normalizes connection test alert messages while updating TES instance configuration.

Sequence diagram for default TES instance selection in SubmitTask

sequenceDiagram
  actor User
  participant SubmitTaskComponent
  participant useInstancesHook

  User->>SubmitTaskComponent: Open SubmitTask page
  SubmitTaskComponent->>useInstancesHook: useInstances()
  useInstancesHook-->>SubmitTaskComponent: instances list

  rect rgb(230,230,250)
    SubmitTaskComponent->>SubmitTaskComponent: useEffect on instances change
    SubmitTaskComponent->>SubmitTaskComponent: Check instances.length > 0 and no formData.tes_instance
    alt ELIXIR_FI instance exists
      SubmitTaskComponent->>SubmitTaskComponent: Find instance with url containing csc-tesk-noauth.rahtiapp.fi
      SubmitTaskComponent->>SubmitTaskComponent: setFormData tes_instance to ELIXIR_FI url
    else No ELIXIR_FI instance
      SubmitTaskComponent->>SubmitTaskComponent: setFormData tes_instance to instances[0].url
    end
  end

  User->>SubmitTaskComponent: Open TES Instance dropdown
  SubmitTaskComponent->>SubmitTaskComponent: Sort instances to prioritize ELIXIR_FI at top
  SubmitTaskComponent-->>User: Display dropdown with ELIXIR_FI preselected and first in list
Loading

Sequence diagram for updated demo run and task submission without alerts

sequenceDiagram
  actor User
  participant SubmitTaskComponent
  participant BackendTESService
  participant Router

  User->>SubmitTaskComponent: Click Run Demo
  SubmitTaskComponent->>SubmitTaskComponent: getDemoTaskData demoType
  SubmitTaskComponent->>SubmitTaskComponent: setFormData with demoData
  SubmitTaskComponent->>SubmitTaskComponent: setError null
  SubmitTaskComponent-->>User: Demo form fields populated (no alert)

  User->>SubmitTaskComponent: Click Submit Task
  SubmitTaskComponent->>BackendTESService: POST submit task with formData
  BackendTESService-->>SubmitTaskComponent: result with message
  SubmitTaskComponent->>Router: navigate to tasks list
  Router-->>User: Tasks page displayed (no success alert popup)
Loading

File-Level Changes

Change Details Files
Default TES instance logic and dropdown ordering updated to prefer the ELIXIR-FI instance.
  • Added a useEffect hook to preselect the ELIXIR-FI TES instance for the form when instances are loaded, falling back to the first instance if unavailable.
  • Updated the TES instance options to sort instances so the ELIXIR-FI endpoint appears first in the list.
frontend/src/pages/SubmitTask.js
Removed browser alert pop-ups when loading demo tasks and upon successful task submission.
  • Commented out the task name mapping and alert notification in handleRunDemo so demo data is just loaded into the form.
  • Commented out success alert logic in handleSubmit so the user is redirected to the tasks page without a blocking pop-up.
frontend/src/pages/SubmitTask.js
Standardized connection test alert copy by removing emoji characters from success and error messages.
  • Adjusted success connection test message string to drop the leading success emoji.
  • Adjusted failure connection test message string to drop the leading failure emoji while keeping all diagnostic details.
frontend/src/pages/Utilities.js
Updated TES instance backend configuration file (diff content not shown).
  • Modified backend .tes_instances configuration, likely to reflect current TES endpoints or health status.
backend/.tes_instances

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 3 issues, and left some high level feedback:

  • In the TES instance , calling .sort directly on instances mutates the original array from state/hooks; create a shallow copy first (e.g. [...instances].sort(...)) to avoid side effects. The ELIXIR-Fi instance URL string is hard-coded in multiple places; consider extracting it into a shared constant to avoid duplication and potential mismatches. Instead of leaving the old alert and task name logic commented out in handleRunDemo and handleSubmit, remove the dead code to keep the component easier to read and maintain. Prompt for AI Agents Please address the comments from this code review: ## Overall Comments - In the TES instance <Select>, calling `.sort` directly on `instances` mutates the original array from state/hooks; create a shallow copy first (e.g. `[...instances].sort(...)`) to avoid side effects. - The ELIXIR-Fi instance URL string is hard-coded in multiple places; consider extracting it into a shared constant to avoid duplication and potential mismatches. - Instead of leaving the old alert and task name logic commented out in `handleRunDemo` and `handleSubmit`, remove the dead code to keep the component easier to read and maintain. ## Individual Comments ### Comment 1 <location> `frontend/src/pages/SubmitTask.js:206-215` </location> <code_context> + // Issue #8: bug: set TES Instance for Demo task to healthy instance + + useEffect(() => { + if (instances.length > 0 && !formData.tes_instance) { + const elixirFiInstance = instances.find( + instance => instance.url && instance.url.includes('csc-tesk-noauth.rahtiapp.fi') + ); + + if (elixirFiInstance) { + setFormData(prev => ({ ...prev, tes_instance: elixirFiInstance.url })); + } else { + setFormData(prev => ({ ...prev, tes_instance: instances[0].url })); + } + } + }, [instances]); + const handleTestConnection = async () => { </code_context> <issue_to_address> **issue (bug_risk):** Effect uses `formData.tes_instance` but only depends on `instances`, which can lead to stale reads and hook lint warnings. Since the effect reads `formData.tes_instance` but only depends on `instances`, it may both trigger `react-hooks/exhaustive-deps` warnings and miss updates if `tes_instance` changes independently of `instances`. Either include the relevant `formData` field (or a derived `hasTesInstance` flag) in the dependency array, or restructure so the choice of `tes_instance` is made where `formData` is initialized / passed in, avoiding reading it directly inside this effect. </issue_to_address> ### Comment 2 <location> `frontend/src/pages/SubmitTask.js:460-469` </location> <code_context> - {instance.name} - </option> - ))} + {instances + .sort((a, b) => { + // Prioritize ELIXIR-Fi instance at the top + const aIsElixirFi = a.url && a.url.includes('csc-tesk-noauth.rahtiapp.fi'); + const bIsElixirFi = b.url && b.url.includes('csc-tesk-noauth.rahtiapp.fi'); + if (aIsElixirFi && !bIsElixirFi) return -1; + if (!aIsElixirFi && bIsElixirFi) return 1; + return 0; + }) + .map((instance, index) => ( + <option key={index} value={instance.url}> + {instance.name} </code_context> <issue_to_address> **issue (bug_risk):** Sorting `instances` directly mutates the array, which can lead to subtle bugs when `instances` comes from state or context. Since `.sort()` mutates the original array, this can violate immutability expectations if `instances` comes from React state or context, leading to hard-to-track bugs. Create a copy first, e.g. `instances.slice().sort(...)` or `[...instances].sort(...)`, before mapping to options. </issue_to_address> ### Comment 3 <location> `frontend/src/pages/SubmitTask.js:208-209` </location> <code_context> + + useEffect(() => { + if (instances.length > 0 && !formData.tes_instance) { + const elixirFiInstance = instances.find( + instance => instance.url && instance.url.includes('csc-tesk-noauth.rahtiapp.fi') + ); + </code_context> <issue_to_address> **suggestion:** The ELIXIR-Fi instance URL substring is hardcoded in multiple places and could be centralized. This substring is also used in the select sorting logic; to avoid divergence and clarify intent, extract it into a shared constant (e.g. `ELIXIR_FI_INSTANCE_SUBSTRING`) and reuse it in both the `find` and sort comparator. Suggested implementation: ```javascript import { useNavigate } from 'react-router-dom'; import styled from 'styled-components'; import { testConnection } from '../services/api'; const ELIXIR_FI_INSTANCE_SUBSTRING = 'csc-tesk-noauth.rahtiapp.fi'; refresh: refreshInstances } = useInstances(); ``` ```javascript const elixirFiInstance = instances.find( instance => instance.url && instance.url.includes(ELIXIR_FI_INSTANCE_SUBSTRING) ); ``` Also update the select sorting logic in this file where the same substring `'csc-tesk-noauth.rahtiapp.fi'` is used. Replace that hardcoded string with the shared `ELIXIR_FI_INSTANCE_SUBSTRING` constant so that both the `find` and the comparator rely on the same centralized value. </issue_to_address> Sourcery is free for open source - if you like our reviews please consider sharing them ✨ X Mastodon LinkedIn Facebook Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR addresses three UX issues related to the task submission form: setting a default healthy TES instance for demo tasks (Issue #8), removing redundant pop-ups when loading demo task data (Issue #9), and removing redundant pop-ups on task submission (Issue #10).

Changes:

  • Added default TES instance selection that prioritizes the Finland (ELIXIR-FI) instance on component mount
  • Removed alert pop-ups when demo task buttons are clicked
  • Removed success alert pop-ups after task submission
  • Sorted TES instance dropdown to show Finland instance first
  • Added status annotations to instance names in the .tes_instances configuration file

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
frontend/src/pages/SubmitTask.js Added useEffect hook to set default instance, removed demo and submission alerts, sorted instance dropdown
backend/.tes_instances Added status annotations (Working/Not Working/etc.) to instance names
frontend/src/pages/Utilities.js Removed emoji characters from connection test messages

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +460 to +468
{instances
.sort((a, b) => {
// Prioritize ELIXIR-Fi instance at the top
const aIsElixirFi = a.url && a.url.includes('csc-tesk-noauth.rahtiapp.fi');
const bIsElixirFi = b.url && b.url.includes('csc-tesk-noauth.rahtiapp.fi');
if (aIsElixirFi && !bIsElixirFi) return -1;
if (!aIsElixirFi && bIsElixirFi) return 1;
return 0;
})
Copy link

Copilot AI Jan 25, 2026

Choose a reason for hiding this comment

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

The inline sort operation only affects the rendering of the dropdown options and does not mutate the original instances array. This means that when demo tasks are loaded via handleRunDemo (lines 303-316), the getDemoTaskData function will use instances[0].url which refers to the first instance in the unsorted array, not necessarily the Finland instance. This defeats the purpose of Issue #8, which requires demo tasks to use the Finland instance. Consider storing a reference to the Finland instance in a separate variable or refactoring getDemoTaskData to use the same instance-finding logic.

Copilot uses AI. Check for mistakes.
Comment on lines +6 to +12
Funnel/OpenPBS @ ELIXIR-CZ (Not Working),https://funnel.cloud.e-infra.cz/
Funnel/Slurm @ ELIXIR-FI (DNS resolution Failure),https://vm4816.kaj.pouta.csc.fi/
TESK/Kubernetes @ ELIXIR-CZ (Prod) (Auth Failure),https://tesk-prod.cloud.e-infra.cz/
TESK/Kubernetes @ ELIXIR-CZ (NA) (403 Forbidden),https://tesk-na.cloud.e-infra.cz/
TESK/Kubernetes @ ELIXIR-DE (DNS resolution Failure),https://tesk.elixir-cloud.bi.denbi.de/
TESK/Kubernetes @ ELIXIR-GR (Working),https://tesk-eu.hypatia-comp.athenarc.gr/
TESK/OpenShift @ ELIXIR-FI (Working),https://csc-tesk-noauth.rahtiapp.fi/
Copy link

Copilot AI Jan 25, 2026

Choose a reason for hiding this comment

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

Adding status annotations like "(Not Working)", "(DNS resolution Failure)", and "(Auth Failure)" directly to instance names in the configuration file is problematic. These status annotations become part of the instance name that's displayed throughout the application, which is misleading because instance health status is dynamic and should be determined at runtime, not hardcoded in configuration. Instance health is already checked dynamically by the backend health check system. Remove these status annotations and let the system determine instance health programmatically.

Suggested change
Funnel/OpenPBS @ ELIXIR-CZ (Not Working),https://funnel.cloud.e-infra.cz/
Funnel/Slurm @ ELIXIR-FI (DNS resolution Failure),https://vm4816.kaj.pouta.csc.fi/
TESK/Kubernetes @ ELIXIR-CZ (Prod) (Auth Failure),https://tesk-prod.cloud.e-infra.cz/
TESK/Kubernetes @ ELIXIR-CZ (NA) (403 Forbidden),https://tesk-na.cloud.e-infra.cz/
TESK/Kubernetes @ ELIXIR-DE (DNS resolution Failure),https://tesk.elixir-cloud.bi.denbi.de/
TESK/Kubernetes @ ELIXIR-GR (Working),https://tesk-eu.hypatia-comp.athenarc.gr/
TESK/OpenShift @ ELIXIR-FI (Working),https://csc-tesk-noauth.rahtiapp.fi/
Funnel/OpenPBS @ ELIXIR-CZ,https://funnel.cloud.e-infra.cz/
Funnel/Slurm @ ELIXIR-FI,https://vm4816.kaj.pouta.csc.fi/
TESK/Kubernetes @ ELIXIR-CZ (Prod),https://tesk-prod.cloud.e-infra.cz/
TESK/Kubernetes @ ELIXIR-CZ (NA),https://tesk-na.cloud.e-infra.cz/
TESK/Kubernetes @ ELIXIR-DE,https://tesk.elixir-cloud.bi.denbi.de/
TESK/Kubernetes @ ELIXIR-GR,https://tesk-eu.hypatia-comp.athenarc.gr/
TESK/OpenShift @ ELIXIR-FI,https://csc-tesk-noauth.rahtiapp.fi/

Copilot uses AI. Check for mistakes.
setFormData(prev => ({ ...prev, tes_instance: instances[0].url }));
}
}
}, [instances]);
Copy link

Copilot AI Jan 25, 2026

Choose a reason for hiding this comment

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

The useEffect hook is missing formData.tes_instance in its dependency array. React's exhaustive-deps rule requires all values used inside the effect that come from the component scope to be listed in the dependency array. Without this, the linter will warn about a missing dependency. Add formData.tes_instance to the dependency array.

Suggested change
}, [instances]);
}, [instances, formData.tes_instance]);

Copilot uses AI. Check for mistakes.
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 5 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +468 to +474
.sort((a, b) => {
const aIsElixirFi = a.url && a.url.includes(ELIXIR_FI_INSTANCE_SUBSTRING);
const bIsElixirFi = b.url && b.url.includes(ELIXIR_FI_INSTANCE_SUBSTRING);
if (aIsElixirFi && !bIsElixirFi) return -1;
if (!aIsElixirFi && bIsElixirFi) return 1;
return 0;
})
Copy link

Copilot AI Jan 26, 2026

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.

Copilot uses AI. Check for mistakes.
Comment on lines +222 to +234
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]);

Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

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

The useEffect hook has a problematic dependency array that includes 'formData.tes_instance'. This can cause unnecessary re-renders or potential infinite loops because the effect modifies the same state it depends on. When the effect sets formData.tes_instance, it triggers the effect again (though the condition '!formData.tes_instance' prevents the actual loop).

A better approach is to only depend on 'instances' and check the condition more carefully, or to remove formData.tes_instance from the dependency array since the effect only runs when it's empty anyway.

Suggested change
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 uses AI. Check for mistakes.
Comment on lines +31 to +50
@instances_bp.route('/api/instances-with-status', methods=['GET'])
def get_instances_with_status():
"""Get all TES instances with their current health status"""
try:
from utils.tes_utils import load_tes_location_data
instances = load_tes_location_data()

# Fetch status for all instances in parallel
with ThreadPoolExecutor(max_workers=8) as pool:
results = list(pool.map(fetch_tes_status, instances))

return jsonify({
'instances': results,
'count': len(results),
'last_updated': datetime.now(timezone.utc).isoformat()
})

except Exception as e:
print(f"Error in get_instances_with_status: {str(e)}")
return jsonify({'error': str(e)}), 500
Copy link

Copilot AI Jan 26, 2026

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.

Copilot uses AI. Check for mistakes.
Comment on lines +466 to +484
{(allInstances.length > 0 ? allInstances : instances)
.slice()
.sort((a, b) => {
const aIsElixirFi = a.url && a.url.includes(ELIXIR_FI_INSTANCE_SUBSTRING);
const bIsElixirFi = b.url && b.url.includes(ELIXIR_FI_INSTANCE_SUBSTRING);
if (aIsElixirFi && !bIsElixirFi) return -1;
if (!aIsElixirFi && bIsElixirFi) return 1;
return 0;
})
.map((instance, index) => (
<option
key={index}
value={instance.url}
>
{getStatusBadge(instance.status)} {instance.name}
{instance.status === 'unhealthy' ? ' (Unhealthy)' : ''}
{instance.status === 'unreachable' ? ' (Unreachable)' : ''}
</option>
))}
Copy link

Copilot AI Jan 26, 2026

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:

  1. Disabling (making unselectable) unhealthy/unreachable instances in the dropdown
  2. Adding a warning when a user selects an unhealthy instance
  3. Only showing unhealthy instances as informational but preventing their selection for task submission

Copilot uses AI. Check for mistakes.
Comment on lines 17 to +23
const initialState = instanceService.getHealthyInstances();
setState(initialState);
const allInstancesState = instanceService.getAllInstancesWithStatus();
setState({
...initialState,
allInstances: allInstancesState.instances
});
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

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

There's a potential state synchronization issue in the useEffect hook. The code gets initialState from getHealthyInstances() and allInstancesState from getAllInstancesWithStatus() separately, then merges them manually. However, the listener added at line 17 receives updates that already include both 'instances' and 'allInstances' (as seen in instanceService.notifyListeners() which passes both fields). This manual merging could be out of sync with the listener updates.

A cleaner approach would be to initialize the state directly from the service's cached data without the manual merge, since the notifyListeners already provides the complete state structure with both fields.

Copilot uses AI. Check for mistakes.
Copy link
Member

@uniqueg uniqueg left a comment

Choose a reason for hiding this comment

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

Please address the one issue I raised, as well as anything that Sourcery and GitHub Copilot complain about.

Also, from your screenshot, I think it would be better to just use emojis in the drop-down to indicate health: ✅ for healthy, ❌ for broken. No details. You can indicate details (403, 500 etc.) in the instance management / health overview, but in the instance selection in the drop-down, I think it's best to keep it simple.

Rest looks fine (as far as I can tell).

import useInstances from '../hooks/useInstances';
import { ArrowLeft, Play, Zap, RefreshCw } from 'lucide-react';

const ELIXIR_FI_INSTANCE_SUBSTRING = 'csc-tesk-noauth.rahtiapp.fi';
Copy link
Member

Choose a reason for hiding this comment

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

Don't hardcore this. Just set it to any healthy instance. That way, we don't need to change anything if the instance goes down.

If there isn't a single healthy instance, don't select a default instance. Users should then check the drop down list themselves and realize that there is currently no healthy instance, prompting them to manage the instances and configure a healthy one.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +461 to +463
// Sort by status: healthy first, then others
if (a.status === 'healthy' && b.status !== 'healthy') return -1;
if (a.status !== 'healthy' && b.status === 'healthy') return 1;
Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

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

The dropdown sorting only separates healthy instances from unhealthy ones but doesn't prioritize the Finland instance as requested in issue #8. According to the issue, the Finland instance (TESK/OpenShift @ ELIXIR-FI) should be the preferred default. Consider adding an additional sort criterion to place the Finland instance first among healthy instances. For example, you could check if the instance URL contains 'csc-tesk-noauth.rahtiapp.fi' and sort it to the top.

Suggested change
// Sort by status: healthy first, then others
if (a.status === 'healthy' && b.status !== 'healthy') return -1;
if (a.status !== 'healthy' && b.status === 'healthy') return 1;
const finlandHost = 'csc-tesk-noauth.rahtiapp.fi';
const aHealthy = a.status === 'healthy';
const bHealthy = b.status === 'healthy';
const aFinlandHealthy = aHealthy && (a.url || '').includes(finlandHost);
const bFinlandHealthy = bHealthy && (b.url || '').includes(finlandHost);
// Prefer the Finland instance among healthy ones
if (aFinlandHealthy && !bFinlandHealthy) return -1;
if (!aFinlandHealthy && bFinlandHealthy) return 1;
// Then sort by status: healthy first, then others
if (aHealthy && !bHealthy) return -1;
if (!aHealthy && bHealthy) return 1;

Copilot uses AI. Check for mistakes.

Choose a reason for hiding this comment

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

We don't need to prioritize the "Finland instance", just any healthy, reachable instance without authorization. If those three criteria are met the instance is suitable for the examples to run.

(Are these criteria met? If I recall correctly, there is a healthy instance we don't have access rights to, so submitting there will fail. Is this taken into consideration? @uniqueg is authorization status something we would like the TES dashboard to check in advance or is that too much overhead?)

})
.map((instance, index) => (
<option
key={index}
Copy link

Copilot AI Jan 28, 2026

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.

Suggested change
key={index}
key={instance.url}

Copilot uses AI. Check for mistakes.
setFormData(prev => ({ ...prev, tes_instance: healthyInstance.url }));
}
}
}, [instances, allInstances, formData.tes_instance]);
Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

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

The useEffect has a dependency on formData.tes_instance which causes it to re-run every time the instance is selected. This creates a problematic behavior: if a user manually selects a different instance, the effect will not reset it back because the condition !formData.tes_instance will be false. However, this dependency is unnecessary and could be removed since we only want to auto-select on initial load when no instance is selected. Consider removing formData.tes_instance from the dependency array.

Suggested change
}, [instances, allInstances, formData.tes_instance]);
}, [instances, allInstances]);

Copilot uses AI. Check for mistakes.
Comment on lines +262 to +268
// 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'
);
Copy link

Copilot AI Jan 28, 2026

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.

Suggested change
// 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'
);

Copilot uses AI. Check for mistakes.

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.

@ninsch3000
Copy link

Some copilot raised issues to address still.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +59 to +66
# If tasks endpoint returns 401/403, mark as unhealthy (auth required but not configured)
if tasks_response.status_code in [401, 403]:
print(f"⚠️ {instance.get('name')} tasks endpoint requires authentication (status {tasks_response.status_code})")
status = "unhealthy"
else:
# Tasks endpoint is accessible (200) or returns other non-auth error
status = "healthy"
except Exception as tasks_error:
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

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

In fetch_tes_status, the instance is marked healthy for any tasks endpoint response that isn’t 401/403. This can incorrectly classify instances as healthy even when /tasks returns 404/500/405, which would still break demo submissions. Consider only marking healthy for 2xx (ideally 200) responses, and treating other status codes as unhealthy (while still keeping the special-case for 401/403).

Copilot uses AI. Check for mistakes.
status = "unhealthy"
else:
# Service-info is accessible, but we need to check if tasks endpoint is usable
# Try a HEAD/OPTIONS request to tasks endpoint to see if it requires auth
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

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

The comment says "Try a HEAD/OPTIONS request" to the tasks endpoint, but the code uses requests.get(...). Either update the comment to match the implementation or switch to HEAD/OPTIONS if that’s the intended (lower-impact) check.

Suggested change
# Try a HEAD/OPTIONS request to tasks endpoint to see if it requires auth
# Try a request to the tasks endpoint to see if it requires auth

Copilot uses AI. Check for mistakes.

tes_endpoint = working_endpoint
print(f"🚀 Submitting task to {tes_endpoint}")
print(f"📦 Task payload: {json.dumps(tes_task, indent=2)}")
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

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

print(f"📦 Task payload: ...") logs the full TES task payload. Since this includes user-provided fields (command, URLs, descriptions), it can leak data and become very noisy in production. Consider using logger.debug (or guarding behind a debug flag) and/or redacting fields before logging.

Suggested change
print(f"📦 Task payload: {json.dumps(tes_task, indent=2)}")
logger.debug("📦 Task payload keys: %s", list(tes_task.keys()))

Copilot uses AI. Check for mistakes.
Comment on lines +367 to +373
print(f"❌ TES returned status {response.status_code}")
print(f"Response headers: {dict(response.headers)}")
print(f"Response body: {response.text[:500]}")

try:
error_data = response.json()
print(f"Error data JSON: {error_data}")
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

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

On submission errors, the code prints full TES response headers and body (Response headers, Response body). Headers/bodies can include sensitive information and can be large; this is better handled via logger.debug with redaction/truncation (and ideally only when debugging).

Suggested change
print(f"❌ TES returned status {response.status_code}")
print(f"Response headers: {dict(response.headers)}")
print(f"Response body: {response.text[:500]}")
try:
error_data = response.json()
print(f"Error data JSON: {error_data}")
logger.debug("TES submission failed with status %s", response.status_code)
logger.debug(
"TES response header names: %s",
list(response.headers.keys())
)
logger.debug(
"TES response body (truncated): %s",
(response.text[:200] + "..." if response.text and len(response.text) > 200 else response.text)
)
try:
error_data = response.json()
logger.debug(
"TES error data JSON (truncated): %s",
str(error_data)[:500] + ("..." if len(str(error_data)) > 500 else "")
)

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants