Skip to content

feat: add column mapping functionality to C&P#1196

Open
gibahjoe wants to merge 24 commits into
mainfrom
1181-bring-column-mapping-into-cp
Open

feat: add column mapping functionality to C&P#1196
gibahjoe wants to merge 24 commits into
mainfrom
1181-bring-column-mapping-into-cp

Conversation

@gibahjoe
Copy link
Copy Markdown
Contributor

@gibahjoe gibahjoe commented May 27, 2026

Description

Adds column-mapping feature for the Check & Provide flow: implements columnMappingController, new check views/components, supporting service and config updates, styles, and unit tests.

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

  • Feature
  • Refactor
  • Bug Fix
  • Optimization
  • Documentation Update

Related Tickets & Documents

QA Instructions, Screenshots, Recordings

Screenshot 2026-05-27 at 16 02 38 Screenshot 2026-05-27 at 16 02 50

Added/updated tests?

  • Yes

(Added/updated unit tests for column mapping, redis loader and status controller)

QA sign off

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

[optional] Are there any post-deployment tasks we need to perform?

None

[optional] Are there any dependencies on other PRs or Work?

None

Summary by CodeRabbit

  • New Features
    • Added a "Map your fields" step with inline validation; results page shows a "Field mapping" summary and "Map again" link.
  • UI
    • New styles for the column-mapping panel, table and error states; improved map rendering when geometry bounds are absent.
  • Configuration
    • manageServiceUrl added for environments; production flag enabled for issue-detail links.
  • Services
    • Jira flow now supports a local fallback that returns a Manage Service link.
  • Tests
    • Extensive unit and view tests added/updated for mapping, caching and task handling.

@gibahjoe gibahjoe linked an issue May 27, 2026 that may be closed by this pull request
4 tasks
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 27, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • ✅ Review completed - (🔄 Check again to review again)

Walkthrough

Adds a column→field mapping workflow and task-log driven issue handling: new column-mapping route/controller and helpers, StatusController decision logic, ResultsController task-log extraction and optional mapping rows, Datasette/Redis caching for provisioning reasons, new templates/styles, updated fixtures, and unit/acceptance tests.

Changes

Column Mapping Feature & task-log integration

