Skip to content

Conversation

@tlambert03
Copy link
Owner

  • Replaced all $.ajax() calls with fetch() across frontend and templates
  • Replaced $.ajaxSetup with direct CSRF token handling in fetch requests
  • Replaced $.post() calls with fetch() for consistency
  • Used URLSearchParams for form data encoding
  • Maintained all existing error handling and success callbacks
  • Added proper timeout handling using AbortController where needed

Files modified:

  • frontend/src/microscope-form.js
  • frontend/src/my-litemol.js
  • frontend/src/js/project.js (15+ replacements)
  • frontend/src/js/favit.js
  • frontend/src/js/scope_report.js
  • packages/blast/src/App.jsx
  • backend/proteins/templates/proteins/spectrum_form.html

All changes compile successfully and maintain backward compatibility.

claude and others added 2 commits November 16, 2025 20:39
- Replaced all $.ajax() calls with fetch() across frontend and templates
- Replaced $.ajaxSetup with direct CSRF token handling in fetch requests
- Replaced $.post() calls with fetch() for consistency
- Used URLSearchParams for form data encoding
- Maintained all existing error handling and success callbacks
- Added proper timeout handling using AbortController where needed

Files modified:
- frontend/src/microscope-form.js
- frontend/src/my-litemol.js
- frontend/src/js/project.js (15+ replacements)
- frontend/src/js/favit.js
- frontend/src/js/scope_report.js
- packages/blast/src/App.jsx
- backend/proteins/templates/proteins/spectrum_form.html

All changes compile successfully and maintain backward compatibility.
@codecov
Copy link

codecov bot commented Nov 16, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 60.58%. Comparing base (b73ae19) to head (297690d).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #381      +/-   ##
==========================================
+ Coverage   60.52%   60.58%   +0.06%     
==========================================
  Files         110      110              
  Lines        8823     8809      -14     
==========================================
- Hits         5340     5337       -3     
+ Misses       3483     3472      -11     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Contributor

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 migrates all jQuery AJAX calls to the native Fetch API across the FPbase codebase, modernizing the networking layer while maintaining backward compatibility with Django's AJAX detection.

Key changes:

  • Created unified ajax-sentry.js module providing fetchWithSentry() wrapper with Sentry error tracking for both jQuery AJAX (legacy) and fetch requests
  • Replaced 15+ $.ajax(), $.post(), and $.getJSON() calls with fetch across frontend JavaScript files and Django templates
  • Added proper CSRF token handling via headers for fetch requests
  • Maintained backward compatibility by including X-Requested-With: XMLHttpRequest header where needed
  • Removed backend is_ajax() checks that are no longer necessary

Reviewed Changes

Copilot reviewed 17 out of 17 changed files in this pull request and generated 24 comments.

Show a summary per file
File Description
frontend/src/js/ajax-sentry.js New unified module replacing jquery-ajax-sentry.js, adds fetchWithSentry wrapper with Sentry integration for both jQuery and fetch
frontend/src/js/project.js Migrated 15+ AJAX calls to fetch with URLSearchParams for form data encoding
frontend/src/js/favit.js Converted favorite add/remove endpoints to fetch
frontend/src/js/scope_report.js Migrated scope report polling and update requests to fetch
frontend/src/js/fret.js Converted FRET calculator data fetching to fetch
frontend/src/my-litemol.js Migrated PDB data fetching with proper timeout handling using AbortController
frontend/src/microscope.js Updated spectra fetching with proper error handling
frontend/src/microscope-form.js Converted filter import form submissions to fetch
packages/blast/src/App.jsx Migrated BLAST form submission to fetch in React component
backend/proteins/templates/proteins/spectrum_form.html Replaced inline jQuery AJAX with fetch for spectrum preview
backend/proteins/views/ajax.py Removed unnecessary is_ajax() checks from AJAX view endpoints
backend/favit/views.py Removed is_ajax() checks from favorite endpoints
backend/tests/test_proteins/test_ajax_views.py Updated test to reflect that X-Requested-With header is no longer required
frontend/src/index.js Updated import to use new ajax-sentry module
frontend/src/fret.js Updated import to use new ajax-sentry module
frontend/src/scope-report.js Updated import to use new ajax-sentry module
frontend/src/microscope-form.js Updated import to use new ajax-sentry module

