Skip to content

CLARIN-DSpace v9/Port #1307 (metadata health check) to the v9 base - #1436

Merged
milanmajchrak merged 1 commit into
dtq-dev-9-basefrom
ufal/port-1307-9-base
Sep 10, 2026
Merged

CLARIN-DSpace v9/Port #1307 (metadata health check) to the v9 base#1436
milanmajchrak merged 1 commit into
dtq-dev-9-basefrom
ufal/port-1307-9-base

Conversation

@milanmajchrak

Copy link
Copy Markdown
Collaborator

What

Ports dataquest-dev/DSpace PR #1307 ("metadata health check", commit 6e0ed0c6cc;
originally ufal/clarin-dspace#1338, issue ufal/clarin-dspace#1317) to dtq-dev-9-base.

Adds the health check org.dspace.health.MetadataCheck, registered as Metadata check:
it runs the metadataqa curation task (ItemMetadataQAChecker) over the whole repository
through a Curator, classifies each reported line against metadata-check-patterns.json,
prints per-type error/warning statistics plus a size-capped list of messages, and writes
errorCount / warningCount (and the per-type errors / warnings arrays) into the
health-report JSON.

Sync card: BE-13 (_sync3/cards/BE-13.md, tranche T4, disposition ADAPT, size M, risk low,
depends_on: X-01).

Why

ItemMetadataQAChecker and its curate.cfg metadataqa registration are already on this
branch, but nothing surfaces their output in the health report, and
report-diff-fields.json already carries two Metadata check mappings that can never resolve
because no check by that name is registered. This PR is what makes those mappings live.

Changes

file how
dspace-api/src/main/java/org/dspace/health/MetadataCheck.java (new, 495 lines) verbatim from the source commit
dspace-api/src/main/resources/metadata-check-patterns.json (new, 52 lines) verbatim
dspace-api/src/test/java/org/dspace/scripts/HealthReportIT.java +2 tests, +3 helpers, +8 imports, +1 constant — 10 -> 12 @Test
dspace/config/modules/healthcheck.cfg hand-merged, 2 lines, not the source hunk
dspace-api/src/main/resources/report-diff-fields.json untouched — already complete (see below)

Both new files are byte-identical with origin/dtq-dev tip (f3793e6a9356, fb2ba7329276),
so "verbatim" is unambiguous. Every API they use exists unchanged on this branch:
Check.run(ReportInfo) (protected abstract, widened to public in MetadataCheck — legal),
Curator.addTask/setReporter/curate(Context,String)/getResult, Check.getReportJson/setReportJson,
and org.json:json:20231013, which is an explicit dspace-api dependency. All four
healthcheck.metadata.* knobs are read with the two-argument getIntProperty(key, default),
so no further config keys are needed.

healthcheck.cfg — hand-merged, deliberately not the source hunk

The source hunk cannot be applied as-is: it renames License -> License summary and
Embargo info -> Embargo check and drops Checksum from the enabled list, because
dtq-dev (7.6.x) and this branch (vanilla 9.3 + 2 CLARIN entries) name and order the checks
differently. This branch's names are kept and Metadata check is appended to both lists:

 healthcheck.checks = General Information,\
     Checksum,\
     Item summary,\
     User summary,\
     Log Analyser Check,\
     License,\
-    Embargo info
+    Embargo info,\
+    Metadata check

 plugin.named.org.dspace.health.Check = \
     ...
-    org.dspace.health.EmbargoInfoCheck =              Embargo info
+    org.dspace.health.EmbargoInfoCheck =              Embargo info,\
+    org.dspace.health.MetadataCheck =                 Metadata check

Consequence for operators: Metadata check is -c 7 here, not -c 5 as on dtq-dev.

HealthReportIT — the two tests are rewritten onto the name-based index

The source tests hardcode new String[]{"health-report", "-c", "5"}. On this branch index 5 is
License, so taken wholesale both tests would run the licence check and fail every
assertion. They use the checkIndex(String) helper this branch already has (added when the
class was restored), plus a new METADATA_CHECK = "Metadata check" constant:

String[] args = new String[]{"health-report", "-c", checkIndex(METADATA_CHECK)};

Everything else in the two tests, and all three helpers the source commit adds
(findItemUri private, findLastReportResult and findCheckByName package-private), is
carried over unchanged.

report-diff-fields.json — pre-existing gap, already closed

