Skip to content

check tool: issue details page#892

Merged
rosado merged 32 commits into
mainfrom
rosado/859-check-issue-details-page
Feb 25, 2025
Merged

check tool: issue details page#892
rosado merged 32 commits into
mainfrom
rosado/859-check-issue-details-page

Conversation

@rosado
Copy link
Copy Markdown
Contributor

@rosado rosado commented Feb 12, 2025

Preview Link

https://submit-pr-892.herokuapp.com/

What type of PR is this? (check all applicable)

  • Refactor
  • Feature

Description

Adds issue details page to the check tool results.

As discussed, the table, to be actually useful, needs changes on the API side - currently it shows all data (as opposed, just the data relevant to the viewed issue).

Also:

  • refactors some of the acceptance tests to remove duplication

Related Tickets & Documents

QA Instructions, Screenshots, Recordings

Go to preview link, submit an URL (with some data issues) and click through to an issue from the results page.

Note: the column names and order in the table are fixed in #897

image

Added/updated tests?

  • Yes

QA sign off

  • Code has been checked and approved
  • Design has been checked and approved
  • Product and business logic has been checked and proved

Summary by CodeRabbit

  • New Features

    • Introduced a feature to display links to individual issue pages on the results page.
    • Added a new HTML template for viewing detailed issue information related to datasets.
  • Style

    • Enhanced error messages by applying bold formatting to key details, improving readability across the interface.
  • Bug Fixes

    • Updated pagination logic to ensure correct navigation, with page numbers now starting at 1.
    • Improved URL handling in various components to reflect the new pagination structure.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 12, 2025

Walkthrough

This pull request introduces a new feature flag (checkIssueDetailsPage), updated styling for error summaries, and several control flow changes across controllers, models, services, routes, views, and tests. In controllers, new middleware and task handling logic are added to process issue details. Models have revised pagination and parameter structures. The services now use asynchronous CSV reading and optional HTML formatting. Additionally, several test files are refactored with new helper functions and updated assertions. These changes are aimed at better modularity and clarity without disrupting existing functionalities.

Changes

File(s) Change Summary
config/default.yaml
config/production.yaml
Added new feature flag checkIssueDetailsPage with enabled status true (default) and false (production) respectively, including comments describing the functionality.
src/assets/scss/index.scss Introduced a new CSS rule for .govuk-error-summary span.column-name to display text in bold.
src/controllers/issueDetailsController.js
src/controllers/resultsController.js
In issueDetailsController.js, added new middleware functions (prepareTask, setPagination, setDetailsOptions) and a new controller class extending PageController. In resultsController.js, updated middleware setup, parameter validation (using valibot), and task aggregation logic.
src/models/responseDetails.js
src/models/requestData.js
In responseDetails.js, updated JSDoc and pagination logic. In requestData.js, restructured the fetchResponseDetails method signature to use an options object for filtering.
src/routes/form-wizard/check/steps.js Added a new route for issue details (/results/:id/issue/:issueType/:field/:pageNumber?) and modified the navigation logic for the /status/:id route to increment the page number.
src/services/performanceDbApi.js
src/services/asyncRequestApi.js
In performanceDbApi.js, refactored initialiseMessages for asynchronous CSV reading and modified getTaskMessage to support an optional HTML format. In asyncRequestApi.js, updated getRequestData to include an optional opts parameter and use the URL constructor.
src/views/check/results/issueDetails.html
src/views/includes/_dataset-issue-page-header.html
src/views/organisations/issueTable.html
Added a new HTML template for displaying issue details and introduced an issue-specific dataset header template, updating the organisation view to employ the new header.
(Multiple Test Files) Refactored tests by introducing helper functions (checkDataFile, checkErrorDataFile), updating assertions for URL patterns and pagination, and expanding test coverage for new controller functions and service logic.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Middleware
    participant IssueDetailsController
    participant performanceDbApi

    Client->>IssueDetailsController: HTTP Request for issue details page
    IssueDetailsController->>Middleware: Execute validateParams (conditional)
    Middleware-->>IssueDetailsController: Return validated request or error
    IssueDetailsController->>Middleware: Call prepareTask middleware
    Middleware->>IssueDetailsController: Extract issueType and field from request
    IssueDetailsController->>IssueDetailsController: Retrieve task from req.aggregatedTasks
    alt Task not found
        IssueDetailsController->>Client: Respond with 404 MiddlewareError
    else Task found
        IssueDetailsController->>performanceDbApi: Call getTaskMessage(task details)
        performanceDbApi-->>IssueDetailsController: Return generated message
        IssueDetailsController->>Middleware: Set req.locals.task with task and message
        IssueDetailsController->>Client: Proceed with request processing
    end
Loading

Possibly related PRs

  • Add issue table view #691: The changes in the main PR, which introduce a new feature flag for displaying links to individual issue pages, are related to the retrieved PR that adds an issue table view, as both involve enhancements to how issue details are presented and navigated within the application.

Suggested reviewers

  • DilwoarH

Poem

