diff --git a/agent/src/main/java/li/strolch/policy/notifications/DefaultNotificationsPolicy.java b/agent/src/main/java/li/strolch/policy/notifications/DefaultNotificationsPolicy.java new file mode 100644 index 000000000..e55e5cf05 --- /dev/null +++ b/agent/src/main/java/li/strolch/policy/notifications/DefaultNotificationsPolicy.java @@ -0,0 +1,51 @@ +package li.strolch.policy.notifications; + +import li.strolch.model.Resource; +import li.strolch.model.StrolchModelConstants; +import li.strolch.persistence.api.StrolchTransaction; +import li.strolch.utils.collections.DateRange; + +import java.time.ZonedDateTime; +import java.util.List; + +import static li.strolch.model.StrolchModelConstants.*; + +public class DefaultNotificationsPolicy extends NotificationsPolicy { + public DefaultNotificationsPolicy(StrolchTransaction tx) { + super(tx); + } + + @Override + public List findUserNotifications() { + return tx().streamResources(StrolchModelConstants.TYPE_NOTIFICATION).filter(this::isForUser).toList(); + } + + protected boolean isForUser(Resource notification) { + if (!isActive(notification)) + return false; + if (isForAll(notification)) + return true; + return isForRole(notification) || isForGroup(notification); + } + + protected boolean isActive(Resource notification) { + return new DateRange() + .from(notification.getDate(BAG_VISIBILITY, PARAM_VISIBLE_FROM), true) + .to(notification.getDate(BAG_VISIBILITY, PARAM_VISIBLE_TO), true) + .contains(ZonedDateTime.now()); + } + + protected boolean isForAll(Resource notification) { + return notification.getBoolean(BAG_VISIBILITY, PARAM_FOR_ALL); + } + + protected boolean isForRole(Resource notification) { + List roles = notification.getStringList(BAG_VISIBILITY, PARAM_ROLES); + return roles.isEmpty() || roles.stream().anyMatch(r -> tx().getCertificate().hasRole(r)); + } + + protected boolean isForGroup(Resource notification) { + List groups = notification.getStringList(BAG_VISIBILITY, PARAM_GROUPS); + return groups.isEmpty() || groups.stream().anyMatch(r -> tx().getCertificate().hasGroup(r)); + } +} diff --git a/agent/src/main/java/li/strolch/policy/notifications/NotificationsPolicy.java b/agent/src/main/java/li/strolch/policy/notifications/NotificationsPolicy.java new file mode 100644 index 000000000..fbd9de341 --- /dev/null +++ b/agent/src/main/java/li/strolch/policy/notifications/NotificationsPolicy.java @@ -0,0 +1,27 @@ +package li.strolch.policy.notifications; + +import li.strolch.model.Resource; +import li.strolch.model.policy.PolicyDef; +import li.strolch.persistence.api.StrolchTransaction; +import li.strolch.policy.StrolchPolicy; + +import java.util.List; + +import static li.strolch.model.StrolchModelConstants.PolicyConstants.POLICY_DEFAULT; +import static li.strolch.model.policy.PolicyDef.getJavaPolicy; +import static li.strolch.model.policy.PolicyDef.getKeyPolicy; + +public abstract class NotificationsPolicy extends StrolchPolicy { + + public NotificationsPolicy(StrolchTransaction tx) { + super(tx); + } + + public abstract List findUserNotifications(); + + public static NotificationsPolicy getDefaultPolicy(StrolchTransaction tx) { + PolicyDef defaultDef = getKeyPolicy(NotificationsPolicy.class, POLICY_DEFAULT); + PolicyDef fallbackDef = getJavaPolicy(NotificationsPolicy.class, DefaultNotificationsPolicy.class); + return tx.getPolicy(NotificationsPolicy.class, defaultDef, fallbackDef); + } +} diff --git a/agent/src/main/java/li/strolch/runtime/StrolchConstants.java b/agent/src/main/java/li/strolch/runtime/StrolchConstants.java index 880f92f1d..2e34386c8 100644 --- a/agent/src/main/java/li/strolch/runtime/StrolchConstants.java +++ b/agent/src/main/java/li/strolch/runtime/StrolchConstants.java @@ -96,5 +96,6 @@ public class StrolchConstants extends StrolchModelConstants { public static final String PRIVILEGE_UPDATE_PREFIX = "Update"; public static final String PRIVILEGE_REMOVE_PREFIX = "Remove"; public static final String PRIVILEGE_GET_PREFIX = "Get"; + public static final String PRIVILEGE_GET_NOTIFICATIONS = "GetNotifications"; } } diff --git a/model/src/main/java/li/strolch/model/StrolchModelConstants.java b/model/src/main/java/li/strolch/model/StrolchModelConstants.java index 41a9ea11f..c4f82e843 100644 --- a/model/src/main/java/li/strolch/model/StrolchModelConstants.java +++ b/model/src/main/java/li/strolch/model/StrolchModelConstants.java @@ -72,6 +72,7 @@ public class StrolchModelConstants { public static final String BAG_RELATIONS = "relations"; public static final String BAG_PARAMETERS = "parameters"; + public static final String BAG_VISIBILITY = "visibility"; public static final String TYPE_PARAMETERS = "Parameters"; public static final String TYPE_VERSION = "Version"; public static final String TYPE_MEMORY = "Memory"; @@ -82,6 +83,8 @@ public class StrolchModelConstants { public static final String TYPE_CONFIGURATION = "Configuration"; public static final String TYPE_OBJECTIVES = "Objectives"; public static final String TYPE_METRIC = "Metric"; + public static final String TYPE_NOTIFICATION = "Notification"; + public static final String TYPE_TEXT = "Text"; public static final String RES_CONFIGURATION = "configuration"; @@ -99,6 +102,11 @@ public class StrolchModelConstants { public static final String PARAM_START_DATE = "startDate"; public static final String PARAM_MODE = "mode"; 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_FOR_ALL = "forAll"; + public static final String PARAM_ROLES = "roles"; + public static final String PARAM_GROUPS = "groups"; public static class PolicyConstants { public static final String POLICY_DEFAULT = "Default"; 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 b32e2460b..0c7c3f940 100644 --- a/web-rest/src/main/java/li/strolch/rest/StrolchRestfulConstants.java +++ b/web-rest/src/main/java/li/strolch/rest/StrolchRestfulConstants.java @@ -48,6 +48,8 @@ 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 new file mode 100644 index 000000000..5b7b8f777 --- /dev/null +++ b/web-rest/src/main/java/li/strolch/rest/endpoint/NotificationResource.java @@ -0,0 +1,109 @@ +/* + * Copyright 2024 Robert von Burg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package li.strolch.rest.endpoint; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +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.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.utils.helper.StringHelper; +import li.strolch.utils.iso8601.ISO8601; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; +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.runtime.StrolchConstants.StrolchPrivilegeConstants.PRIVILEGE_GET_NOTIFICATIONS; +import static li.strolch.utils.helper.ExceptionHelper.getCallerMethod; +import static li.strolch.utils.helper.ExceptionHelper.getCallerMethodNoClass; + +/** + * @author Robert von Burg + */ +@Path("strolch/notifications") +public class NotificationResource { + + private static final Logger logger = LoggerFactory.getLogger(NotificationResource.class); + + private StrolchTransaction openTx(Certificate certificate) { + String realm = certificate.getRealm(); + if (StringHelper.isEmpty(realm)) + realm = REALM; + return RestfulStrolchComponent.getInstance().openTx(certificate, realm, getCallerMethod()); + } + + private static Certificate validateCertificate(HttpServletRequest request) { + Certificate cert = (Certificate) request.getAttribute(STROLCH_CERTIFICATE); + RestfulStrolchComponent rest = RestfulStrolchComponent.getInstance(); + rest.validate(cert).validateAction(PRIVILEGE_GET_NOTIFICATIONS, getCallerMethodNoClass(2)); + return cert; + } + + @GET + @Produces(MediaType.APPLICATION_JSON) + public Response getNotifications(@Context HttpServletRequest request) { + Certificate cert = validateCertificate(request); + 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()); + } catch (Exception e) { + logger.error(e.getMessage(), e); + return ResponseUtil.toResponse(e); + } + } + + private static Function notificationToJson(StrolchAgent agent, Certificate cert) { + return notification -> { + JsonObject notificationJ = new JsonObject(); + + String lang = cert.getLocale().getLanguage(); + Optional textBagO = ofNullable(notification.getParameterBag(lang)) + .or(() -> ofNullable(notification.getParameterBag(agent.getLocale().getLanguage()))) + .or(() -> notification.streamOfParameterBagsByType(TYPE_TEXT).findFirst()); + if (textBagO.isEmpty()) + return null; + ParameterBag textBag = textBagO.get(); + + notificationJ.addProperty(PARAM_TITLE, textBag.getString(PARAM_TITLE)); + notificationJ.addProperty(PARAM_TEXT, textBag.getString(PARAM_TEXT)); + notificationJ.addProperty(PARAM_VISIBLE_FROM, ISO8601.toString(textBag.getDate(PARAM_VISIBLE_FROM))); + notificationJ.addProperty(PARAM_VISIBLE_TO, ISO8601.toString(textBag.getDate(PARAM_VISIBLE_TO))); + + return notificationJ; + }; + } +}