Layer / File(s) Summary
Routing: register step
src/routes/form-wizard/check/steps.js
Imports columnMappingController and adds the '/column-mapping/:id' wizard step wired to check/column-mapping.html and entryPoint: true.
Controller & mapping helpers
src/controllers/columnMappingController.js
Adds ColumnMappingController (GET/POST) and exported pure helpers to build mapping options/rows, parse bracketed form fields, validate mappings, apply selections, and construct submitted column_mapping payloads.
Views, macro and SCSS
src/views/check/column-mapping.html, src/views/check/components/column-mapping.html, src/assets/scss/index.scss
New mapping page and columnMapping macro render editable/selectable mappings, error summary wiring and mapped/unmapped status; SCSS adds error row, table padding, panel and unmap-select styling.
API plumbing & results integration
src/services/asyncRequestApi.js, src/controllers/resultsController.js, src/views/check/results/results.html
Adds postCheckRequest; ResultsController now extracts issues from task-log, conditionally enables column mapping, aggregates issue counts using issue.count, and results view shows a "Field mapping" summary when mapping rows exist.
Status decision & dataset loading
src/controllers/statusController.js
Adds middlewareSetup() to load dataset info and shouldShowColumnMapping() to decide redirect to column-mapping vs results based on statutory status, mapped completeness, spare uploaded columns, and external issues.
ResultData parsing & tasks
src/models/requestData.js
hasErrors() uses task-log; getColumnFieldLog() builds column log from column-mapping plus task-log entries; getIssueTasks() normalises issue tasks from task-log.
Redis/Datasette caching
src/utils/redisLoader.js
Adds Datasette usage, increases CACHE_TTL to 6 hours, provides getCachedJson/setCachedJson, and exports getProvisionReasonsForDataset and isStatutoryDataset.
Fixtures & WireMock stubs
docker/request-api-stub/wiremock/__files/*, mappings/*
WireMock fixtures updated to replace error-summary/column-field-log with task-log and column-mapping arrays used by new parsing logic.
Map JS bbox tweaks
src/assets/js/map.js
Avoids recomputing bbox when present and returns [] when no valid coordinates found.
Tests & acceptance
test/unit/*, test/acceptance/request_check.test.js
Adds unit tests for column mapping helpers and templates, redisLoader provisioning tests, updates RequestData/Results/Status tests to task-log model, adjusts JS unit and acceptance tests to updated behaviours.
Misc config & ignore
config/default.yaml, config/production.yaml, .gitignore, vite.config.js
Minor YAML edits, features.checkIssueDetailsPage.enabled set to true in production, .claude/ added to .gitignore, and Vite coverage exclude updated.

Sequence Diagram

sequenceDiagram
  participant User
  participant StatusController
  participant StatusLogic as shouldShowColumnMapping
  participant ColumnController as ColumnMappingController
  participant AsyncAPI as postCheckRequest
  participant ResultsView

  User->>StatusController: POST /status/:id
  StatusController->>StatusLogic: shouldShowColumnMapping(requestData)
  alt mapping needed
    StatusController->>User: redirect /column-mapping/:id
    User->>ColumnController: GET /column-mapping/:id
    ColumnController->>User: render mapping form
    User->>ColumnController: POST fieldMap
    ColumnController->>ColumnController: validateColumnMapping
    alt invalid
      ColumnController->>User: 400 re-render with errors
    else
      ColumnController->>AsyncAPI: postCheckRequest(mapping)
      AsyncAPI->>ColumnController: response(request_id)
      ColumnController->>User: redirect /check/status/:newId
    end
  else
    StatusController->>User: redirect /results/:id/1
  end
  User->>ResultsView: view results
  ResultsView->>User: show Field mapping summary if present
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

enhancement

Suggested reviewers

  • DilwoarH
  • rosado

🐰 I hopped through rows of code today,

Mapping columns on my way,
With templates, tests and a tidy plan,
I helped each field become a fan,
Hooray — the mappings work and stay!

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.13% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Out of Scope Changes check ❓ Inconclusive The PR includes some unrelated changes (map.js bounding box fix, config flag updates, .gitignore, WireMock stubs) that are not directly required by #1181 but are supporting or incidental modifications. Clarify whether the map.js fix, production.yaml config change, and WireMock stub updates are essential dependencies or should be separated into a distinct PR.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add column mapping functionality to C&P' directly and clearly describes the main feature being added—column mapping functionality for the Check & Provide flow.
Linked Issues check ✅ Passed The PR fully implements the column mapping feature as specified in issue #1181, including controller implementation, UI components, validation logic, service integration, and comprehensive test coverage.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 1181-bring-column-mapping-into-cp

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 27, 2026

Coverage Report

Status Category Percentage Covered / Total
🔵 Lines 66.42% 7602 / 11445
🔵 Statements 66.42% 7602 / 11445
🔵 Functions 64.56% 317 / 491
🔵 Branches 77.53% 1146 / 1478
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
src/assets/js/map.js 64.1% 74.13% 75% 64.1% 54-68, 86-87, 124-126, 190-228, 237-253, 268-327, 333, 377-399, 410-415, 428-429, 483-484, 487-488, 501-503
src/controllers/CheckAnswersController.js 82.74% 71.87% 71.42% 82.74% 12-29, 135-139, 178-184, 189-191, 208-213
src/controllers/columnMappingController.js 53.88% 82.97% 60% 53.88% 12-50, 53-71, 74-117, 125-148, 157-188, 214-215, 242-251, 259-260, 395, 397-398, 411
src/controllers/issueDetailsController.js 64% 33.33% 11.11% 64% 34-35, 51-52, 64-77, 86, 91-109, 136-140, 143-149
src/controllers/resultsController.js 62.33% 65.51% 58.06% 62.33% 21-40, 57-65, 78-82, 86-100, 108-109, 111-112, 116-117, 154-156, 186-194, 206-207, 229-243, 266-267, 276-284, 292-295, 298-302, 313-314, 398-400, 416-422, 430-447, 454-474, 486-496, 499-501, 510-523, 565-584
src/controllers/statusController.js 71.18% 72.3% 87.5% 71.18% 22-27, 31-59, 71-72, 90-92, 101-102, 118-119, 129, 131-132, 167-168, 195, 205
src/models/requestData.js 94.67% 85% 94.44% 94.67% 45-49, 137-138, 142, 197-202
src/routes/form-wizard/check/steps.js 0% 0% 0% 0% 1-148
src/services/asyncRequestApi.js 97.18% 80% 80% 97.18% 37-38
src/services/jiraService.js 98.13% 95% 100% 98.13% 73-74
src/utils/redisLoader.js 69.28% 68.75% 81.81% 69.28% 24-27, 32-35, 54-55, 67-68, 116-117, 137-139, 141-142, 152-153, 160-176, 180-190
Generated in workflow #1478 for commit 80d3f64 by the Vitest Coverage Report Action

@gibahjoe gibahjoe marked this pull request as ready for review May 27, 2026 15:13
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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
test/unit/redisLoader.test.js (1)

93-97: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Update TTL assertions to the new 6-hour cache expiry.

These expectations still assert 300, but runtime now uses 21600 (6 hours), which is causing the current pipeline failures.

Suggested fix
-        300,
+        21600,
@@
-        300,
+        21600,
@@
-        300,
+        21600,

Also applies to: 173-185

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/unit/redisLoader.test.js` around lines 93 - 97, Update the TTL used in
the test assertions from 300 to 21600 to match the new 6-hour cache expiry:
change the expect calls that reference mockRedisClient.setEx (e.g., the
assertion verifying setEx was called with 'deploy-123:dataset:example-dataset')
to expect 21600 as the TTL (this same change needs to be applied to the second
block of assertions around the test lines 173-185 that also call
mockRedisClient.setEx); keep the other arguments (key and JSON.stringify
payload) unchanged.
🧹 Nitpick comments (6)
test/unit/views/check/columnMapping.test.js (2)

46-46: ⚡ Quick win

Avoid asserting inline style implementation details.

This assertion is brittle and can fail on harmless CSS/template refactors; prefer asserting behaviour/content rather than inline style text.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/unit/views/check/columnMapping.test.js` at line 46, The test is
asserting an inline style string which is brittle; update the assertion that
currently reads against document.querySelector('td').getAttribute('style') to
instead assert behavior from the element itself—for example, get the td via
document.querySelector('td') and assert its computed style verticalAlign (via
window.getComputedStyle(td).verticalAlign) equals 'middle', or assert that a
specific CSS class is present on the td that conveys the same layout; replace
the brittle inline-style check with one of these behavior-oriented assertions in
columnMapping.test.js.

50-53: ⚡ Quick win

Use field-scoped selectors instead of first-match .govuk-form-group.

These checks depend on DOM order and can become flaky. Target the row/control for notes directly (or assert error-row count) so the test validates intent, not incidental ordering.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/unit/views/check/columnMapping.test.js` around lines 50 - 53, Replace
brittle first-match selectors with field-scoped ones that target the "notes"
control: find the form-group/row that contains the notes label or input (e.g.,
locate the label or input with text/for "notes" and get its closest
.govuk-form-group or tr) and run your assertions against that element (check its
class includes or excludes govuk-form-group--error and that the error-row tr
with .govuk-form-group--error contains the text "notes"); alternatively assert
the count of error rows if appropriate. Ensure you still assert the button text
using button[form="columnMappingForm"].
src/views/check/column-mapping.html (1)

43-43: ⚡ Quick win

Move inline styles to CSS.

The inline style style="background-color: #f3f2f1; padding: 15px 20px;" should be defined as a CSS class to maintain separation of concerns and reusability.

♻️ Define CSS class for grey panel

Add to src/assets/scss/index.scss:

.column-mapping-panel {
  background-color: `#f3f2f1`;
  padding: govuk-spacing(3) govuk-spacing(4);
}

Then update the template:

-    <div class="govuk-!-margin-bottom-6"  style="background-color: `#f3f2f1`; padding: 15px 20px;">
+    <div class="govuk-!-margin-bottom-6 column-mapping-panel">
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/views/check/column-mapping.html` at line 43, Replace the inline style on
the div in src/views/check/column-mapping.html by creating a reusable CSS class
(e.g., .column-mapping-panel) in your global stylesheet
(src/assets/scss/index.scss) that sets background-color and padding, then update
the div (which currently uses govuk-!-margin-bottom-6) to include the new
.column-mapping-panel class instead of the inline style so styling is separated
and reusable.
src/controllers/resultsController.js (1)

238-243: 💤 Low value

Simplify filter condition.

The filter checks !mappedField || !col and toUpperCase() === 'IGNORE' separately. The logic is correct but could be slightly clearer with early returns or combined conditions.

♻️ Alternative filter for clarity
-        .filter(({ mappedField, col }) => {
-          // skip empty values and explicit IGNORE mappings
-          if (!mappedField || !col) return false
-          if (typeof mappedField === 'string' && mappedField.toUpperCase() === 'IGNORE') return false
-          return true
-        })
+        .filter(({ mappedField, col }) => 
+          mappedField && col && 
+          !(typeof mappedField === 'string' && mappedField.toUpperCase() === 'IGNORE')
+        )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/controllers/resultsController.js` around lines 238 - 243, The filter
callback in resultsController.js currently uses multiple if returns to exclude
empty values and 'IGNORE' mappings; replace it with a single, clearer boolean
return expression in the .filter callback (refer to the .filter(({ mappedField,
col }) => { ... }) function) that returns true only when mappedField and col are
truthy and mappedField is not the string 'IGNORE' (case-insensitive). Ensure you
preserve the case-insensitive check for mappedField by normalizing it (e.g.,
toUpperCase) only when it's a string before comparing.
src/controllers/columnMappingController.js (1)

177-177: ⚡ Quick win

Guard against undefined body.columns.

If body.columns is undefined, the expression [body.columns].filter(Boolean) creates [undefined] which then filters to []. While this works, it's clearer to handle undefined explicitly.

♻️ Clearer handling for undefined columns
-  const columns = Array.isArray(body.columns) ? body.columns : [body.columns].filter(Boolean)
+  const columns = body.columns ? (Array.isArray(body.columns) ? body.columns : [body.columns]) : []
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/controllers/columnMappingController.js` at line 177, The current
assignment to const columns (in columnMappingController.js) uses
Array.isArray(body.columns) ? body.columns : [body.columns].filter(Boolean),
which is unclear when body.columns is undefined; update the expression to
explicitly handle undefined/null by checking body.columns first (e.g., if
Array.isArray(body.columns) return body.columns, else if body.columns is truthy
wrap it in [body.columns], otherwise return an empty array) so columns is always
a clear array value.
src/views/check/components/column-mapping.html (1)

80-83: ⚡ Quick win

Move inline styles to CSS for the un-map dropdown.

The native <select> element uses extensive inline styles that should be extracted to a CSS class for maintainability and consistency with GOV.UK patterns.

♻️ Define CSS class for mapped-status dropdown

Add to src/assets/scss/index.scss:

.column-mapping__unmap-select {
  background-color: govuk-colour('green');
  color: govuk-colour('white');
  font-weight: 700;
  font-size: 0.875rem;
  padding: 2px 8px;
  border: none;
  border-radius: 0;
  cursor: pointer;
  appearance: auto;
}

Then update the template:

-                  <select name="map[{{ row.column }}]" style="background-color: `#00703c`; color: `#ffffff`; font-weight: 700; font-size: 0.875rem; padding: 2px 8px; border: none; border-radius: 0; cursor: pointer; appearance: auto;">
+                  <select name="map[{{ row.column }}]" class="column-mapping__unmap-select">
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/views/check/components/column-mapping.html` around lines 80 - 83, Extract
the inline styles from the select element (name="map[{{ row.column }}]") into a
reusable CSS class (e.g., .column-mapping__unmap-select) in your main SCSS file,
implementing the visual rules shown (green background, white text, bold,
0.875rem font-size, 2px 8px padding, no border, no radius, pointer cursor,
appearance:auto); then update the template to remove the inline style and add
class="column-mapping__unmap-select" to the select so the styling is maintained
via CSS instead of inline attributes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/controllers/statusController.js`:
- Around line 81-83: The blocking-error gate currently only checks the first 50
rows because statusController.js calls fetchResponseDetails(0, 50); replace that
single-page fetch with a pagination loop that repeatedly calls
fetchResponseDetails(offset, limit) (e.g., offset += limit) and accumulates
responseDetails.getRows() until a call returns fewer rows than limit (or none),
then compute the blocking-error gate from the full accumulated rows; apply the
same pagination fix to the other occurrence around the block that spans lines
referenced (the 91-98 logic) so both places use the full dataset rather than
just the first 50 rows.

In `@src/utils/redisLoader.js`:
- Line 97: The call to normaliseDatasetFields is missing the dataset argument so
the helper's dataset !== field guard never runs; update the invocation where
response.formattedData is used (the line calling normaliseDatasetFields) to pass
the current dataset variable as the second parameter (i.e.,
normaliseDatasetFields(response.formattedData, dataset)) so the function
receives both formatted data and dataset and the guard in normaliseDatasetFields
can operate correctly.

---

Outside diff comments:
In `@test/unit/redisLoader.test.js`:
- Around line 93-97: Update the TTL used in the test assertions from 300 to
21600 to match the new 6-hour cache expiry: change the expect calls that
reference mockRedisClient.setEx (e.g., the assertion verifying setEx was called
with 'deploy-123:dataset:example-dataset') to expect 21600 as the TTL (this same
change needs to be applied to the second block of assertions around the test
lines 173-185 that also call mockRedisClient.setEx); keep the other arguments
(key and JSON.stringify payload) unchanged.

---

Nitpick comments:
In `@src/controllers/columnMappingController.js`:
- Line 177: The current assignment to const columns (in
columnMappingController.js) uses Array.isArray(body.columns) ? body.columns :
[body.columns].filter(Boolean), which is unclear when body.columns is undefined;
update the expression to explicitly handle undefined/null by checking
body.columns first (e.g., if Array.isArray(body.columns) return body.columns,
else if body.columns is truthy wrap it in [body.columns], otherwise return an
empty array) so columns is always a clear array value.

In `@src/controllers/resultsController.js`:
- Around line 238-243: The filter callback in resultsController.js currently
uses multiple if returns to exclude empty values and 'IGNORE' mappings; replace
it with a single, clearer boolean return expression in the .filter callback
(refer to the .filter(({ mappedField, col }) => { ... }) function) that returns
true only when mappedField and col are truthy and mappedField is not the string
'IGNORE' (case-insensitive). Ensure you preserve the case-insensitive check for
mappedField by normalizing it (e.g., toUpperCase) only when it's a string before
comparing.

In `@src/views/check/column-mapping.html`:
- Line 43: Replace the inline style on the div in
src/views/check/column-mapping.html by creating a reusable CSS class (e.g.,
.column-mapping-panel) in your global stylesheet (src/assets/scss/index.scss)
that sets background-color and padding, then update the div (which currently
uses govuk-!-margin-bottom-6) to include the new .column-mapping-panel class
instead of the inline style so styling is separated and reusable.

In `@src/views/check/components/column-mapping.html`:
- Around line 80-83: Extract the inline styles from the select element
(name="map[{{ row.column }}]") into a reusable CSS class (e.g.,
.column-mapping__unmap-select) in your main SCSS file, implementing the visual
rules shown (green background, white text, bold, 0.875rem font-size, 2px 8px
padding, no border, no radius, pointer cursor, appearance:auto); then update the
template to remove the inline style and add class="column-mapping__unmap-select"
to the select so the styling is maintained via CSS instead of inline attributes.

In `@test/unit/views/check/columnMapping.test.js`:
- Line 46: The test is asserting an inline style string which is brittle; update
the assertion that currently reads against
document.querySelector('td').getAttribute('style') to instead assert behavior
from the element itself—for example, get the td via document.querySelector('td')
and assert its computed style verticalAlign (via
window.getComputedStyle(td).verticalAlign) equals 'middle', or assert that a
specific CSS class is present on the td that conveys the same layout; replace
the brittle inline-style check with one of these behavior-oriented assertions in
columnMapping.test.js.
- Around line 50-53: Replace brittle first-match selectors with field-scoped
ones that target the "notes" control: find the form-group/row that contains the
notes label or input (e.g., locate the label or input with text/for "notes" and
get its closest .govuk-form-group or tr) and run your assertions against that
element (check its class includes or excludes govuk-form-group--error and that
the error-row tr with .govuk-form-group--error contains the text "notes");
alternatively assert the count of error rows if appropriate. Ensure you still
assert the button text using button[form="columnMappingForm"].
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 9a6981dc-bd09-4f51-bb50-085725fa446e

📥 Commits

Reviewing files that changed from the base of the PR and between 1f339c3 and dd8fc70.

📒 Files selected for processing (15)
  • config/default.yaml
  • src/assets/scss/index.scss
  • src/controllers/columnMappingController.js
  • src/controllers/resultsController.js
  • src/controllers/statusController.js
  • src/routes/form-wizard/check/steps.js
  • src/services/asyncRequestApi.js
  • src/utils/redisLoader.js
  • src/views/check/column-mapping.html
  • src/views/check/components/column-mapping.html
  • src/views/check/results/results.html
  • test/unit/columnMappingController.test.js
  • test/unit/redisLoader.test.js
  • test/unit/statusController.test.js
  • test/unit/views/check/columnMapping.test.js

Comment thread src/controllers/statusController.js
Comment thread src/utils/redisLoader.js Outdated
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 28, 2026

Actionable comments posted: 0

pooleycodes and others added 8 commits May 28, 2026 14:15
- Refactored StatusController to include middleware for fetching dataset information from platform API.
- Updated shouldShowColumnMapping function to consider unique dataset fields and handle geometry/point field logic.
- Removed unused getDatasetFields function and related normalisation logic from redisLoader.
- Modified requestData model to include mandatory field information in column mapping.
- Improved column mapping HTML to provide clearer user instructions and handle user ignored fields.
- Enhanced unit tests for StatusController and column mapping to cover new functionality and edge cases.
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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
test/unit/views/check/columnMapping.test.js (1)

86-113: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Test name contradicts assertions.

The test is named "does not show not provided for required fields" but lines 111-112 assert that option[value="na"] does exist and contains "Not provided". Either the test name is misleading or the assertions are incorrect.

Proposed fix (if the option should appear)
-  it('does not show not provided for required fields', () => {
+  it('shows not provided option for required fields', () => {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/unit/views/check/columnMapping.test.js` around lines 86 - 113, The test
"does not show not provided for required fields" is inconsistent with its
assertions which expect option[value="na"] to exist and contain "Not provided";
either rename the test or update the assertions: if the UI should show the "Not
provided" option for required fields, rename the it(...) description to "shows
not provided for required fields" (in test file columnMapping.test.js) so it
matches the expectations, otherwise change the two expect(...) lines to assert
absence (use toBeNull() / not.toContain) to reflect that "na" should not be
rendered for required mappings.
config/default.yaml (1)

127-127: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Trailing space in variable value appears unintentional.

The value plan has a trailing space which will affect display text formatting. This looks like an accidental edit.

Proposed fix
-      variable: plan 
+      variable: plan
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@config/default.yaml` at line 127, The YAML entry where the key "variable" is
set to the string "plan " contains an unintended trailing space; update the
value for the "variable" field from "plan " to "plan" (remove the trailing
whitespace) and verify there are no other occurrences of the same trailing-space
value in the config/default.yaml so display/formatting is correct.
🧹 Nitpick comments (2)
src/controllers/resultsController.js (1)

276-296: 💤 Low value

extractIssuesFromResults appears to be dead code.

extractIssuesFromResults (lines 276-285) is no longer used in the middleware pipeline since it was replaced by extractIssuesFromTaskLog. Consider removing it if it's not needed elsewhere.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/controllers/resultsController.js` around lines 276 - 296, The function
extractIssuesFromResults appears to be dead code; search for any remaining
references to extractIssuesFromResults and, if none exist, remove the entire
function (exported symbol extractIssuesFromResults) from the file and exports so
only the active middleware (extractIssuesFromTaskLog and
filterOutInternalIssues) remain; if there are other callers that still need the
behavior, instead update those callers to use
req.locals.requestData.getIssueTasks() or adapt extractIssuesFromResults to
delegate to that method (using responseDetails.response -> issue_logs) and keep
the export.
test/unit/requestData.test.js (1)

9-15: 💤 Low value

Incorrect mock path for logger.

The mock path '../utils/logger.js' doesn't match the actual import path '../../src/utils/logger.js'. This mock may not intercept the real logger, though the vi.spyOn on line 17 provides a fallback.

Proposed fix
-vi.mock('../utils/logger.js', () => {
+vi.mock('../../src/utils/logger.js', () => {
   return {
     default: {
       error: vi.fn()
     }
   }
 })
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/unit/requestData.test.js` around lines 9 - 15, The test's vi.mock call
is targeting the wrong module path so it doesn't intercept the real logger
import; update the vi.mock invocation to match the actual import used in the
test (replace the current mock path with the same module string used by the code
under test, ensure the mock returns an object with default.error: vi.fn()), and
then you can rely on that mock instead of the vi.spyOn fallback currently
present (adjust or remove the vi.spyOn on the logger's error if redundant).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@config/production.yaml`:
- Line 36: The production config currently sets the feature flag
checkIssueDetailsPage (enabled: true) which exposes issue-detail pages to all
users; confirm whether this is intentional for this release and if not revert to
disabled or gate it behind a rollout/feature-flag system. If it should remain
off until QA/design/product sign-off, change the production setting for
checkIssueDetailsPage back to false (or implement conditional gating/percentage
rollout) and add a short comment referencing the pending sign-offs to prevent
accidental enablement.

In `@src/controllers/statusController.js`:
- Around line 38-40: The error handling uses the wrong variable for the dataset
name: replace references to req.params.dataset with the already-extracted
params.dataset inside fetchDatasetPlatformInfo so the thrown Error and
logger.warn use params.dataset (the variable set on line 33) instead of
req.params.dataset; update both the Error constructor and the logger.warn call
to reference params.dataset to ensure accurate diagnostics.

---

Outside diff comments:
In `@config/default.yaml`:
- Line 127: The YAML entry where the key "variable" is set to the string "plan "
contains an unintended trailing space; update the value for the "variable" field
from "plan " to "plan" (remove the trailing whitespace) and verify there are no
other occurrences of the same trailing-space value in the config/default.yaml so
display/formatting is correct.

In `@test/unit/views/check/columnMapping.test.js`:
- Around line 86-113: The test "does not show not provided for required fields"
is inconsistent with its assertions which expect option[value="na"] to exist and
contain "Not provided"; either rename the test or update the assertions: if the
UI should show the "Not provided" option for required fields, rename the it(...)
description to "shows not provided for required fields" (in test file
columnMapping.test.js) so it matches the expectations, otherwise change the two
expect(...) lines to assert absence (use toBeNull() / not.toContain) to reflect
that "na" should not be rendered for required mappings.

---

Nitpick comments:
In `@src/controllers/resultsController.js`:
- Around line 276-296: The function extractIssuesFromResults appears to be dead
code; search for any remaining references to extractIssuesFromResults and, if
none exist, remove the entire function (exported symbol
extractIssuesFromResults) from the file and exports so only the active
middleware (extractIssuesFromTaskLog and filterOutInternalIssues) remain; if
there are other callers that still need the behavior, instead update those
callers to use req.locals.requestData.getIssueTasks() or adapt
extractIssuesFromResults to delegate to that method (using
responseDetails.response -> issue_logs) and keep the export.

In `@test/unit/requestData.test.js`:
- Around line 9-15: The test's vi.mock call is targeting the wrong module path
so it doesn't intercept the real logger import; update the vi.mock invocation to
match the actual import used in the test (replace the current mock path with the
same module string used by the code under test, ensure the mock returns an
object with default.error: vi.fn()), and then you can rely on that mock instead
of the vi.spyOn fallback currently present (adjust or remove the vi.spyOn on the
logger's error if redundant).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 59c1cceb-ac4a-4b37-8304-7735d291d1ee

📥 Commits

Reviewing files that changed from the base of the PR and between 6573aa5 and d57d7bc.

📒 Files selected for processing (25)
  • .gitignore
  • config/default.yaml
  • config/production.yaml
  • docker/request-api-stub/wiremock/__files/check_file/article-4/request-complete-errors.json
  • docker/request-api-stub/wiremock/__files/check_file/article-4/request-complete.json
  • docker/request-api-stub/wiremock/__files/check_url/article-4/request-complete.json
  • docker/request-api-stub/wiremock/mappings/requests_bbw7ebivop3leg7ue9vka4-31b3b282-b265-4f7c-ac66-db61e3f81be2.json
  • docker/request-api-stub/wiremock/mappings/requests_bbw7ebivop3leg7ue9vka4-7ffceaf2-278c-46d3-9b8e-f403a36bd93f.json
  • src/assets/js/map.js
  • src/controllers/columnMappingController.js
  • src/controllers/issueDetailsController.js
  • src/controllers/resultsController.js
  • src/controllers/statusController.js
  • src/models/requestData.js
  • src/utils/redisLoader.js
  • src/views/check/column-mapping.html
  • src/views/check/components/column-mapping.html
  • test/acceptance/request_check.test.js
  • test/unit/columnMappingController.test.js
  • test/unit/javascript/map.test.js
  • test/unit/redisLoader.test.js
  • test/unit/requestData.test.js
  • test/unit/resultsController.test.js
  • test/unit/statusController.test.js
  • test/unit/views/check/columnMapping.test.js
💤 Files with no reviewable changes (1)
  • src/utils/redisLoader.js
✅ Files skipped from review due to trivial changes (4)
  • docker/request-api-stub/wiremock/__files/check_url/article-4/request-complete.json
  • .gitignore
  • docker/request-api-stub/wiremock/mappings/requests_bbw7ebivop3leg7ue9vka4-7ffceaf2-278c-46d3-9b8e-f403a36bd93f.json
  • test/unit/javascript/map.test.js
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/views/check/components/column-mapping.html
  • src/views/check/column-mapping.html
  • test/unit/statusController.test.js
  • test/unit/columnMappingController.test.js
  • src/controllers/columnMappingController.js

Comment thread config/production.yaml
Comment thread src/controllers/statusController.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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/controllers/columnMappingController.js (1)

97-105: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

spareUploadedColumns is always empty on POST — spare-column auto-IGNORE never executes.

buildSelectableColumns(columnMappingRows) is called without responseRows, so it defaults to [] and the function returns []. Consequently buildSubmittedColumnMapping never reaches the branch that auto-assigns IGNORE to spare uploaded columns, so the submitted column_mapping omits them and the backend re-check will treat them as unmapped again. buildColumnMappingOptions already computed the populated list as options.uploadedColumns (using responseDetails.getRows()), so reuse it and drop the redundant prepareColumnMappingContext call.

🐛 Proposed fix
       const params = requestData.getParams() ?? {}
-      const uniqueDatasetFields = req.uniqueDatasetFields || []
-      const { columnMappingRows } = await prepareColumnMappingContext(requestData, uniqueDatasetFields)
-      const spareUploadedColumns = buildSelectableColumns(columnMappingRows)
+      const spareUploadedColumns = options.uploadedColumns
       const columnMapping = buildSubmittedColumnMapping({
         existingMapping: params.column_mapping,
         body: req.body,
         spareUploadedColumns
       })
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/controllers/columnMappingController.js` around lines 97 - 105, The
spareUploadedColumns list is empty because buildSelectableColumns is called with
columnMappingRows instead of the full response rows; update the controller to
stop calling prepareColumnMappingContext redundantly and instead reuse the
uploaded columns computed by buildColumnMappingOptions (options.uploadedColumns,
which comes from responseDetails.getRows()) when building spareUploadedColumns
for buildSubmittedColumnMapping; ensure you pass that uploadedColumns value into
buildSubmittedColumnMapping so the auto-IGNORE branch for spare uploaded columns
can execute (references: prepareColumnMappingContext, buildColumnMappingOptions,
options.uploadedColumns, responseDetails.getRows, buildSelectableColumns,
buildSubmittedColumnMapping, columnMappingRows, spareUploadedColumns).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/controllers/resultsController.js`:
- Around line 229-240: The columnMappingRows creation in resultsController.js
uses value: { html: column } which allows stored XSS; change it to use value: {
text: column } so the template (which feeds options.columnMappingRows into
govukSummaryList) treats the column as escaped text. Update the block that maps
requestData.getColumnFieldLog() into columnMappingRows (variable name
columnMappingRows) to set value.text instead of value.html and ensure column is
coerced to a string or empty string as before.

---

Outside diff comments:
In `@src/controllers/columnMappingController.js`:
- Around line 97-105: The spareUploadedColumns list is empty because
buildSelectableColumns is called with columnMappingRows instead of the full
response rows; update the controller to stop calling prepareColumnMappingContext
redundantly and instead reuse the uploaded columns computed by
buildColumnMappingOptions (options.uploadedColumns, which comes from
responseDetails.getRows()) when building spareUploadedColumns for
buildSubmittedColumnMapping; ensure you pass that uploadedColumns value into
buildSubmittedColumnMapping so the auto-IGNORE branch for spare uploaded columns
can execute (references: prepareColumnMappingContext, buildColumnMappingOptions,
options.uploadedColumns, responseDetails.getRows, buildSelectableColumns,
buildSubmittedColumnMapping, columnMappingRows, spareUploadedColumns).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: d60cf08b-b639-4e1f-b277-0fb85a39a1b1

📥 Commits

Reviewing files that changed from the base of the PR and between d57d7bc and 849d42e.

📒 Files selected for processing (9)
  • config/default.yaml
  • src/controllers/columnMappingController.js
  • src/controllers/resultsController.js
  • src/controllers/statusController.js
  • test/unit/columnMappingController.test.js
  • test/unit/requestData.test.js
  • test/unit/statusController.test.js
  • test/unit/views/check/columnMapping.test.js
  • vite.config.js
🚧 Files skipped from review as they are similar to previous changes (5)
  • test/unit/statusController.test.js
  • test/unit/requestData.test.js
  • test/unit/views/check/columnMapping.test.js
  • test/unit/columnMappingController.test.js
  • src/controllers/statusController.js

Comment thread src/controllers/resultsController.js
…sController and shortened manage service url
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.

Bring Column Mapping into C&P

2 participants