I’m a little rabbit, hopping on the code,
With new flags and routes on a winding road.
CSS turns bold, controllers sing their tune,
Tests are sprightly dancing from morning to noon.
Through fields and details, our system’s reborn—
A bouncy celebration in every line drawn! 🐰✨


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cda702c and f774b59.

📒 Files selected for processing (3)
  • src/views/check/results/issueDetails.html (1 hunks)
  • src/views/organisations/issueDetails.html (1 hunks)
  • src/views/organisations/issueTable.html (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/views/check/results/issueDetails.html
  • src/views/organisations/issueTable.html
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: run-tests / test
🔇 Additional comments (1)
src/views/organisations/issueDetails.html (1)

105-107: Dynamic URL Generation for Check Service Link
The update utilising the checkToolDeepLink filter to construct a context-aware link is a solid enhancement. This approach now generates a URL based on the organisation and dataset variables, which streamlines navigation for different data contexts. Please ensure that the filter both correctly constructs and safely escapes the URL to mitigate any potential XSS issues, and verify that cases with missing or malformed values are gracefully handled.


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Feb 12, 2025

Coverage Report

Status Category Percentage Covered / Total
🔵 Lines 66.06% 5233 / 7921
🔵 Statements 66.06% 5233 / 7921
🔵 Functions 64.26% 232 / 361
🔵 Branches 80.79% 715 / 885
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
src/controllers/ShareResultsController.js 100% 100% 100% 100%
src/controllers/issueDetailsController.js 68.13% 40% 16.66% 68.13% 29-30, 45-46, 58-71, 95-99, 102-108
src/controllers/resultsController.js 49.56% 63.63% 36.36% 49.56% 19-35, 62-63, 67-71, 79-80, 82-83, 87-88, 109-111, 125-133, 168-169, 178-186, 189-201, 214-238, 248-249, 268-284, 287-290, 298-310, 317-338, 349-359, 362-364, 367-396
src/models/requestData.js 95.19% 96% 100% 95.19% 40-44
src/models/responseDetails.js 94.6% 83.6% 87.5% 94.6% 61, 117-118, 143-145, 214, 218, 228-230
src/routes/form-wizard/check/steps.js 0% 0% 0% 0% 1-129
src/services/asyncRequestApi.js 100% 77.77% 100% 100%
src/services/performanceDbApi.js 62.79% 85.29% 30.76% 62.79% 30-31, 47-48, 62-63, 79-107, 144-146, 209-213, 269-275, 286-300, 310-319, 328-348, 352-360, 369-381, 392-420
Generated in workflow #867 for commit f774b59 by the Vitest Coverage Report Action

@rosado rosado added the enhancement New feature or request label Feb 12, 2025
@rosado rosado self-assigned this Feb 12, 2025
@rosado rosado marked this pull request as ready for review February 12, 2025 14:17
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (10)
src/controllers/issueDetailsController.js (3)

1-7: Consider correcting the name of 'aggreatedTasks'
The property name req.aggreatedTasks appears to have a typo. Consider renaming it to req.aggregatedTasks for clarity.


15-21: Typographical error in the error message
The error message has 'isssue' spelt with three 's'. Consider correcting it to 'issue' to maintain consistency and clarity.

-    return next(new MiddlewareError(`No isssue of type '${issueType}' for field ${field}`, 404))
+    return next(new MiddlewareError(`No issue of type '${issueType}' for field ${field}`, 404))

53-69: Potential collision when merging options
Using Object.assign(req.form.options, req.locals) might overwrite existing properties in req.form.options. Consider ensuring that collisions are handled or documented if that is the intended behaviour.

src/controllers/resultsController.js (3)

205-220: Confusing function naming causes potential misunderstanding
The function name filterOutTasksByQualityCriterialLevel suggests discarding tasks, but it actually returns true for tasks that should be retained. Clarify or rename the function to align with its inclusion logic.


250-273: Check for potential parameter encoding
makeTaskParam constructs an href using raw issueType and field. Consider encoding potentially unsafe characters in the URL to avoid unpredictable behaviours if these values contain special characters.


324-342: Check aggregator map usage
While preserving missing column tasks in req.aggreatedTasks, ensure no duplication occurs if getMissingColumnTasks is invoked multiple times. If each field can only be reported once, guard against repeated entries.

config/production.yaml (1)

26-30: Fix indentation and trailing spaces.

The feature flag configuration has inconsistent indentation and trailing spaces.

Apply this diff to fix the formatting:

  checkIssueDetailsPage:
    # https://github.com/digital-land/submit/issues/786
-    # enabling this feature will cause the results page to present 
+    # enabling this feature will cause the results page to present
    # links to individual issue pages
    enabled: false
🧰 Tools
🪛 YAMLlint (1.35.1)

[error] 28-28: trailing spaces

(trailing-spaces)


[warning] 30-30: wrong indentation: expected 6 but found 4

(indentation)

src/views/check/results/issueDetails.html (2)

33-37: Add aria-label to error summary for better accessibility.

The error summary should have an aria-label to improve screen reader experience.

Apply this diff:

    {{ govukErrorSummary({
      titleText: criteriaLabels[options.task.qualityCriteriaLevel] or 'There is a problem',
+     ariaLabel: "Issue details",
      errorList: [ { html: options.task.message, href: ""}]
    }) }}

81-85: Add descriptive text to the list items.

The ordered list items could benefit from more descriptive text to better guide users.

Apply this diff:

-      <li>Fix the errors indicated</li>
-      <li>Use the <a href="/check">check service</a> to make sure the data meets
-        the standard</li>
-      <li>Publish the updated data on the data URL</li>
+      <li>Fix the errors indicated above in your dataset</li>
+      <li>Use the <a href="/check">check service</a> to validate that your data meets
+        all required standards</li>
+      <li>Publish the updated and validated data on your data URL</li>
config/default.yaml (1)

105-109: Fix indentation and trailing spaces.

The feature flag configuration has inconsistent indentation and trailing spaces.

Apply this diff to fix the formatting:

  checkIssueDetailsPage:
    # https://github.com/digital-land/submit/issues/786
-    # enabling this feature will cause the results page to present 
+    # enabling this feature will cause the results page to present
    # links to individual issue pages
    enabled: true
🧰 Tools
🪛 YAMLlint (1.35.1)

[error] 107-107: trailing spaces

(trailing-spaces)


[warning] 109-109: wrong indentation: expected 6 but found 4

(indentation)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 963de5a and c25a5de.

📒 Files selected for processing (13)
  • config/default.yaml (1 hunks)
  • config/production.yaml (1 hunks)
  • src/assets/scss/index.scss (1 hunks)
  • src/controllers/issueDetailsController.js (1 hunks)
  • src/controllers/resultsController.js (6 hunks)
  • src/models/responseDetails.js (1 hunks)
  • src/routes/form-wizard/check/steps.js (2 hunks)
  • src/services/performanceDbApi.js (3 hunks)
  • src/views/check/results/issueDetails.html (1 hunks)
  • src/views/includes/_dataset-issue-page-header.html (1 hunks)
  • src/views/organisations/issueTable.html (1 hunks)
  • test/acceptance/request_check.test.js (4 hunks)
  • test/unit/performanceDbApi.test.js (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • src/views/organisations/issueTable.html
  • src/models/responseDetails.js
🧰 Additional context used
🪛 YAMLlint (1.35.1)
config/production.yaml

[error] 28-28: trailing spaces

(trailing-spaces)


[warning] 30-30: wrong indentation: expected 6 but found 4

(indentation)

config/default.yaml

[error] 107-107: trailing spaces

(trailing-spaces)


[warning] 109-109: wrong indentation: expected 6 but found 4

(indentation)

🔇 Additional comments (11)
src/controllers/issueDetailsController.js (2)

8-14: Documentation looks good
The introductory doc comment clearly explains the purpose of the middleware function.


35-51: Verify the repeated usage of getRequestDataMiddleware
The array conditionally includes results.getRequestDataMiddleware in line 37, but then also unconditionally calls the same middleware in line 39. Confirm if this duplication is intentional or if one usage can be removed.

src/controllers/resultsController.js (2)

191-200: Doc comment is clear
The high-level explanation of how tasks are aggregated is clear and helpful for future maintainers.


235-237: Verify intended use of filtering logic
Confirm that [2, 3].includes(issue.quality_criteria_level) is aligned with your definition of blocking or non-blocking issues. If there's a possibility of new levels in future, consider making this more flexible.

src/routes/form-wizard/check/steps.js (1)

12-12: Good integration of the new controller
Importing issueDetailsController is consistent with the overall design and helps maintain a clear separation of concerns.

test/unit/performanceDbApi.test.js (1)

76-79: LGTM!

The test case correctly verifies the HTML formatting functionality of the getTaskMessage method.

test/acceptance/request_check.test.js (2)

24-66: LGTM! Well-structured refactoring.

The function effectively encapsulates common test logic, reducing code duplication while maintaining readability and proper error handling.


68-121: LGTM! Comprehensive test coverage.

The function effectively encapsulates common test logic and includes thorough verification of the new issue details page functionality.

src/services/performanceDbApi.js (1)

191-235: LGTM! Well-implemented feature.

The HTML formatting support is implemented in a clean, backwards-compatible way with proper documentation.

src/views/includes/_dataset-issue-page-header.html (1)

1-10: LGTM! Well-structured template.

The template follows GOV.UK Design System patterns and implements proper conditional rendering for different data scenarios.

src/assets/scss/index.scss (1)

226-228: LGTM!

The CSS rule for emphasising column names in error summaries is well-scoped and follows GOV.UK design patterns.

Comment thread src/routes/form-wizard/check/steps.js Outdated
Comment thread test/unit/performanceDbApi.test.js Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
src/controllers/issueDetailsController.js (2)

7-13: Enhance JSDoc documentation.

The JSDoc could be more descriptive about the middleware's purpose and parameters.

 /**
- * Middleware. Updates req with `task`
+ * Middleware that prepares task data for the issue details page.
+ * Extracts issue type and field from request parameters and retrieves
+ * the corresponding task from aggregated tasks.
  *
- * @param {*} req request
- * @param {*} res response
- * @param {*} next next function
+ * @param {Object} req - Express request object
+ * @param {Object} req.params - Request parameters
+ * @param {string} req.params.issueType - Type of the issue
+ * @param {string} req.params.field - Field name
+ * @param {Map} req.aggreatedTasks - Map of aggregated tasks
+ * @param {Object} res - Express response object
+ * @param {Function} next - Express next middleware function
+ * @throws {MiddlewareError} When task is not found
  */

19-19: Fix typo in error message.

There's a typo in the word "isssue" (three 's').

-    return next(new MiddlewareError(`No isssue of type '${issueType}' for field ${field}`, 404))
+    return next(new MiddlewareError(`No issue of type '${issueType}' for field ${field}`, 404))
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6dd9cad and 56773df.

📒 Files selected for processing (1)
  • src/controllers/issueDetailsController.js (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: run-tests / test
🔇 Additional comments (1)
src/controllers/issueDetailsController.js (1)

34-34: Verify feature flag configuration.

Ensure that the checkIssueDetailsPage feature flag is properly configured in both default and production configurations.

✅ Verification successful

🏁 Script executed:

#!/bin/bash
# Description: Check feature flag configuration in config files

echo "Checking feature flag configuration..."
rg -A 2 "checkIssueDetailsPage" config/

Length of output: 556


Feature flag configuration verified.

The configuration for checkIssueDetailsPage is present and correctly set up in both config/default.yaml and config/production.yaml.

Comment thread src/controllers/issueDetailsController.js
Comment thread src/controllers/issueDetailsController.js Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/controllers/issueDetailsController.js (1)

24-35: 🛠️ Refactor suggestion

Improve error handling and format validation.

The code needs better format validation and error handling:

  1. The format parameter should be validated
  2. The fallback message could be more descriptive
-  let message = issueType // fallback
+  const ALLOWED_FORMATS = ['html', 'text']
+  const format = 'html'
+  
+  if (!ALLOWED_FORMATS.includes(format)) {
+    return next(new MiddlewareError(`Invalid format: ${format}`, 400))
+  }
+
+  let message = `Issue type: ${issueType}, Field: ${field}` // more descriptive fallback
   try {
     message = performanceDbApi.getTaskMessage({
       issue_type: task.issueType,
       num_issues: task.count,
       rowCount: req.totalRows,
       field: task.field,
-      format: 'html'
+      format
     })
   } catch (error) {
     logger.warn('prepareTask/getTaskMessage failure', { type: types.App, errorMessage: error.message, errorStack: error.stack })
+    return next(new MiddlewareError(`Failed to generate task message: ${error.message}`, 500))
   }
🧹 Nitpick comments (3)
src/controllers/issueDetailsController.js (3)

21-21: Fix typo in error message.

There's a typo in the word "isssue" (three 's' characters).

-    return next(new MiddlewareError(`No isssue of type '${issueType}' for field ${field}`, 404))
+    return next(new MiddlewareError(`No issue of type '${issueType}' for field ${field}`, 404))

51-51: Complete the inline comment.

The comment appears to be incomplete: "we get this to ensure 'missing column issues".

-  results.getBlockingTasks, // we get this to ensure 'missing column issues
+  results.getBlockingTasks, // we get this to ensure 'missing column' issues are handled

65-72: Improve error handling in locals method.

The current implementation might swallow errors from super.locals. Consider moving the try-catch block to only wrap the Object.assign operation.

   async locals (req, res, next) {
     try {
       Object.assign(req.form.options, req.locals)
-      super.locals(req, res, next)
     } catch (error) {
       next(error)
+      return
     }
+    super.locals(req, res, next)
   }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 56773df and 2032ced.

📒 Files selected for processing (1)
  • src/controllers/issueDetailsController.js (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: run-tests / test
🔇 Additional comments (1)
src/controllers/issueDetailsController.js (1)

1-8: LGTM! All necessary dependencies are properly imported.

The imports are well-organised and cover all required functionality for the controller.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🧹 Nitpick comments (5)
test/unit/issueDetailsController.test.js (2)

5-11: Consider enhancing test data setup.

The request template could be more comprehensive to test edge cases:

  • Empty aggregatedTasks map
  • Invalid issue types
  • Missing fields
 const reqTemplate = {
   locals: {},
   aggregatedTasks: new Map([
-    ['missing column|name', { field: 'name' }]]),
+    ['missing column|name', { field: 'name' }],
+    ['invalid flag|status', { field: 'status' }],
+    ['unknown_issue|field', { field: 'field' }]]),
   params: { issueType: 'missing column', field: 'name' },
   totalRows: 10
 }

13-18: Add test coverage for error scenarios.

The test only covers the happy path. Consider adding tests for:

  • Non-existent issue types
  • Missing fields
  • Invalid HTML formatting
src/controllers/issueDetailsController.js (2)

29-29: Fix typo in error message.

There's a typo in the error message: "isssue" should be "issue".

-    return next(new MiddlewareError(`No isssue of type '${issueType}' for field ${field}`, 404))
+    return next(new MiddlewareError(`No issue of type '${issueType}' for field ${field}`, 404))

68-85: Consider middleware order optimisation.

The middleware chain could be optimised:

  1. validateParams should be first to fail fast
  2. Feature flag check could be combined with validation
  3. Error handling middleware should be last
 const middlewares = [
+  validateParams,
   isFeatureEnabled('checkIssueDetailsPage')
-    ? validateParams
+    ? (req, res, next) => next()
     : (req, res, next) => { return next(new MiddlewareError('Not found', 404)) },
   results.getRequestDataMiddleware,
   results.fetchResponseDetails,
-  results.checkForErroredResponse,
   results.setupTableParams,
   setPagination,
   results.getIssueTypesWithQualityCriteriaLevels,
   results.extractIssuesFromResults,
   results.addQualityCriteriaLevelsToIssues,
   results.aggregateIssues,
   results.getBlockingTasks,
   results.getTotalRows,
   prepareTask,
+  results.checkForErroredResponse,
   results.setupError
 ]
src/controllers/resultsController.js (1)

196-228: Improve task aggregation performance.

The task aggregation could be optimised using reduce instead of a for-of loop.

-  const taskMap = new Map()
-  for (const issue of issues) {
-    if (filterOutTasksByQualityCriterialLevel(issue)) {
-      const key = `${issue['issue-type']}|${issue.field}`
-      const task = taskMap.get(key)
-      if (!task) {
-        taskMap.set(key, {
-          issueType: issue['issue-type'],
-          field: issue.field,
-          qualityCriteriaLevel: issue.quality_criteria_level,
-          count: 1
-        })
-      } else {
-        task.count++
-      }
-    }
-  }
+  const taskMap = issues.reduce((map, issue) => {
+    if (filterOutTasksByQualityCriterialLevel(issue)) {
+      const key = `${issue['issue-type']}|${issue.field}`
+      const task = map.get(key)
+      if (!task) {
+        map.set(key, {
+          issueType: issue['issue-type'],
+          field: issue.field,
+          qualityCriteriaLevel: issue.quality_criteria_level,
+          count: 1
+        })
+      } else {
+        task.count++
+      }
+    }
+    return map
+  }, new Map())
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5c37a97 and fb967cb.

📒 Files selected for processing (5)
  • src/controllers/issueDetailsController.js (1 hunks)
  • src/controllers/resultsController.js (9 hunks)
  • src/models/responseDetails.js (4 hunks)
  • src/services/performanceDbApi.js (4 hunks)
  • test/unit/issueDetailsController.test.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/models/responseDetails.js
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: run-tests / test
🔇 Additional comments (1)
test/unit/issueDetailsController.test.js (1)

20-29: Verify next() function calls.

The test doesn't verify if next() was called. Add assertions to ensure proper middleware flow.

 const next = vi.fn()
 prepareTask(req, {}, next)
 expect(req.locals.task.message.trim()).toBe('2 entries have <span class="column-name">some flag</span> fields that must have valid YES or NO values')
+expect(next).toHaveBeenCalledTimes(1)
+expect(next).toHaveBeenCalledWith()

Comment thread src/controllers/issueDetailsController.js
Comment thread src/controllers/issueDetailsController.js Outdated
Comment thread src/controllers/resultsController.js
Comment thread src/controllers/resultsController.js Outdated
Comment thread src/services/performanceDbApi.js
Comment thread src/services/performanceDbApi.js
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (2)
src/controllers/issueDetailsController.js (2)

36-47: ⚠️ Potential issue

Improve error handling in message generation.

The catch block only logs the error but continues execution, which could lead to undefined behaviour.

    try {
      message = performanceDbApi.getTaskMessage({
        issue_type: task.issueType,
        num_issues: task.count,
        rowCount: req.totalRows,
        field: task.field,
        format: 'html'
      })
    } catch (error) {
      logger.warn('prepareTask/getTaskMessage failure', { type: types.App, errorMessage: error.message, errorStack: error.stack })
+     return next(new MiddlewareError(`Failed to generate task message: ${error.message}`, 500))
    }

61-63: ⚠️ Potential issue

Prevent XSS vulnerability in pagination URL construction.

The pagination URL construction doesn't validate or sanitise inputs.

+  const sanitizedId = encodeURIComponent(id)
+  const sanitizedIssueType = encodeURIComponent(issueType)
+  const sanitizedField = encodeURIComponent(field)
   const pagination = responseDetails.getPagination(req.parsedParams.pageNumber, {
-    href: (item) => `/check/results/${id}/issue/${issueType}/${field}/${item}`
+    href: (item) => `/check/results/${sanitizedId}/issue/${sanitizedIssueType}/${sanitizedField}/${encodeURIComponent(item)}`
   })
🧹 Nitpick comments (1)
config/default.yaml (1)

106-110: Fix YAML formatting issues.

Please address the following formatting issues:

  1. Remove trailing spaces in the comments on line 108
  2. Fix indentation on line 110 to match the 6-space indentation used throughout the file

Apply these changes:

   checkIssueDetailsPage:
     # https://github.com/digital-land/submit/issues/786
-    # enabling this feature will cause the results page to present 
-    # links to individual issue pages
-    enabled: true
+    # enabling this feature will cause the results page to present
+    # links to individual issue pages
+      enabled: true
🧰 Tools
🪛 YAMLlint (1.35.1)

[error] 108-108: trailing spaces

(trailing-spaces)


[warning] 110-110: wrong indentation: expected 6 but found 4

(indentation)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fb967cb and 86d84b0.

📒 Files selected for processing (9)
  • config/default.yaml (1 hunks)
  • src/controllers/issueDetailsController.js (1 hunks)
  • src/controllers/resultsController.js (9 hunks)
  • src/models/requestData.js (2 hunks)
  • src/serverSetup/routes.js (2 hunks)
  • src/services/asyncRequestApi.js (1 hunks)
  • src/views/organisations/issueTable.html (1 hunks)
  • test/acceptance/request_check.test.js (4 hunks)
  • test/unit/requestData.test.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/views/organisations/issueTable.html
🧰 Additional context used
🧠 Learnings (1)
src/controllers/resultsController.js (1)
Learnt from: rosado
PR: digital-land/submit#892
File: src/controllers/resultsController.js:91-98
Timestamp: 2025-02-20T15:38:57.104Z
Learning: In the results controller, page number validation is handled by the `validateParams` middleware using valibot schema with `minValue(1)`, making additional validation in the controller methods unnecessary.
🪛 GitHub Actions: Code change pipeline to development by @rosado
src/controllers/issueDetailsController.js

[error] 27-49: 'issueType' is not defined. (no-undef)

🪛 YAMLlint (1.35.1)
config/default.yaml

[error] 108-108: trailing spaces

(trailing-spaces)


[warning] 110-110: wrong indentation: expected 6 but found 4

(indentation)

🔇 Additional comments (10)
src/services/asyncRequestApi.js (1)

57-64: Well-structured changes to support issue details page.

The refactoring improves URL handling by using the URL constructor and adds flexibility with the optional opts parameter.

src/models/requestData.js (1)

7-47: Well-structured implementation with proper validation.

The changes demonstrate good practices:

  • Clear parameter validation using valibot
  • Comprehensive JSDoc documentation
  • Proper handling of special cases (e.g., 'missing column' issue type)
test/unit/requestData.test.js (1)

72-72: LGTM! Test updated to match new method signature.

The test case has been correctly updated to use the new object-based parameter structure for the fetchResponseDetails method.

test/acceptance/request_check.test.js (2)

24-66: LGTM! Well-structured refactoring of test logic.

The checkDataFile function effectively encapsulates common test logic, reducing code duplication and improving maintainability. The function handles both JavaScript enabled and disabled scenarios appropriately.


68-121: LGTM! Comprehensive test coverage for error scenarios.

The checkErrorDataFile function provides thorough testing of error scenarios, including verification of error messages and 404 routing. The implementation aligns well with the new issue details page feature.

src/controllers/resultsController.js (5)

1-10: LGTM! Clean feature flag implementation.

The feature flag is properly imported and initialized, allowing for controlled rollout of the issue details page feature.


91-113: Verify error handling for detailsOptions.

The detailsOptions parameter is destructured with optional chaining, but there's no explicit error handling for malformed options.

Consider adding validation for the detailsOptions object structure:

-  const detailsOpts = req.locals.detailsOptions ?? {}
+  const detailsOpts = req.locals.detailsOptions ?? {}
+  if (detailsOpts.issue && (!detailsOpts.issue.issueType || !detailsOpts.issue.field)) {
+    return next(new Error('Invalid issue details options'))
+  }

213-249: LGTM! Improved task aggregation logic.

The refactored aggregation logic is more maintainable and properly handles the quality criteria level filtering.


251-284: Add input validation for task parameters.

The makeTaskParam function doesn't validate all required fields before use.


312-402: LGTM! Well-structured task handling functions.

The new task handling functions are well-organized and properly support the issue details page feature. The parameter validation schema is correctly implemented using valibot.

Comment thread src/serverSetup/routes.js Outdated
Comment thread src/serverSetup/routes.js Outdated
Comment thread src/controllers/issueDetailsController.js Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (3)
src/controllers/issueDetailsController.js (3)

36-47: ⚠️ Potential issue

Improve error handling in message generation.

The catch block only logs the error but continues execution. This could lead to undefined behaviour.

   try {
     message = performanceDbApi.getTaskMessage({
       issue_type: task.issueType,
       num_issues: task.count,
       rowCount: req.totalRows,
       field: task.field,
       format: 'html'
     })
   } catch (error) {
     logger.warn('prepareTask/getTaskMessage failure', { type: types.App, errorMessage: error.message, errorStack: error.stack })
+    return next(new MiddlewareError(`Failed to generate task message: ${error.message}`, 500))
   }

37-43: 🛠️ Refactor suggestion

Validate format parameter and handle potential errors.

The code doesn't validate the 'format' parameter or handle potential errors from getTaskMessage.

+  const ALLOWED_FORMATS = ['html', 'text']
+  const format = 'html'
+  
+  if (!ALLOWED_FORMATS.includes(format)) {
+    return next(new MiddlewareError(`Invalid format: ${format}`, 400))
+  }
+
   message = performanceDbApi.getTaskMessage({
     issue_type: task.issueType,
     num_issues: task.count,
     rowCount: req.totalRows,
     field: task.field,
-    format: 'html'
+    format
   })

61-63: ⚠️ Potential issue

Validate pagination parameters.

The pagination URL construction doesn't validate or sanitise inputs, which could lead to XSS vulnerabilities.

+  const sanitizedId = encodeURIComponent(id)
+  const sanitizedIssueType = encodeURIComponent(issueType)
+  const sanitizedField = encodeURIComponent(field)
   const pagination = responseDetails.getPagination(req.parsedParams.pageNumber, {
-    href: (item) => `/check/results/${id}/issue/${issueType}/${field}/${item}`
+    href: (item) => `/check/results/${sanitizedId}/issue/${sanitizedIssueType}/${sanitizedField}/${encodeURIComponent(item)}`
   })
🧹 Nitpick comments (5)
src/controllers/issueDetailsController.js (5)

11-15: Consider enhancing the pageNumber validation.

The current validation could be more robust by adding an upper limit and handling invalid number strings.

 const validateParams = validateQueryParams({
   schema: v.object({
-    pageNumber: v.optional(v.pipe(v.string(), v.transform(parseInt), v.minValue(1)), '1')
+    pageNumber: v.optional(
+      v.pipe(
+        v.string(),
+        v.transform((value) => {
+          const parsed = parseInt(value, 10)
+          if (isNaN(parsed)) throw new Error('Invalid number')
+          return parsed
+        }),
+        v.number([
+          v.minValue(1),
+          v.maxValue(1000) // Prevent excessive pagination
+        ])
+      ),
+      '1'
+    )
   })
 })

29-29: Fix typo in error message.

There's a typo in the word 'isssue' (three 's').

-    return next(new MiddlewareError(`No isssue of type '${issueTypeSlug}' for field ${field}`, 404))
+    return next(new MiddlewareError(`No issue of type '${issueTypeSlug}' for field ${field}`, 404))

68-71: Consider validating params before assignment.

The direct assignment of request parameters could be unsafe. Consider validating and sanitising the inputs.

 async function setDetailsOptions (req, res, next) {
+  const sanitizedParams = Object.fromEntries(
+    Object.entries(req.params).map(([key, value]) => [key, encodeURIComponent(value)])
+  )
-  req.locals.detailsOptions = { issue: { ...req.params } }
+  req.locals.detailsOptions = { issue: sanitizedParams }
   next()
 }

73-91: Add documentation for the middleware pipeline.

The middleware pipeline would benefit from documentation explaining the purpose and dependencies of each middleware in the chain.

+/**
+ * Middleware pipeline for processing issue details:
+ * 1. Feature flag check and parameter validation
+ * 2. Request data setup and error handling
+ * 3. Response details and pagination
+ * 4. Issue processing and quality criteria
+ * 5. Task preparation and error setup
+ */
 const middlewares = [
   isFeatureEnabled('checkIssueDetailsPage')
     ? validateParams
     : (req, res, next) => { return next(new MiddlewareError('Not found', 404)) },
   // ... rest of the middleware chain
 ]

101-108: Enhance error handling in locals method.

Consider adding more context to the error and logging before passing it to next.

   async locals (req, res, next) {
     try {
       Object.assign(req.form.options, req.locals)
       super.locals(req, res, next)
     } catch (error) {
+      logger.error('Failed to process locals', {
+        type: types.App,
+        errorMessage: error.message,
+        errorStack: error.stack
+      })
+      error.message = `Failed to process locals: ${error.message}`
       next(error)
     }
   }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 86d84b0 and 8518b18.

📒 Files selected for processing (1)
  • src/controllers/issueDetailsController.js (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: run-tests / test

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
src/controllers/resultsController.js (2)

213-249: Consider using Map for issue type lookup.

The current implementation iterates through all issues. Consider creating a Map for issue types to improve performance when dealing with large datasets.

 export function aggregateIssues (req, res, next) {
   const { issues } = req
+  const issueTypeMap = new Map(issues.map(issue => [
+    `${issue['issue-type']}|${issue.field}`,
+    issue
+  ]))
   const taskMap = new Map()
-  for (const issue of issues) {
+  for (const [key, issue] of issueTypeMap.entries()) {
     if (filterOutTasksByQualityCriterialLevel(issue)) {
-      const key = `${issue['issue-type']}|${issue.field}`
       const task = taskMap.get(key)

398-402: Enhance validation error messages.

Consider adding custom error messages to the valibot schema to improve user experience when validation fails.

 const validateParams = validateQueryParams({
   schema: v.object({
-    pageNumber: v.optional(v.pipe(v.string(), v.transform(parseInt), v.minValue(1)), '1')
+    pageNumber: v.optional(v.pipe(
+      v.string([v.message('Page number must be a string')]),
+      v.transform(parseInt),
+      v.minValue(1, [v.message('Page number must be greater than 0')])
+    ), '1')
   })
 })
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8518b18 and 3239a71.

📒 Files selected for processing (5)
  • src/assets/scss/index.scss (1 hunks)
  • src/controllers/resultsController.js (9 hunks)
  • src/routes/form-wizard/check/steps.js (3 hunks)
  • test/unit/publishRequestAPI.test.js (1 hunks)
  • test/unit/requestData.test.js (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/assets/scss/index.scss
  • test/unit/requestData.test.js
🧰 Additional context used
🧠 Learnings (1)
src/controllers/resultsController.js (1)
Learnt from: rosado
PR: digital-land/submit#892
File: src/controllers/resultsController.js:91-98
Timestamp: 2025-02-20T15:38:57.104Z
Learning: In the results controller, page number validation is handled by the `validateParams` middleware using valibot schema with `minValue(1)`, making additional validation in the controller methods unnecessary.
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: run-tests / test
🔇 Additional comments (7)
src/routes/form-wizard/check/steps.js (3)

13-13: LGTM! Import statement follows consistent pattern.

The import of issueDetailsController is correctly placed and follows the established pattern of controller imports.


97-105: Parameter validation needed for issueType and field.

The route accepts dynamic parameters without explicit validation. If issueType or field are unrecognised or maliciously crafted, it could lead to unexpected UI or logs. Consider adding validation logic or sanitisation before processing them.


97-105: LGTM! Route configuration follows established patterns.

The new route configuration is well-structured and consistent with other routes in the file. It correctly:

  • Extends baseSettings
  • Sets appropriate flags (noPost, checkJourney, entryPoint, forwardQuery)
  • Specifies the correct template and controller
src/controllers/resultsController.js (4)

1-10: Clean imports and feature flag setup!

The imports are well-organized, and the feature flag implementation follows best practices.


97-106: Fix undefined parsedParams error.

The static analysis indicates that req.parsedParams is undefined in tests. Ensure validateParams middleware is executed before fetchResponseDetails.


267-283: Add input validation for task parameters.

The makeTaskParam function doesn't validate all required fields before use.


16-49:

Details

❓ Verification inconclusive

Verify middleware execution order.

The middleware stack looks good, but ensure that validateParams executes before any middleware that depends on req.parsedParams.


🏁 Script executed:

#!/bin/bash
# Search for middleware that uses req.parsedParams
rg -A 3 'req\.parsedParams' src/

Length of output: 3787


Action: Confirm Middleware Execution Order

The middleware stack in src/controllers/resultsController.js appears to be in the correct order with validateParams invoked first. This ensures that any downstream middleware reliant on req.parsedParams receives it correctly. However, as several other parts of the code (e.g. in src/middleware/common.middleware.js and controllers like issueDetailsController.js) also depend on req.parsedParams, please double-check that any future reordering or modifications preserve this critical execution order.

Comment thread test/unit/publishRequestAPI.test.js
Comment thread src/routes/form-wizard/check/steps.js
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
src/controllers/ShareResultsController.js (1)

25-27: LGTM! Consider making the page number configurable.

The path update is consistent with the new routing structure. However, since this number appears to be significant for navigation, consider making it a configurable constant to avoid magic numbers.

+ const DEFAULT_RESULTS_PAGE = '1'
+
  generateResultsLink (id) {
-    return `${config.url}check/results/${id}/1`
+    return `${config.url}check/results/${id}/${DEFAULT_RESULTS_PAGE}`
  }
test/unit/shareResultsController.test.js (1)

35-37: LGTM! Consider using a shared constant for the page number.

The test updates are thorough and consistent with the new routing structure. To improve maintainability, consider using a shared constant for the page number, which would align with the suggested enhancement in ShareResultsController.

+ const DEFAULT_RESULTS_PAGE = '1'
+
  it('should set shareLink in req.locals and req.form.options', async () => {
    await shareResultsController.locals(req, res, next)

-    const expectedLink = `${config.url}check/results/123/1`
+    const expectedLink = `${config.url}check/results/123/${DEFAULT_RESULTS_PAGE}`
    expect(req.locals.shareLink).toBe(expectedLink)
    expect(req.form.options.shareLink).toBe(expectedLink)
  })

Also applies to: 43-44, 71-71, 79-79

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3239a71 and cda702c.

📒 Files selected for processing (4)
  • performance-test/locustfile.py (1 hunks)
  • src/controllers/ShareResultsController.js (1 hunks)
  • test/unit/middleware/common.middleware.test.js (1 hunks)
  • test/unit/shareResultsController.test.js (2 hunks)
✅ Files skipped from review due to trivial changes (1)
  • test/unit/middleware/common.middleware.test.js
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: run-tests / test
🔇 Additional comments (1)
performance-test/locustfile.py (1)

62-62: LGTM! Path update aligns with new routing structure.

The change from /0 to /1 in the results path is consistent with the broader routing updates across the application.

Comment thread src/views/check/results/issueDetails.html Outdated
@coderabbitai coderabbitai Bot mentioned this pull request Jul 28, 2025
11 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

No open projects
Status: Done Archived

Development

Successfully merging this pull request may close these issues.

Check tool: Issue details page - new template & route

5 participants