From 22359e51d8092b4e08d251c8f2d07113de0db572 Mon Sep 17 00:00:00 2001 From: Robert von Burg Date: Tue, 27 Feb 2024 16:44:53 +0100 Subject: [PATCH] [New] Added CRUD services for notifications --- .../li/strolch/runtime/StrolchConstants.java | 2 + .../strolch/model/StrolchModelConstants.java | 3 + .../CreateNotificationService.java | 104 +++++++++++++++ .../RemoveNotificationService.java | 35 +++++ .../UpdateNotificationService.java | 46 +++++++ .../strolch/rest/StrolchRestfulConstants.java | 2 - .../rest/endpoint/NotificationResource.java | 126 ++++++++++++++++-- 7 files changed, 303 insertions(+), 15 deletions(-) create mode 100644 service/src/main/java/li/strolch/service/notifications/CreateNotificationService.java create mode 100644 service/src/main/java/li/strolch/service/notifications/RemoveNotificationService.java create mode 100644 service/src/main/java/li/strolch/service/notifications/UpdateNotificationService.java diff --git a/agent/src/main/java/li/strolch/runtime/StrolchConstants.java b/agent/src/main/java/li/strolch/runtime/StrolchConstants.java index 2e34386c8..d0f809be6 100644 --- a/agent/src/main/java/li/strolch/runtime/StrolchConstants.java +++ b/agent/src/main/java/li/strolch/runtime/StrolchConstants.java @@ -97,5 +97,7 @@ public class StrolchConstants extends StrolchModelConstants { public static final String PRIVILEGE_REMOVE_PREFIX = "Remove"; public static final String PRIVILEGE_GET_PREFIX = "Get"; public static final String PRIVILEGE_GET_NOTIFICATIONS = "GetNotifications"; + public static final String PRIVILEGE_GET_NOTIFICATION = "GetNotification"; + public static final String PRIVILEGE_GET_NOTIFICATIONS_ALL = "GetNotificationsAll"; } } diff --git a/model/src/main/java/li/strolch/model/StrolchModelConstants.java b/model/src/main/java/li/strolch/model/StrolchModelConstants.java index 2ba7a1cd8..88e17d440 100644 --- a/model/src/main/java/li/strolch/model/StrolchModelConstants.java +++ b/model/src/main/java/li/strolch/model/StrolchModelConstants.java @@ -105,9 +105,12 @@ public class StrolchModelConstants { public static final String PARAM_GROUP = "group"; public static final String PARAM_VISIBLE_FROM = "visibleFrom"; public static final String PARAM_VISIBLE_TO = "visibleTo"; + public static final String PARAM_TITLE = "title"; + public static final String PARAM_TEXT = "text"; public static final String PARAM_FOR_ALL = "forAll"; public static final String PARAM_ROLES = "roles"; public static final String PARAM_LOCATIONS = "locations"; + public static final String PARAM_LANGUAGES = "languages"; public static final String PARAM_GROUPS = "groups"; public static class PolicyConstants { diff --git a/service/src/main/java/li/strolch/service/notifications/CreateNotificationService.java b/service/src/main/java/li/strolch/service/notifications/CreateNotificationService.java new file mode 100644 index 000000000..c3bf6f251 --- /dev/null +++ b/service/src/main/java/li/strolch/service/notifications/CreateNotificationService.java @@ -0,0 +1,104 @@ +package li.strolch.service.notifications; + +import com.google.gson.JsonObject; +import li.strolch.agent.api.StrolchAgent; +import li.strolch.model.ParameterBag; +import li.strolch.model.Resource; +import li.strolch.model.builder.ResourceBuilder; +import li.strolch.model.json.FromFlatJsonVisitor; +import li.strolch.persistence.api.StrolchTransaction; +import li.strolch.runtime.configuration.SupportedLanguage; +import li.strolch.service.JsonServiceArgument; +import li.strolch.service.api.AbstractService; +import li.strolch.service.api.ServiceResult; +import li.strolch.utils.dbc.DBC; + +import java.util.Set; +import java.util.stream.Collectors; + +import static li.strolch.model.StrolchModelConstants.*; + +public class CreateNotificationService extends AbstractService { + @Override + protected ServiceResult getResultInstance() { + return new ServiceResult(); + } + + @Override + public JsonServiceArgument getArgumentInstance() { + return new JsonServiceArgument(); + } + + @Override + protected ServiceResult internalDoService(JsonServiceArgument arg) throws Exception { + DBC.PRE.assertNotNull("JsonElement must be set", arg.jsonElement); + DBC.PRE.assertNotNull("JsonElement must be a JsonObject", arg.jsonElement.isJsonObject()); + + JsonObject jsonObject = arg.jsonElement.getAsJsonObject(); + Resource notification = buildNotification(jsonObject, getSupportedLanguages(getAgent())); + + try (StrolchTransaction tx = openArgOrUserTx(arg)) { + tx.add(notification); + tx.commitOnClose(); + } + + return ServiceResult.success(); + } + + protected static Resource buildNotification(JsonObject jsonObject, Set supportedLanguages) { + DBC.PRE.assertTrue("JsonObject must have languages!", jsonObject.has(PARAM_LANGUAGES)); + JsonObject languagesJ = jsonObject.get(PARAM_LANGUAGES).getAsJsonObject(); + DBC.PRE.assertNotEmpty("JsonObject must have at least one languages!", languagesJ.keySet()); + + FromFlatJsonVisitor visitor = new FromFlatJsonVisitor(jsonObject); + visitor.optionalParameter(PARAM_ROLES); + visitor.optionalParameter(PARAM_LOCATIONS); + visitor.optionalParameter(PARAM_FOR_ALL); + Resource notification = newNotification(); + notification.accept(visitor); + + for (String language : languagesJ.keySet()) { + if (!supportedLanguages.contains(language)) + throw new IllegalArgumentException("The agent doesn't support language " + language); + JsonObject languageJ = languagesJ.get(language).getAsJsonObject(); + String title = languageJ.get(PARAM_TITLE).getAsString(); + String text = languageJ.get(PARAM_TEXT).getAsString(); + + ParameterBag languageBag = new ParameterBag(language, language, TYPE_TEXT); + languageBag.setString(PARAM_TITLE, title); + languageBag.setString(PARAM_TEXT, text); + notification.addParameterBag(languageBag); + } + return notification; + } + + protected static Set getSupportedLanguages(StrolchAgent agent) { + return agent + .getStrolchConfiguration() + .getRuntimeConfiguration() + .getSupportedLanguages() + .stream() + .map(SupportedLanguage::locale) + .collect(Collectors.toSet()); + } + + protected static Resource newNotification() { + ResourceBuilder notificationBuilder = new ResourceBuilder(TYPE_NOTIFICATION, TYPE_NOTIFICATION) + + .bag(BAG_VISIBILITY, TYPE_VISIBILITY) + + .date(PARAM_VISIBLE_FROM).end() + + .date(PARAM_VISIBLE_TO).end() + + .booleanB(PARAM_FOR_ALL).end() + + .stringList(PARAM_ROLES).end() + + .stringList(PARAM_LOCATIONS).end() + + .endBag(); + + return notificationBuilder.build(); + } +} diff --git a/service/src/main/java/li/strolch/service/notifications/RemoveNotificationService.java b/service/src/main/java/li/strolch/service/notifications/RemoveNotificationService.java new file mode 100644 index 000000000..342ec7de7 --- /dev/null +++ b/service/src/main/java/li/strolch/service/notifications/RemoveNotificationService.java @@ -0,0 +1,35 @@ +package li.strolch.service.notifications; + +import li.strolch.model.Resource; +import li.strolch.persistence.api.StrolchTransaction; +import li.strolch.service.StringArgument; +import li.strolch.service.api.AbstractService; +import li.strolch.service.api.ServiceResult; +import li.strolch.utils.dbc.DBC; + +import static li.strolch.model.StrolchModelConstants.TYPE_NOTIFICATION; + +public class RemoveNotificationService extends AbstractService { + @Override + protected ServiceResult getResultInstance() { + return new ServiceResult(); + } + + @Override + public StringArgument getArgumentInstance() { + return new StringArgument(); + } + + @Override + protected ServiceResult internalDoService(StringArgument arg) throws Exception { + DBC.PRE.assertNotEmpty("value must be set", arg.value); + + try (StrolchTransaction tx = openArgOrUserTx(arg)) { + Resource notification = tx.getResourceBy(TYPE_NOTIFICATION, arg.value, true); + tx.remove(notification); + tx.commitOnClose(); + } + + return ServiceResult.success(); + } +} diff --git a/service/src/main/java/li/strolch/service/notifications/UpdateNotificationService.java b/service/src/main/java/li/strolch/service/notifications/UpdateNotificationService.java new file mode 100644 index 000000000..a4dd894b6 --- /dev/null +++ b/service/src/main/java/li/strolch/service/notifications/UpdateNotificationService.java @@ -0,0 +1,46 @@ +package li.strolch.service.notifications; + +import com.google.gson.JsonObject; +import li.strolch.model.Resource; +import li.strolch.model.Tags; +import li.strolch.persistence.api.StrolchTransaction; +import li.strolch.service.JsonServiceArgument; +import li.strolch.service.api.AbstractService; +import li.strolch.service.api.ServiceResult; +import li.strolch.utils.dbc.DBC; + +import static li.strolch.service.notifications.CreateNotificationService.buildNotification; +import static li.strolch.service.notifications.CreateNotificationService.getSupportedLanguages; + +public class UpdateNotificationService extends AbstractService { + @Override + protected ServiceResult getResultInstance() { + return new ServiceResult(); + } + + @Override + public JsonServiceArgument getArgumentInstance() { + return new JsonServiceArgument(); + } + + @Override + protected ServiceResult internalDoService(JsonServiceArgument arg) throws Exception { + DBC.PRE.assertNotEmpty("objectId must be set", arg.objectId); + DBC.PRE.assertNotNull("JsonElement must be set", arg.jsonElement); + DBC.PRE.assertNotNull("JsonElement must be a JsonObject", arg.jsonElement.isJsonObject()); + + JsonObject jsonObject = arg.jsonElement.getAsJsonObject(); + DBC.PRE.assertEquals("arg ID and jsonObject ID must be the same", arg.objectId, + jsonObject.get(Tags.Json.ID).getAsString()); + + Resource notification = buildNotification(jsonObject, getSupportedLanguages(getAgent())); + notification.setId(arg.objectId); + + try (StrolchTransaction tx = openArgOrUserTx(arg)) { + tx.update(notification); + tx.commitOnClose(); + } + + return ServiceResult.success(); + } +} diff --git a/web-rest/src/main/java/li/strolch/rest/StrolchRestfulConstants.java b/web-rest/src/main/java/li/strolch/rest/StrolchRestfulConstants.java index 0c7c3f940..b32e2460b 100644 --- a/web-rest/src/main/java/li/strolch/rest/StrolchRestfulConstants.java +++ b/web-rest/src/main/java/li/strolch/rest/StrolchRestfulConstants.java @@ -48,8 +48,6 @@ public class StrolchRestfulConstants { public static final String PARAM_TO = "to"; public static final String PARAM_FILTER = "filter"; public static final String PARAM_QUERY = "query"; - public static final String PARAM_TITLE = "title"; - public static final String PARAM_TEXT = "text"; public static final MediaType TEXT_CSV_TYPE = new MediaType("text", "csv"); public static final String TEXT_CSV = "text/csv"; diff --git a/web-rest/src/main/java/li/strolch/rest/endpoint/NotificationResource.java b/web-rest/src/main/java/li/strolch/rest/endpoint/NotificationResource.java index 6a3775811..3630bb1d8 100644 --- a/web-rest/src/main/java/li/strolch/rest/endpoint/NotificationResource.java +++ b/web-rest/src/main/java/li/strolch/rest/endpoint/NotificationResource.java @@ -15,23 +15,31 @@ */ package li.strolch.rest.endpoint; -import com.google.gson.JsonArray; import com.google.gson.JsonObject; +import com.google.gson.JsonParser; import jakarta.servlet.http.HttpServletRequest; -import jakarta.ws.rs.GET; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.Produces; +import jakarta.ws.rs.*; import jakarta.ws.rs.core.Context; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; import li.strolch.agent.api.StrolchAgent; import li.strolch.model.ParameterBag; import li.strolch.model.Resource; +import li.strolch.model.Tags; +import li.strolch.model.json.StrolchRootElementToJsonVisitor; +import li.strolch.persistence.api.Operation; import li.strolch.persistence.api.StrolchTransaction; import li.strolch.policy.notifications.NotificationsPolicy; import li.strolch.privilege.model.Certificate; import li.strolch.rest.RestfulStrolchComponent; import li.strolch.rest.helper.ResponseUtil; +import li.strolch.service.JsonServiceArgument; +import li.strolch.service.StringArgument; +import li.strolch.service.api.ServiceHandler; +import li.strolch.service.api.ServiceResult; +import li.strolch.service.notifications.CreateNotificationService; +import li.strolch.service.notifications.RemoveNotificationService; +import li.strolch.service.notifications.UpdateNotificationService; import li.strolch.utils.helper.StringHelper; import li.strolch.utils.iso8601.ISO8601; import org.slf4j.Logger; @@ -44,10 +52,12 @@ import java.util.function.Function; import static java.util.Optional.ofNullable; import static li.strolch.model.StrolchModelConstants.*; -import static li.strolch.privilege.base.PrivilegeConstants.REALM; -import static li.strolch.rest.StrolchRestfulConstants.*; +import static li.strolch.rest.RestfulStrolchComponent.getInstance; +import static li.strolch.rest.StrolchRestfulConstants.DATA; +import static li.strolch.rest.StrolchRestfulConstants.STROLCH_CERTIFICATE; +import static li.strolch.rest.helper.ResponseUtil.*; import static li.strolch.runtime.StrolchConstants.DEFAULT_REALM; -import static li.strolch.runtime.StrolchConstants.StrolchPrivilegeConstants.PRIVILEGE_GET_NOTIFICATIONS; +import static li.strolch.runtime.StrolchConstants.StrolchPrivilegeConstants.*; import static li.strolch.utils.helper.ExceptionHelper.getCallerMethod; import static li.strolch.utils.helper.ExceptionHelper.getCallerMethodNoClass; @@ -66,30 +76,60 @@ public class NotificationResource { return RestfulStrolchComponent.getInstance().openTx(certificate, realm, getCallerMethod()); } - private static Certificate validateCertificate(HttpServletRequest request) { + private static Certificate validateCertificate(HttpServletRequest request, String privilege) { Certificate cert = (Certificate) request.getAttribute(STROLCH_CERTIFICATE); RestfulStrolchComponent rest = RestfulStrolchComponent.getInstance(); - rest.validate(cert).validateAction(PRIVILEGE_GET_NOTIFICATIONS, getCallerMethodNoClass(2)); + rest.validate(cert).validateAction(privilege, getCallerMethodNoClass(2)); return cert; } @GET @Produces(MediaType.APPLICATION_JSON) - public Response getNotifications(@Context HttpServletRequest request) { - Certificate cert = validateCertificate(request); + public Response getUserNotifications(@Context HttpServletRequest request) { + Certificate cert = validateCertificate(request, PRIVILEGE_GET_NOTIFICATIONS); try (StrolchTransaction tx = openTx(cert)) { List notifications = NotificationsPolicy.getDefaultPolicy(tx).findUserNotifications(); Function visitor = notificationToJson(tx.getAgent(), cert); - return ResponseUtil.toResponse(DATA, notifications.stream().map(visitor).filter(Objects::nonNull).toList()); + return toResponse(DATA, notifications.stream().map(visitor).filter(Objects::nonNull).toList()); } catch (Exception e) { logger.error(e.getMessage(), e); - return ResponseUtil.toResponse(e); + return toResponse(e); + } + } + + @GET + @Path("{id}") + @Produces(MediaType.APPLICATION_JSON) + public Response getNotification(@Context HttpServletRequest request, @PathParam("id") String id) { + Certificate cert = validateCertificate(request, PRIVILEGE_GET_NOTIFICATION); + try (StrolchTransaction tx = openTx(cert)) { + Resource notification = tx.getResourceBy(TYPE_NOTIFICATION, id, true); + tx.assertHasPrivilege(Operation.GET, notification); + return toResponse(DATA, notificationToJson(tx.getAgent(), cert).apply(notification)); + } catch (Exception e) { + logger.error(e.getMessage(), e); + return toResponse(e); + } + } + + @GET + @Path("all") + @Produces(MediaType.APPLICATION_JSON) + public Response getAllNotifications(@Context HttpServletRequest request) { + Certificate cert = validateCertificate(request, PRIVILEGE_GET_NOTIFICATIONS_ALL); + try (StrolchTransaction tx = openTx(cert)) { + StrolchRootElementToJsonVisitor visitor = new StrolchRootElementToJsonVisitor().flatBags(); + return toResponse(DATA, tx.streamResources(TYPE_NOTIFICATION).map(a -> a.accept(visitor)).toList()); + } catch (Exception e) { + logger.error(e.getMessage(), e); + return toResponse(e); } } private static Function notificationToJson(StrolchAgent agent, Certificate cert) { return notification -> { JsonObject notificationJ = new JsonObject(); + notificationJ.addProperty(Tags.Json.ID, notification.getId()); String lang = cert.getLocale().getLanguage(); Optional textBagO = ofNullable(notification.getParameterBag(lang)) @@ -109,4 +149,64 @@ public class NotificationResource { return notificationJ; }; } + + @POST + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response create(@Context HttpServletRequest request, String data) { + Certificate cert = (Certificate) request.getAttribute(STROLCH_CERTIFICATE); + + // get JSON + JsonObject jsonObject = JsonParser.parseString(data).getAsJsonObject(); + + // create service and argument + CreateNotificationService svc = new CreateNotificationService(); + ServiceHandler svcHandler = getInstance().getServiceHandler(); + JsonServiceArgument arg = svc.getArgumentInstance(); + arg.jsonElement = jsonObject; + + // call service + ServiceResult result = svcHandler.doService(cert, svc, arg); + return toResponse(result); + } + + @PUT + @Path("{id}") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response update(@Context HttpServletRequest request, @PathParam("id") String id, String data) { + Certificate cert = (Certificate) request.getAttribute(STROLCH_CERTIFICATE); + + // get JSON + JsonObject jsonObject = JsonParser.parseString(data).getAsJsonObject(); + + // create service and argument + UpdateNotificationService svc = new UpdateNotificationService(); + ServiceHandler svcHandler = getInstance().getServiceHandler(); + JsonServiceArgument arg = svc.getArgumentInstance(); + arg.objectId = id; + arg.jsonElement = jsonObject; + + // call service + ServiceResult result = svcHandler.doService(cert, svc, arg); + return toResponse(result); + } + + @DELETE + @Path("{id}") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response remove(@Context HttpServletRequest request, @PathParam("id") String id) { + Certificate cert = (Certificate) request.getAttribute(STROLCH_CERTIFICATE); + + // create service and argument + RemoveNotificationService svc = new RemoveNotificationService(); + ServiceHandler svcHandler = getInstance().getServiceHandler(); + StringArgument arg = svc.getArgumentInstance(); + arg.value = id; + + // call service + ServiceResult result = svcHandler.doService(cert, svc, arg); + return toResponse(result); + } }