From 61dfa01426d15e97ed017acbdd9b5b5687c77963 Mon Sep 17 00:00:00 2001 From: Thisal Tennakoon Date: Thu, 4 Jul 2024 21:15:37 +0530 Subject: [PATCH 1/2] Added notification REST APIs --- .../wso2/carbon/apimgt/api/APIManager.java | 66 +++ .../carbon/apimgt/api/model/Notification.java | 68 ++++ .../apimgt/api/model/NotificationList.java | 60 +++ .../apimgt/impl/AbstractAPIManager.java | 85 ++++ .../apimgt/impl/dao/NotificationDAO.java | 378 ++++++++++++++++++ .../impl/dao/constants/SQLConstants.java | 106 +++++ .../rest/api/common/RestApiConstants.java | 3 + .../src/main/resources/devportal-api.yaml | 279 +++++++++++++ .../src/main/resources/publisher-api.yaml | 266 ++++++++++++ .../v1/dto/NotificationActionRequestDTO.java | 83 ++++ .../api/publisher/v1/dto/NotificationDTO.java | 164 ++++++++ .../publisher/v1/dto/NotificationListDTO.java | 152 +++++++ .../api/publisher/v1/NotificationsApi.java | 139 +++++++ .../publisher/v1/NotificationsApiService.java | 31 ++ .../v1/impl/NotificationsApiServiceImpl.java | 122 ++++++ .../src/main/resources/publisher-api.yaml | 266 ++++++++++++ .../src/main/webapp/WEB-INF/beans.xml | 1 + .../src/main/webapp/WEB-INF/web.xml | 3 +- .../rest/api/store/v1/NotificationsApi.java | 139 +++++++ .../api/store/v1/NotificationsApiService.java | 31 ++ .../v1/dto/NotificationActionRequestDTO.java | 83 ++++ .../api/store/v1/dto/NotificationDTO.java | 164 ++++++++ .../api/store/v1/dto/NotificationListDTO.java | 152 +++++++ .../v1/impl/NotificationsApiServiceImpl.java | 142 +++++++ .../src/main/resources/devportal-api.yaml | 279 +++++++++++++ .../src/main/webapp/WEB-INF/beans.xml | 1 + .../src/main/webapp/WEB-INF/web.xml | 3 +- .../src/main/resources/sql/h2.sql | 17 + 28 files changed, 3281 insertions(+), 2 deletions(-) create mode 100644 components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/model/Notification.java create mode 100644 components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/model/NotificationList.java create mode 100644 components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/dao/NotificationDAO.java create mode 100644 components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1.common/src/gen/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/dto/NotificationActionRequestDTO.java create mode 100644 components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1.common/src/gen/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/dto/NotificationDTO.java create mode 100644 components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1.common/src/gen/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/dto/NotificationListDTO.java create mode 100644 components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/gen/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/NotificationsApi.java create mode 100644 components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/gen/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/NotificationsApiService.java create mode 100644 components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/impl/NotificationsApiServiceImpl.java create mode 100644 components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/gen/java/org/wso2/carbon/apimgt/rest/api/store/v1/NotificationsApi.java create mode 100644 components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/gen/java/org/wso2/carbon/apimgt/rest/api/store/v1/NotificationsApiService.java create mode 100644 components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/gen/java/org/wso2/carbon/apimgt/rest/api/store/v1/dto/NotificationActionRequestDTO.java create mode 100644 components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/gen/java/org/wso2/carbon/apimgt/rest/api/store/v1/dto/NotificationDTO.java create mode 100644 components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/gen/java/org/wso2/carbon/apimgt/rest/api/store/v1/dto/NotificationListDTO.java create mode 100644 components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/store/v1/impl/NotificationsApiServiceImpl.java diff --git a/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/APIManager.java b/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/APIManager.java index 94ed069bbc9c..798f8721c210 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/APIManager.java +++ b/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/APIManager.java @@ -27,6 +27,8 @@ import org.wso2.carbon.apimgt.api.model.Application; import org.wso2.carbon.apimgt.api.model.Documentation; import org.wso2.carbon.apimgt.api.model.DocumentationContent; +import org.wso2.carbon.apimgt.api.model.Notification; +import org.wso2.carbon.apimgt.api.model.NotificationList; import org.wso2.carbon.apimgt.api.model.ResourceFile; import org.wso2.carbon.apimgt.api.model.SubscribedAPI; import org.wso2.carbon.apimgt.api.model.Subscriber; @@ -533,4 +535,68 @@ Map searchPaginatedAPIs(String searchQuery, String organization, */ Map searchPaginatedContent(String searchQuery, String orgId, int start, int end) throws APIManagementException; + + /** + * Changes the status of a notification for a specific user in a given organization. + * + * @param username The username of the user. + * @param organization The organization to which the user belongs. + * @param notificationUUID The unique identifier of the notification. + * @param isMarkAsRead Whether the notification should be marked as read. + * @param portalToDisplay The portal where the notification is displayed. + * @return The updated Notification object. + * @throws APIManagementException If an error occurs while changing the notification status. + */ + Notification changeNotificationStatus(String username, String organization, String notificationUUID, + boolean isMarkAsRead, String portalToDisplay) throws APIManagementException; + + /** + * Deletes a notification for a specific user in a given organization. + * + * @param username The username of the user. + * @param organization The organization to which the user belongs. + * @param notificationUUID The unique identifier of the notification to be deleted. + * @param portalToDisplay The portal where the notification is displayed. + * @throws APIManagementException If an error occurs while deleting the notification. + */ + void deleteNotification(String username, String organization, String notificationUUID, String portalToDisplay) + throws APIManagementException; + + /** + * Retrieves a list of notifications for a given user. + * + * @param username the username of the user to retrieve notifications for + * @param organization the organization to which the user belongs + * @param portalToDisplay the portal where the notifications are to be displayed + * @param limit the maximum number of notifications to retrieve + * @param offset the starting point in the list of notifications to retrieve + * @return a list of notifications matching the criteria + * @throws APIManagementException if there is an error while retrieving notifications + */ + NotificationList getNotifications(String username, String organization, String portalToDisplay, int limit, + int offset) throws APIManagementException; + + /** + * Marks all notifications as read for a specific user in a given organization. + * + * @param username The username of the user. + * @param organization The organization to which the user belongs. + * @param portalToDisplay The portal where the notifications are displayed. + * @param limit The maximum number of notifications to return after marking as read. + * @param offset The starting point within the list of notifications. + * @return A NotificationList object containing the updated list of notifications. + * @throws APIManagementException If an error occurs while marking the notifications as read. + */ + NotificationList markAllNotificationsAsRead(String username, String organization, String portalToDisplay, int limit, + int offset) throws APIManagementException; + + /** + * Deletes all notifications for a specific user in a given organization. + * + * @param username The username of the user. + * @param organization The organization to which the user belongs. + * @param portalToDisplay The portal where the notifications are displayed. + * @throws APIManagementException If an error occurs while deleting the notifications. + */ + void deleteAllNotifications(String username, String organization, String portalToDisplay) throws APIManagementException; } diff --git a/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/model/Notification.java b/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/model/Notification.java new file mode 100644 index 000000000000..4f1c1e778c9b --- /dev/null +++ b/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/model/Notification.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com/). + * + * WSO2 LLC. licenses this file to you 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 org.wso2.carbon.apimgt.api.model; + +public class Notification { + private String notificationId; + private String notificationType; + private String comments; + private String createdTime; + private Boolean isRead; + + public String getNotificationId() { + return notificationId; + } + + public void setNotificationId(String notificationId) { + this.notificationId = notificationId; + } + + public String getNotificationType() { + return notificationType; + } + + public void setNotificationType(String notificationType) { + this.notificationType = notificationType; + } + + public String getComments() { + return comments; + } + + public void setComments(String comments) { + this.comments = comments; + } + + public String getCreatedTime() { + return createdTime; + } + + public void setCreatedTime(String createdTime) { + this.createdTime = createdTime; + } + + public Boolean getIsRead() { + return isRead; + } + + public void setIsRead(Boolean read) { + this.isRead = read; + } + +} diff --git a/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/model/NotificationList.java b/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/model/NotificationList.java new file mode 100644 index 000000000000..576e58620292 --- /dev/null +++ b/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/model/NotificationList.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com/). + * + * WSO2 LLC. licenses this file to you 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 org.wso2.carbon.apimgt.api.model; + +import java.util.ArrayList; +import java.util.List; + +public class NotificationList { + + private Integer count = null; + private List list = new ArrayList(); + private Pagination pagination = null; + + private Integer unreadCount = null; + + public Integer getCount() { + return count; + } + + public void setCount(Integer count) { + this.count = count; + } + + public List getList() { + return list; + } + + public void setList(List list) { + this.list = list; + } + + public Pagination getPagination() { + return pagination; + } + + public void setPagination(Pagination pagination) { + this.pagination = pagination; + } + + public Integer getUnreadCount() { return unreadCount; } + + public void setUnreadCount(Integer unreadCount) { this.unreadCount = unreadCount; } + +} diff --git a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/AbstractAPIManager.java b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/AbstractAPIManager.java index a33d7936f85a..43b1f4ae28ae 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/AbstractAPIManager.java +++ b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/AbstractAPIManager.java @@ -47,6 +47,7 @@ import org.wso2.carbon.apimgt.api.model.policy.PolicyConstants; import org.wso2.carbon.apimgt.impl.dao.ApiMgtDAO; import org.wso2.carbon.apimgt.impl.dao.EnvironmentSpecificAPIPropertyDAO; +import org.wso2.carbon.apimgt.impl.dao.NotificationDAO; import org.wso2.carbon.apimgt.impl.dao.ScopesDAO; import org.wso2.carbon.apimgt.impl.dto.ThrottleProperties; import org.wso2.carbon.apimgt.impl.dto.WorkflowDTO; @@ -82,6 +83,7 @@ public abstract class AbstractAPIManager implements APIManager { // API definitions from swagger v2.0 protected Log log = LogFactory.getLog(getClass()); protected ApiMgtDAO apiMgtDAO; + protected NotificationDAO notificationDAO; protected EnvironmentSpecificAPIPropertyDAO environmentSpecificAPIPropertyDAO; protected ScopesDAO scopesDAO; protected int tenantId = MultitenantConstants.INVALID_TENANT_ID; //-1 the issue does not occur.; @@ -105,6 +107,7 @@ public AbstractAPIManager(String username) throws APIManagementException { public AbstractAPIManager(String username, String organization) throws APIManagementException { apiMgtDAO = ApiMgtDAO.getInstance(); + notificationDAO = NotificationDAO.getInstance(); scopesDAO = ScopesDAO.getInstance(); environmentSpecificAPIPropertyDAO = EnvironmentSpecificAPIPropertyDAO.getInstance(); @@ -1673,4 +1676,86 @@ protected boolean isOauthAppValidation() { } return true; } + + /** + * Changes the status of a notification for a specific user in a given organization. + * + * @param username The username of the user. + * @param organization The organization to which the user belongs. + * @param notificationUUID The unique identifier of the notification. + * @param isMarkAsRead Whether the notification should be marked as read. + * @param portalToDisplay The portal where the notification is displayed. + * @return The updated Notification object. + * @throws APIManagementException If an error occurs while changing the notification status. + */ + @Override + public Notification changeNotificationStatus(String username, String organization, String notificationUUID, + boolean isMarkAsRead, String portalToDisplay) throws APIManagementException { + notificationDAO.changeNotificationStatus(username, organization, notificationUUID, isMarkAsRead, portalToDisplay); + return notificationDAO.getNotification(notificationUUID, username, organization, portalToDisplay); + } + + /** + * Deletes a notification for a specific user in a given organization. + * + * @param username The username of the user. + * @param organization The organization to which the user belongs. + * @param notificationUUID The unique identifier of the notification to be deleted. + * @param portalToDisplay The portal where the notification is displayed. + * @throws APIManagementException If an error occurs while deleting the notification. + */ + @Override + public void deleteNotification(String username, String organization, String notificationUUID, String portalToDisplay) + throws APIManagementException { + notificationDAO.deleteNotification(username, organization, notificationUUID, portalToDisplay); + } + + /** + * Retrieves a list of notifications for a given user. + * + * @param username the username of the user to retrieve notifications for + * @param organization the organization to which the user belongs + * @param portalToDisplay the portal where the notifications are to be displayed + * @param limit the maximum number of notifications to retrieve + * @param offset the starting point in the list of notifications to retrieve + * @return a list of notifications matching the criteria + * @throws APIManagementException if there is an error while retrieving notifications + */ + @Override + public NotificationList getNotifications(String username, String organization, String portalToDisplay, int limit, + int offset) throws APIManagementException { + return notificationDAO.getNotifications(username, organization, portalToDisplay, limit, offset); + } + + /** + * Marks all notifications as read for a specific user in a given organization. + * + * @param username The username of the user. + * @param organization The organization to which the user belongs. + * @param portalToDisplay The portal where the notifications are displayed. + * @param limit The maximum number of notifications to return after marking as read. + * @param offset The starting point within the list of notifications. + * @return A NotificationList object containing the updated list of notifications. + * @throws APIManagementException If an error occurs while marking the notifications as read. + */ + @Override + public NotificationList markAllNotificationsAsRead(String username, String organization, String portalToDisplay, + int limit, int offset) throws APIManagementException { + notificationDAO.markAllNotificationsAsRead(username, organization, portalToDisplay); + return notificationDAO.getNotifications(username, organization, portalToDisplay, limit, offset); + } + + /** + * Deletes all notifications for a specific user in a given organization. + * + * @param username The username of the user. + * @param organization The organization to which the user belongs. + * @param portalToDisplay The portal where the notifications are displayed. + * @throws APIManagementException If an error occurs while deleting the notifications. + */ + @Override + public void deleteAllNotifications(String username, String organization, String portalToDisplay) + throws APIManagementException { + notificationDAO.deleteAllNotifications(username, organization, portalToDisplay); + } } diff --git a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/dao/NotificationDAO.java b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/dao/NotificationDAO.java new file mode 100644 index 000000000000..537005f1f93c --- /dev/null +++ b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/dao/NotificationDAO.java @@ -0,0 +1,378 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com/). + * + * WSO2 LLC. licenses this file to you 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 org.wso2.carbon.apimgt.impl.dao; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.apimgt.api.APIManagementException; +import org.wso2.carbon.apimgt.api.model.Pagination; +import org.wso2.carbon.apimgt.api.model.Notification; +import org.wso2.carbon.apimgt.api.model.NotificationList; +import org.wso2.carbon.apimgt.impl.dao.constants.SQLConstants; +import org.wso2.carbon.apimgt.impl.utils.APIMgtDBUtil; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +public class NotificationDAO { + + private static final Log log = LogFactory.getLog(NotificationDAO.class); + private static final NotificationDAO INSTANCE = new NotificationDAO(); + + private NotificationDAO() { + } + + public static NotificationDAO getInstance() { + return INSTANCE; + } + + // Tentative + public void addNotification(String comment) throws APIManagementException { + + String addNotificationQuery = SQLConstants.PortalNotifications.ADD_NOTIFICATION; + String addEndUserQuery = SQLConstants.PortalNotifications.ADD_NOTIFICATION_END_USER; + + try (Connection conn = APIMgtDBUtil.getConnection()) { + conn.setAutoCommit(false); + try (PreparedStatement ps = conn.prepareStatement(addNotificationQuery)) { + + String notificationId = UUID.randomUUID().toString(); + ps.setString(1, notificationId); + ps.setString(2, "SUBSCRIPTION_UPDATE"); + ps.setTimestamp(3, Timestamp.from(Instant.now())); + ps.setString(4, comment); + + int rowsAffected = ps.executeUpdate(); + + if (rowsAffected > 0) { + addEndUser(conn, addEndUserQuery, notificationId, "admin", "carbon.super", + "devportal"); + addEndUser(conn, addEndUserQuery, notificationId, "admin", "carbon.super", + "publisher"); + conn.commit(); + } + } catch (SQLException e) { + conn.rollback(); + throw new APIManagementException("Error while adding notification", e); + } + } catch (SQLException e) { + throw new APIManagementException("Error while establishing database connection", e); + } + } + + // Tentative + private void addEndUser(Connection conn, String addEndUserQuery, String notificationId, String destinationUser, + String organization, String portalToDisplay) throws SQLException { + + try (PreparedStatement ps = conn.prepareStatement(addEndUserQuery)) { + ps.setString(1, notificationId); + ps.setString(2, destinationUser); + ps.setString(3, organization); + ps.setString(4, portalToDisplay); + ps.executeUpdate(); + } + } + + /** + * Retrieves a specific notification by its UUID for a given user. + * + * @param notificationUUID the unique identifier of the notification to be retrieved + * @param username the username of the user to retrieve the notification for + * @param organization the organization to which the user belongs + * @param portalToDisplay the portal where the notification is displayed + * @return the notification matching the given UUID and user, or {@code null} if not found + * @throws APIManagementException if there is an error while retrieving the notification + */ + public Notification getNotification(String notificationUUID, String username, String organization, + String portalToDisplay) throws APIManagementException { + + String sqlQuery = SQLConstants.PortalNotifications.GET_NOTIFICATION_BY_ID; + try (Connection conn = APIMgtDBUtil.getConnection(); + PreparedStatement ps = conn.prepareStatement(sqlQuery)) { + ps.setString(1, notificationUUID); + ps.setString(2, username); + ps.setString(3, organization); + ps.setString(4, portalToDisplay); + try (ResultSet rs = ps.executeQuery()) { + if (rs.next()) { + Notification notification = new Notification(); + notification.setNotificationId(rs.getString("NOTIFICATION_ID")); + notification.setNotificationType(rs.getString("NOTIFICATION_TYPE")); + notification.setCreatedTime(rs.getTimestamp("CREATED_TIME").toString()); + notification.setComments(rs.getString("CONTENT")); + notification.setIsRead(rs.getBoolean("IS_READ")); + return notification; + } + } + } catch (SQLException e) { + throw new APIManagementException("Failed to get notification by id", e); + } + return null; + } + + /** + * Changes the status of a notification for a given user. + * + * @param username the username of the user whose notification status is to be changed + * @param organization the organization to which the user belongs + * @param notificationUUID the unique identifier of the notification + * @param isMarkAsRead boolean flag indicating whether to mark the notification as read or unread + * @param portalToDisplay the portal where the notification is displayed + * @throws APIManagementException if there is an error while changing the notification status + */ + public void changeNotificationStatus(String username, String organization, String notificationUUID, + boolean isMarkAsRead, String portalToDisplay) throws APIManagementException { + + String sqlQuery = SQLConstants.PortalNotifications.CHANGE_NOTIFICATION_STATUS; + try (Connection conn = APIMgtDBUtil.getConnection()) { + conn.setAutoCommit(false); + try (PreparedStatement ps = conn.prepareStatement(sqlQuery)) { + ps.setBoolean(1, isMarkAsRead); + ps.setString(2, notificationUUID); + ps.setString(3, username); + ps.setString(4, organization); + ps.setString(5, portalToDisplay); + ps.executeUpdate(); + conn.commit(); + } catch (SQLException e) { + try { + conn.rollback(); + } catch (SQLException e1) { + log.error("Failed to rollback notification status change", e1); + } + } + } catch (SQLException e) { + throw new APIManagementException("Failed to change notification status", e); + } + } + + /** + * Deletes a notification for a given user. + * + * @param username the username of the user whose notification is to be deleted + * @param organization the organization to which the user belongs + * @param notificationUUID the unique identifier of the notification to be deleted + * @param portalToDisplay the portal where the notification is displayed + * @throws APIManagementException if there is an error while deleting the notification + */ + public void deleteNotification(String username, String organization, String notificationUUID, String portalToDisplay) + throws APIManagementException { + + try (Connection conn = APIMgtDBUtil.getConnection()) { + conn.setAutoCommit(false); + try (PreparedStatement ps = conn.prepareStatement(SQLConstants.PortalNotifications. + DELETE_NOTIFICATION_BY_ID_FROM_AM_NOTIFICATION_END_USERS)) { + ps.setString(1, notificationUUID); + ps.setString(2, username); + ps.setString(3, organization); + ps.setString(4, portalToDisplay); + int rowsAffected = ps.executeUpdate(); + if (rowsAffected > 0) { + try (PreparedStatement preparedStatement = conn.prepareStatement(SQLConstants.PortalNotifications. + DELETE_NOTIFICATION_BY_ID_FROM_AM_NOTIFICATION)) { + preparedStatement.setString(1, notificationUUID); + preparedStatement.setString(2, notificationUUID); + preparedStatement.executeUpdate(); + } + conn.commit(); + } + } catch (SQLException e) { + conn.rollback(); + throw new APIManagementException("Failed to delete notification by id", e); + } + } catch (SQLException e) { + throw new APIManagementException("Failed to establish database connection", e); + } + } + + /** + * Retrieves a list of notifications for a given user. + * + * @param username the username of the user to retrieve notifications for + * @param organization the organization to which the user belongs + * @param portalToDisplay the portal where the notifications are to be displayed + * @param limit the maximum number of notifications to retrieve + * @param offset the starting point in the list of notifications to retrieve + * @return a list of notifications matching the criteria, including pagination information + * @throws APIManagementException if there is an error while retrieving notifications + */ + public NotificationList getNotifications(String username, String organization, String portalToDisplay, int limit, + int offset) throws APIManagementException { + + NotificationList notificationList = new NotificationList(); + Pagination pagination = new Pagination(); + pagination.setLimit(limit); + pagination.setOffset(offset); + notificationList.setPagination(pagination); + int totalNotificationCount = 0; + int unreadNotificationCount = 0; + int notificationCount = 0; + + try (Connection conn = APIMgtDBUtil.getConnection()) { + try (PreparedStatement ps = conn.prepareStatement(SQLConstants.PortalNotifications. + GET_TOTAL_NOTIFICATIONS_COUNT_FOR_USER)) { + ps.setString(1, username); + ps.setString(2, organization); + ps.setString(3, portalToDisplay); + try (ResultSet rs = ps.executeQuery()) { + if (rs.next()) { + totalNotificationCount = rs.getInt("NOTIFICATION_COUNT"); + pagination.setTotal(totalNotificationCount); + } + } + } catch (SQLException e) { + throw new APIManagementException("Failed to retrieve notification count of the user " + username, e); + } + + if (totalNotificationCount > 0) { + try (PreparedStatement ps = conn.prepareStatement(SQLConstants.PortalNotifications. + GET_UNREAD_NOTIFICATION_COUNT_FOR_USER)) { + ps.setString(1, username); + ps.setString(2, organization); + ps.setString(3, portalToDisplay); + try (ResultSet rs = ps.executeQuery()) { + if (rs.next()) { + unreadNotificationCount = rs.getInt("UNREAD_NOTIFICATION_COUNT"); + } + } + } catch (SQLException e) { + throw new APIManagementException("Failed to retrieve unread notification count of the user " + username, + e); + } + + if (limit > 0) { + try (PreparedStatement ps = + conn.prepareStatement(SQLConstants.PortalNotifications.GET_NOTIFICATIONS)) { + ps.setString(1, username); + ps.setString(2, organization); + ps.setString(3, portalToDisplay); + ps.setInt(4, offset); + ps.setInt(5, limit); + try (ResultSet rs = ps.executeQuery()) { + List list = new ArrayList<>(); + while (rs.next()) { + Notification notification = new Notification(); + notification.setNotificationId(rs.getString("NOTIFICATION_ID")); + notification.setNotificationType(rs.getString("NOTIFICATION_TYPE")); + notification.setComments(rs.getString("CONTENT")); + notification.setCreatedTime(rs.getTimestamp("CREATED_TIME").toString()); + notification.setIsRead(rs.getBoolean("IS_READ")); + list.add(notification); + } + notificationList.setList(list); + notificationCount = list.size(); + } catch (SQLException e) { + throw new APIManagementException("Failed to retrieve notifications of the user " + username, e); + } + } + } + } + notificationList.setUnreadCount(unreadNotificationCount); + notificationList.setCount(notificationCount); + + if (limit < 0) { // Tentative: Remove this else block + try { + for (int i = 0; i < limit * -1; i++) { + addNotification("Notification " + i); + Thread.sleep(3000); + } + } catch (InterruptedException e) { + log.error("Error while adding notifications", e); + } + } + } catch (SQLException e) { + throw new APIManagementException("Failed to retrieve notifications of the user " + username, e); + } + return notificationList; + } + + /** + * Marks all notifications as read for a given user. + * + * @param username the username of the user whose notifications are to be marked as read + * @param organization the organization to which the user belongs + * @param portalToDisplay the portal where the notifications are displayed + * @throws APIManagementException if there is an error while marking the notifications as read + */ + public void markAllNotificationsAsRead(String username, String organization, String portalToDisplay) + throws APIManagementException { + + String sqlQuery = SQLConstants.PortalNotifications.MARK_ALL_NOTIFICATIONS_AS_READ; + try (Connection conn = APIMgtDBUtil.getConnection()) { + conn.setAutoCommit(false); + try (PreparedStatement ps = conn.prepareStatement(sqlQuery)) { + ps.setString(1, username); + ps.setString(2, organization); + ps.setString(3, portalToDisplay); + ps.executeUpdate(); + conn.commit(); + } catch (SQLException e) { + try { + conn.rollback(); + } catch (SQLException e1) { + log.error("Failed to rollback notification status change", e1); + } + } + } catch (SQLException e) { + throw new APIManagementException("Failed to change notification status", e); + } + } + + /** + * Deletes all notifications for a given user. + * + * @param username the username of the user whose notifications are to be deleted + * @param organization the organization to which the user belongs + * @param portalToDisplay the portal where the notifications are displayed + * @throws APIManagementException if there is an error while deleting the notifications + */ + public void deleteAllNotifications(String username, String organization, String portalToDisplay) throws APIManagementException { + + String sqlQuery = SQLConstants.PortalNotifications.DELETE_ALL_NOTIFICATIONS_OF_USER_FROM_AM_NOTIFICATION_END_USERS; + try (Connection conn = APIMgtDBUtil.getConnection()) { + conn.setAutoCommit(false); + try (PreparedStatement ps = conn.prepareStatement(sqlQuery)) { + ps.setString(1, username); + ps.setString(2, organization); + ps.setString(3, portalToDisplay); + int rowsAffected = ps.executeUpdate(); + if (rowsAffected > 0) { + String deleteNotifications = + SQLConstants.PortalNotifications.DELETE_ALL_UNUSED_NOTIFICATIONS_FROM_AM_NOTIFICATION; + try (PreparedStatement preparedStatement = conn.prepareStatement(deleteNotifications)) { + preparedStatement.executeUpdate(); + } + conn.commit(); + } + } catch (SQLException e) { + conn.rollback(); + throw new APIManagementException("Failed to delete notifications", e); + } + } catch (SQLException e) { + throw new APIManagementException("Failed to establish database connection", e); + } + } +} diff --git a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/dao/constants/SQLConstants.java b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/dao/constants/SQLConstants.java index 063d16f7093b..bf3204b83f76 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/dao/constants/SQLConstants.java +++ b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/dao/constants/SQLConstants.java @@ -3168,6 +3168,112 @@ public class SQLConstants { " AND API.ORGANIZATION = ?" + " AND SUBS.SUB_STATUS != '" + APIConstants.SubscriptionStatus.REJECTED + "'"; + public static class PortalNotifications { + + public static final String CHANGE_NOTIFICATION_STATUS = + "UPDATE AM_NOTIFICATION_END_USERS " + + "SET IS_READ = ? " + + "WHERE NOTIFICATION_ID = ? " + + " AND DESTINATION_USER = ? " + + " AND ORGANIZATION = ? " + + " AND PORTAL_TO_DISPLAY = ?"; + + public static final String GET_NOTIFICATION_BY_ID = + "SELECT " + + " NOTIF.NOTIFICATION_ID, " + + " NOTIF.NOTIFICATION_TYPE, " + + " NOTIF.CREATED_TIME, " + + " NOTIF.CONTENT, " + + " END_USER.IS_READ " + + "FROM AM_NOTIFICATION NOTIF " + + "INNER JOIN AM_NOTIFICATION_END_USERS END_USER ON NOTIF.NOTIFICATION_ID = END_USER.NOTIFICATION_ID " + + "WHERE " + + " END_USER.NOTIFICATION_ID = ? " + + " AND END_USER.DESTINATION_USER = ? " + + " AND END_USER.ORGANIZATION = ? " + + " AND END_USER.PORTAL_TO_DISPLAY = ?"; + + public static final String DELETE_NOTIFICATION_BY_ID_FROM_AM_NOTIFICATION_END_USERS = + "DELETE FROM AM_NOTIFICATION_END_USERS " + + "WHERE NOTIFICATION_ID = ? " + + "AND DESTINATION_USER = ? " + + "AND ORGANIZATION = ? " + + "AND PORTAL_TO_DISPLAY = ?"; + + public static final String DELETE_NOTIFICATION_BY_ID_FROM_AM_NOTIFICATION = + "DELETE FROM AM_NOTIFICATION " + + "WHERE NOTIFICATION_ID = ? " + + "AND NOT EXISTS ( " + + " SELECT 1 FROM AM_NOTIFICATION_END_USERS " + + " WHERE NOTIFICATION_ID = ? " + + ")"; + + public static final String GET_TOTAL_NOTIFICATIONS_COUNT_FOR_USER = + "SELECT COUNT(*) AS NOTIFICATION_COUNT " + + "FROM AM_NOTIFICATION_END_USERS " + + "WHERE DESTINATION_USER = ? " + + "AND ORGANIZATION = ? " + + "AND PORTAL_TO_DISPLAY = ?"; + + public static final String GET_UNREAD_NOTIFICATION_COUNT_FOR_USER = + "SELECT COUNT(*) AS UNREAD_NOTIFICATION_COUNT " + + "FROM AM_NOTIFICATION_END_USERS " + + "WHERE DESTINATION_USER = ? " + + "AND ORGANIZATION = ? " + + "AND PORTAL_TO_DISPLAY = ? " + + "AND IS_READ = FALSE"; + + public static final String GET_NOTIFICATIONS = + "SELECT " + + " NOTIF.NOTIFICATION_ID, " + + " NOTIF.NOTIFICATION_TYPE, " + + " NOTIF.CREATED_TIME, " + + " NOTIF.CONTENT, " + + " END_USER.IS_READ " + + "FROM AM_NOTIFICATION NOTIF " + + "INNER JOIN AM_NOTIFICATION_END_USERS END_USER ON NOTIF.NOTIFICATION_ID = END_USER.NOTIFICATION_ID " + + "WHERE " + + " END_USER.DESTINATION_USER = ? " + + " AND END_USER.ORGANIZATION = ? " + + " AND END_USER.PORTAL_TO_DISPLAY = ? " + + "ORDER BY NOTIF.CREATED_TIME DESC " + + "LIMIT ?, ?"; + + public static final String MARK_ALL_NOTIFICATIONS_AS_READ = + "UPDATE AM_NOTIFICATION_END_USERS SET IS_READ = TRUE " + + "WHERE IS_READ = FALSE " + + "AND DESTINATION_USER = ? " + + "AND ORGANIZATION = ? " + + "AND PORTAL_TO_DISPLAY = ?"; + + public static final String DELETE_ALL_NOTIFICATIONS_OF_USER_FROM_AM_NOTIFICATION_END_USERS = + "DELETE FROM AM_NOTIFICATION_END_USERS " + + "WHERE DESTINATION_USER = ? " + + "AND ORGANIZATION = ? " + + "AND PORTAL_TO_DISPLAY = ?"; + + public static final String DELETE_ALL_UNUSED_NOTIFICATIONS_FROM_AM_NOTIFICATION = + "DELETE FROM AM_NOTIFICATION " + + "WHERE NOTIFICATION_ID IN ( " + + " SELECT NOTIFICATION_ID " + + " FROM AM_NOTIFICATION " + + " EXCEPT " + + " SELECT NOTIFICATION_ID " + + " FROM AM_NOTIFICATION_END_USERS " + + ")"; + + // Tentative + public static final String ADD_NOTIFICATION = + "INSERT INTO AM_NOTIFICATION " + + "(NOTIFICATION_ID, NOTIFICATION_TYPE, CREATED_TIME, CONTENT) " + + "VALUES (?, ?, ?, ?)"; + + public static final String ADD_NOTIFICATION_END_USER = + "INSERT INTO AM_NOTIFICATION_END_USERS " + + "(NOTIFICATION_ID, DESTINATION_USER, ORGANIZATION, PORTAL_TO_DISPLAY) " + + "VALUES (?, ?, ?, ?)"; + } + /** Throttle related constants**/ public static class ThrottleSQLConstants{ diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.common/src/main/java/org/wso2/carbon/apimgt/rest/api/common/RestApiConstants.java b/components/apimgt/org.wso2.carbon.apimgt.rest.api.common/src/main/java/org/wso2/carbon/apimgt/rest/api/common/RestApiConstants.java index 2de0f6b2a50b..b4a413f4691f 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.rest.api.common/src/main/java/org/wso2/carbon/apimgt/rest/api/common/RestApiConstants.java +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.common/src/main/java/org/wso2/carbon/apimgt/rest/api/common/RestApiConstants.java @@ -350,4 +350,7 @@ public final class RestApiConstants { public static final String RESOURCE_PATH_GATEWAY_POLICIES = "gateway-policies"; public static final String AUTH_TOKEN_INFO = "AUTH_TOKEN_INFO"; + + public static final String PUBLISHER_PORTAL = "publisher"; + public static final String DEV_PORTAL = "devportal"; } diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.common/src/main/resources/devportal-api.yaml b/components/apimgt/org.wso2.carbon.apimgt.rest.api.common/src/main/resources/devportal-api.yaml index b99d43642ed8..34c831387de1 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.rest.api.common/src/main/resources/devportal-api.yaml +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.common/src/main/resources/devportal-api.yaml @@ -3818,9 +3818,263 @@ paths: - lang: Curl source: 'curl -k -X POST -d request.json "https://localhost:9443/api/am/devportal/v3/apis/marketplace-assistant/chat"' + ###################################################### + # The "Portal Notifications" resource APIs + ###################################################### + /notifications: + get: + tags: + - Notifications + summary: Retrieves all notifications for the user. + description: This operation can be used to retrieve all notifications for the user. + parameters: + - $ref: '#/components/parameters/limit' + - $ref: '#/components/parameters/offset' + responses: + 200: + description: | + OK. + List of Notifications are returned. + headers: + Content-Type: + description: | + The content type of the body. + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/NotificationList' + 403: + $ref: '#/components/responses/Forbidden' + 404: + $ref: '#/components/responses/NotFound' + 500: + $ref: '#/components/responses/InternalServerError' + security: + - OAuth2Security: + - apim:notifications_view + x-code-samples: + - lang: Curl + source: 'curl -k -H "Authorization: Bearer ae4eae22-3f65-387b-a171-d37eaa366fa8" + "https://localhost:9443/api/am/devportal/v3/notifications"' + operationId: getNotifications + + patch: + tags: + - Notifications + summary: Mark all notifications as read + description: | + This operation can be used to mark all notifications as read. + parameters: + - $ref: '#/components/parameters/limit' + - $ref: '#/components/parameters/offset' + responses: + 200: + description: | + OK. + Successful response with updated Notifications. + headers: + Content-Type: + description: | + The content type of the body. + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/NotificationList' + 400: + $ref: '#/components/responses/BadRequest' + 401: + $ref: '#/components/responses/Unauthorized' + 403: + $ref: '#/components/responses/Forbidden' + 404: + $ref: '#/components/responses/NotFound' + 500: + $ref: '#/components/responses/InternalServerError' + security: + - OAuth2Security: + - apim:notifications_manage + - apim:notifications_view + x-code-samples: + - lang: Curl + source: | + curl -k -X PATCH -H "Authorization: Bearer ae4eae22-3f65-387b-a171-d37eaa366fa8" \ + -H "Content-Type: application/json" \ + "https://localhost:9443/api/am/devportal/v3/notifications/update-status" + operationId: markAllNotificationsAsRead + + delete: + tags: + - Notifications + summary: Delete all notifications + description: | + This operation can be used to delete all the notifications of a user. + responses: + 200: + description: | + OK. + Resource successfully deleted. + 401: + $ref: '#/components/responses/Unauthorized' + 404: + $ref: '#/components/responses/NotFound' + 500: + $ref: '#/components/responses/InternalServerError' + security: + - OAuth2Security: + - apim:notifications_manage + - apim:notifications_view + x-code-samples: + - lang: Curl + source: | + curl -k -X DELETE -H "Authorization: Bearer ae4eae22-3f65-387b-a171-d37eaa366fa8" + "https://localhost:9443/api/am/devportal/v3/notifications" + operationId: deleteAllNotifications + + /notifications/{notificationId}: + patch: + tags: + - Notifications + summary: Mark a notification as read or unread + description: | + This operation can be used to mark a notification as read or unread. + parameters: + - $ref: '#/components/parameters/notificationId' + requestBody: + required: true + content: + application/json: + schema: + title: Notification action request + type: object + properties: + markAsRead: + type: boolean + description: True to mark as read, false to mark as unread + example: true + required: + - markAsRead + responses: + 200: + description: | + OK. + Successful response with updated Notification. + headers: + Content-Type: + description: | + The content type of the body. + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/Notification' + 400: + $ref: '#/components/responses/BadRequest' + 401: + $ref: '#/components/responses/Unauthorized' + 403: + $ref: '#/components/responses/Forbidden' + 404: + $ref: '#/components/responses/NotFound' + 500: + $ref: '#/components/responses/InternalServerError' + security: + - OAuth2Security: + - apim:notifications_manage + - apim:notifications_view + x-code-samples: + - lang: Curl + source: | + curl -k -X PATCH -H "Authorization:Bearer ae4eae22-3f65-387b-a171-d37eaa366fa8" \ + -H "Content-Type: application/json" -d @data.json \ + "https://localhost:9443/api/am/devportal/v3/notifications/863aef13-dab4-48b4-bf58-7363cd29601a" + operationId: changeNotificationStatus + + delete: + tags: + - Notifications + summary: Delete a Notification + description: | + This operation can be used to delete a notification. + parameters: + - $ref: '#/components/parameters/notificationId' + responses: + 200: + description: | + OK. + Resource successfully deleted. + 401: + $ref: '#/components/responses/Unauthorized' + 403: + $ref: '#/components/responses/Forbidden' + 404: + $ref: '#/components/responses/NotFound' + 500: + $ref: '#/components/responses/InternalServerError' + security: + - OAuth2Security: + - apim:notifications_manage + - apim:notifications_view + x-code-samples: + - lang: Curl + source: | + curl -k -X DELETE -H "Authorization:Bearer ae4eae22-3f65-387b-a171-d37eaa366fa8" \ + "https://localhost:9443/api/am/devportal/v3/notifications/863aef13-dab4-48b4-bf58-7363cd29601a" + operationId: deleteNotification components: schemas: + Notification: + title: Notification + description: Object with basic notification details. + type: object + properties: + id: + type: string + readOnly: true + example: 863aef13-dab4-48b4-bf58-7363cd29601a + type: + type: string + readOnly: true + example: APPLICATION_CREATION + content: + type: string + maxLength: 512 + readOnly: true + example: Application creation is rejected due to some reason. + createdTime: + type: string + readOnly: true + example: 2021-02-11 09:57:25 + isRead: + type: boolean + example: false + NotificationList: + title: Notification List + description: A list of notifications. + type: object + properties: + count: + type: integer + readOnly: true + description: Number of notifications returned. + example: 1 + unreadCount: + type: integer + readOnly: true + description: Number of unread notifications returned. + example: 5 + list: + type: array + readOnly: true + items: + $ref: '#/components/schemas/Notification' + pagination: + $ref: '#/components/schemas/Pagination' ApplicationThrottleReset: title: Reset application level throttling type: object @@ -6267,6 +6521,20 @@ components: description: The specified resource does not exist moreInfo: "" error: [] + Forbidden: + description: Forbidden. The request must be conditional but no condition has + been specified. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + example: + code: 403 + message: Forbidden + description: The request must be conditional but no condition has been + specified + moreInfo: "" + error: [] PreconditionFailed: description: Precondition Failed. The request has not been performed because one of the preconditions is not met. @@ -6319,6 +6587,15 @@ components: moreInfo: "" error: [ ] parameters: + notificationId: + name: notificationId + in: path + description: | + UUID of the notification. + required: true + schema: + type: string + example: 863aef13-dab4-48b4-bf58-7363cd29601a parentCommentID: name: replyTo in: query @@ -6586,3 +6863,5 @@ components: apim:sub_alert_manage: Retrieve, subscribe and configure Developer Portal alert types apim:app_import_export: Import and export applications related operations apim:admin: Manage all admin operations + apim:notifications_manage: Manage notifications + apim:notifications_view: View notifications diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.common/src/main/resources/publisher-api.yaml b/components/apimgt/org.wso2.carbon.apimgt.rest.api.common/src/main/resources/publisher-api.yaml index b7ba04b85b54..b67e8fa99527 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.rest.api.common/src/main/resources/publisher-api.yaml +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.common/src/main/resources/publisher-api.yaml @@ -8681,8 +8681,263 @@ paths: "https://127.0.0.1:9443/api/am/publisher/v4/api-products/890a4f4d-09eb-4877-a323-57f6ce2ed79b/lifecycle-state/pending-tasks"' operationId: deleteAPIProductLifecycleStatePendingTasks + ###################################################### + # The "Portal Notifications" resource APIs + ###################################################### + /notifications: + get: + tags: + - Notifications + summary: Retrieves all notifications for the user. + description: This operation can be used to retrieve all notifications for the user. + parameters: + - $ref: '#/components/parameters/limit' + - $ref: '#/components/parameters/offset' + responses: + 200: + description: | + OK. + List of Notifications are returned. + headers: + Content-Type: + description: | + The content type of the body. + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/NotificationList' + 403: + $ref: '#/components/responses/Forbidden' + 404: + $ref: '#/components/responses/NotFound' + 500: + $ref: '#/components/responses/InternalServerError' + security: + - OAuth2Security: + - apim:notifications_view + x-code-samples: + - lang: Curl + source: 'curl -k -H "Authorization: Bearer ae4eae22-3f65-387b-a171-d37eaa366fa8" + "https://localhost:9443/api/am/publisher/v4/notifications"' + operationId: getNotifications + + patch: + tags: + - Notifications + summary: Mark all notifications as read + description: | + This operation can be used to mark all notifications as read. + parameters: + - $ref: '#/components/parameters/limit' + - $ref: '#/components/parameters/offset' + responses: + 200: + description: | + OK. + Successful response with updated Notifications. + headers: + Content-Type: + description: | + The content type of the body. + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/NotificationList' + 400: + $ref: '#/components/responses/BadRequest' + 401: + $ref: '#/components/responses/Unauthorized' + 403: + $ref: '#/components/responses/Forbidden' + 404: + $ref: '#/components/responses/NotFound' + 500: + $ref: '#/components/responses/InternalServerError' + security: + - OAuth2Security: + - apim:notifications_manage + - apim:notifications_view + x-code-samples: + - lang: Curl + source: | + curl -k -X PATCH -H "Authorization: Bearer ae4eae22-3f65-387b-a171-d37eaa366fa8" \ + -H "Content-Type: application/json" \ + "https://localhost:9443/api/am/publisher/v4/notifications/update-status" + operationId: markAllNotificationsAsRead + + delete: + tags: + - Notifications + summary: Delete all notifications + description: | + This operation can be used to delete all the notifications of a user. + responses: + 200: + description: | + OK. + Resource successfully deleted. + 401: + $ref: '#/components/responses/Unauthorized' + 404: + $ref: '#/components/responses/NotFound' + 500: + $ref: '#/components/responses/InternalServerError' + security: + - OAuth2Security: + - apim:notifications_manage + - apim:notifications_view + x-code-samples: + - lang: Curl + source: | + curl -k -X DELETE -H "Authorization: Bearer ae4eae22-3f65-387b-a171-d37eaa366fa8" + "https://localhost:9443/api/am/publisher/v4/notifications" + operationId: deleteAllNotifications + + /notifications/{notificationId}: + patch: + tags: + - Notifications + summary: Mark a notification as read or unread + description: | + This operation can be used to mark a notification as read or unread. + parameters: + - $ref: '#/components/parameters/notificationId' + requestBody: + required: true + content: + application/json: + schema: + title: Notification action request + type: object + properties: + markAsRead: + type: boolean + description: True to mark as read, false to mark as unread + example: true + required: + - markAsRead + responses: + 200: + description: | + OK. + Successful response with updated Notification. + headers: + Content-Type: + description: | + The content type of the body. + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/Notification' + 400: + $ref: '#/components/responses/BadRequest' + 401: + $ref: '#/components/responses/Unauthorized' + 403: + $ref: '#/components/responses/Forbidden' + 404: + $ref: '#/components/responses/NotFound' + 500: + $ref: '#/components/responses/InternalServerError' + security: + - OAuth2Security: + - apim:notifications_manage + - apim:notifications_view + x-code-samples: + - lang: Curl + source: | + curl -k -X PATCH -H "Authorization:Bearer ae4eae22-3f65-387b-a171-d37eaa366fa8" \ + -H "Content-Type: application/json" -d @data.json \ + "https://localhost:9443/api/am/publisher/v4/notifications/863aef13-dab4-48b4-bf58-7363cd29601a" + operationId: changeNotificationStatus + + delete: + tags: + - Notifications + summary: Delete a Notification + description: | + This operation can be used to delete a notification. + parameters: + - $ref: '#/components/parameters/notificationId' + responses: + 200: + description: | + OK. + Resource successfully deleted. + 401: + $ref: '#/components/responses/Unauthorized' + 403: + $ref: '#/components/responses/Forbidden' + 404: + $ref: '#/components/responses/NotFound' + 500: + $ref: '#/components/responses/InternalServerError' + security: + - OAuth2Security: + - apim:notifications_manage + - apim:notifications_view + x-code-samples: + - lang: Curl + source: | + curl -k -X DELETE -H "Authorization:Bearer ae4eae22-3f65-387b-a171-d37eaa366fa8" \ + "https://localhost:9443/api/am/publisher/v4/notifications/863aef13-dab4-48b4-bf58-7363cd29601a" + operationId: deleteNotification + components: schemas: + Notification: + title: Notification + description: Object with basic notification details. + type: object + properties: + id: + type: string + readOnly: true + example: 863aef13-dab4-48b4-bf58-7363cd29601a + type: + type: string + readOnly: true + example: APPLICATION_CREATION + content: + type: string + maxLength: 512 + readOnly: true + example: Application creation is rejected due to some reason. + createdTime: + type: string + readOnly: true + example: 2021-02-11 09:57:25 + isRead: + type: boolean + example: false + NotificationList: + title: Notification List + description: A list of notifications. + type: object + properties: + count: + type: integer + readOnly: true + description: Number of notifications returned. + example: 1 + unreadCount: + type: integer + readOnly: true + description: Number of unread notifications returned. + example: 5 + list: + type: array + readOnly: true + items: + $ref: '#/components/schemas/Notification' + pagination: + $ref: '#/components/schemas/Pagination' Comment: title: Comment required: @@ -12640,6 +12895,15 @@ components: moreInfo: "" error: [] parameters: + notificationId: + name: notificationId + in: path + description: | + UUID of the notification. + required: true + schema: + type: string + example: 863aef13-dab4-48b4-bf58-7363cd29601a replyLimit: name: replyLimit in: query @@ -13130,3 +13394,5 @@ components: apim:tier_manage: View, update and delete throttling policies apim:api_list_view: View, Retrieve API list apim:api_definition_view: View, Retrieve API definition + apim:notifications_manage: Manage notifications + apim:notifications_view: View notifications diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1.common/src/gen/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/dto/NotificationActionRequestDTO.java b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1.common/src/gen/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/dto/NotificationActionRequestDTO.java new file mode 100644 index 000000000000..69b4ae692a62 --- /dev/null +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1.common/src/gen/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/dto/NotificationActionRequestDTO.java @@ -0,0 +1,83 @@ +package org.wso2.carbon.apimgt.rest.api.publisher.v1.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import javax.validation.constraints.*; + + +import io.swagger.annotations.*; +import java.util.Objects; + +import javax.xml.bind.annotation.*; +import org.wso2.carbon.apimgt.rest.api.common.annotations.Scope; +import com.fasterxml.jackson.annotation.JsonCreator; + +import javax.validation.Valid; + + + +public class NotificationActionRequestDTO { + + private Boolean markAsRead = null; + + /** + * True to mark as read, false to mark as unread + **/ + public NotificationActionRequestDTO markAsRead(Boolean markAsRead) { + this.markAsRead = markAsRead; + return this; + } + + + @ApiModelProperty(example = "true", required = true, value = "True to mark as read, false to mark as unread") + @JsonProperty("markAsRead") + @NotNull + public Boolean isMarkAsRead() { + return markAsRead; + } + public void setMarkAsRead(Boolean markAsRead) { + this.markAsRead = markAsRead; + } + + + @Override + public boolean equals(java.lang.Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + NotificationActionRequestDTO notificationActionRequest = (NotificationActionRequestDTO) o; + return Objects.equals(markAsRead, notificationActionRequest.markAsRead); + } + + @Override + public int hashCode() { + return Objects.hash(markAsRead); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class NotificationActionRequestDTO {\n"); + + sb.append(" markAsRead: ").append(toIndentedString(markAsRead)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(java.lang.Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } +} + diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1.common/src/gen/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/dto/NotificationDTO.java b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1.common/src/gen/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/dto/NotificationDTO.java new file mode 100644 index 000000000000..b93fbcba51b3 --- /dev/null +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1.common/src/gen/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/dto/NotificationDTO.java @@ -0,0 +1,164 @@ +package org.wso2.carbon.apimgt.rest.api.publisher.v1.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import javax.validation.constraints.*; + +/** + * Object with basic notification details. + **/ + +import io.swagger.annotations.*; +import java.util.Objects; + +import javax.xml.bind.annotation.*; +import org.wso2.carbon.apimgt.rest.api.common.annotations.Scope; +import com.fasterxml.jackson.annotation.JsonCreator; + +import javax.validation.Valid; + +@ApiModel(description = "Object with basic notification details.") + +public class NotificationDTO { + + private String id = null; + private String type = null; + private String content = null; + private String createdTime = null; + private Boolean isRead = null; + + /** + **/ + public NotificationDTO id(String id) { + this.id = id; + return this; + } + + + @ApiModelProperty(example = "863aef13-dab4-48b4-bf58-7363cd29601a", value = "") + @JsonProperty("id") + public String getId() { + return id; + } + public void setId(String id) { + this.id = id; + } + + /** + **/ + public NotificationDTO type(String type) { + this.type = type; + return this; + } + + + @ApiModelProperty(example = "APPLICATION_CREATION", value = "") + @JsonProperty("type") + public String getType() { + return type; + } + public void setType(String type) { + this.type = type; + } + + /** + **/ + public NotificationDTO content(String content) { + this.content = content; + return this; + } + + + @ApiModelProperty(example = "Application creation is rejected due to some reason.", value = "") + @JsonProperty("content") + @Size(max=512) public String getContent() { + return content; + } + public void setContent(String content) { + this.content = content; + } + + /** + **/ + public NotificationDTO createdTime(String createdTime) { + this.createdTime = createdTime; + return this; + } + + + @ApiModelProperty(example = "2021-02-11 09:57:25", value = "") + @JsonProperty("createdTime") + public String getCreatedTime() { + return createdTime; + } + public void setCreatedTime(String createdTime) { + this.createdTime = createdTime; + } + + /** + **/ + public NotificationDTO isRead(Boolean isRead) { + this.isRead = isRead; + return this; + } + + + @ApiModelProperty(example = "false", value = "") + @JsonProperty("isRead") + public Boolean isIsRead() { + return isRead; + } + public void setIsRead(Boolean isRead) { + this.isRead = isRead; + } + + + @Override + public boolean equals(java.lang.Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + NotificationDTO notification = (NotificationDTO) o; + return Objects.equals(id, notification.id) && + Objects.equals(type, notification.type) && + Objects.equals(content, notification.content) && + Objects.equals(createdTime, notification.createdTime) && + Objects.equals(isRead, notification.isRead); + } + + @Override + public int hashCode() { + return Objects.hash(id, type, content, createdTime, isRead); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class NotificationDTO {\n"); + + sb.append(" id: ").append(toIndentedString(id)).append("\n"); + sb.append(" type: ").append(toIndentedString(type)).append("\n"); + sb.append(" content: ").append(toIndentedString(content)).append("\n"); + sb.append(" createdTime: ").append(toIndentedString(createdTime)).append("\n"); + sb.append(" isRead: ").append(toIndentedString(isRead)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(java.lang.Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } +} + diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1.common/src/gen/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/dto/NotificationListDTO.java b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1.common/src/gen/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/dto/NotificationListDTO.java new file mode 100644 index 000000000000..831cfa377f17 --- /dev/null +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1.common/src/gen/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/dto/NotificationListDTO.java @@ -0,0 +1,152 @@ +package org.wso2.carbon.apimgt.rest.api.publisher.v1.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import java.util.ArrayList; +import java.util.List; +import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.NotificationDTO; +import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.PaginationDTO; +import javax.validation.constraints.*; + +/** + * A list of notifications. + **/ + +import io.swagger.annotations.*; +import java.util.Objects; + +import javax.xml.bind.annotation.*; +import org.wso2.carbon.apimgt.rest.api.common.annotations.Scope; +import com.fasterxml.jackson.annotation.JsonCreator; + +import javax.validation.Valid; + +@ApiModel(description = "A list of notifications.") + +public class NotificationListDTO { + + private Integer count = null; + private Integer unreadCount = null; + private List list = new ArrayList(); + private PaginationDTO pagination = null; + + /** + * Number of notifications returned. + **/ + public NotificationListDTO count(Integer count) { + this.count = count; + return this; + } + + + @ApiModelProperty(example = "1", value = "Number of notifications returned.") + @JsonProperty("count") + public Integer getCount() { + return count; + } + public void setCount(Integer count) { + this.count = count; + } + + /** + * Number of unread notifications returned. + **/ + public NotificationListDTO unreadCount(Integer unreadCount) { + this.unreadCount = unreadCount; + return this; + } + + + @ApiModelProperty(example = "5", value = "Number of unread notifications returned.") + @JsonProperty("unreadCount") + public Integer getUnreadCount() { + return unreadCount; + } + public void setUnreadCount(Integer unreadCount) { + this.unreadCount = unreadCount; + } + + /** + **/ + public NotificationListDTO list(List list) { + this.list = list; + return this; + } + + + @ApiModelProperty(value = "") + @Valid + @JsonProperty("list") + public List getList() { + return list; + } + public void setList(List list) { + this.list = list; + } + + /** + **/ + public NotificationListDTO pagination(PaginationDTO pagination) { + this.pagination = pagination; + return this; + } + + + @ApiModelProperty(value = "") + @Valid + @JsonProperty("pagination") + public PaginationDTO getPagination() { + return pagination; + } + public void setPagination(PaginationDTO pagination) { + this.pagination = pagination; + } + + + @Override + public boolean equals(java.lang.Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + NotificationListDTO notificationList = (NotificationListDTO) o; + return Objects.equals(count, notificationList.count) && + Objects.equals(unreadCount, notificationList.unreadCount) && + Objects.equals(list, notificationList.list) && + Objects.equals(pagination, notificationList.pagination); + } + + @Override + public int hashCode() { + return Objects.hash(count, unreadCount, list, pagination); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class NotificationListDTO {\n"); + + sb.append(" count: ").append(toIndentedString(count)).append("\n"); + sb.append(" unreadCount: ").append(toIndentedString(unreadCount)).append("\n"); + sb.append(" list: ").append(toIndentedString(list)).append("\n"); + sb.append(" pagination: ").append(toIndentedString(pagination)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(java.lang.Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } +} + diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/gen/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/NotificationsApi.java b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/gen/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/NotificationsApi.java new file mode 100644 index 000000000000..0a5f36665146 --- /dev/null +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/gen/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/NotificationsApi.java @@ -0,0 +1,139 @@ +package org.wso2.carbon.apimgt.rest.api.publisher.v1; + +import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.ErrorDTO; +import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.NotificationActionRequestDTO; +import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.NotificationDTO; +import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.NotificationListDTO; +import org.wso2.carbon.apimgt.rest.api.publisher.v1.NotificationsApiService; +import org.wso2.carbon.apimgt.rest.api.publisher.v1.impl.NotificationsApiServiceImpl; +import org.wso2.carbon.apimgt.api.APIManagementException; + +import javax.ws.rs.*; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.SecurityContext; +import javax.inject.Inject; + +import io.swagger.annotations.*; +import java.io.InputStream; + +import org.apache.cxf.jaxrs.ext.MessageContext; +import org.apache.cxf.jaxrs.ext.multipart.Attachment; +import org.apache.cxf.jaxrs.ext.multipart.Multipart; + +import java.util.Map; +import java.util.List; +import javax.validation.constraints.*; +@Path("/notifications") + +@Api(description = "the notifications API") + + + + +public class NotificationsApi { + + @Context MessageContext securityContext; + +NotificationsApiService delegate = new NotificationsApiServiceImpl(); + + + @PATCH + @Path("/{notificationId}") + @Consumes({ "application/json" }) + @Produces({ "application/json" }) + @ApiOperation(value = "Mark a notification as read or unread", notes = "This operation can be used to mark a notification as read or unread. ", response = NotificationDTO.class, authorizations = { + @Authorization(value = "OAuth2Security", scopes = { + @AuthorizationScope(scope = "apim:notifications_manage", description = "Manage notifications"), + @AuthorizationScope(scope = "apim:notifications_view", description = "View notifications") + }) + }, tags={ "Notifications", }) + @ApiResponses(value = { + @ApiResponse(code = 200, message = "OK. Successful response with updated Notification. ", response = NotificationDTO.class), + @ApiResponse(code = 400, message = "Bad Request. Invalid request or validation error.", response = ErrorDTO.class), + @ApiResponse(code = 401, message = "Unauthorized. The user is not authorized.", response = ErrorDTO.class), + @ApiResponse(code = 403, message = "Forbidden. The request must be conditional but no condition has been specified.", response = ErrorDTO.class), + @ApiResponse(code = 404, message = "Not Found. The specified resource does not exist.", response = ErrorDTO.class), + @ApiResponse(code = 500, message = "Internal Server Error.", response = ErrorDTO.class) }) + public Response changeNotificationStatus(@ApiParam(value = "UUID of the notification. ",required=true) @PathParam("notificationId") String notificationId, @ApiParam(value = "" ,required=true) NotificationActionRequestDTO notificationActionRequestDTO) throws APIManagementException{ + return delegate.changeNotificationStatus(notificationId, notificationActionRequestDTO, securityContext); + } + + @DELETE + + + @Produces({ "application/json" }) + @ApiOperation(value = "Delete all notifications", notes = "This operation can be used to delete all the notifications of a user. ", response = Void.class, authorizations = { + @Authorization(value = "OAuth2Security", scopes = { + @AuthorizationScope(scope = "apim:notifications_manage", description = "Manage notifications"), + @AuthorizationScope(scope = "apim:notifications_view", description = "View notifications") + }) + }, tags={ "Notifications", }) + @ApiResponses(value = { + @ApiResponse(code = 200, message = "OK. Resource successfully deleted. ", response = Void.class), + @ApiResponse(code = 401, message = "Unauthorized. The user is not authorized.", response = ErrorDTO.class), + @ApiResponse(code = 404, message = "Not Found. The specified resource does not exist.", response = ErrorDTO.class), + @ApiResponse(code = 500, message = "Internal Server Error.", response = ErrorDTO.class) }) + public Response deleteAllNotifications() throws APIManagementException{ + return delegate.deleteAllNotifications(securityContext); + } + + @DELETE + @Path("/{notificationId}") + + @Produces({ "application/json" }) + @ApiOperation(value = "Delete a Notification", notes = "This operation can be used to delete a notification. ", response = Void.class, authorizations = { + @Authorization(value = "OAuth2Security", scopes = { + @AuthorizationScope(scope = "apim:notifications_manage", description = "Manage notifications"), + @AuthorizationScope(scope = "apim:notifications_view", description = "View notifications") + }) + }, tags={ "Notifications", }) + @ApiResponses(value = { + @ApiResponse(code = 200, message = "OK. Resource successfully deleted. ", response = Void.class), + @ApiResponse(code = 401, message = "Unauthorized. The user is not authorized.", response = ErrorDTO.class), + @ApiResponse(code = 403, message = "Forbidden. The request must be conditional but no condition has been specified.", response = ErrorDTO.class), + @ApiResponse(code = 404, message = "Not Found. The specified resource does not exist.", response = ErrorDTO.class), + @ApiResponse(code = 500, message = "Internal Server Error.", response = ErrorDTO.class) }) + public Response deleteNotification(@ApiParam(value = "UUID of the notification. ",required=true) @PathParam("notificationId") String notificationId) throws APIManagementException{ + return delegate.deleteNotification(notificationId, securityContext); + } + + @GET + + + @Produces({ "application/json" }) + @ApiOperation(value = "Retrieves all notifications for the user.", notes = "This operation can be used to retrieve all notifications for the user.", response = NotificationListDTO.class, authorizations = { + @Authorization(value = "OAuth2Security", scopes = { + @AuthorizationScope(scope = "apim:notifications_view", description = "View notifications") + }) + }, tags={ "Notifications", }) + @ApiResponses(value = { + @ApiResponse(code = 200, message = "OK. List of Notifications are returned. ", response = NotificationListDTO.class), + @ApiResponse(code = 403, message = "Forbidden. The request must be conditional but no condition has been specified.", response = ErrorDTO.class), + @ApiResponse(code = 404, message = "Not Found. The specified resource does not exist.", response = ErrorDTO.class), + @ApiResponse(code = 500, message = "Internal Server Error.", response = ErrorDTO.class) }) + public Response getNotifications( @ApiParam(value = "Maximum size of resource array to return. ", defaultValue="25") @DefaultValue("25") @QueryParam("limit") Integer limit, @ApiParam(value = "Starting point within the complete list of items qualified. ", defaultValue="0") @DefaultValue("0") @QueryParam("offset") Integer offset) throws APIManagementException{ + return delegate.getNotifications(limit, offset, securityContext); + } + + @PATCH + + + @Produces({ "application/json" }) + @ApiOperation(value = "Mark all notifications as read", notes = "This operation can be used to mark all notifications as read. ", response = NotificationListDTO.class, authorizations = { + @Authorization(value = "OAuth2Security", scopes = { + @AuthorizationScope(scope = "apim:notifications_manage", description = "Manage notifications"), + @AuthorizationScope(scope = "apim:notifications_view", description = "View notifications") + }) + }, tags={ "Notifications" }) + @ApiResponses(value = { + @ApiResponse(code = 200, message = "OK. Successful response with updated Notifications. ", response = NotificationListDTO.class), + @ApiResponse(code = 400, message = "Bad Request. Invalid request or validation error.", response = ErrorDTO.class), + @ApiResponse(code = 401, message = "Unauthorized. The user is not authorized.", response = ErrorDTO.class), + @ApiResponse(code = 403, message = "Forbidden. The request must be conditional but no condition has been specified.", response = ErrorDTO.class), + @ApiResponse(code = 404, message = "Not Found. The specified resource does not exist.", response = ErrorDTO.class), + @ApiResponse(code = 500, message = "Internal Server Error.", response = ErrorDTO.class) }) + public Response markAllNotificationsAsRead( @ApiParam(value = "Maximum size of resource array to return. ", defaultValue="25") @DefaultValue("25") @QueryParam("limit") Integer limit, @ApiParam(value = "Starting point within the complete list of items qualified. ", defaultValue="0") @DefaultValue("0") @QueryParam("offset") Integer offset) throws APIManagementException{ + return delegate.markAllNotificationsAsRead(limit, offset, securityContext); + } +} diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/gen/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/NotificationsApiService.java b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/gen/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/NotificationsApiService.java new file mode 100644 index 000000000000..a66a005a242f --- /dev/null +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/gen/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/NotificationsApiService.java @@ -0,0 +1,31 @@ +package org.wso2.carbon.apimgt.rest.api.publisher.v1; + +import org.wso2.carbon.apimgt.rest.api.publisher.v1.*; +import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.*; + +import org.apache.cxf.jaxrs.ext.MessageContext; +import org.apache.cxf.jaxrs.ext.multipart.Attachment; +import org.apache.cxf.jaxrs.ext.multipart.Multipart; + +import org.wso2.carbon.apimgt.api.APIManagementException; + +import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.ErrorDTO; +import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.NotificationActionRequestDTO; +import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.NotificationDTO; +import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.NotificationListDTO; + +import java.util.List; + +import java.io.InputStream; + +import javax.ws.rs.core.Response; +import javax.ws.rs.core.SecurityContext; + + +public interface NotificationsApiService { + public Response changeNotificationStatus(String notificationId, NotificationActionRequestDTO notificationActionRequestDTO, MessageContext messageContext) throws APIManagementException; + public Response deleteAllNotifications(MessageContext messageContext) throws APIManagementException; + public Response deleteNotification(String notificationId, MessageContext messageContext) throws APIManagementException; + public Response getNotifications(Integer limit, Integer offset, MessageContext messageContext) throws APIManagementException; + public Response markAllNotificationsAsRead(Integer limit, Integer offset, MessageContext messageContext) throws APIManagementException; +} diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/impl/NotificationsApiServiceImpl.java b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/impl/NotificationsApiServiceImpl.java new file mode 100644 index 000000000000..c2173243f6c0 --- /dev/null +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/impl/NotificationsApiServiceImpl.java @@ -0,0 +1,122 @@ +package org.wso2.carbon.apimgt.rest.api.publisher.v1.impl; + +import org.wso2.carbon.apimgt.api.APIManagementException; +import org.wso2.carbon.apimgt.api.APIProvider; +import org.wso2.carbon.apimgt.api.model.Notification; +import org.wso2.carbon.apimgt.api.model.NotificationList; +import org.wso2.carbon.apimgt.rest.api.common.RestApiCommonUtil; +import org.wso2.carbon.apimgt.rest.api.common.RestApiConstants; + +import org.apache.cxf.jaxrs.ext.MessageContext; + +import org.wso2.carbon.apimgt.rest.api.publisher.v1.NotificationsApiService; +import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.NotificationActionRequestDTO; +import org.wso2.carbon.apimgt.rest.api.util.utils.RestApiUtil; + +import javax.ws.rs.core.Response; + + +public class NotificationsApiServiceImpl implements NotificationsApiService { + + private final String portalToDisplay = RestApiConstants.PUBLISHER_PORTAL; + + /** + * Changes the status of a notification. + * + * @param notificationId The ID of the notification to change. + * @param notificationActionRequestDTO The request containing the action to perform on the notification + * eg: Set markAsRead to true to mark the notification as read or false to + * mark it as unread. + * @param messageContext The message context for the request. + * @return A Response object containing the updated notification. + * @throws APIManagementException If an error occurs while changing the notification status. + */ + @Override + public Response changeNotificationStatus(String notificationId, NotificationActionRequestDTO notificationActionRequestDTO, + MessageContext messageContext) throws APIManagementException { + String username = RestApiCommonUtil.getLoggedInUsername(); + String organization = RestApiUtil.getValidatedOrganization(messageContext); + APIProvider apiProvider = RestApiCommonUtil.getLoggedInUserProvider(); + Notification notification = apiProvider.changeNotificationStatus(username, organization, notificationId, + notificationActionRequestDTO.isMarkAsRead(), portalToDisplay); + return Response.ok().entity(notification).build(); + } + + /** + * Deletes a notification. + * + * @param notificationId The ID of the notification to delete. + * @param messageContext The message context for the request. + * @return A Response object indicating the result of the delete operation. + * @throws APIManagementException If an error occurs while deleting the notification. + */ + @Override + public Response deleteNotification(String notificationId, MessageContext messageContext) throws APIManagementException { + String username = RestApiCommonUtil.getLoggedInUsername(); + String organization = RestApiUtil.getValidatedOrganization(messageContext); + APIProvider apiProvider = RestApiCommonUtil.getLoggedInUserProvider(); + apiProvider.deleteNotification(username, organization, notificationId, portalToDisplay); + return Response.ok().build(); + } + + /** + * Retrieves a list of notifications. + * + * @param limit The maximum number of notifications to return. If null, the default limit is used. + * @param offset The starting point within the list of notifications. If null, the default offset is used. + * @param messageContext The message context for the request. + * @return A Response object containing the list of notifications. + * @throws APIManagementException If an error occurs while retrieving the notifications. + */ + @Override + public Response getNotifications(Integer limit, Integer offset, MessageContext messageContext) + throws APIManagementException { + + limit = limit != null ? limit : RestApiConstants.PAGINATION_LIMIT_DEFAULT; + offset = offset != null ? offset : RestApiConstants.PAGINATION_OFFSET_DEFAULT; + + String username = RestApiCommonUtil.getLoggedInUsername(); + String organization = RestApiUtil.getValidatedOrganization(messageContext); + APIProvider apiProvider = RestApiCommonUtil.getLoggedInUserProvider(); + + NotificationList notificationList = apiProvider.getNotifications(username, organization, portalToDisplay, limit, + offset); + return Response.ok().entity(notificationList).build(); + } + + /** + * Marks all notifications as read. + * + * @param limit The maximum number of notifications to return after marking as read. If null, the default limit is used. + * @param offset The starting point within the list of notifications. If null, the default offset is used. + * @param messageContext The message context for the request. + * @return A Response object containing the updated list of notifications. + * @throws APIManagementException If an error occurs while marking the notifications as read. + */ + @Override + public Response markAllNotificationsAsRead(Integer limit, Integer offset, MessageContext messageContext) + throws APIManagementException { + String username = RestApiCommonUtil.getLoggedInUsername(); + String organization = RestApiUtil.getValidatedOrganization(messageContext); + APIProvider apiProvider = RestApiCommonUtil.getLoggedInUserProvider(); + NotificationList notificationList = apiProvider.markAllNotificationsAsRead(username, organization, + portalToDisplay, limit, offset); + return Response.ok().entity(notificationList).build(); + } + + /** + * Deletes all notifications. + * + * @param messageContext The message context for the request. + * @return A Response object indicating the result of the delete operation. + * @throws APIManagementException If an error occurs while deleting the notifications. + */ + @Override + public Response deleteAllNotifications(MessageContext messageContext) throws APIManagementException { + String username = RestApiCommonUtil.getLoggedInUsername(); + String organization = RestApiUtil.getValidatedOrganization(messageContext); + APIProvider apiProvider = RestApiCommonUtil.getLoggedInUserProvider(); + apiProvider.deleteAllNotifications(username, organization, portalToDisplay); + return Response.ok().build(); + } +} diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/main/resources/publisher-api.yaml b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/main/resources/publisher-api.yaml index b7ba04b85b54..b67e8fa99527 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/main/resources/publisher-api.yaml +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/main/resources/publisher-api.yaml @@ -8681,8 +8681,263 @@ paths: "https://127.0.0.1:9443/api/am/publisher/v4/api-products/890a4f4d-09eb-4877-a323-57f6ce2ed79b/lifecycle-state/pending-tasks"' operationId: deleteAPIProductLifecycleStatePendingTasks + ###################################################### + # The "Portal Notifications" resource APIs + ###################################################### + /notifications: + get: + tags: + - Notifications + summary: Retrieves all notifications for the user. + description: This operation can be used to retrieve all notifications for the user. + parameters: + - $ref: '#/components/parameters/limit' + - $ref: '#/components/parameters/offset' + responses: + 200: + description: | + OK. + List of Notifications are returned. + headers: + Content-Type: + description: | + The content type of the body. + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/NotificationList' + 403: + $ref: '#/components/responses/Forbidden' + 404: + $ref: '#/components/responses/NotFound' + 500: + $ref: '#/components/responses/InternalServerError' + security: + - OAuth2Security: + - apim:notifications_view + x-code-samples: + - lang: Curl + source: 'curl -k -H "Authorization: Bearer ae4eae22-3f65-387b-a171-d37eaa366fa8" + "https://localhost:9443/api/am/publisher/v4/notifications"' + operationId: getNotifications + + patch: + tags: + - Notifications + summary: Mark all notifications as read + description: | + This operation can be used to mark all notifications as read. + parameters: + - $ref: '#/components/parameters/limit' + - $ref: '#/components/parameters/offset' + responses: + 200: + description: | + OK. + Successful response with updated Notifications. + headers: + Content-Type: + description: | + The content type of the body. + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/NotificationList' + 400: + $ref: '#/components/responses/BadRequest' + 401: + $ref: '#/components/responses/Unauthorized' + 403: + $ref: '#/components/responses/Forbidden' + 404: + $ref: '#/components/responses/NotFound' + 500: + $ref: '#/components/responses/InternalServerError' + security: + - OAuth2Security: + - apim:notifications_manage + - apim:notifications_view + x-code-samples: + - lang: Curl + source: | + curl -k -X PATCH -H "Authorization: Bearer ae4eae22-3f65-387b-a171-d37eaa366fa8" \ + -H "Content-Type: application/json" \ + "https://localhost:9443/api/am/publisher/v4/notifications/update-status" + operationId: markAllNotificationsAsRead + + delete: + tags: + - Notifications + summary: Delete all notifications + description: | + This operation can be used to delete all the notifications of a user. + responses: + 200: + description: | + OK. + Resource successfully deleted. + 401: + $ref: '#/components/responses/Unauthorized' + 404: + $ref: '#/components/responses/NotFound' + 500: + $ref: '#/components/responses/InternalServerError' + security: + - OAuth2Security: + - apim:notifications_manage + - apim:notifications_view + x-code-samples: + - lang: Curl + source: | + curl -k -X DELETE -H "Authorization: Bearer ae4eae22-3f65-387b-a171-d37eaa366fa8" + "https://localhost:9443/api/am/publisher/v4/notifications" + operationId: deleteAllNotifications + + /notifications/{notificationId}: + patch: + tags: + - Notifications + summary: Mark a notification as read or unread + description: | + This operation can be used to mark a notification as read or unread. + parameters: + - $ref: '#/components/parameters/notificationId' + requestBody: + required: true + content: + application/json: + schema: + title: Notification action request + type: object + properties: + markAsRead: + type: boolean + description: True to mark as read, false to mark as unread + example: true + required: + - markAsRead + responses: + 200: + description: | + OK. + Successful response with updated Notification. + headers: + Content-Type: + description: | + The content type of the body. + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/Notification' + 400: + $ref: '#/components/responses/BadRequest' + 401: + $ref: '#/components/responses/Unauthorized' + 403: + $ref: '#/components/responses/Forbidden' + 404: + $ref: '#/components/responses/NotFound' + 500: + $ref: '#/components/responses/InternalServerError' + security: + - OAuth2Security: + - apim:notifications_manage + - apim:notifications_view + x-code-samples: + - lang: Curl + source: | + curl -k -X PATCH -H "Authorization:Bearer ae4eae22-3f65-387b-a171-d37eaa366fa8" \ + -H "Content-Type: application/json" -d @data.json \ + "https://localhost:9443/api/am/publisher/v4/notifications/863aef13-dab4-48b4-bf58-7363cd29601a" + operationId: changeNotificationStatus + + delete: + tags: + - Notifications + summary: Delete a Notification + description: | + This operation can be used to delete a notification. + parameters: + - $ref: '#/components/parameters/notificationId' + responses: + 200: + description: | + OK. + Resource successfully deleted. + 401: + $ref: '#/components/responses/Unauthorized' + 403: + $ref: '#/components/responses/Forbidden' + 404: + $ref: '#/components/responses/NotFound' + 500: + $ref: '#/components/responses/InternalServerError' + security: + - OAuth2Security: + - apim:notifications_manage + - apim:notifications_view + x-code-samples: + - lang: Curl + source: | + curl -k -X DELETE -H "Authorization:Bearer ae4eae22-3f65-387b-a171-d37eaa366fa8" \ + "https://localhost:9443/api/am/publisher/v4/notifications/863aef13-dab4-48b4-bf58-7363cd29601a" + operationId: deleteNotification + components: schemas: + Notification: + title: Notification + description: Object with basic notification details. + type: object + properties: + id: + type: string + readOnly: true + example: 863aef13-dab4-48b4-bf58-7363cd29601a + type: + type: string + readOnly: true + example: APPLICATION_CREATION + content: + type: string + maxLength: 512 + readOnly: true + example: Application creation is rejected due to some reason. + createdTime: + type: string + readOnly: true + example: 2021-02-11 09:57:25 + isRead: + type: boolean + example: false + NotificationList: + title: Notification List + description: A list of notifications. + type: object + properties: + count: + type: integer + readOnly: true + description: Number of notifications returned. + example: 1 + unreadCount: + type: integer + readOnly: true + description: Number of unread notifications returned. + example: 5 + list: + type: array + readOnly: true + items: + $ref: '#/components/schemas/Notification' + pagination: + $ref: '#/components/schemas/Pagination' Comment: title: Comment required: @@ -12640,6 +12895,15 @@ components: moreInfo: "" error: [] parameters: + notificationId: + name: notificationId + in: path + description: | + UUID of the notification. + required: true + schema: + type: string + example: 863aef13-dab4-48b4-bf58-7363cd29601a replyLimit: name: replyLimit in: query @@ -13130,3 +13394,5 @@ components: apim:tier_manage: View, update and delete throttling policies apim:api_list_view: View, Retrieve API list apim:api_definition_view: View, Retrieve API definition + apim:notifications_manage: Manage notifications + apim:notifications_view: View notifications diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/main/webapp/WEB-INF/beans.xml b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/main/webapp/WEB-INF/beans.xml index c71906db5707..682b340b5baf 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/main/webapp/WEB-INF/beans.xml +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/main/webapp/WEB-INF/beans.xml @@ -36,6 +36,7 @@ + diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/main/webapp/WEB-INF/web.xml b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/main/webapp/WEB-INF/web.xml index b75d351bf530..4e637fc529bb 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/main/webapp/WEB-INF/web.xml +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/main/webapp/WEB-INF/web.xml @@ -52,7 +52,8 @@ org.wso2.carbon.apimgt.rest.api.publisher.v1.KeyManagersApi, org.wso2.carbon.apimgt.rest.api.publisher.v1.OperationPoliciesApi, org.wso2.carbon.apimgt.rest.api.publisher.v1.LinterCustomRulesApi, - org.wso2.carbon.apimgt.rest.api.publisher.v1.GatewayPoliciesApi + org.wso2.carbon.apimgt.rest.api.publisher.v1.GatewayPoliciesApi, + org.wso2.carbon.apimgt.rest.api.publisher.v1.NotificationsApi diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/gen/java/org/wso2/carbon/apimgt/rest/api/store/v1/NotificationsApi.java b/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/gen/java/org/wso2/carbon/apimgt/rest/api/store/v1/NotificationsApi.java new file mode 100644 index 000000000000..2905fb6e021b --- /dev/null +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/gen/java/org/wso2/carbon/apimgt/rest/api/store/v1/NotificationsApi.java @@ -0,0 +1,139 @@ +package org.wso2.carbon.apimgt.rest.api.store.v1; + +import org.wso2.carbon.apimgt.rest.api.store.v1.dto.ErrorDTO; +import org.wso2.carbon.apimgt.rest.api.store.v1.dto.NotificationActionRequestDTO; +import org.wso2.carbon.apimgt.rest.api.store.v1.dto.NotificationDTO; +import org.wso2.carbon.apimgt.rest.api.store.v1.dto.NotificationListDTO; +import org.wso2.carbon.apimgt.rest.api.store.v1.NotificationsApiService; +import org.wso2.carbon.apimgt.rest.api.store.v1.impl.NotificationsApiServiceImpl; +import org.wso2.carbon.apimgt.api.APIManagementException; + +import javax.ws.rs.*; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.SecurityContext; +import javax.inject.Inject; + +import io.swagger.annotations.*; +import java.io.InputStream; + +import org.apache.cxf.jaxrs.ext.MessageContext; +import org.apache.cxf.jaxrs.ext.multipart.Attachment; +import org.apache.cxf.jaxrs.ext.multipart.Multipart; + +import java.util.Map; +import java.util.List; +import javax.validation.constraints.*; +@Path("/notifications") + +@Api(description = "the notifications API") + + + + +public class NotificationsApi { + + @Context MessageContext securityContext; + +NotificationsApiService delegate = new NotificationsApiServiceImpl(); + + + @PATCH + @Path("/{notificationId}") + @Consumes({ "application/json" }) + @Produces({ "application/json" }) + @ApiOperation(value = "Mark a notification as read or unread", notes = "This operation can be used to mark a notification as read or unread. ", response = NotificationDTO.class, authorizations = { + @Authorization(value = "OAuth2Security", scopes = { + @AuthorizationScope(scope = "apim:notifications_manage", description = "Manage notifications"), + @AuthorizationScope(scope = "apim:notifications_view", description = "View notifications") + }) + }, tags={ "Notifications", }) + @ApiResponses(value = { + @ApiResponse(code = 200, message = "OK. Successful response with updated Notification. ", response = NotificationDTO.class), + @ApiResponse(code = 400, message = "Bad Request. Invalid request or validation error.", response = ErrorDTO.class), + @ApiResponse(code = 401, message = "Unauthorized. The user is not authorized.", response = ErrorDTO.class), + @ApiResponse(code = 403, message = "Forbidden. The request must be conditional but no condition has been specified.", response = ErrorDTO.class), + @ApiResponse(code = 404, message = "Not Found. The specified resource does not exist.", response = ErrorDTO.class), + @ApiResponse(code = 500, message = "Internal Server Error.", response = ErrorDTO.class) }) + public Response changeNotificationStatus(@ApiParam(value = "UUID of the notification. ",required=true) @PathParam("notificationId") String notificationId, @ApiParam(value = "" ,required=true) NotificationActionRequestDTO notificationActionRequestDTO) throws APIManagementException{ + return delegate.changeNotificationStatus(notificationId, notificationActionRequestDTO, securityContext); + } + + @DELETE + + + @Produces({ "application/json" }) + @ApiOperation(value = "Delete all notifications", notes = "This operation can be used to delete all the notifications of a user. ", response = Void.class, authorizations = { + @Authorization(value = "OAuth2Security", scopes = { + @AuthorizationScope(scope = "apim:notifications_manage", description = "Manage notifications"), + @AuthorizationScope(scope = "apim:notifications_view", description = "View notifications") + }) + }, tags={ "Notifications", }) + @ApiResponses(value = { + @ApiResponse(code = 200, message = "OK. Resource successfully deleted. ", response = Void.class), + @ApiResponse(code = 401, message = "Unauthorized. The user is not authorized.", response = ErrorDTO.class), + @ApiResponse(code = 404, message = "Not Found. The specified resource does not exist.", response = ErrorDTO.class), + @ApiResponse(code = 500, message = "Internal Server Error.", response = ErrorDTO.class) }) + public Response deleteAllNotifications() throws APIManagementException{ + return delegate.deleteAllNotifications(securityContext); + } + + @DELETE + @Path("/{notificationId}") + + @Produces({ "application/json" }) + @ApiOperation(value = "Delete a Notification", notes = "This operation can be used to delete a notification. ", response = Void.class, authorizations = { + @Authorization(value = "OAuth2Security", scopes = { + @AuthorizationScope(scope = "apim:notifications_manage", description = "Manage notifications"), + @AuthorizationScope(scope = "apim:notifications_view", description = "View notifications") + }) + }, tags={ "Notifications", }) + @ApiResponses(value = { + @ApiResponse(code = 200, message = "OK. Resource successfully deleted. ", response = Void.class), + @ApiResponse(code = 401, message = "Unauthorized. The user is not authorized.", response = ErrorDTO.class), + @ApiResponse(code = 403, message = "Forbidden. The request must be conditional but no condition has been specified.", response = ErrorDTO.class), + @ApiResponse(code = 404, message = "Not Found. The specified resource does not exist.", response = ErrorDTO.class), + @ApiResponse(code = 500, message = "Internal Server Error.", response = ErrorDTO.class) }) + public Response deleteNotification(@ApiParam(value = "UUID of the notification. ",required=true) @PathParam("notificationId") String notificationId) throws APIManagementException{ + return delegate.deleteNotification(notificationId, securityContext); + } + + @GET + + + @Produces({ "application/json" }) + @ApiOperation(value = "Retrieves all notifications for the user.", notes = "This operation can be used to retrieve all notifications for the user.", response = NotificationListDTO.class, authorizations = { + @Authorization(value = "OAuth2Security", scopes = { + @AuthorizationScope(scope = "apim:notifications_view", description = "View notifications") + }) + }, tags={ "Notifications", }) + @ApiResponses(value = { + @ApiResponse(code = 200, message = "OK. List of Notifications are returned. ", response = NotificationListDTO.class), + @ApiResponse(code = 403, message = "Forbidden. The request must be conditional but no condition has been specified.", response = ErrorDTO.class), + @ApiResponse(code = 404, message = "Not Found. The specified resource does not exist.", response = ErrorDTO.class), + @ApiResponse(code = 500, message = "Internal Server Error.", response = ErrorDTO.class) }) + public Response getNotifications( @ApiParam(value = "Maximum size of resource array to return. ", defaultValue="25") @DefaultValue("25") @QueryParam("limit") Integer limit, @ApiParam(value = "Starting point within the complete list of items qualified. ", defaultValue="0") @DefaultValue("0") @QueryParam("offset") Integer offset) throws APIManagementException{ + return delegate.getNotifications(limit, offset, securityContext); + } + + @PATCH + + + @Produces({ "application/json" }) + @ApiOperation(value = "Mark all notifications as read", notes = "This operation can be used to mark all notifications as read. ", response = NotificationListDTO.class, authorizations = { + @Authorization(value = "OAuth2Security", scopes = { + @AuthorizationScope(scope = "apim:notifications_manage", description = "Manage notifications"), + @AuthorizationScope(scope = "apim:notifications_view", description = "View notifications") + }) + }, tags={ "Notifications" }) + @ApiResponses(value = { + @ApiResponse(code = 200, message = "OK. Successful response with updated Notifications. ", response = NotificationListDTO.class), + @ApiResponse(code = 400, message = "Bad Request. Invalid request or validation error.", response = ErrorDTO.class), + @ApiResponse(code = 401, message = "Unauthorized. The user is not authorized.", response = ErrorDTO.class), + @ApiResponse(code = 403, message = "Forbidden. The request must be conditional but no condition has been specified.", response = ErrorDTO.class), + @ApiResponse(code = 404, message = "Not Found. The specified resource does not exist.", response = ErrorDTO.class), + @ApiResponse(code = 500, message = "Internal Server Error.", response = ErrorDTO.class) }) + public Response markAllNotificationsAsRead( @ApiParam(value = "Maximum size of resource array to return. ", defaultValue="25") @DefaultValue("25") @QueryParam("limit") Integer limit, @ApiParam(value = "Starting point within the complete list of items qualified. ", defaultValue="0") @DefaultValue("0") @QueryParam("offset") Integer offset) throws APIManagementException{ + return delegate.markAllNotificationsAsRead(limit, offset, securityContext); + } +} diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/gen/java/org/wso2/carbon/apimgt/rest/api/store/v1/NotificationsApiService.java b/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/gen/java/org/wso2/carbon/apimgt/rest/api/store/v1/NotificationsApiService.java new file mode 100644 index 000000000000..010778d55b4e --- /dev/null +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/gen/java/org/wso2/carbon/apimgt/rest/api/store/v1/NotificationsApiService.java @@ -0,0 +1,31 @@ +package org.wso2.carbon.apimgt.rest.api.store.v1; + +import org.wso2.carbon.apimgt.rest.api.store.v1.*; +import org.wso2.carbon.apimgt.rest.api.store.v1.dto.*; + +import org.apache.cxf.jaxrs.ext.MessageContext; +import org.apache.cxf.jaxrs.ext.multipart.Attachment; +import org.apache.cxf.jaxrs.ext.multipart.Multipart; + +import org.wso2.carbon.apimgt.api.APIManagementException; + +import org.wso2.carbon.apimgt.rest.api.store.v1.dto.ErrorDTO; +import org.wso2.carbon.apimgt.rest.api.store.v1.dto.NotificationActionRequestDTO; +import org.wso2.carbon.apimgt.rest.api.store.v1.dto.NotificationDTO; +import org.wso2.carbon.apimgt.rest.api.store.v1.dto.NotificationListDTO; + +import java.util.List; + +import java.io.InputStream; + +import javax.ws.rs.core.Response; +import javax.ws.rs.core.SecurityContext; + + +public interface NotificationsApiService { + public Response changeNotificationStatus(String notificationId, NotificationActionRequestDTO notificationActionRequestDTO, MessageContext messageContext) throws APIManagementException; + public Response deleteAllNotifications(MessageContext messageContext) throws APIManagementException; + public Response deleteNotification(String notificationId, MessageContext messageContext) throws APIManagementException; + public Response getNotifications(Integer limit, Integer offset, MessageContext messageContext) throws APIManagementException; + public Response markAllNotificationsAsRead(Integer limit, Integer offset, MessageContext messageContext) throws APIManagementException; +} diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/gen/java/org/wso2/carbon/apimgt/rest/api/store/v1/dto/NotificationActionRequestDTO.java b/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/gen/java/org/wso2/carbon/apimgt/rest/api/store/v1/dto/NotificationActionRequestDTO.java new file mode 100644 index 000000000000..aa6e142c7759 --- /dev/null +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/gen/java/org/wso2/carbon/apimgt/rest/api/store/v1/dto/NotificationActionRequestDTO.java @@ -0,0 +1,83 @@ +package org.wso2.carbon.apimgt.rest.api.store.v1.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import javax.validation.constraints.*; + + +import io.swagger.annotations.*; +import java.util.Objects; + +import javax.xml.bind.annotation.*; +import org.wso2.carbon.apimgt.rest.api.common.annotations.Scope; +import com.fasterxml.jackson.annotation.JsonCreator; + +import javax.validation.Valid; + + + +public class NotificationActionRequestDTO { + + private Boolean markAsRead = null; + + /** + * True to mark as read, false to mark as unread + **/ + public NotificationActionRequestDTO markAsRead(Boolean markAsRead) { + this.markAsRead = markAsRead; + return this; + } + + + @ApiModelProperty(example = "true", required = true, value = "True to mark as read, false to mark as unread") + @JsonProperty("markAsRead") + @NotNull + public Boolean isMarkAsRead() { + return markAsRead; + } + public void setMarkAsRead(Boolean markAsRead) { + this.markAsRead = markAsRead; + } + + + @Override + public boolean equals(java.lang.Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + NotificationActionRequestDTO notificationActionRequest = (NotificationActionRequestDTO) o; + return Objects.equals(markAsRead, notificationActionRequest.markAsRead); + } + + @Override + public int hashCode() { + return Objects.hash(markAsRead); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class NotificationActionRequestDTO {\n"); + + sb.append(" markAsRead: ").append(toIndentedString(markAsRead)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(java.lang.Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } +} + diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/gen/java/org/wso2/carbon/apimgt/rest/api/store/v1/dto/NotificationDTO.java b/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/gen/java/org/wso2/carbon/apimgt/rest/api/store/v1/dto/NotificationDTO.java new file mode 100644 index 000000000000..cadd14c391c5 --- /dev/null +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/gen/java/org/wso2/carbon/apimgt/rest/api/store/v1/dto/NotificationDTO.java @@ -0,0 +1,164 @@ +package org.wso2.carbon.apimgt.rest.api.store.v1.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import javax.validation.constraints.*; + +/** + * Object with basic notification details. + **/ + +import io.swagger.annotations.*; +import java.util.Objects; + +import javax.xml.bind.annotation.*; +import org.wso2.carbon.apimgt.rest.api.common.annotations.Scope; +import com.fasterxml.jackson.annotation.JsonCreator; + +import javax.validation.Valid; + +@ApiModel(description = "Object with basic notification details.") + +public class NotificationDTO { + + private String id = null; + private String type = null; + private String content = null; + private String createdTime = null; + private Boolean isRead = null; + + /** + **/ + public NotificationDTO id(String id) { + this.id = id; + return this; + } + + + @ApiModelProperty(example = "863aef13-dab4-48b4-bf58-7363cd29601a", value = "") + @JsonProperty("id") + public String getId() { + return id; + } + public void setId(String id) { + this.id = id; + } + + /** + **/ + public NotificationDTO type(String type) { + this.type = type; + return this; + } + + + @ApiModelProperty(example = "APPLICATION_CREATION", value = "") + @JsonProperty("type") + public String getType() { + return type; + } + public void setType(String type) { + this.type = type; + } + + /** + **/ + public NotificationDTO content(String content) { + this.content = content; + return this; + } + + + @ApiModelProperty(example = "Application creation is rejected due to some reason.", value = "") + @JsonProperty("content") + @Size(max=512) public String getContent() { + return content; + } + public void setContent(String content) { + this.content = content; + } + + /** + **/ + public NotificationDTO createdTime(String createdTime) { + this.createdTime = createdTime; + return this; + } + + + @ApiModelProperty(example = "2021-02-11 09:57:25", value = "") + @JsonProperty("createdTime") + public String getCreatedTime() { + return createdTime; + } + public void setCreatedTime(String createdTime) { + this.createdTime = createdTime; + } + + /** + **/ + public NotificationDTO isRead(Boolean isRead) { + this.isRead = isRead; + return this; + } + + + @ApiModelProperty(example = "false", value = "") + @JsonProperty("isRead") + public Boolean isIsRead() { + return isRead; + } + public void setIsRead(Boolean isRead) { + this.isRead = isRead; + } + + + @Override + public boolean equals(java.lang.Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + NotificationDTO notification = (NotificationDTO) o; + return Objects.equals(id, notification.id) && + Objects.equals(type, notification.type) && + Objects.equals(content, notification.content) && + Objects.equals(createdTime, notification.createdTime) && + Objects.equals(isRead, notification.isRead); + } + + @Override + public int hashCode() { + return Objects.hash(id, type, content, createdTime, isRead); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class NotificationDTO {\n"); + + sb.append(" id: ").append(toIndentedString(id)).append("\n"); + sb.append(" type: ").append(toIndentedString(type)).append("\n"); + sb.append(" content: ").append(toIndentedString(content)).append("\n"); + sb.append(" createdTime: ").append(toIndentedString(createdTime)).append("\n"); + sb.append(" isRead: ").append(toIndentedString(isRead)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(java.lang.Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } +} + diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/gen/java/org/wso2/carbon/apimgt/rest/api/store/v1/dto/NotificationListDTO.java b/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/gen/java/org/wso2/carbon/apimgt/rest/api/store/v1/dto/NotificationListDTO.java new file mode 100644 index 000000000000..afc4ab4995da --- /dev/null +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/gen/java/org/wso2/carbon/apimgt/rest/api/store/v1/dto/NotificationListDTO.java @@ -0,0 +1,152 @@ +package org.wso2.carbon.apimgt.rest.api.store.v1.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import java.util.ArrayList; +import java.util.List; +import org.wso2.carbon.apimgt.rest.api.store.v1.dto.NotificationDTO; +import org.wso2.carbon.apimgt.rest.api.store.v1.dto.PaginationDTO; +import javax.validation.constraints.*; + +/** + * A list of notifications. + **/ + +import io.swagger.annotations.*; +import java.util.Objects; + +import javax.xml.bind.annotation.*; +import org.wso2.carbon.apimgt.rest.api.common.annotations.Scope; +import com.fasterxml.jackson.annotation.JsonCreator; + +import javax.validation.Valid; + +@ApiModel(description = "A list of notifications.") + +public class NotificationListDTO { + + private Integer count = null; + private Integer unreadCount = null; + private List list = new ArrayList(); + private PaginationDTO pagination = null; + + /** + * Number of notifications returned. + **/ + public NotificationListDTO count(Integer count) { + this.count = count; + return this; + } + + + @ApiModelProperty(example = "1", value = "Number of notifications returned.") + @JsonProperty("count") + public Integer getCount() { + return count; + } + public void setCount(Integer count) { + this.count = count; + } + + /** + * Number of unread notifications returned. + **/ + public NotificationListDTO unreadCount(Integer unreadCount) { + this.unreadCount = unreadCount; + return this; + } + + + @ApiModelProperty(example = "5", value = "Number of unread notifications returned.") + @JsonProperty("unreadCount") + public Integer getUnreadCount() { + return unreadCount; + } + public void setUnreadCount(Integer unreadCount) { + this.unreadCount = unreadCount; + } + + /** + **/ + public NotificationListDTO list(List list) { + this.list = list; + return this; + } + + + @ApiModelProperty(value = "") + @Valid + @JsonProperty("list") + public List getList() { + return list; + } + public void setList(List list) { + this.list = list; + } + + /** + **/ + public NotificationListDTO pagination(PaginationDTO pagination) { + this.pagination = pagination; + return this; + } + + + @ApiModelProperty(value = "") + @Valid + @JsonProperty("pagination") + public PaginationDTO getPagination() { + return pagination; + } + public void setPagination(PaginationDTO pagination) { + this.pagination = pagination; + } + + + @Override + public boolean equals(java.lang.Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + NotificationListDTO notificationList = (NotificationListDTO) o; + return Objects.equals(count, notificationList.count) && + Objects.equals(unreadCount, notificationList.unreadCount) && + Objects.equals(list, notificationList.list) && + Objects.equals(pagination, notificationList.pagination); + } + + @Override + public int hashCode() { + return Objects.hash(count, unreadCount, list, pagination); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class NotificationListDTO {\n"); + + sb.append(" count: ").append(toIndentedString(count)).append("\n"); + sb.append(" unreadCount: ").append(toIndentedString(unreadCount)).append("\n"); + sb.append(" list: ").append(toIndentedString(list)).append("\n"); + sb.append(" pagination: ").append(toIndentedString(pagination)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(java.lang.Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } +} + diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/store/v1/impl/NotificationsApiServiceImpl.java b/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/store/v1/impl/NotificationsApiServiceImpl.java new file mode 100644 index 000000000000..c5c672dbada7 --- /dev/null +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/store/v1/impl/NotificationsApiServiceImpl.java @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com/). + * + * WSO2 LLC. licenses this file to you 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 org.wso2.carbon.apimgt.rest.api.store.v1.impl; + +import org.apache.cxf.jaxrs.ext.MessageContext; + +import javax.ws.rs.core.Response; + +import org.wso2.carbon.apimgt.api.APIConsumer; +import org.wso2.carbon.apimgt.api.APIManagementException; +import org.wso2.carbon.apimgt.api.model.Notification; +import org.wso2.carbon.apimgt.api.model.NotificationList; +import org.wso2.carbon.apimgt.rest.api.common.RestApiCommonUtil; +import org.wso2.carbon.apimgt.rest.api.common.RestApiConstants; +import org.wso2.carbon.apimgt.rest.api.store.v1.NotificationsApiService; +import org.wso2.carbon.apimgt.rest.api.store.v1.dto.NotificationActionRequestDTO; +import org.wso2.carbon.apimgt.rest.api.util.utils.RestApiUtil; + +public class NotificationsApiServiceImpl implements NotificationsApiService { + + private final String portalToDisplay = RestApiConstants.DEV_PORTAL; + + /** + * Changes the status of a notification. + * + * @param notificationId The ID of the notification to change. + * @param notificationActionRequestDTO The request containing the action to perform on the notification + * eg: Set markAsRead to true to mark the notification as read or false to + * mark it as unread. + * @param messageContext The message context for the request. + * @return A Response object containing the updated notification. + * @throws APIManagementException If an error occurs while changing the notification status. + */ + @Override + public Response changeNotificationStatus(String notificationId, NotificationActionRequestDTO notificationActionRequestDTO, + MessageContext messageContext) throws APIManagementException { + String username = RestApiCommonUtil.getLoggedInUsername(); + String organization = RestApiUtil.getValidatedOrganization(messageContext); + APIConsumer apiConsumer = RestApiCommonUtil.getLoggedInUserConsumer(); + Notification notification = apiConsumer.changeNotificationStatus(username, organization, notificationId, + notificationActionRequestDTO.isMarkAsRead(), portalToDisplay); + return Response.ok().entity(notification).build(); + } + + /** + * Deletes a notification. + * + * @param notificationId The ID of the notification to delete. + * @param messageContext The message context for the request. + * @return A Response object indicating the result of the delete operation. + * @throws APIManagementException If an error occurs while deleting the notification. + */ + @Override + public Response deleteNotification(String notificationId, MessageContext messageContext) throws APIManagementException { + String username = RestApiCommonUtil.getLoggedInUsername(); + String organization = RestApiUtil.getValidatedOrganization(messageContext); + APIConsumer apiConsumer = RestApiCommonUtil.getLoggedInUserConsumer(); + apiConsumer.deleteNotification(username, organization, notificationId, portalToDisplay); + return Response.ok().build(); + } + + /** + * Retrieves a list of notifications. + * + * @param limit The maximum number of notifications to return. If null, the default limit is used. + * @param offset The starting point within the list of notifications. If null, the default offset is used. + * @param messageContext The message context for the request. + * @return A Response object containing the list of notifications. + * @throws APIManagementException If an error occurs while retrieving the notifications. + */ + @Override + public Response getNotifications(Integer limit, Integer offset, MessageContext messageContext) + throws APIManagementException { + + limit = limit != null ? limit : RestApiConstants.PAGINATION_LIMIT_DEFAULT; + offset = offset != null ? offset : RestApiConstants.PAGINATION_OFFSET_DEFAULT; + + String username = RestApiCommonUtil.getLoggedInUsername(); + String organization = RestApiUtil.getValidatedOrganization(messageContext); + APIConsumer apiConsumer = RestApiCommonUtil.getLoggedInUserConsumer(); + + NotificationList notificationList = apiConsumer.getNotifications(username, organization, portalToDisplay, limit, + offset); + return Response.ok().entity(notificationList).build(); + } + + /** + * Marks all notifications as read. + * + * @param limit The maximum number of notifications to return after marking as read. If null, the default limit is used. + * @param offset The starting point within the list of notifications. If null, the default offset is used. + * @param messageContext The message context for the request. + * @return A Response object containing the updated list of notifications. + * @throws APIManagementException If an error occurs while marking the notifications as read. + */ + @Override + public Response markAllNotificationsAsRead(Integer limit, Integer offset, MessageContext messageContext) + throws APIManagementException { + + limit = limit != null ? limit : RestApiConstants.PAGINATION_LIMIT_DEFAULT; + offset = offset != null ? offset : RestApiConstants.PAGINATION_OFFSET_DEFAULT; + + String username = RestApiCommonUtil.getLoggedInUsername(); + String organization = RestApiUtil.getValidatedOrganization(messageContext); + APIConsumer apiConsumer = RestApiCommonUtil.getLoggedInUserConsumer(); + NotificationList notificationList = apiConsumer.markAllNotificationsAsRead(username, organization, + portalToDisplay, limit, offset); + return Response.ok().entity(notificationList).build(); + } + + /** + * Deletes all notifications. + * + * @param messageContext The message context for the request. + * @return A Response object indicating the result of the delete operation. + * @throws APIManagementException If an error occurs while deleting the notifications. + */ + @Override + public Response deleteAllNotifications(MessageContext messageContext) throws APIManagementException { + String username = RestApiCommonUtil.getLoggedInUsername(); + String organization = RestApiUtil.getValidatedOrganization(messageContext); + APIConsumer apiConsumer = RestApiCommonUtil.getLoggedInUserConsumer(); + apiConsumer.deleteAllNotifications(username, organization, portalToDisplay); + return Response.ok().build(); + } +} diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/resources/devportal-api.yaml b/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/resources/devportal-api.yaml index b99d43642ed8..34c831387de1 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/resources/devportal-api.yaml +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/resources/devportal-api.yaml @@ -3818,9 +3818,263 @@ paths: - lang: Curl source: 'curl -k -X POST -d request.json "https://localhost:9443/api/am/devportal/v3/apis/marketplace-assistant/chat"' + ###################################################### + # The "Portal Notifications" resource APIs + ###################################################### + /notifications: + get: + tags: + - Notifications + summary: Retrieves all notifications for the user. + description: This operation can be used to retrieve all notifications for the user. + parameters: + - $ref: '#/components/parameters/limit' + - $ref: '#/components/parameters/offset' + responses: + 200: + description: | + OK. + List of Notifications are returned. + headers: + Content-Type: + description: | + The content type of the body. + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/NotificationList' + 403: + $ref: '#/components/responses/Forbidden' + 404: + $ref: '#/components/responses/NotFound' + 500: + $ref: '#/components/responses/InternalServerError' + security: + - OAuth2Security: + - apim:notifications_view + x-code-samples: + - lang: Curl + source: 'curl -k -H "Authorization: Bearer ae4eae22-3f65-387b-a171-d37eaa366fa8" + "https://localhost:9443/api/am/devportal/v3/notifications"' + operationId: getNotifications + + patch: + tags: + - Notifications + summary: Mark all notifications as read + description: | + This operation can be used to mark all notifications as read. + parameters: + - $ref: '#/components/parameters/limit' + - $ref: '#/components/parameters/offset' + responses: + 200: + description: | + OK. + Successful response with updated Notifications. + headers: + Content-Type: + description: | + The content type of the body. + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/NotificationList' + 400: + $ref: '#/components/responses/BadRequest' + 401: + $ref: '#/components/responses/Unauthorized' + 403: + $ref: '#/components/responses/Forbidden' + 404: + $ref: '#/components/responses/NotFound' + 500: + $ref: '#/components/responses/InternalServerError' + security: + - OAuth2Security: + - apim:notifications_manage + - apim:notifications_view + x-code-samples: + - lang: Curl + source: | + curl -k -X PATCH -H "Authorization: Bearer ae4eae22-3f65-387b-a171-d37eaa366fa8" \ + -H "Content-Type: application/json" \ + "https://localhost:9443/api/am/devportal/v3/notifications/update-status" + operationId: markAllNotificationsAsRead + + delete: + tags: + - Notifications + summary: Delete all notifications + description: | + This operation can be used to delete all the notifications of a user. + responses: + 200: + description: | + OK. + Resource successfully deleted. + 401: + $ref: '#/components/responses/Unauthorized' + 404: + $ref: '#/components/responses/NotFound' + 500: + $ref: '#/components/responses/InternalServerError' + security: + - OAuth2Security: + - apim:notifications_manage + - apim:notifications_view + x-code-samples: + - lang: Curl + source: | + curl -k -X DELETE -H "Authorization: Bearer ae4eae22-3f65-387b-a171-d37eaa366fa8" + "https://localhost:9443/api/am/devportal/v3/notifications" + operationId: deleteAllNotifications + + /notifications/{notificationId}: + patch: + tags: + - Notifications + summary: Mark a notification as read or unread + description: | + This operation can be used to mark a notification as read or unread. + parameters: + - $ref: '#/components/parameters/notificationId' + requestBody: + required: true + content: + application/json: + schema: + title: Notification action request + type: object + properties: + markAsRead: + type: boolean + description: True to mark as read, false to mark as unread + example: true + required: + - markAsRead + responses: + 200: + description: | + OK. + Successful response with updated Notification. + headers: + Content-Type: + description: | + The content type of the body. + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/Notification' + 400: + $ref: '#/components/responses/BadRequest' + 401: + $ref: '#/components/responses/Unauthorized' + 403: + $ref: '#/components/responses/Forbidden' + 404: + $ref: '#/components/responses/NotFound' + 500: + $ref: '#/components/responses/InternalServerError' + security: + - OAuth2Security: + - apim:notifications_manage + - apim:notifications_view + x-code-samples: + - lang: Curl + source: | + curl -k -X PATCH -H "Authorization:Bearer ae4eae22-3f65-387b-a171-d37eaa366fa8" \ + -H "Content-Type: application/json" -d @data.json \ + "https://localhost:9443/api/am/devportal/v3/notifications/863aef13-dab4-48b4-bf58-7363cd29601a" + operationId: changeNotificationStatus + + delete: + tags: + - Notifications + summary: Delete a Notification + description: | + This operation can be used to delete a notification. + parameters: + - $ref: '#/components/parameters/notificationId' + responses: + 200: + description: | + OK. + Resource successfully deleted. + 401: + $ref: '#/components/responses/Unauthorized' + 403: + $ref: '#/components/responses/Forbidden' + 404: + $ref: '#/components/responses/NotFound' + 500: + $ref: '#/components/responses/InternalServerError' + security: + - OAuth2Security: + - apim:notifications_manage + - apim:notifications_view + x-code-samples: + - lang: Curl + source: | + curl -k -X DELETE -H "Authorization:Bearer ae4eae22-3f65-387b-a171-d37eaa366fa8" \ + "https://localhost:9443/api/am/devportal/v3/notifications/863aef13-dab4-48b4-bf58-7363cd29601a" + operationId: deleteNotification components: schemas: + Notification: + title: Notification + description: Object with basic notification details. + type: object + properties: + id: + type: string + readOnly: true + example: 863aef13-dab4-48b4-bf58-7363cd29601a + type: + type: string + readOnly: true + example: APPLICATION_CREATION + content: + type: string + maxLength: 512 + readOnly: true + example: Application creation is rejected due to some reason. + createdTime: + type: string + readOnly: true + example: 2021-02-11 09:57:25 + isRead: + type: boolean + example: false + NotificationList: + title: Notification List + description: A list of notifications. + type: object + properties: + count: + type: integer + readOnly: true + description: Number of notifications returned. + example: 1 + unreadCount: + type: integer + readOnly: true + description: Number of unread notifications returned. + example: 5 + list: + type: array + readOnly: true + items: + $ref: '#/components/schemas/Notification' + pagination: + $ref: '#/components/schemas/Pagination' ApplicationThrottleReset: title: Reset application level throttling type: object @@ -6267,6 +6521,20 @@ components: description: The specified resource does not exist moreInfo: "" error: [] + Forbidden: + description: Forbidden. The request must be conditional but no condition has + been specified. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + example: + code: 403 + message: Forbidden + description: The request must be conditional but no condition has been + specified + moreInfo: "" + error: [] PreconditionFailed: description: Precondition Failed. The request has not been performed because one of the preconditions is not met. @@ -6319,6 +6587,15 @@ components: moreInfo: "" error: [ ] parameters: + notificationId: + name: notificationId + in: path + description: | + UUID of the notification. + required: true + schema: + type: string + example: 863aef13-dab4-48b4-bf58-7363cd29601a parentCommentID: name: replyTo in: query @@ -6586,3 +6863,5 @@ components: apim:sub_alert_manage: Retrieve, subscribe and configure Developer Portal alert types apim:app_import_export: Import and export applications related operations apim:admin: Manage all admin operations + apim:notifications_manage: Manage notifications + apim:notifications_view: View notifications diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/webapp/WEB-INF/beans.xml b/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/webapp/WEB-INF/beans.xml index 071c143fd1b5..00666044159b 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/webapp/WEB-INF/beans.xml +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/webapp/WEB-INF/beans.xml @@ -30,6 +30,7 @@ + diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/webapp/WEB-INF/web.xml b/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/webapp/WEB-INF/web.xml index 5f789228c98f..941bcb16442b 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/webapp/WEB-INF/web.xml +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/webapp/WEB-INF/web.xml @@ -49,7 +49,8 @@ org.wso2.carbon.apimgt.rest.api.store.v1.KeyManagersApi, org.wso2.carbon.apimgt.rest.api.store.v1.MeApi, org.wso2.carbon.apimgt.rest.api.store.v1.WebhooksApi, - org.wso2.carbon.apimgt.rest.api.store.v1.MarketplaceAssistantApi + org.wso2.carbon.apimgt.rest.api.store.v1.MarketplaceAssistantApi, + org.wso2.carbon.apimgt.rest.api.store.v1.NotificationsApi diff --git a/features/apimgt/org.wso2.carbon.apimgt.core.feature/src/main/resources/sql/h2.sql b/features/apimgt/org.wso2.carbon.apimgt.core.feature/src/main/resources/sql/h2.sql index ec4d4d9cb886..5ba9e5d39de7 100644 --- a/features/apimgt/org.wso2.carbon.apimgt.core.feature/src/main/resources/sql/h2.sql +++ b/features/apimgt/org.wso2.carbon.apimgt.core.feature/src/main/resources/sql/h2.sql @@ -2449,6 +2449,23 @@ CREATE TABLE IF NOT EXISTS AM_SUBJECT_ENTITY_REVOKED_EVENT ( PRIMARY KEY (ENTITY_ID, ENTITY_TYPE, ORGANIZATION) ); +CREATE TABLE IF NOT EXISTS AM_NOTIFICATION ( + NOTIFICATION_ID VARCHAR(64) NOT NULL UNIQUE, + NOTIFICATION_TYPE VARCHAR(50) NOT NULL, + CREATED_TIME TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + CONTENT VARCHAR(255) NOT NULL, + PRIMARY KEY (NOTIFICATION_ID) +); + +CREATE TABLE IF NOT EXISTS AM_NOTIFICATION_END_USERS ( + NOTIFICATION_ID VARCHAR(64) NOT NULL, + DESTINATION_USER VARCHAR(255) NOT NULL, + ORGANIZATION VARCHAR(100) NOT NULL, + IS_READ BOOLEAN DEFAULT FALSE, + PORTAL_TO_DISPLAY VARCHAR(64) NOT NULL, + PRIMARY KEY (NOTIFICATION_ID, DESTINATION_USER, ORGANIZATION, PORTAL_TO_DISPLAY), + FOREIGN KEY (NOTIFICATION_ID) REFERENCES AM_NOTIFICATION(NOTIFICATION_ID) ON DELETE CASCADE +); From 7929d8881763b7687adc0b068c37299a08c8d11d Mon Sep 17 00:00:00 2001 From: Thisal Tennakoon Date: Sun, 7 Jul 2024 23:24:10 +0530 Subject: [PATCH 2/2] Remove ADD_NOTIFICATION --- .../apimgt/impl/dao/NotificationDAO.java | 61 ------------------- .../impl/dao/constants/SQLConstants.java | 11 ---- 2 files changed, 72 deletions(-) diff --git a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/dao/NotificationDAO.java b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/dao/NotificationDAO.java index 537005f1f93c..edff9bd157fd 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/dao/NotificationDAO.java +++ b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/dao/NotificationDAO.java @@ -31,11 +31,8 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; -import java.sql.Timestamp; -import java.time.Instant; import java.util.ArrayList; import java.util.List; -import java.util.UUID; public class NotificationDAO { @@ -49,53 +46,6 @@ public static NotificationDAO getInstance() { return INSTANCE; } - // Tentative - public void addNotification(String comment) throws APIManagementException { - - String addNotificationQuery = SQLConstants.PortalNotifications.ADD_NOTIFICATION; - String addEndUserQuery = SQLConstants.PortalNotifications.ADD_NOTIFICATION_END_USER; - - try (Connection conn = APIMgtDBUtil.getConnection()) { - conn.setAutoCommit(false); - try (PreparedStatement ps = conn.prepareStatement(addNotificationQuery)) { - - String notificationId = UUID.randomUUID().toString(); - ps.setString(1, notificationId); - ps.setString(2, "SUBSCRIPTION_UPDATE"); - ps.setTimestamp(3, Timestamp.from(Instant.now())); - ps.setString(4, comment); - - int rowsAffected = ps.executeUpdate(); - - if (rowsAffected > 0) { - addEndUser(conn, addEndUserQuery, notificationId, "admin", "carbon.super", - "devportal"); - addEndUser(conn, addEndUserQuery, notificationId, "admin", "carbon.super", - "publisher"); - conn.commit(); - } - } catch (SQLException e) { - conn.rollback(); - throw new APIManagementException("Error while adding notification", e); - } - } catch (SQLException e) { - throw new APIManagementException("Error while establishing database connection", e); - } - } - - // Tentative - private void addEndUser(Connection conn, String addEndUserQuery, String notificationId, String destinationUser, - String organization, String portalToDisplay) throws SQLException { - - try (PreparedStatement ps = conn.prepareStatement(addEndUserQuery)) { - ps.setString(1, notificationId); - ps.setString(2, destinationUser); - ps.setString(3, organization); - ps.setString(4, portalToDisplay); - ps.executeUpdate(); - } - } - /** * Retrieves a specific notification by its UUID for a given user. * @@ -292,17 +242,6 @@ public NotificationList getNotifications(String username, String organization, S } notificationList.setUnreadCount(unreadNotificationCount); notificationList.setCount(notificationCount); - - if (limit < 0) { // Tentative: Remove this else block - try { - for (int i = 0; i < limit * -1; i++) { - addNotification("Notification " + i); - Thread.sleep(3000); - } - } catch (InterruptedException e) { - log.error("Error while adding notifications", e); - } - } } catch (SQLException e) { throw new APIManagementException("Failed to retrieve notifications of the user " + username, e); } diff --git a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/dao/constants/SQLConstants.java b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/dao/constants/SQLConstants.java index bf3204b83f76..c42cc13fdbd6 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/dao/constants/SQLConstants.java +++ b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/dao/constants/SQLConstants.java @@ -3261,17 +3261,6 @@ public static class PortalNotifications { " SELECT NOTIFICATION_ID " + " FROM AM_NOTIFICATION_END_USERS " + ")"; - - // Tentative - public static final String ADD_NOTIFICATION = - "INSERT INTO AM_NOTIFICATION " + - "(NOTIFICATION_ID, NOTIFICATION_TYPE, CREATED_TIME, CONTENT) " + - "VALUES (?, ?, ?, ?)"; - - public static final String ADD_NOTIFICATION_END_USER = - "INSERT INTO AM_NOTIFICATION_END_USERS " + - "(NOTIFICATION_ID, DESTINATION_USER, ORGANIZATION, PORTAL_TO_DISPLAY) " + - "VALUES (?, ?, ?, ?)"; } /** Throttle related constants**/