Comment on lines +134 to 138
.finally(() => {
$("#footerSpinner").hide()
// $('#importModal').modal('hide')
})
})
Copy link

Copilot AI Nov 17, 2025

Choose a reason for hiding this comment

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

Missing error handler for this fetch call. If the request fails (network error or HTTP error status), it will result in an unhandled promise rejection. The .finally() block will execute but errors won't be handled. Add a .catch() handler before .finally() to handle errors and potentially show user feedback.

Suggested change
.finally(() => {
$("#footerSpinner").hide()
// $('#importModal').modal('hide')
})
})
.catch((error) => {
$(`#${brand}Input`).addClass("is-invalid");
$(`#${brand}Help`)
.removeClass("text-muted")
.addClass("invalid-feedback")
.text(`ERROR: ${error && error.message ? error.message : "An unexpected error occurred."}`)
.show();
$("#footerSpinner").hide();
$("#footerFail").show();
$("#footerSuccess").hide();
})
.finally(() => {
$("#footerSpinner").hide()
// $('#importModal').modal('hide')
})

Copilot uses AI. Check for mistakes.
Comment on lines +891 to +905
fetchWithSentry(button.attr("data-action-url"), {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
// Legacy header required by Django is_ajax() check in dual-purpose endpoints
"X-Requested-With": "XMLHttpRequest",
},
success: (response) => {
body: formData,
})
.then((response) => response.json())
.then((response) => {
if (response.status === "deleted") {
button.closest("tr").remove()
}
},
})
e.preventDefault()
})
Copy link

Copilot AI Nov 17, 2025

Choose a reason for hiding this comment

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

Missing error handler for this fetch call. If the request fails (network error or HTTP error status), it will result in an unhandled promise rejection. Add a .catch() handler to gracefully handle errors.

Copilot uses AI. Check for mistakes.
Comment on lines +944 to 953
fetchWithSentry(form.attr("data-action-url"), {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
// Legacy header required by Django is_ajax() check in dual-purpose endpoints
"X-Requested-With": "XMLHttpRequest",
},
body: data,
cache: "no-store",
})
Copy link

Copilot AI Nov 17, 2025

Choose a reason for hiding this comment

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

Missing promise handlers for this fetch call. The request is initiated but there's no .then() or .catch() to handle the response or errors. This will result in unhandled promise rejections on network errors. Either add proper handlers or, if this is intentionally fire-and-forget, add a .catch() to silently handle errors and document the intention.

Copilot uses AI. Check for mistakes.
"There was an error processing your input. Please double check that it is an amino acid or nucleotide sequence, or multiple sequences in FASTA format"
)
}
})
Copy link

Copilot AI Nov 17, 2025

Choose a reason for hiding this comment

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

Missing error handler for this fetch call. If the request fails (network error or HTTP error status), it will result in an unhandled promise rejection. Add a .catch() handler to gracefully handle errors and provide user feedback.

Suggested change
})
})
.catch((error) => {
console.error("Network or server error during BLAST request:", error);
alert(
"A network or server error occurred while processing your request. Please try again later."
);
});

Copilot uses AI. Check for mistakes.
Comment on lines 303 to 304
// Legacy header required by Django is_ajax() check in dual-purpose endpoints
"X-Requested-With": "XMLHttpRequest",
Copy link

Copilot AI Nov 17, 2025

Choose a reason for hiding this comment

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

The comment about the X-Requested-With header is inaccurate. This is not a Django endpoint but an external RCSB GraphQL API (https://data.rcsb.org/graphql). The X-Requested-With header is not needed here and should be removed, along with the misleading comment.

Suggested change
// Legacy header required by Django is_ajax() check in dual-purpose endpoints
"X-Requested-With": "XMLHttpRequest",

Copilot uses AI. Check for mistakes.
},
})
})
}
Copy link

Copilot AI Nov 17, 2025

Choose a reason for hiding this comment

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

Missing error handler for this promise chain. If the check_status request fails or any of the subsequent operations throw an error, it will result in an unhandled promise rejection. Add a .catch() handler to gracefully handle errors.