The card lists this file as missing on the target. That is stale: it was restored in full by
the test-parity card X-01 (#1425) and is byte-identical with dtq-dev, so it already
contains all four Metadata check lines. This PR does not touch it.

$ git diff --quiet origin/dtq-dev origin/dtq-dev-9-base -- dspace-api/src/main/resources/report-diff-fields.json && echo IDENTICAL
IDENTICAL
$ git diff origin/dtq-dev-9-base <head> --stat -- dspace-api/src/main/resources/report-diff-fields.json
(empty)

Related, and not folded in here: that resource addresses the licence row as
/checks/[name=License summary]/report/licenses while this branch registers the check as
License, so the licences row is silently dropped from report-diff. That is fixed
separately in #1433; the two changes touch different lines of different files and do not
conflict in either merge order.

Testing

mvn --no-transfer-progress -V clean install -P-assembly -DskipTests (full reactor, clean
first) -> BUILD SUCCESS, Total time: 06:42 min, 0 checkstyle violations in every module
including dspace-api, where the new 495-line class lives.

Integration tests, dspace-api, run locally on Windows (they do run — the card's "may need CI"
caveat did not apply):

$ rm -rf dspace-api/target/failsafe-reports
$ mvn -o -pl dspace-api verify -DskipIntegrationTests=false     -Dit.test=HealthReportIT,ItemMetadataQACheckerIT     -Denforcer.skip=true -Dcheckstyle.skip=true -Dlicense.skip=true -Dxml.skip=true
[INFO] Tests run: 17, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 139.3 s -- in org.dspace.curate.ItemMetadataQACheckerIT
[INFO] Tests run: 12, Failures: 0, Errors: 0, Skipped: 0, Time elapsed:  15.34 s -- in org.dspace.scripts.HealthReportIT
[INFO] Tests run: 29, Failures: 0, Errors: 0, Skipped: 0
[INFO] BUILD SUCCESS

HealthReportIT goes 10 -> 12 @Test and all 12 pass; ItemMetadataQACheckerIT (17 tests)
is the regression check for the metadataqa task this check drives, and is unaffected.
Numbers read from dspace-api/target/failsafe-reports/*.txt with their mtimes (11:30:54 and
11:31:10), not from the console tail.

Negative control. Reverting only the healthcheck.cfg registration — leaving both new tests
in place, rebuilding the test environment (rm -rf dspace-api/target/testing, reinstall) and
re-running HealthReportIT — turns testMetadataCheck and
testMetadataCheckWithRestrictedReportSize red, because checkIndex("Metadata check") no longer
resolves. So the two lines of config are load-bearing and the tests are not vacuous. Full output
in the gate comment below.

Static checks (card AC-BE-13-1):

$ git ls-tree <head> -- .../MetadataCheck.java .../metadata-check-patterns.json .../report-diff-fields.json | wc -l   -> 3
$ grep -cE 'org.dspace.health.MetadataCheck *= *Metadata check'  healthcheck.cfg                                     -> 1
$ sed -n '/^healthcheck.checks/,/^$/p' healthcheck.cfg | grep -c 'Metadata check'                                    -> 1
$ grep -c "License summary\|Embargo check" healthcheck.cfg                                                          -> 0
$ grep -c "Metadata check"  report-diff-fields.json                                                                  -> 4
$ git diff origin/dtq-dev-9-base <head> --stat -- .../report-diff-fields.json                                        -> (empty)
$ grep -c '@Test' HealthReportIT.java  -> 12      $ grep -c '"-c", "5"' HealthReportIT.java  -> 0

Resulting check order (Metadata check is index 7):

0 General Information · 1 Checksum · 2 Item summary · 3 User summary
4 Log Analyser Check  · 5 License  · 6 Embargo info · 7 Metadata check

Not verified here: the [shell-gated] live check on dev-6
(docker exec dspace8603 /dspace/bin/dspace health-report -c 7) — I have no shell on that host.
HealthReportIT#testMetadataCheck covers the same path in-process: it reads the persisted
ReportResult JSON, finds the Metadata check entry by name and asserts integer errorCount
(4) and warningCount (1) plus the per-type errors/warnings arrays.

One thing a reviewer should know: most of report-diff-fields.json is inert on this branch for
an unrelated reason. InfoCheck, ItemCheck and UserCheck are byte-identical with vanilla
dspace-9.3 and call setReportJson nowhere, so 23 of the resource's 26 mappings have no
emitter. This PR adds the third emitter (MetadataCheck, after LicenseCheck) and makes 2 of
those mappings live. The other 23 are a separate defect, not addressed here.

🤖 Generated with Claude Code

…ata health check (#1307)

Source: 6e0ed0c (dtq-dev PR #1307)

Adds the health check org.dspace.health.MetadataCheck, registered as
"Metadata check". It runs the `metadataqa` curation task
(ItemMetadataQAChecker) over the whole repository through a Curator,
classifies every reported line against metadata-check-patterns.json,
prints per-type error/warning statistics plus a size-capped list of
messages, and writes errorCount / warningCount and the per-type errors /
warnings arrays into the health-report JSON. Report size is bounded by
four healthcheck.metadata.* keys, all read with the two-argument
getIntProperty(key, default), so no further configuration is required.

MetadataCheck.java and metadata-check-patterns.json are copied verbatim:
both blobs are identical to origin/dtq-dev tip (f3793e6,
fb2ba73) and every API they use is unchanged on this branch --
Check.run(ReportInfo) (protected abstract, widened to public here, which
is legal), Curator.addTask/setReporter/curate(Context,String)/getResult,
Check.getReportJson/setReportJson, and org.json:json:20231013, an
explicit dspace-api dependency. org.apache.commons.collections.ListUtils
(collections 3) is left as the source has it; swapping it for
collections4 is a repo-wide cleanup, not part of this port.

healthcheck.cfg is hand-merged, not taken from the source hunk. The
source hunk also renames License -> License summary and Embargo info ->
Embargo check and drops Checksum from the enabled list, because dtq-dev
(7.6.x) and this branch (vanilla 9.3 plus two CLARIN entries) name and
order the checks differently. This branch's names are kept and
"Metadata check" is appended to both lists, so it becomes -c 7 here, not
-c 5 as on dtq-dev.

HealthReportIT gains this commit's two tests (testMetadataCheck,
testMetadataCheckWithRestrictedReportSize) and its three helpers
(findItemUri, and the package-private findLastReportResult and
findCheckByName), reaching 12 @test. The one adaptation: the source
tests hardcode -c 5, which addresses the License check here, so both
resolve the index through the checkIndex(String) helper this branch
already carries, via a new METADATA_CHECK constant. Everything else in
the two tests is byte-identical with the source; the only other
difference in the whole file is the added constant and its javadoc.

report-diff-fields.json is deliberately untouched. It was restored in
full by card X-01 (#1425), is byte-identical with dtq-dev and already
contains the four "Metadata check" lines this commit would otherwise
add; this port is what makes those mappings resolve.

Co-authored-by: Milan Kuchtiak <kuchtiak@ufal.mff.cuni.cz>
Co-authored-by: Ondrej Kosarko <kosarko@ufal.mff.cuni.cz>

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@milanmajchrak

Copy link
Copy Markdown
Collaborator Author

Local gate outputs (per sync rule 2.5)

Base: origin/dtq-dev-9-base = 233d87fe6f. Head: d6a5e04ebb.

B1 — CI-equivalent compile + checkstyle + license + enforcer

$ mvn --no-transfer-progress -V clean install -P-assembly -DskipTests
[INFO] BUILD SUCCESS
[INFO] Total time:  06:42 min
EXIT=0
$ grep -c "You have 0 Checkstyle violations" b1.log      -> 15   (no module reports a non-zero count)

Integration tests

$ rm -rf dspace-api/target/failsafe-reports
$ mvn -o -pl dspace-api verify -DskipIntegrationTests=false \
    -Dit.test=HealthReportIT,ItemMetadataQACheckerIT \
    -Denforcer.skip=true -Dcheckstyle.skip=true -Dlicense.skip=true -Dxml.skip=true
[INFO] Tests run: 17, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 139.3 s -- in org.dspace.curate.ItemMetadataQACheckerIT
[INFO] Tests run: 12, Failures: 0, Errors: 0, Skipped: 0, Time elapsed:  15.34 s -- in org.dspace.scripts.HealthReportIT
[INFO] Tests run: 29, Failures: 0, Errors: 0, Skipped: 0
[INFO] BUILD SUCCESS

per-class reports, with mtimes:
  org.dspace.curate.ItemMetadataQACheckerIT.txt   11:30:54
  org.dspace.scripts.HealthReportIT.txt           11:31:10

Negative control — the two new tests are not vacuous

Reverting only dspace/config/modules/healthcheck.cfg (tests untouched), refreshing the test
environment (rm -rf dspace-api/target/testing + reinstall of .,dspace,dspace-api,dspace-server-webapp)
and re-running:

[ERROR] Tests run: 12, Failures: 0, Errors: 2, Skipped: 0, Time elapsed: 47.66 s <<< FAILURE!
org.dspace.scripts.HealthReportIT.testMetadataCheck                          <<< ERROR!
org.dspace.scripts.HealthReportIT.testMetadataCheckWithRestrictedReportSize  <<< ERROR!
java.lang.IllegalStateException: Check not configured in healthcheck.checks: Metadata check

All 12 still run; exactly the two tests this PR adds fail, and only when the registration is
removed. Config restored afterwards (git status clean, branch diff back to the 4 files).

Observed once, not caused by this PR: an earlier ItemMetadataQACheckerIT run returned
Tests run: 17, Failures: 0, Errors: 1, the error being testItemVersion1 failing in teardown
with java.util.ConcurrentModificationException inside
org.hibernate.resource.jdbc.internal.ResourceRegistryStandardImpl.closeAll. The next run of the
same class was 17/17. It is an intermittent Hibernate teardown race in a test this PR does not
touch; flagging it rather than hiding it.

@milanmajchrak
milanmajchrak merged commit ac1eeec into dtq-dev-9-base Sep 10, 2026
12 checks passed
@milanmajchrak
milanmajchrak deleted the ufal/port-1307-9-base branch September 10, 2026 10:43
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.

1 participant