Skip to content

SpreadsheetValidator

Paulo Gomes da Cruz Junior edited this page Jun 17, 2026 · 1 revision

SpreadsheetValidator Service

Type: Component (Service) Domain: Spreadsheet Processing / District Volume Import Status: Complete (Issue #905) Last updated: 2026-06-17

Overview

SpreadsheetValidator validates a worksheet's header structure against a MatrixConfig. It checks that group headers and sub-column headers are present at the expected rows and column positions, returning a ValidationResult with descriptive error messages.

Used both as a pre-upload validator (via interiorValidator and coastValidator) and inline during processor execution.

Public Interface

Class: SpreadsheetValidator

Stateless service. No constructor parameters.

validateWorksheet(worksheet, config): ValidationResult

Validates worksheet headers against a matrix configuration.

Parameter Type Required Description
worksheet ExcelJS.Worksheet yes Worksheet from ExcelReader.read()
config MatrixConfig yes Expected matrix layout configuration

Returns: ValidationResult — { valid: boolean, errors: string[] }. Errors contain human-readable messages identifying missing or mispositioned headers.

Validation Checks

Check 1 — Data row presence

If worksheet.rowCount < config.dataStartRow, returns immediately with a single error: no data rows found.

Check 2 — Group header labels

For each group in config.groups, reads worksheet cell at (config.groupHeaderRow, group.colStart). If the cell is empty or whitespace-only, an error is added: Missing group header at column N, expected "label".

Check 3 — Sub-column header labels

For each sub-column in each group, reads the cell at (config.columnHeaderRow, colIdx). If empty, an error is added: Missing column header at column N (row M), expected "header".

Whitespace normalization: all cell text is cleaned via replace(/\s+/g, ' ').trim() before comparison. This matches the cleaning applied by MatrixParser and the configs.

Design Principles

Early Exit on No Data

If the worksheet has no data rows, the validator returns immediately without checking headers. This avoids misleading "missing header" errors when the file is fundamentally empty.

Non-Throwing API

Returns errors as a ValidationResult rather than throwing exceptions. Callers always receive a result object, never an unhandled rejection.

Headers-Only Validation

SpreadsheetValidator does NOT validate individual cell values or data types — that is the responsibility of downstream mappers and Zod schemas.

Dependencies

Dependency Purpose
exceljs Worksheet row/cell access
types.ts (local) MatrixConfig, ValidationResult

Constraints

Constraint Detail
Headers only Does not check data cell values, types, or ranges
Whitespace normalization Header text is cleaned before checking
Case-sensitive Compares cell text as-is (use interiorValidator/coastValidator for case-insensitive sheet names)

Code Location

Artifact Path
Service frontend/src/services/spreadsheet/spreadsheetValidator.ts
Types frontend/src/services/spreadsheet/types.ts
Tests frontend/src/services/spreadsheet/spreadsheetValidator.unit.test.ts

Test Coverage

5 tests covering: valid worksheet, no data rows, missing group header, missing column header, and multiple simultaneous errors.

Consumers

Consumer Location Usage
interiorValidator src/services/districtvolumes/validators/ Pre-upload validation for Interior files
coastValidator src/services/districtvolumes/validators/ Pre-upload validation for Coast files
DistrictVolumeProcessor src/services/districtvolumes/processors/ Internal validation during processing

Usage Example

import { ExcelReader } from '@/services/spreadsheet/excelReader';
import { SpreadsheetValidator } from '@/services/spreadsheet/spreadsheetValidator';
import { interiorMatrixConfig } from '@/services/districtvolumes/config/interiorMatrixConfig';

const reader = new ExcelReader();
const worksheet = await reader.read(file, 'Interior');

const validator = new SpreadsheetValidator();
const result = validator.validateWorksheet(worksheet, interiorMatrixConfig);

if (!result.valid) {
  console.error(result.errors); // ['Missing group header at column 2, expected "Dry Belt m3/ha"']
}

References

Reference Link
GitHub Issue #905
Related page ExcelReader
Related page MatrixParser

Waste Plus Wiki

Home

Architecture

Codes

Decisions

Knowledge Base

Development

Changes

Clone this wiki locally