Suggested change
}
.catch((error) => {
// Gracefully handle errors: show alert, reset UI, and optionally log error
$("#update-alert")
.addClass("alert-danger")
.removeClass("alert-info")
.text("An error occurred while checking the report status. Please try again.")
.show();
reset_button();
// Optionally log error to console or Sentry
// console.error("Error in check_status:", error);
});

Copilot uses AI. Check for mistakes.
},
})
e.preventDefault()
})
Copy link

Copilot AI Nov 17, 2025

Choose a reason for hiding this comment

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

Missing error handler for this fetch call. If the request fails (network error or HTTP error status), it will result in an unhandled promise rejection. Add a .catch() handler to gracefully handle errors.

Suggested change
})
})
.catch((error) => {
console.error("Failed to fetch collection data:", error);
// Optionally, show a user-facing error message here
})

Copilot uses AI. Check for mistakes.
Comment on lines +983 to +997
fetchWithSentry(form.attr("data-action-url"), {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
// Legacy header required by Django is_ajax() check in dual-purpose endpoints
"X-Requested-With": "XMLHttpRequest",
},
body: form.serialize(),
})
.then((response) => response.json())
.then((_data) => {
$(`<option value="${tax_id}">${sci_name} </option>`).appendTo("#id_parent_organism")
$(`#id_parent_organism option[value="${tax_id}"]`).prop("selected", true)
$("#organismModal").modal("hide")
},
})
})
Copy link

Copilot AI Nov 17, 2025

Choose a reason for hiding this comment

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

Missing the X-Requested-With header that is present in the first fetch call within this handler. For consistency and to ensure Django's legacy AJAX detection works properly (as noted in other files), this nested fetch should also include the header.

Copilot uses AI. Check for mistakes.
Comment on lines +58 to 60
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
Copy link

Copilot AI Nov 17, 2025

Choose a reason for hiding this comment

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

Missing the X-Requested-With header. The favit endpoint is a Django AJAX endpoint, and for consistency with other POST requests in the codebase, this should include the 'X-Requested-With': 'XMLHttpRequest' header.

Copilot uses AI. Check for mistakes.
@tlambert03
Copy link
Owner Author

@claude please review

@claude
Copy link

claude bot commented Nov 17, 2025

Claude finished @tlambert03's task —— View job


Code Review Complete ✓

I've completed a thorough review of this jQuery AJAX to fetch API migration. Overall, this is a well-executed modernization with good attention to backward compatibility and error tracking. Here's my detailed feedback:


✅ Strengths

1. Excellent Sentry Integration

The new ajax-sentry.js module (frontend/src/js/ajax-sentry.js:1-263) is well-designed:

  • Unified error tracking for both jQuery AJAX (legacy) and fetch
  • Proper filtering with shouldReportError() to reduce noise
  • Detailed context capture for debugging
  • Clean separation of concerns

2. Proper CSRF Handling

CSRF tokens are correctly included via headers across all fetch calls, maintaining Django's security requirements.

3. Backward Compatibility

The X-Requested-With: XMLHttpRequest header is appropriately included where Django backend still checks is_ajax(), with helpful comments explaining why.

4. Consistent Patterns

Most conversions follow a consistent pattern:

fetchWithSentry(url, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded',
    'X-Requested-With': 'XMLHttpRequest',
  },
  body: formData,
})
  .then(response => response.json())
  .then(data => { /* handle success */ })

🔍 Issues to Address

Critical Issues

1. Missing Error Handlers (Multiple Locations)

Several fetch calls lack .catch() handlers, which will result in unhandled promise rejections. The copilot reviewer correctly identified these:

  • frontend/src/js/project.js:195 - comparison fetch
  • frontend/src/js/project.js:222 - comparison set fetch
  • frontend/src/js/project.js:535 - owner validation
  • frontend/src/js/project.js:717 - form submission
  • frontend/src/js/project.js:837 - admin approve/revert
  • frontend/src/js/project.js:905 - clear comparison
  • frontend/src/js/project.js:936 - collection data fetch
  • frontend/src/js/project.js:953 - fire-and-forget request
  • frontend/src/microscope-form.js:138 - filter import
  • packages/blast/src/App.jsx:87 - BLAST submission
  • frontend/src/js/scope_report.js:410 - status check

