Skip to content

Arcgis Detail, Auth Data Only, Capitalization and No Dataset Spec Fallback#1152

Merged
pooleycodes merged 10 commits intomainfrom
plugin-error-detail
Feb 12, 2026
Merged

Arcgis Detail, Auth Data Only, Capitalization and No Dataset Spec Fallback#1152
pooleycodes merged 10 commits intomainfrom
plugin-error-detail

Conversation

@pooleycodes
Copy link
Contributor

@pooleycodes pooleycodes commented Feb 6, 2026

Description

Few things added to this:

  • Detail in error summary if we believe arcgis to have failed
  • Capitalization / and non capitalize of all datasets
  • Service works were no dataset exists under a spec (fallback to dataset field table)
  • Correct Record count based on non auth or auth data
  • Table only shows auth data

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

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

Related Tickets & Documents

QA Instructions, Screenshots, Recordings

  • Make sure individual datasets task lists look the same in dev and production.
  • Make sure capitalization's of datasets is appropriate throughout the service.
  • Test with an arcgis URL without the feature layer
  • Check developement-plan-timetable now loads correctly
  • Check record count is as expected and authoritative data only shows in data view.

Added/updated tests?

We encourage you to keep the code coverage percentage at 80% and above.

  • Yes
  • No, and this is why: Please replace this line with details on why tests have not been included
  • I need help with writing tests

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

    • Enabled provision-based datasets feature.
    • Added targeted error messaging for data-layer URL issues.
  • Improvements

    • Standardised readable dataset name display across pages.
    • Simplified and optimised entity-issue counting for performance.
    • Adjusted data-fetching paths to use a performance-oriented source.
  • Bug Fixes

    • Made dataset field required in task lists.
    • Prevented null original values when rendering issue details.
  • Updates

    • Tests updated to reflect naming and validation changes.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 6, 2026

Walkthrough

Enables provision-based datasets; swaps entity/entry issue-count fetching to a performance-DB-backed path; updates performanceDbApi to accept lpa and optional dataset; changes dataset slug-to-readable-name filter to accept an optional capitalize flag and updates many templates and tests to use slug-based readable names.

Changes

