Allow Dataset Creation API to take Template ID - #12405
Conversation
This comment has been minimized.
This comment has been minimized.
1 similar comment
This comment has been minimized.
This comment has been minimized.
5b23c8f to
ce7a795
Compare
This comment has been minimized.
This comment has been minimized.
1 similar comment
This comment has been minimized.
This comment has been minimized.
ce7a795 to
e8f7f1f
Compare
This comment has been minimized.
This comment has been minimized.
|
@stevenwinship to support the SPA, we also need the Get Dataset API to return the template_id, can you add that to this PR? Or would it be better to have a separate issue? |
Update from Steve, this is already in the current PR. thanks! |
e8f7f1f to
665d78c
Compare
This comment has been minimized.
This comment has been minimized.
665d78c to
99a1d0e
Compare
This comment has been minimized.
This comment has been minimized.
57b5e40 to
409d37d
Compare
This comment has been minimized.
This comment has been minimized.
409d37d to
02485c3
Compare
This comment has been minimized.
This comment has been minimized.
02485c3 to
ca86a75
Compare
This comment has been minimized.
This comment has been minimized.
ca86a75 to
5d521c1
Compare
This comment has been minimized.
This comment has been minimized.
5d521c1 to
d1c2b58
Compare
This comment has been minimized.
This comment has been minimized.
d1c2b58 to
b3a5be4
Compare
This comment has been minimized.
This comment has been minimized.
b3a5be4 to
ec3d937
Compare
This comment has been minimized.
This comment has been minimized.
ec3d937 to
c0812ca
Compare
This comment has been minimized.
This comment has been minimized.
c0812ca to
0c2bdbc
Compare
This comment has been minimized.
This comment has been minimized.
0c2bdbc to
48a1ed8
Compare
|
📦 Pushed preview images as 🚢 See on GHCR. Use by referencing with full name as printed above, mind the registry name. |
rtreacy
left a comment
There was a problem hiding this comment.
Claude
PR #12405 Review: Allow Dataset Creation API to take Template ID
What it does
Adds support for "templateId": $templateId in the POST /dataverses/{identifier}/datasets JSON payload. JsonParser.parseDataset() now looks up the template and calls dataset.setTemplate(template); JsonPrinter echoes templateId back in dataset JSON; and CreateNewDatasetCommand's existing (Dataset, DataverseRequest, Template, boolean) constructor is tweaked to fall back to theDataset.getTemplate() when no Template object is passed explicitly — which is how the value set by the JSON parser reaches the command that bumps the template's usage count. JsonParser's constructor gained a TemplateServiceBean dependency, which required updating every call site (production and test) — hence the size of the diff.
Correctness issues
- Missing dataverse scoping on the template lookup (authorization gap). templateService.find(templateId) (JsonParser.java:292) looks up any template in the system by ID, with no check that it belongs to the target dataverse (owner) or one of its ancestors. Contrast with the UI flow (DatasetPage.java:2247), which only offers templates from dataverseService.find(ownerId).getTemplates(). Via this API, a user with AddDataset permission on dataverse A can supply the numeric ID of a template that lives in an unrelated, possibly private/restricted dataverse B, and:
-
dataset.setTemplate(template) persists that cross-dataverse association and is echoed back as templateId in the dataset JSON.
-
CreateNewDatasetCommand.postPersist calls ctxt.templates().incrementUsageCount(template.getId()) unconditionally on that foreign template.
The practical impact is limited — in this API flow getVersionToPersist() still calls the no-arg theDataset.getOrCreateEditVersion(), so the template's field defaults are never applied to the new dataset (unlike the UI path); the effect is essentially an ID-based association plus a usage-count bump on a template the caller may have no visibility into. Still, it's a real scoping gap versus the UI's behavior and worth validating (template.getDataverse() is an ancestor of owner, or reject with 400) before merging.
-
Non-existent templateId fails silently. templateService.find() returns null for an unknown ID (thin wrapper over em.find), and the code does nothing to handle that — dataset.setTemplate(null) is a no-op. Every other invalid-value case in this same method throws a JsonParseException (see the Invalid dataset type: ... branch immediately above this new code, JsonParser.java:288). A typo'd or stale templateId should produce a clear 400 error ("Invalid templateId: " + templateId), not silently create the dataset without the requested template.
-
obj.getInt("templateId", 0) can throw an uncaught ClassCastException. If a caller sends "templateId": "5" (string instead of number), JsonObject.getInt throws ClassCastException, not JsonParseException. The only wrapper at the call site (Dataverses.parseDataset, line ~779) catches JsonParsingException | JsonParseException — a ClassCastException would propagate as an unhandled 500 instead of a clean 400. Worth wrapping the read (or catching/rethrowing as JsonParseException) for consistency with how the rest of this method reports bad input.
Test coverage
- The new DataversesIT test covers the happy path well (create with a valid templateId, verify it round-trips via GET, clean up).
- No test for an invalid/non-existent templateId (would currently reveal issue #2 — silently succeeds instead of erroring).
- No test asserting that a templateId from an unrelated dataverse is rejected (would currently reveal issue #1 — silently succeeds).
- Since neither is currently rejected, existing tests can't catch these gaps; adding negative-path tests here would both document intended behavior and force the scoping/error-handling to be decided explicitly.
Style / minor
- Several files unrelated in substance to the feature (ImportGenericServiceBean.java, ImportServiceBean.java, SchemaDotOrgExporterTest.java, FeedbackUtilTest.java, DataversesIT.java) show large import-block reshuffles/wildcards (import edu.harvard.iq.dataverse.;, jakarta.ejb.;, java.util.*;) alongside the one-line change they actually needed (adding a TemplateServiceBean mock/field). This looks like an IDE "organize imports" pass swept up in the commit — harmless (the project's style guide explicitly says wildcard imports are "neither encouraged nor discouraged," and checkstyle's AvoidStarImport is disabled), but it's pure churn that bloats the diff and increases merge-conflict risk for unrelated in-flight PRs touching these same files. Worth trimming to just the needed changes if easy to do, though not blocking.
- Long.valueOf(templateId) where templateId is declared int templateId = obj.getInt(...) is a bit roundabout (auto-boxing an int into a Long via valueOf rather than just widening) — cosmetic only.
Verdict
The core wiring (constructor threading, JSON round-trip) is correct and the happy-path test passes. Before merging, I'd want: (1) a decision on whether templateId should be scoped to the target dataverse's own/inherited templates, since that's the existing UI behavior and its absence is the main risk here, and (2) explicit error handling for an unknown templateId instead of silent success, matching the pattern used for every other invalid field in this method.
What this PR does / why we need it: Users creating Datasets need to be able to set the Template via the Create API
Which issue(s) this PR closes:#12132
Special notes for your reviewer:
Suggestions on how to test this:
Does this PR introduce a user interface change? If mockups are available, please link/include them here:
Is there a release notes update needed for this change?:
Additional documentation: