From 04ac6ceee4250bb1817d5a02eb61801b2dd7f0fa Mon Sep 17 00:00:00 2001 From: milanmajchrak Date: Wed, 27 May 2026 14:17:49 +0200 Subject: [PATCH 1/2] Expect 422 for empty value on clarin-license select patch --- .../ClarinWorkspaceItemRestRepositoryIT.java | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinWorkspaceItemRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinWorkspaceItemRestRepositoryIT.java index aae48128354..af3b3e87be7 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinWorkspaceItemRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinWorkspaceItemRestRepositoryIT.java @@ -901,12 +901,13 @@ public void getWorkspaceItemReturnsDistinctLicenseSections() throws Exception { } /** - * PATCH on `/sections/clarin-license/select` with an empty value must clear - * the previously selected license: `dc.rights*` metadata is removed and the - * license is detached from the uploaded bitstream. + * PATCH on `/sections/clarin-license/select` with an empty value must be + * rejected as a client error (422): the section endpoint requires a + * non-empty license name. The previously selected license must remain + * untouched on the item and on its bitstreams. */ @Test - public void patchSelectWithEmptyValueClearsLicense() throws Exception { + public void patchSelectWithEmptyValueFails() throws Exception { context.turnOffAuthorisationSystem(); WorkspaceItem witem = createWorkspaceItemWithFile(); @@ -930,7 +931,7 @@ public void patchSelectWithEmptyValueClearsLicense() throws Exception { getClient(tokenAdmin).perform(get("/api/core/clarinlicenses/" + clarinLicense.getID())) .andExpect(jsonPath("$.bitstreams", is(1))); - // Now clear the selection with an empty value + // Now attempt to "clear" the selection with an empty value – this must fail ops.clear(); Map emptyValue = new HashMap(); emptyValue.put("value", ""); @@ -938,12 +939,13 @@ public void patchSelectWithEmptyValueClearsLicense() throws Exception { getClient(tokenAdmin).perform(patch("/api/submission/workspaceitems/" + witem.getID()) .content(getPatchContent(ops)) .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) - .andExpect(status().isOk()); + .andExpect(status().isUnprocessableEntity()); - // Item metadata cleared and license detached from bitstream - assertClarinLicenseMetadata(witem, "dc", "rights", null, null, true); + // The previously selected license must still be present on the item + // and still attached to the uploaded bitstream. + assertClarinLicenseMetadata(witem, "dc", "rights", null, clarinLicenseName, false); getClient(tokenAdmin).perform(get("/api/core/clarinlicenses/" + clarinLicense.getID())) - .andExpect(jsonPath("$.bitstreams", is(0))); + .andExpect(jsonPath("$.bitstreams", is(1))); } /** From 8092e856930f23ab279e080f046b324ef5ff8af2 Mon Sep 17 00:00:00 2001 From: milanmajchrak Date: Wed, 27 May 2026 14:21:33 +0200 Subject: [PATCH 2/2] Treat blank value as clear on clarin-license section select --- .../step/ClarinLicenseResourceStep.java | 14 +++++-------- .../ClarinWorkspaceItemRestRepositoryIT.java | 20 +++++++++---------- 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/ClarinLicenseResourceStep.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/ClarinLicenseResourceStep.java index b3e315af445..9568f13942c 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/ClarinLicenseResourceStep.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/ClarinLicenseResourceStep.java @@ -12,7 +12,6 @@ import com.fasterxml.jackson.databind.JsonNode; import org.apache.commons.collections4.CollectionUtils; -import org.apache.commons.lang3.StringUtils; import org.dspace.app.rest.exception.ClarinLicenseNotFoundException; import org.dspace.app.rest.exception.UnprocessableEntityException; import org.dspace.app.rest.model.patch.JsonValueEvaluator; @@ -87,14 +86,11 @@ public void doPatchProcessing(Context context, HttpServletRequest currentRequest "The operation '" + op.getOp() + "' is not supported for path " + path); } String licenseName = extractLicenseName(op); - // Section endpoint: a missing or blank license name is treated as a - // client error (422). The legacy `/license` path in - // WorkspaceItemRestRepository intentionally treats a blank value as - // "clear the current license" for backwards compatibility. - if (StringUtils.isBlank(licenseName)) { - throw new UnprocessableEntityException( - "The patch value for path " + path + " must contain a non-empty license name."); - } + // A blank/missing license name is intentionally treated as a request + // to clear the currently selected CLARIN license, mirroring the + // legacy `/license` path in WorkspaceItemRestRepository. + // {@link ClarinLicenseSubmissionUtils#applyLicense} handles a blank + // name as a "clear" operation. try { ClarinLicenseSubmissionUtils.applyLicense(context, source.getItem(), licenseName); } catch (ClarinLicenseNotFoundException ex) { diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinWorkspaceItemRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinWorkspaceItemRestRepositoryIT.java index af3b3e87be7..aae48128354 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinWorkspaceItemRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinWorkspaceItemRestRepositoryIT.java @@ -901,13 +901,12 @@ public void getWorkspaceItemReturnsDistinctLicenseSections() throws Exception { } /** - * PATCH on `/sections/clarin-license/select` with an empty value must be - * rejected as a client error (422): the section endpoint requires a - * non-empty license name. The previously selected license must remain - * untouched on the item and on its bitstreams. + * PATCH on `/sections/clarin-license/select` with an empty value must clear + * the previously selected license: `dc.rights*` metadata is removed and the + * license is detached from the uploaded bitstream. */ @Test - public void patchSelectWithEmptyValueFails() throws Exception { + public void patchSelectWithEmptyValueClearsLicense() throws Exception { context.turnOffAuthorisationSystem(); WorkspaceItem witem = createWorkspaceItemWithFile(); @@ -931,7 +930,7 @@ public void patchSelectWithEmptyValueFails() throws Exception { getClient(tokenAdmin).perform(get("/api/core/clarinlicenses/" + clarinLicense.getID())) .andExpect(jsonPath("$.bitstreams", is(1))); - // Now attempt to "clear" the selection with an empty value – this must fail + // Now clear the selection with an empty value ops.clear(); Map emptyValue = new HashMap(); emptyValue.put("value", ""); @@ -939,13 +938,12 @@ public void patchSelectWithEmptyValueFails() throws Exception { getClient(tokenAdmin).perform(patch("/api/submission/workspaceitems/" + witem.getID()) .content(getPatchContent(ops)) .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) - .andExpect(status().isUnprocessableEntity()); + .andExpect(status().isOk()); - // The previously selected license must still be present on the item - // and still attached to the uploaded bitstream. - assertClarinLicenseMetadata(witem, "dc", "rights", null, clarinLicenseName, false); + // Item metadata cleared and license detached from bitstream + assertClarinLicenseMetadata(witem, "dc", "rights", null, null, true); getClient(tokenAdmin).perform(get("/api/core/clarinlicenses/" + clarinLicense.getID())) - .andExpect(jsonPath("$.bitstreams", is(1))); + .andExpect(jsonPath("$.bitstreams", is(0))); } /**