From 02db4ea659c61ac95c9ba86b3070769b08ee4582 Mon Sep 17 00:00:00 2001 From: Oliver Guggenberger Date: Tue, 3 Sep 2024 09:30:21 +0200 Subject: [PATCH] GPU-1722: REST design and implementation, improvements, javadoc --- .../publish/protocol/PublishLogEntry.java | 93 ++++++++++++++----- .../protocol/PublishProtocolService.java | 52 +++++++++-- .../impl/PublishProtocolResourceImpl.java | 58 ++++++++++++ .../resources/contentnode_de_DE.properties | 2 + .../resources/contentnode_en_EN.properties | 2 + .../contentnode/rest/model/PublishLogDto.java | 15 +++ .../rest/model/response/GenericItemList.java | 12 +++ .../resource/PublishProtocolResource.java | 52 +++++++++++ 8 files changed, 254 insertions(+), 32 deletions(-) create mode 100644 cms-core/src/main/java/com/gentics/contentnode/rest/resource/impl/PublishProtocolResourceImpl.java create mode 100644 cms-restapi/src/main/java/com/gentics/contentnode/rest/model/PublishLogDto.java create mode 100644 cms-restapi/src/main/java/com/gentics/contentnode/rest/model/response/GenericItemList.java create mode 100644 cms-restapi/src/main/java/com/gentics/contentnode/rest/resource/PublishProtocolResource.java diff --git a/cms-core/src/main/java/com/gentics/contentnode/publish/protocol/PublishLogEntry.java b/cms-core/src/main/java/com/gentics/contentnode/publish/protocol/PublishLogEntry.java index 790d44660..9395adf00 100644 --- a/cms-core/src/main/java/com/gentics/contentnode/publish/protocol/PublishLogEntry.java +++ b/cms-core/src/main/java/com/gentics/contentnode/publish/protocol/PublishLogEntry.java @@ -2,10 +2,14 @@ import com.gentics.api.lib.exception.NodeException; import com.gentics.contentnode.db.DBUtils; +import java.time.LocalDateTime; import java.util.ArrayList; -import java.util.Date; import java.util.List; +import java.util.Optional; +/** + * Represents a log entry entity for the publish protocol. + */ public class PublishLogEntry { private int id; @@ -18,11 +22,20 @@ public class PublishLogEntry { private int user; - private Date date; + private LocalDateTime date; public PublishLogEntry() { } + /** + * Constructs a new PublishLogEntry with the specified object ID, type, status, and user. + * Used to persist entity to database. The id and date fields are auto generated. + * + * @param objId the object ID + * @param type the type of the object + * @param status the status of the publish operation + * @param user the user performing the operation + */ public PublishLogEntry(int objId, String type, int status, int user) { this.objId = objId; this.type = type; @@ -30,7 +43,17 @@ public PublishLogEntry(int objId, String type, int status, int user) { this.user = user; } - public PublishLogEntry(int id, int objId, String type, int status, int user, Date date) { + /** + * Constructs a new PublishLogEntry with the specified ID, object ID, type, status, user, and date. + * + * @param id the ID of the log entry + * @param objId the object ID + * @param type the type of the object + * @param status the status of the publish operation + * @param user the user performing the operation + * @param date the date of the log entry + */ + public PublishLogEntry(int id, int objId, String type, int status, int user, LocalDateTime date) { this.id = id; this.objId = objId; this.type = type; @@ -39,6 +62,12 @@ public PublishLogEntry(int id, int objId, String type, int status, int user, Dat this.date = date; } + + /** + * Saves the publish log entry to the database. + * + * @throws NodeException if an error occurs during the save operation + */ public void save() throws NodeException { DBUtils.executeInsert( "INSERT INTO publish_protocol (obj_id, type, state, user) VALUES (?, ?, ?, ?)", @@ -50,30 +79,50 @@ public void save() throws NodeException { }); } - public PublishLogEntry load(int id) throws NodeException { - return DBUtils.select("SELECT * FROM publish_protocol WHERE id = " + id, resultSet -> { - int objId = resultSet.getInt(1); - String type = resultSet.getString(2); - int state = resultSet.getInt(3); - int user = resultSet.getInt(4); - Date date = resultSet.getDate(5); - - return new PublishLogEntry(id, objId, type, state, user, date); + /** + * Loads a publish log entry by a specified field and value. + * + * @param fieldName the name of the field + * @param value the value of the field + * @return an Optional containing the publish log entry if found, otherwise empty + * @throws NodeException if an error occurs during the load operation + */ + public Optional loadByField(String fieldName, int value) throws NodeException { + var query = String.format("SELECT * FROM publish_protocol WHERE %s = %s ORDER BY id DESC", fieldName, value); + return DBUtils.select(query, resultSet -> { + if (resultSet.next()) { + var objId = resultSet.getInt("obj_id"); + var type = resultSet.getString("type"); + var state = resultSet.getBoolean("state") ? 1 : 0; + var user = resultSet.getInt("user"); + var timestamp = resultSet.getTimestamp("date"); + LocalDateTime dateTime = timestamp.toLocalDateTime(); + + return Optional.of(new PublishLogEntry(id, objId, type, state, user, dateTime)); + } + return Optional.empty(); }); } + /** + * Loads all publish log entries from the database. + * + * @return a list of publish log entries + * @throws NodeException if an error occurs during the load operation + */ public List loadAll() throws NodeException { return DBUtils.select("SELECT * FROM publish_protocol", resultSet -> { List entries = new ArrayList<>(); while (resultSet.next()) { - int id = resultSet.getInt("id"); - int objId = resultSet.getInt("obj_id"); - String type = resultSet.getString("type"); - int state = resultSet.getInt("state"); - int user = resultSet.getInt("user"); - Date date = resultSet.getDate("date"); - - entries.add(new PublishLogEntry(id, objId, type, state, user, date)); + var id = resultSet.getInt("id"); + var objId = resultSet.getInt("obj_id"); + var type = resultSet.getString("type"); + var state = resultSet.getInt("state"); + var user = resultSet.getInt("user"); + var timestamp = resultSet.getTimestamp("date"); + LocalDateTime dateTime = timestamp.toLocalDateTime(); + + entries.add(new PublishLogEntry(id, objId, type, state, user, dateTime)); } return entries; }); @@ -120,11 +169,11 @@ public void setUser(int user) { this.user = user; } - public Date getDate() { + public LocalDateTime getDate() { return date; } - public void setDate(Date date) { + public void setDate(LocalDateTime date) { this.date = date; } diff --git a/cms-core/src/main/java/com/gentics/contentnode/publish/protocol/PublishProtocolService.java b/cms-core/src/main/java/com/gentics/contentnode/publish/protocol/PublishProtocolService.java index 7a9ad3026..7b816fe1c 100644 --- a/cms-core/src/main/java/com/gentics/contentnode/publish/protocol/PublishProtocolService.java +++ b/cms-core/src/main/java/com/gentics/contentnode/publish/protocol/PublishProtocolService.java @@ -4,20 +4,29 @@ import static com.gentics.contentnode.object.Page.TYPE_PAGE; import com.gentics.api.lib.exception.NodeException; +import com.gentics.contentnode.i18n.I18NHelper; import com.gentics.contentnode.object.PublishableNodeObject; +import com.gentics.contentnode.rest.exceptions.EntityNotFoundException; import com.gentics.lib.log.NodeLogger; import java.util.ArrayList; import java.util.List; +/** + * Service for managing publish protocol entries. + */ public class PublishProtocolService { private static final NodeLogger logger = NodeLogger.getNodeLogger(PublishProtocolService.class); - - public PublishProtocolService() { - } - - + /** + * Logs the publish state of a given object. + * + * @param the type of the publishable node object + * @param object the publishable node object + * @param status the status of the publish operation + * @param user the user performing the operation + * @throws NodeException if an error occurs during logging + */ public void logPublishState(T object, int status, int user) throws NodeException { var publishLogEntry = new PublishLogEntry(object.getId(), getType(object.getTType()), status, @@ -29,21 +38,44 @@ public void logPublishState(T object, int stat publishLogEntry.save(); } - public PublishLogEntry getPublishLogEntries(int id) throws NodeException { - return new PublishLogEntry().load(id); + /** + * Retrieves a publish log entry by its object ID. + * + * @param objId the object ID of the publish log entry + * @return the publish log entry + * @throws NodeException if an error occurs during retrieval + * @throws EntityNotFoundException if the entry is not found + */ + public PublishLogEntry getPublishLogEntryByObjectId(int objId) throws NodeException { + return new PublishLogEntry().loadByField("obj_id", objId).orElseThrow( + () -> new EntityNotFoundException(I18NHelper.get("publish_protocol.entry_not_found", + String.valueOf(objId)))); } + + /** + * Retrieves all publish log entries. + * + * @return a list of publish log entries + * @throws NodeException if an error occurs during retrieval + */ public List getPublishLogEntries() throws NodeException { try { return new PublishLogEntry().loadAll(); - } - catch (Exception e) { + } catch (Exception e) { logger.error("Something went wrong while retrieving the publish protocol", e); return new ArrayList<>(); } } - public String getType(int ttype) { + + /** + * Gets the type of the publishable node object. + * + * @param ttype the type code of the publishable node object + * @return the type as a string + */ + private String getType(int ttype) { return switch (ttype) { case TYPE_PAGE -> PublishType.PAGE.toString(); case TYPE_FORM -> PublishType.FORM.toString(); diff --git a/cms-core/src/main/java/com/gentics/contentnode/rest/resource/impl/PublishProtocolResourceImpl.java b/cms-core/src/main/java/com/gentics/contentnode/rest/resource/impl/PublishProtocolResourceImpl.java new file mode 100644 index 000000000..2866f2491 --- /dev/null +++ b/cms-core/src/main/java/com/gentics/contentnode/rest/resource/impl/PublishProtocolResourceImpl.java @@ -0,0 +1,58 @@ +package com.gentics.contentnode.rest.resource.impl; + +import static com.gentics.contentnode.factory.Trx.supply; + +import com.gentics.api.lib.exception.NodeException; +import com.gentics.contentnode.etc.ContentNodeHelper; +import com.gentics.contentnode.etc.Function; +import com.gentics.contentnode.factory.Trx; +import com.gentics.contentnode.publish.protocol.PublishLogEntry; +import com.gentics.contentnode.publish.protocol.PublishProtocolService; +import com.gentics.contentnode.rest.model.PublishLogDto; +import com.gentics.contentnode.rest.model.response.GenericItemList; +import com.gentics.contentnode.rest.resource.PublishProtocolResource; +import com.gentics.contentnode.rest.resource.parameter.PagingParameterBean; +import com.gentics.contentnode.rest.util.ListBuilder; +import javax.ws.rs.BeanParam; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +@Produces({MediaType.APPLICATION_JSON}) +@Path("/publish/state") +public class PublishProtocolResourceImpl implements PublishProtocolResource { + + private final PublishProtocolService publishProtocolService = new PublishProtocolService(); + + private final Function MAP2REST = (publishLogEntry) -> new PublishLogDto( + publishLogEntry.getObjId(), + publishLogEntry.getType(), + publishLogEntry.getState() == 1 ? "ONLINE" : "OFFLINE", + publishLogEntry.getUser(), + publishLogEntry.getDate().toString() + ); + + @Override + @GET + @Path("/{objId}") + public PublishLogDto get(@PathParam("objId") Integer objId) throws NodeException { + try (Trx trx = ContentNodeHelper.trx()) { + return MAP2REST.apply(this.publishProtocolService.getPublishLogEntryByObjectId(objId)); + } + } + + @Override + @GET + @Path("/") + public GenericItemList list( + @BeanParam PagingParameterBean paging) throws NodeException { + var publishLogEntries = supply(publishProtocolService::getPublishLogEntries); + + return ListBuilder.from(publishLogEntries, MAP2REST) + .page(paging) + .to(new GenericItemList<>()); + } + +} diff --git a/cms-core/src/main/resources/contentnode_de_DE.properties b/cms-core/src/main/resources/contentnode_de_DE.properties index 368948c68..5da3c71a7 100644 --- a/cms-core/src/main/resources/contentnode_de_DE.properties +++ b/cms-core/src/main/resources/contentnode_de_DE.properties @@ -9,6 +9,7 @@ actions=Aktionen log=Protokoll rest.general.error=Es ist ein Fehler beim Zugriff auf das Backend System aufgetreten. rest.invalid.json.error=Das Format des Request Bodies war inkorrekt. +rest.invalid.request=Ungültige Anfrage rest.incorrect.body.error=Dem Aufruf wurde ein falsches Objekt \u00FCbergeben. rest.feature.required=Das erforderliche Feature {0} ist nicht aktiviert. rest.permission.required=Sie haben unzureichende Berechtigungen f\u00FCr diese Anfrage. @@ -1139,3 +1140,4 @@ feature.asset_management=Asset Management feature.asset_management.help=Im Node k\u00F6nnen Bilder und Dateien aus externen Asset Management Systemen \u00FCbernommen werden. feature.webp_conversion=Automatische WEBP-Konvertierung feature.webp_conversion.help=Hochgeladene Bilder werden automatisch in das WEBP-Format konvertiert. Scheduler-Task "Convert Images" konvertiert bestehende Bilder in Nodes die das Feature aktiviert haben. +publish_protocol.entry_not_found=Publish Log Eintrag mit ID {0} wurde nicht gefunden. diff --git a/cms-core/src/main/resources/contentnode_en_EN.properties b/cms-core/src/main/resources/contentnode_en_EN.properties index f3867f2fa..a09921969 100644 --- a/cms-core/src/main/resources/contentnode_en_EN.properties +++ b/cms-core/src/main/resources/contentnode_en_EN.properties @@ -10,6 +10,7 @@ log=Log rest.general.error=An error occurred while accessing the backend system. rest.invalid.json.error=The format of the request body was incorrect. rest.incorrect.body.error=The request body contained an incorrect object. +rest.invalid.request=Invalid request. rest.feature.required=The required feature {0} is not activated. rest.permission.required=You have insufficient permissions for this request. validation.policies.anycontent=any content @@ -1118,3 +1119,4 @@ feature.asset_management=Asset Management feature.asset_management.help=It is possible to include images and files from external Asset Management Systems. feature.webp_conversion=Automatic WEBP conversion feature.webp_conversion.help=Uploaded images will be automatically converted to WEBP format. Scheduler task "Convert Images" will convert existing images in nodes which have the feature enabled. +publish_protocol.entry_not_found=Publish Log entry with ID {0} not found. diff --git a/cms-restapi/src/main/java/com/gentics/contentnode/rest/model/PublishLogDto.java b/cms-restapi/src/main/java/com/gentics/contentnode/rest/model/PublishLogDto.java new file mode 100644 index 000000000..a0fe08b3c --- /dev/null +++ b/cms-restapi/src/main/java/com/gentics/contentnode/rest/model/PublishLogDto.java @@ -0,0 +1,15 @@ +package com.gentics.contentnode.rest.model; + +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement +public record PublishLogDto( + int objId, + String type, + String state, + int user, + String date +) { + +} + diff --git a/cms-restapi/src/main/java/com/gentics/contentnode/rest/model/response/GenericItemList.java b/cms-restapi/src/main/java/com/gentics/contentnode/rest/model/response/GenericItemList.java new file mode 100644 index 000000000..dc9141381 --- /dev/null +++ b/cms-restapi/src/main/java/com/gentics/contentnode/rest/model/response/GenericItemList.java @@ -0,0 +1,12 @@ +package com.gentics.contentnode.rest.model.response; + +import java.io.Serial; +import javax.xml.bind.annotation.XmlRootElement; + + +@XmlRootElement +public class GenericItemList extends AbstractListResponse { + + @Serial + private static final long serialVersionUID = -5713150794537109477L; +} diff --git a/cms-restapi/src/main/java/com/gentics/contentnode/rest/resource/PublishProtocolResource.java b/cms-restapi/src/main/java/com/gentics/contentnode/rest/resource/PublishProtocolResource.java new file mode 100644 index 000000000..afa2d36f2 --- /dev/null +++ b/cms-restapi/src/main/java/com/gentics/contentnode/rest/resource/PublishProtocolResource.java @@ -0,0 +1,52 @@ +package com.gentics.contentnode.rest.resource; + + +import com.gentics.contentnode.rest.model.PublishLogDto; +import com.gentics.contentnode.rest.model.response.GenericItemList; +import com.gentics.contentnode.rest.resource.parameter.PagingParameterBean; +import com.webcohesion.enunciate.metadata.rs.ResponseCode; +import com.webcohesion.enunciate.metadata.rs.StatusCodes; +import javax.ws.rs.BeanParam; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; + +/** + * Interface for managing publish protocol resources. + */ +@Path("/publish/state") +public interface PublishProtocolResource { + + + /** + * Retrieves a publish protocol entry by its object ID. + * + * @param objId the object ID of the publish protocol entry + * @return the publish protocol entry + * @throws Exception if an error occurs during retrieval + */ + @GET + @Path("/{objId}") + @StatusCodes({ + @ResponseCode(code = 200, condition = "Publish protocol entry is returned."), + @ResponseCode(code = 404, condition = "Not found") + + }) + PublishLogDto get(@PathParam("objId") Integer objId) throws Exception; + + /** + * Retrieves a list of publish protocol entries with pagination. + * + * @param paging the paging parameters + * @return a list of publish protocol entries + * @throws Exception if an error occurs during retrieval + */ + @GET + @Path("/") + @StatusCodes({ + @ResponseCode(code = 200, condition = "Publish protocol list is returned.") + }) + GenericItemList list( + @BeanParam PagingParameterBean paging) throws Exception; + +}