Create new endpoint to save style editor schema per content type#36070
Create new endpoint to save style editor schema per content type#36070dario-daza wants to merge 9 commits into
Conversation
|
Claude finished @dario-daza's task in 1m 46s —— View job Code Review
Findings1. if (!UtilMethods.isSet(idOrVar)) {
return Response.status(Response.Status.NOT_FOUND).build();
}JAX-RS path parameters are never 2. 3. 4. Frontend: this.#http.patch<DotCMSResponse>(`v1/contenttype/id/${contentType.id}/metadata`, metadataPatch)
5. Integration test hardcodes admin credentials "Basic " + Base64.getEncoder().encodeToString("admin@dotcms.com:admin".getBytes())This pattern matches many other tests in the project, so it is not unique to this PR, but it is worth flagging: if the integration environment ever changes the default admin password this test silently authenticates as unauthenticated (returns 401 instead of 403/404, masking the actual assertion failures). Consider using the 6. Empty-patch early return re-loads and returns the content type unnecessarily Logger.warn(this, "No metadata patch found for " + idOrVar);
final Map<String, Object> responseMap = new HashMap<>(
contentTypeHelper.contentTypeToMap(existing, user)
);
return Response.ok(new ResponseEntityContentTypeDetailView(responseMap)).build();An WorkflowResource cleanup ( Rollback risk (already noted in the prior comment): the frontend switches to the new endpoint in the same PR, so a backend-only rollback leaves the N-frontend calling a 404 path. The old Overall the implementation is clean and well-tested. The issues above are low-risk: #2 (missing constant) is the most likely to cause a future silent bug. |
…or-schema-per-content-type
|
Pull Request Unsafe to Rollback!!!
|
…or-schema-per-content-type
…or-schema-per-content-type
| if (!UtilMethods.isSet(idOrVar)) { | ||
| return Response.status(Response.Status.NOT_FOUND).build(); | ||
| } | ||
| final ContentType existing = contentTypeAPI.find(idOrVar); |
There was a problem hiding this comment.
Race condition: lost update on metadata PATCH
Unguarded read-modify-write: reads existing.metadata(), merges in memory, and calls save(). Metadata is a single JSON column that save() rewrites whole — no optimistic locking. Two concurrent PATCHes on the same Content Type clobber each other (last write wins, a key is silently lost).
Recommendation: move the read-merge-save (and the DOT_STYLE_EDITOR_SCHEMA normalization) out of the REST layer into contentTypeHelper, and serialize it with the striped lock already used in the codebase (DotConcurrentFactory.getInstance().getIdentifierStripedLock(), same as ESContentletAPIImpl checkin), re-reading inside the lock:
return lockManager.tryLock("ct-metadata-" + existing.id(), () -> {
final ContentType current = contentTypeAPI.find(idOrVar); // re-read inside the lock
final Map<String, Object> merged = new HashMap<>(
current.metadata() != null ? current.metadata() : Map.of());
patch.forEach((k, v) -> { if (v == null) merged.remove(k); else merged.put(k, v); });
return contentTypeAPI.save(ContentTypeBuilder.builder(current).metadata(merged).build());
});Note: the striped lock is JVM-local. Fine for low-frequency metadata writes; if multi-node safety is needed, follow up (separate PR) with optimistic locking via modDate or a DB-level atomic JSON merge.
Proposed Changes
Create the new
PATCH /v1/contenttype/id/{idOrVar}/metadataendpoint to avoid editing other Content Type information when creating and saving a Style Editor Schema for a Content Type.BE
PATCH /v1/contenttype/id/{idOrVar}/metadataendpoint to handle the metadata of the Content Types, specifically theDOT_STYLE_EDITOR_SCHEMAthat is related to the Style Editor Schema.FE
DotCrudServicewithhttp.This PR fixes: #35781
This PR fixes: #35979