Cohort / File(s) Summary
Config
config/default.yaml
Toggles features.provisionBasedDatasets.enabled from false to true.
Core middleware & service
src/middleware/common.middleware.js, src/services/performanceDbApi.js
Adds public fetchEntityIssueCountsPerformanceDb; refactors entity-issue counting SQL; performanceDbApi.fetchEntityIssueCounts now accepts (lpa, dataset) and conditionally filters by dataset; populates req.entityCount in authority flows.
Slug-to-name filter & usage
src/filters/makeDatasetSlugToReadableNameFilter.js, src/utils/datasetSubjectLoader.js
Returned filter gains optional capitalize parameter (defaults false); callers updated to pass true where capitalised names required.
Middleware consumers
src/middleware/datasetOverview.middleware.js, src/middleware/dataview.middleware.js, src/middleware/lpa-overview.middleware.js
Replaces fetchEntityIssueCounts/fetchEntryIssueCounts with fetchEntityIssueCountsPerformanceDb; removes entryIssueCounts usage; adjusts taskCount logic; adds conditional onlyIf fallback to fetch entity counts.
Other middleware tweaks
src/middleware/dataset-failed-expectation-details.middleware.js, src/middleware/entityIssueDetails.middleware.js
Adds pageNumber from parsedParams to template params; guards originalValue assignment to avoid nulls.
Schema
src/routes/schemas.js
OrgDatasetTaskList nested dataset field changed from optional to required NonEmptyString.
Views/templates
src/views/... (many templates, e.g. components/dataset-banner.html, includes/_dataset-page-header.html, organisations/*, submit/*)
Replace dataset.name with `dataset.dataset
Error view
src/views/check/error-redirect.html
Adds special branch for errorMessage "URL must be the data layer" and guards generic support block.
Tests
test/unit/*, test/integration/* (multiple updated tests)
Updated to use slug-based dataset fields and call datasetSlugToReadableName(..., true) where expected; adjusted assertions and selectors (including .first() usage).

Sequence Diagram

sequenceDiagram
    participant Client as Client
    participant Middleware as Middleware Chain
    participant PerfDB as Performance DB API
    participant DB as Database

    Client->>Middleware: Request dataset overview
    Middleware->>Middleware: Execute middleware chain (includes onlyIf/fallback)
    Middleware->>PerfDB: fetchEntityIssueCounts(lpa, dataset?)
    PerfDB->>DB: Run simplified aggregation query (counts by field, issue_type, dataset)
    DB-->>PerfDB: Return aggregated counts
    PerfDB-->>Middleware: Return issue counts
    Middleware->>Middleware: Set req.entityIssueCounts / req.entityCount
    Middleware-->>Client: Render template using slug-to-readable-name filter and performance DB counts
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

enhancement

Suggested reviewers

  • DilwoarH
  • rosado

Poem

🐰 I hopped through slugs and middleware lanes,
I nudged the counts to faster plains,
I capitalised when asked with care,
And rendered names from slugs so fair.
A joyful twitch — datasets now sing!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The title references four distinct changes (ArcGIS detail, auth data only, capitalization, no dataset spec fallback) but the changeset spans 30+ files with diverse modifications that don't all align equally with each component mentioned. Consider a more specific title focusing on the primary change, such as 'Refactor entity issue counts and enable capitalization rules' or split into multiple PRs if the changes represent distinct features.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch plugin-error-detail

No actionable comments were generated in the recent review. 🎉

🧹 Recent nitpick comments
src/middleware/common.middleware.js (1)

206-212: Log message may display undefined for count.

When authoritativeResult?.data?.count is undefined, line 211 will log "Authoritative data found with count undefined". Consider only including the count in the log when it's defined, or adjusting the message.

Suggested tweak
-      logger.info(`Authoritative data found with count ${count}, skipping non-authoritative check`)
+      logger.info(`Authoritative data found${count !== undefined ? ` with count ${count}` : ''}, skipping non-authoritative check`)

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


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

github-actions bot commented Feb 6, 2026

Coverage Report

Status Category Percentage Covered / Total
🔵 Lines 66.16% 6753 / 10206
🔵 Statements 66.16% 6753 / 10206
🔵 Functions 63.95% 275 / 430
🔵 Branches 79.21% 911 / 1150
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
src/filters/makeDatasetSlugToReadableNameFilter.js 100% 100% 100% 100%
src/middleware/common.middleware.js 65.95% 89.6% 37.09% 65.95% 31-40, 44, 58-78, 91-92, 94-95, 107-109, 113, 129-136, 150-154, 179-194, 209-210, 230-231, 253-267, 325-338, 344-366, 450-458, 474-487, 552, 561-563, 593-595, 643-684, 724-728, 733-736, 748-752, 780-794, 801-815, 821-829, 833-849, 918-920, 993-1000, 1015-1070, 1101-1102, 1105-1108, 1111-1124, 1149-1154, 1164-1167, 1176
src/middleware/dataset-failed-expectation-details.middleware.js 0% 0% 0% 0% 1-123
src/middleware/datasetOverview.middleware.js 84.68% 58.06% 33.33% 84.68% 17-37, 79-83, 94-99, 136-138, 220
src/middleware/dataview.middleware.js 100% 64.28% 60% 100%
src/middleware/entityIssueDetails.middleware.js 100% 73.07% 100% 100%
src/middleware/lpa-overview.middleware.js 80.79% 77.21% 63.63% 80.79% 19-20, 62-64, 76-78, 84-86, 108-119, 136-138, 148-150, 199-200, 203-204, 259-267, 281, 283, 285, 338-341, 397-405, 418-429, 432-444
src/routes/schemas.js 100% 100% 100% 100%
src/services/performanceDbApi.js 64.61% 85.41% 28.57% 64.61% 38-39, 62-63, 83-84, 100-128, 171-173, 246-250, 255-268, 335-341, 352-366, 376-385, 396-416, 420-428, 437-449, 460-488
src/utils/datasetSubjectLoader.js 100% 96.42% 100% 100%
Generated in workflow #1335 for commit a732cf7 by the Vitest Coverage Report Action

… through only auth or some, work with no dataset but collection
@pooleycodes pooleycodes changed the title detail on why arcgis may have failed Arcgis Detail, Auth Data Only, Capitalization and No Dataset Spec Fallback Feb 10, 2026
Copy link
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: 7

Caution

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

⚠️ Outside diff range comments (1)
src/views/organisations/dataset-overview.html (1)

235-235: ⚠️ Potential issue | 🟡 Minor

Inconsistency: guidance link still uses dataset.name while sibling links use the filter.

Lines 228 and 231 were updated to use dataset.dataset | datasetSlugToReadableName, but the guidance link on line 235 still reads {{ dataset.name }} guidance. This creates a mixed sourcing of display labels within the same <ul>, which could produce visually inconsistent results if dataset.name and the filter output diverge — or if dataset.name is unavailable in fallback scenarios (which this PR aims to handle).

Suggested fix
-          <a class="govuk-link" href="{{ dataset.dataset | getDatasetGuidanceUrl }}">{{ dataset.name }} guidance</a>
+          <a class="govuk-link" href="{{ dataset.dataset | getDatasetGuidanceUrl }}">{{ dataset.dataset | datasetSlugToReadableName(true) }} guidance</a>
🤖 Fix all issues with AI agents
In `@src/middleware/dataview.middleware.js`:
- Around line 84-87: The taskCount computation uses entityIssueCounts.length
without guarding for undefined and can throw; update the expression that defines
taskCount in dataview.middleware.js (the line assigning taskCount based on
authority) to mirror the safe pattern used in datasetOverview.middleware.js by
checking entityIssueCounts (e.g., use entityIssueCounts ?
entityIssueCounts.length : 0) so when req.entityIssueCounts is missing the code
returns 0 instead of throwing; keep the authority conditional (authority !==
'some' ? ...) but replace the right-hand side with the guarded expression.

In `@src/views/check/error-redirect.html`:
- Around line 58-62: In the ArcGIS error block (the {% elif errorMessage == "URL
must be the data layer" %} branch), close the mailto anchor and fix the
duplicated word: update the paragraph containing the email link (the <a
href="mailto:digitalland@communities.gov.uk"> element) to include a closing </a>
before </p>, and remove the extra "the" in "If you believe you have provided the
information correctly and the the problem persists" so it reads "...and the
problem persists"; the affected elements are the paragraphs with id="bad-upload"
and the mailto anchor.

In `@src/views/organisations/http-error.html`:
- Line 5: The breadcrumb and page title are inconsistent: the page title uses
the slug-based filter datasetSlugToReadableName applied to dataset.dataset
inside the pageName set, while the breadcrumb still renders dataset.name |
capitalize; update the breadcrumb rendering to use the same slug-to-readable
transformation (i.e., replace uses of dataset.name | capitalize with
dataset.dataset | datasetSlugToReadableName(true)) so both the page title and
breadcrumb display the same readable dataset name (ensure you update any
breadcrumb template code that references dataset.name).

In `@src/views/organisations/issueDetails.html`:
- Around line 10-12: Breadcrumb still uses dataset.name | capitalize while the
page title uses dataset.dataset | datasetSlugToReadableName(true); update the
breadcrumb in issueDetails.html (around the breadcrumb block at line 37) to use
the same filter call dataset.dataset | datasetSlugToReadableName(true) instead
of dataset.name | capitalize so the readable dataset name is consistent with the
page title.

In `@src/views/submit/dataset-details.html`:
- Line 30: The template currently applies the Nunjucks capitalize filter to
options.datasetName but the rest of the codebase uses
datasetSlugToReadableName(true) at render time; fix this for consistency by
removing the template capitalize filter and either (A) have the controller set
options.datasetName = datasetSlugToReadableName(dataset, true) before rendering,
or (B) pass the raw slug into the template and replace the template expression
with the datasetSlugToReadableName(true) filter (e.g., use options.dataset or
the slug variable and call datasetSlugToReadableName(true)); update the template
expression and controller assignment accordingly so capitalization is handled in
one consistent place.

In `@test/unit/http-errorPage.test.js`:
- Line 22: The test expectation for pageTitle uses the raw slug in
params.dataset.dataset but the template applies datasetSlugToReadableName(true)
which capitalises the first letter; update the assertion in
test/unit/http-errorPage.test.js so pageTitle uses the same transformed value
(i.e., combine params.organisation.name + " - " + the output of
datasetSlugToReadableName(true) for params.dataset.dataset + " - Task list -
Check and provide planning data") or otherwise assert the capitalised form to
match the template/filter behavior.

In `@test/unit/views/organisations/issueDetailsPage.test.js`:
- Line 25: Update the breadcrumb assertion in
test/unit/views/organisations/issueDetailsPage.test.js so it expects the dataset
display name used by the template: replace the use of params.dataset.dataset
with params.dataset.name (the same property the template renders via
dataset.name | capitalize) so the test matches
src/views/organisations/issueDetails.html's output.
🧹 Nitpick comments (9)
test/integration/authoritative_data.playwright.test.js (1)

17-17: .first() silences strict-mode but reduces specificity.

Using .first() works around Playwright's strict-mode error when multiple elements match 'Data is not from an authoritative source'. This is a reasonable pragmatic fix, though be aware it will silently pass even if the first matching element is no longer the intended one (e.g. if page structure changes). A narrower locator scoped to a specific parent container would be more resilient, but not essential here.

src/middleware/entityIssueDetails.middleware.js (1)

55-67: Subtle inconsistency between html and originalValue truthiness checks.

Line 62 uses a truthy check (html ? ...), so values like 0 or false produce an empty string for html. Line 63 uses html != null, so those same values would produce "0" or "false" for originalValue. This means html could be "" while originalValue is "0" for a numeric-zero input.

If this is intentional (preserving the original value for display/comparison while rendering empty HTML), it's fine — but worth a brief inline comment to prevent future confusion.

src/views/submit/lpa-details.html (1)

19-19: Inconsistent capitalisation approach compared to other templates.

This line uses the Nunjucks capitalize filter on options.datasetName, whereas the rest of the PR consistently uses datasetSlugToReadableName(true) on the dataset slug. If options.datasetName is already a readable name, capitalize will lowercase everything after the first character (e.g. "Tree Preservation Order" → "Tree preservation order"). Consider aligning with the pattern used elsewhere.

Also note that line 27 renders options.datasetName without any capitalisation at all — this may be intentional for the back-link text, but worth confirming consistency.

src/views/organisations/issueTable.html (1)

37-37: Breadcrumb still uses dataset.name | capitalize instead of dataset.dataset | datasetSlugToReadableName(true).

The pageName on lines 10/12 was updated to use the new filter, but the breadcrumb text on line 37 still uses the old dataset.name | capitalize approach. This could produce mismatched naming between the breadcrumb and the page title. Consider aligning for consistency.

♻️ Suggested fix
-      text: dataset.name | capitalize,
+      text: dataset.dataset | datasetSlugToReadableName(true),
test/unit/views/organisations/get-startedPage.test.js (1)

22-22: Organisation breadcrumb missing href in test expectation.

The template at get-started.html Line 27 renders the organisation breadcrumb with href: '/organisations/' + organisation.organisation, but this test expectation omits the href for that item. If runGenericPageTests validates hrefs, this could cause the test to miss a regression. Consider adding it for completeness:

-    breadcrumbs: [{ text: 'Home', href: '/' }, { text: 'Organisations', href: '/organisations' }, { text: params.organisation.name }, { text: 'Get started' }]
+    breadcrumbs: [{ text: 'Home', href: '/' }, { text: 'Organisations', href: '/organisations' }, { text: params.organisation.name, href: `/organisations/${params.organisation.organisation}` }, { text: 'Get started' }]
src/views/organisations/dataset-overview.html (1)

207-207: aria-label still references dataset.name instead of the filter.

The map aria-label uses {{ dataset.name }} directly. For consistency and to support the fallback scenario where dataset.name may be absent, consider switching to the filter here as well.

Suggested fix
-        <div id="map" class="app-map" role="region" aria-label="Map illustrating {{ dataset.name }} geometries. Use your keyboard to interact with the map. For screen reader users, use the arrow keys to navigate the map and the plus and minus keys to zoom in and out."></div>
+        <div id="map" class="app-map" role="region" aria-label="Map illustrating {{ dataset.dataset | datasetSlugToReadableName }} geometries. Use your keyboard to interact with the map. For screen reader users, use the arrow keys to navigate the map and the plus and minus keys to zoom in and out."></div>
src/filters/makeDatasetSlugToReadableNameFilter.js (1)

17-17: Stale JSDoc: @throws {Error} is no longer accurate.

The function no longer throws when a slug is not found in the mapping — it falls back to returning the transformed slug with a debug log. The @throws annotation should be removed to avoid misleading consumers.

Suggested fix
    * `@param` {boolean} [capitalize=false] - Whether to capitalize the first letter.
    * `@returns` {string} The readable name corresponding to the provided slug.
-   * `@throws` {Error} - If the provided slug is not found in the dataset name mapping.
    */
src/middleware/datasetTaskList.middleware.js (1)

93-93: Stale JSDoc: req.entryIssueCounts is no longer used.

Line 93 documents req.entryIssueCounts as a parameter, but this property is no longer destructured or referenced in prepareTasks. Remove it to keep the documentation accurate.

Suggested fix
  * `@param` {Object} req.entityCount total entity count under `count` field
  * `@param` {Object[]} req.resources: An array of resource objects.
  * `@param` {Object[]} req.sources: An array of source objects.
- * `@param` {Object} req.entryIssueCounts: An object containing the issue counts for the entries in the dataset.
  * `@param` {Object} req.entityIssueCounts: An object containing the issue counts for the entities in the dataset.
src/middleware/common.middleware.js (1)

206-212: Log message on line 211 always fires, even when count is undefined.

When count is undefined, the log message will read "Authoritative data found with count undefined, skipping non-authoritative check". This is harmless but slightly misleading. Consider moving it inside the if block or adjusting the message.

✏️ Suggestion — log conditionally or adjust wording
       if (count !== undefined) {
         req.entityCount = { entity_count: count }
       }
-      logger.info(`Authoritative data found with count ${count}, skipping non-authoritative check`)
+      logger.info(`Authoritative data found${count !== undefined ? ` with count ${count}` : ''}, skipping non-authoritative check`)

@Ben-Hodgkiss Ben-Hodgkiss self-requested a review February 12, 2026 10:53
Ben-Hodgkiss
Ben-Hodgkiss previously approved these changes Feb 12, 2026
@pooleycodes pooleycodes merged commit 15f9e26 into main Feb 12, 2026
5 checks passed
@pooleycodes pooleycodes deleted the plugin-error-detail branch February 12, 2026 14:50
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.

2 participants