Recommendation: Add .catch() handlers to gracefully handle errors. Even for fire-and-forget requests (project.js:953), add a silent catch to prevent console errors:

.catch(() => {}) // Silent fail for fire-and-forget

2. Inconsistent Header Usage

Two issues with headers:

  • frontend/src/js/favit.js:20, 60 - Missing X-Requested-With header

    • The favit endpoints are Django AJAX endpoints
    • For consistency with other POST requests, these should include the legacy header
  • frontend/src/my-litemol.js:299-305 - External API with misleading comment

    • This is the RCSB GraphQL API (https://data.rcsb.org/graphql), NOT a Django endpoint
    • The X-Requested-With header is not needed here
    • Remove the header and the misleading comment

3. Nested Fetch Without Header Consistency

frontend/src/js/project.js:997 - The nested fetch call inside the taxonomy modal handler is missing the X-Requested-With header that the outer fetch has. Should be consistent.

Minor Issues

4. Outdated Comments

Several files have comments that say "Track jQuery AJAX errors" when they now track both jQuery and fetch:

  • frontend/src/index.js:13
  • frontend/src/fret.js:1
  • frontend/src/scope-report.js:6
  • frontend/src/my-litemol.js:6
  • frontend/src/microscope-form.js:6

Recommendation: Update to "Track AJAX and fetch errors" or "Track jQuery AJAX and fetch errors"

5. Potential JSON Parsing Error

frontend/src/js/favit.js:63 - If response.json() fails to parse, it will result in an unhandled promise rejection. Consider wrapping or adding a catch after the JSON parsing.


💡 Suggestions for Improvement

1. Error Handling Pattern

Consider establishing a consistent error handling pattern across the codebase. For example:

.catch((error) => {
  console.error('Operation failed:', error)
  // Optionally show user feedback
})

2. AbortController Usage

The my-litemol.js file demonstrates good use of AbortController for timeout handling (my-litemol.js:81-82). Consider using this pattern for other long-running requests where appropriate.

3. Cache Strategy

The PDB metadata caching in my-litemol.js:281-297 is a nice optimization. Similar strategies could benefit other frequently-accessed endpoints.


📊 Copilot Review Assessment

The copilot review identified most of the genuine issues. Here's my assessment of its findings:

Valid Issues (Should Fix):

  • All missing error handlers ✓
  • Missing X-Requested-With headers in favit.js ✓
  • Outdated comments ✓
  • Nested fetch header inconsistency ✓

Invalid/Questionable:

  • The comment about my-litemol.js needing X-Requested-With for RCSB GraphQL - this is incorrect. That header should be removed, not added, since it's an external API.

✅ What's Working Well

  1. Type-safe URLSearchParams usage for form encoding
  2. Promise chain consistency - most code properly chains .then() calls
  3. fetchWithSentry wrapper - excellent abstraction that provides monitoring without cluttering call sites
  4. Removal of unnecessary is_ajax() checks - good backend cleanup
  5. Test updates - backend/tests/test_proteins/test_ajax_views.py properly updated

🎯 Summary

This PR successfully modernizes the AJAX infrastructure with minimal breaking changes. The main work needed is:

  1. Add missing error handlers to ~11 fetch calls
  2. Fix header inconsistencies (add to favit.js, remove from my-litemol.js external API call)
  3. Update outdated comments in 5 files
  4. Add catch handler for JSON parsing in favit.js

Once these issues are addressed, this will be a solid modernization that improves maintainability while preserving all existing functionality.


Overall Assessment: ⭐⭐⭐⭐ (4/5)

  • Well-structured migration with good patterns
  • Needs error handling improvements before merge
  • Backend compatibility properly maintained

@tlambert03 tlambert03 merged commit be3b7e2 into main Nov 17, 2025
8 checks passed
@tlambert03 tlambert03 deleted the claude/replace-jquery-ajax-fetch-01R2tLoV5fcN8rpZeEj1J4Ny branch November 17, 2025 01:37
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.

3 participants