diff --git a/notifications/notifications/src/main/kotlin/org/opensearch/notifications/NotificationPlugin.kt b/notifications/notifications/src/main/kotlin/org/opensearch/notifications/NotificationPlugin.kt index cbdca21e8e3..3e0bd28239a 100644 --- a/notifications/notifications/src/main/kotlin/org/opensearch/notifications/NotificationPlugin.kt +++ b/notifications/notifications/src/main/kotlin/org/opensearch/notifications/NotificationPlugin.kt @@ -26,19 +26,15 @@ import org.opensearch.notifications.action.CreateNotificationConfigAction import org.opensearch.notifications.action.DeleteNotificationConfigAction import org.opensearch.notifications.action.GetChannelListAction import org.opensearch.notifications.action.GetNotificationConfigAction -import org.opensearch.notifications.action.GetNotificationEventAction import org.opensearch.notifications.action.GetPluginFeaturesAction import org.opensearch.notifications.action.PublishNotificationAction import org.opensearch.notifications.action.SendNotificationAction import org.opensearch.notifications.action.SendTestNotificationAction import org.opensearch.notifications.action.UpdateNotificationConfigAction import org.opensearch.notifications.index.ConfigIndexingActions -import org.opensearch.notifications.index.EventIndexingActions import org.opensearch.notifications.index.NotificationConfigIndex -import org.opensearch.notifications.index.NotificationEventIndex import org.opensearch.notifications.resthandler.NotificationChannelListRestHandler import org.opensearch.notifications.resthandler.NotificationConfigRestHandler -import org.opensearch.notifications.resthandler.NotificationEventRestHandler import org.opensearch.notifications.resthandler.NotificationFeaturesRestHandler import org.opensearch.notifications.resthandler.NotificationStatsRestHandler import org.opensearch.notifications.resthandler.SendTestMessageRestHandler @@ -105,10 +101,8 @@ class NotificationPlugin : ActionPlugin, Plugin(), NotificationCoreExtension { this.clusterService = clusterService PluginSettings.addSettingsUpdateConsumer(clusterService) NotificationConfigIndex.initialize(client, clusterService) - NotificationEventIndex.initialize(client, clusterService) ConfigIndexingActions.initialize(NotificationConfigIndex, UserAccessManager) - SendMessageActionHelper.initialize(NotificationConfigIndex, NotificationEventIndex, UserAccessManager) - EventIndexingActions.initialize(NotificationEventIndex, UserAccessManager) + SendMessageActionHelper.initialize(NotificationConfigIndex, UserAccessManager) return listOf() } @@ -135,10 +129,6 @@ class NotificationPlugin : ActionPlugin, Plugin(), NotificationCoreExtension { NotificationsActions.GET_NOTIFICATION_CONFIG_ACTION_TYPE, GetNotificationConfigAction::class.java ), - ActionPlugin.ActionHandler( - NotificationsActions.GET_NOTIFICATION_EVENT_ACTION_TYPE, - GetNotificationEventAction::class.java - ), ActionPlugin.ActionHandler( NotificationsActions.GET_CHANNEL_LIST_ACTION_TYPE, GetChannelListAction::class.java @@ -173,7 +163,6 @@ class NotificationPlugin : ActionPlugin, Plugin(), NotificationCoreExtension { log.debug("$LOG_PREFIX:getRestHandlers") return listOf( NotificationConfigRestHandler(), - NotificationEventRestHandler(), NotificationFeaturesRestHandler(), NotificationChannelListRestHandler(), SendTestMessageRestHandler(), diff --git a/notifications/notifications/src/main/kotlin/org/opensearch/notifications/action/GetNotificationEventAction.kt b/notifications/notifications/src/main/kotlin/org/opensearch/notifications/action/GetNotificationEventAction.kt deleted file mode 100644 index f045e1b0e54..00000000000 --- a/notifications/notifications/src/main/kotlin/org/opensearch/notifications/action/GetNotificationEventAction.kt +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.opensearch.notifications.action - -import org.opensearch.action.ActionListener -import org.opensearch.action.ActionRequest -import org.opensearch.action.support.ActionFilters -import org.opensearch.client.Client -import org.opensearch.common.inject.Inject -import org.opensearch.common.xcontent.NamedXContentRegistry -import org.opensearch.commons.authuser.User -import org.opensearch.commons.notifications.action.GetNotificationEventRequest -import org.opensearch.commons.notifications.action.GetNotificationEventResponse -import org.opensearch.commons.notifications.action.NotificationsActions -import org.opensearch.commons.utils.recreateObject -import org.opensearch.notifications.index.EventIndexingActions -import org.opensearch.tasks.Task -import org.opensearch.transport.TransportService - -/** - * Get notification event transport action - */ -internal class GetNotificationEventAction @Inject constructor( - transportService: TransportService, - client: Client, - actionFilters: ActionFilters, - val xContentRegistry: NamedXContentRegistry -) : PluginBaseAction( - NotificationsActions.GET_NOTIFICATION_EVENT_NAME, - transportService, - client, - actionFilters, - ::GetNotificationEventRequest -) { - - /** - * {@inheritDoc} - * Transform the request and call super.doExecute() to support call from other plugins. - */ - override fun doExecute( - task: Task?, - request: ActionRequest, - listener: ActionListener - ) { - val transformedRequest = request as? GetNotificationEventRequest - ?: recreateObject(request) { GetNotificationEventRequest(it) } - super.doExecute(task, transformedRequest, listener) - } - - /** - * {@inheritDoc} - */ - override fun executeRequest( - request: GetNotificationEventRequest, - user: User? - ): GetNotificationEventResponse { - return EventIndexingActions.get(request, user) - } -} diff --git a/notifications/notifications/src/main/kotlin/org/opensearch/notifications/action/SendTestNotificationAction.kt b/notifications/notifications/src/main/kotlin/org/opensearch/notifications/action/SendTestNotificationAction.kt index 4f6e9476515..a09538bf480 100644 --- a/notifications/notifications/src/main/kotlin/org/opensearch/notifications/action/SendTestNotificationAction.kt +++ b/notifications/notifications/src/main/kotlin/org/opensearch/notifications/action/SendTestNotificationAction.kt @@ -60,7 +60,7 @@ internal class SendTestNotificationAction @Inject constructor( channelIds, object : ActionListener { override fun onResponse(sendNotificationResponse: SendNotificationResponse) { - log.info("$LOG_PREFIX:SendTestNotificationAction-send:${sendNotificationResponse.notificationId}") + log.info("$LOG_PREFIX:SendTestNotificationAction-send:${sendNotificationResponse.notificationEvent}") listener.onResponse(sendNotificationResponse) } diff --git a/notifications/notifications/src/main/kotlin/org/opensearch/notifications/index/EventIndexingActions.kt b/notifications/notifications/src/main/kotlin/org/opensearch/notifications/index/EventIndexingActions.kt deleted file mode 100644 index 57be2a57dcd..00000000000 --- a/notifications/notifications/src/main/kotlin/org/opensearch/notifications/index/EventIndexingActions.kt +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.opensearch.notifications.index - -import org.opensearch.OpenSearchStatusException -import org.opensearch.commons.authuser.User -import org.opensearch.commons.notifications.action.GetNotificationEventRequest -import org.opensearch.commons.notifications.action.GetNotificationEventResponse -import org.opensearch.commons.notifications.model.NotificationEventInfo -import org.opensearch.commons.notifications.model.NotificationEventSearchResult -import org.opensearch.commons.utils.logger -import org.opensearch.notifications.NotificationPlugin.Companion.LOG_PREFIX -import org.opensearch.notifications.metrics.Metrics -import org.opensearch.notifications.security.UserAccess -import org.opensearch.rest.RestStatus - -/** - * NotificationEvent indexing operation actions. - */ -object EventIndexingActions { - private val log by logger(EventIndexingActions::class.java) - - private lateinit var operations: EventOperations - private lateinit var userAccess: UserAccess - - fun initialize(operations: EventOperations, userAccess: UserAccess) { - this.operations = operations - this.userAccess = userAccess - } - - /** - * Get NotificationEvent info - * @param request [GetNotificationEventRequest] object - * @param user the user info object - * @return [GetNotificationEventResponse] - */ - fun get(request: GetNotificationEventRequest, user: User?): GetNotificationEventResponse { - log.info("$LOG_PREFIX:NotificationEvent-get $request") - userAccess.validateUser(user) - return when (request.eventIds.size) { - 0 -> getAll(request, user) - 1 -> info(request.eventIds.first(), user) - else -> info(request.eventIds, user) - } - } - - /** - * Get NotificationEvent info - * @param eventId event id - * @param user the user info object - * @return [GetNotificationEventResponse] - */ - private fun info(eventId: String, user: User?): GetNotificationEventResponse { - log.info("$LOG_PREFIX:NotificationEvent-info $eventId") - val eventDoc = operations.getNotificationEvent(eventId) - eventDoc - ?: run { - Metrics.NOTIFICATIONS_EVENTS_INFO_USER_ERROR_INVALID_CONFIG_ID.counter.increment() - throw OpenSearchStatusException("NotificationEvent $eventId not found", RestStatus.NOT_FOUND) - } - val metadata = eventDoc.eventDoc.metadata - if (!userAccess.doesUserHaveAccess(user, metadata.access)) { - Metrics.NOTIFICATIONS_PERMISSION_USER_ERROR.counter.increment() - throw OpenSearchStatusException("Permission denied for NotificationEvent $eventId", RestStatus.FORBIDDEN) - } - val eventInfo = NotificationEventInfo( - eventId, - metadata.lastUpdateTime, - metadata.createdTime, - eventDoc.eventDoc.event - ) - return GetNotificationEventResponse(NotificationEventSearchResult(eventInfo)) - } - - /** - * Get NotificationEvent info - * @param eventIds event id set - * @param user the user info object - * @return [GetNotificationEventResponse] - */ - private fun info(eventIds: Set, user: User?): GetNotificationEventResponse { - log.info("$LOG_PREFIX:NotificationEvent-info $eventIds") - val eventDocs = operations.getNotificationEvents(eventIds) - if (eventDocs.size != eventIds.size) { - val mutableSet = eventIds.toMutableSet() - eventDocs.forEach { mutableSet.remove(it.docInfo.id) } - Metrics.NOTIFICATIONS_EVENTS_INFO_SYSTEM_ERROR.counter.increment() - throw OpenSearchStatusException( - "NotificationEvent $mutableSet not found", - RestStatus.NOT_FOUND - ) - } - eventDocs.forEach { - val currentMetadata = it.eventDoc.metadata - if (!userAccess.doesUserHaveAccess(user, currentMetadata.access)) { - Metrics.NOTIFICATIONS_PERMISSION_USER_ERROR.counter.increment() - throw OpenSearchStatusException( - "Permission denied for NotificationEvent ${it.docInfo.id}", - RestStatus.FORBIDDEN - ) - } - } - val eventSearchResult = eventDocs.map { - NotificationEventInfo( - it.docInfo.id!!, - it.eventDoc.metadata.lastUpdateTime, - it.eventDoc.metadata.createdTime, - it.eventDoc.event - ) - } - return GetNotificationEventResponse(NotificationEventSearchResult(eventSearchResult)) - } - - /** - * Get all NotificationEvent matching the criteria - * @param request [GetNotificationEventRequest] object - * @param user the user info object - * @return [GetNotificationEventResponse] - */ - private fun getAll(request: GetNotificationEventRequest, user: User?): GetNotificationEventResponse { - log.info("$LOG_PREFIX:NotificationEvent-getAll") - val searchResult = operations.getAllNotificationEvents( - userAccess.getSearchAccessInfo(user), - request - ) - return GetNotificationEventResponse(searchResult) - } -} diff --git a/notifications/notifications/src/main/kotlin/org/opensearch/notifications/index/EventOperations.kt b/notifications/notifications/src/main/kotlin/org/opensearch/notifications/index/EventOperations.kt deleted file mode 100644 index 6d54cb73f18..00000000000 --- a/notifications/notifications/src/main/kotlin/org/opensearch/notifications/index/EventOperations.kt +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ -package org.opensearch.notifications.index - -import org.opensearch.commons.notifications.action.GetNotificationEventRequest -import org.opensearch.commons.notifications.model.NotificationEventSearchResult -import org.opensearch.notifications.model.NotificationEventDoc -import org.opensearch.notifications.model.NotificationEventDocInfo -import org.opensearch.rest.RestStatus - -/** - * Interface for notification events Operations. - */ -interface EventOperations { - /** - * create a new doc for NotificationEventDoc - * @param eventDoc the Notification Event Doc - * @param id optional id to use as id for the document - * @return Notification Event id if successful, null otherwise - * @throws java.util.concurrent.ExecutionException with a cause - */ - fun createNotificationEvent(eventDoc: NotificationEventDoc, id: String? = null): String? - - /** - * Query index for Notification Event with ID - * @param ids set of the document ids to get info - * @return list of NotificationEventDocInfo on success, null otherwise - */ - fun getNotificationEvents(ids: Set): List - - /** - * Query index for Notification Event with ID - * @param id the id for the document - * @return NotificationEventDocInfo on success, null otherwise - */ - fun getNotificationEvent(id: String): NotificationEventDocInfo? - - /** - * Query index for NotificationEventDocs for given access details - * @param access the list of access details to search NotificationEventDocs for. - * @param request [GetNotificationEventRequest] object - * @return search result of NotificationEventDocs - */ - fun getAllNotificationEvents( - access: List, - request: GetNotificationEventRequest - ): NotificationEventSearchResult - - /** - * update NotificationEventDoc for given id - * @param id the id for the document - * @param notificationEventDoc the NotificationEventDoc data - * @return true if successful, false otherwise - */ - fun updateNotificationEvent(id: String, notificationEventDoc: NotificationEventDoc): Boolean - - /** - * delete NotificationEventDoc for given id - * @param id the id for the document - * @return true if successful, false otherwise - */ - fun deleteNotificationEvent(id: String): Boolean - - /** - * delete NotificationEventDoc for given ids - * @param ids set of the document ids to delete - * @return map of id to status - */ - fun deleteNotificationEvents(ids: Set): Map -} diff --git a/notifications/notifications/src/main/kotlin/org/opensearch/notifications/index/NotificationEventIndex.kt b/notifications/notifications/src/main/kotlin/org/opensearch/notifications/index/NotificationEventIndex.kt deleted file mode 100644 index 2a1e06b28db..00000000000 --- a/notifications/notifications/src/main/kotlin/org/opensearch/notifications/index/NotificationEventIndex.kt +++ /dev/null @@ -1,279 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.opensearch.notifications.index - -import org.opensearch.ResourceAlreadyExistsException -import org.opensearch.action.DocWriteResponse -import org.opensearch.action.admin.indices.create.CreateIndexRequest -import org.opensearch.action.bulk.BulkRequest -import org.opensearch.action.delete.DeleteRequest -import org.opensearch.action.get.GetRequest -import org.opensearch.action.get.GetResponse -import org.opensearch.action.get.MultiGetRequest -import org.opensearch.action.index.IndexRequest -import org.opensearch.action.search.SearchRequest -import org.opensearch.action.update.UpdateRequest -import org.opensearch.client.Client -import org.opensearch.cluster.service.ClusterService -import org.opensearch.common.unit.TimeValue -import org.opensearch.common.xcontent.LoggingDeprecationHandler -import org.opensearch.common.xcontent.NamedXContentRegistry -import org.opensearch.common.xcontent.XContentHelper -import org.opensearch.common.xcontent.XContentType -import org.opensearch.commons.notifications.action.GetNotificationEventRequest -import org.opensearch.commons.notifications.model.NotificationEventInfo -import org.opensearch.commons.notifications.model.NotificationEventSearchResult -import org.opensearch.commons.notifications.model.SearchResults -import org.opensearch.commons.utils.logger -import org.opensearch.index.query.QueryBuilders -import org.opensearch.notifications.NotificationPlugin.Companion.LOG_PREFIX -import org.opensearch.notifications.model.DocInfo -import org.opensearch.notifications.model.DocMetadata.Companion.ACCESS_LIST_TAG -import org.opensearch.notifications.model.DocMetadata.Companion.METADATA_TAG -import org.opensearch.notifications.model.NotificationEventDoc -import org.opensearch.notifications.model.NotificationEventDocInfo -import org.opensearch.notifications.settings.PluginSettings -import org.opensearch.notifications.util.SecureIndexClient -import org.opensearch.rest.RestStatus -import org.opensearch.search.SearchHit -import org.opensearch.search.builder.SearchSourceBuilder -import org.opensearch.search.sort.SortOrder -import java.util.concurrent.TimeUnit - -/** - * Class for doing index operations to maintain notification events in cluster. - */ -@Suppress("TooManyFunctions") -internal object NotificationEventIndex : EventOperations { - private val log by logger(NotificationEventIndex::class.java) - private const val INDEX_NAME = ".opensearch-notifications-event" - private const val MAPPING_FILE_NAME = "notifications-event-mapping.yml" - private const val SETTINGS_FILE_NAME = "notifications-event-settings.yml" - - private lateinit var client: Client - private lateinit var clusterService: ClusterService - - private val searchHitParser = object : SearchResults.SearchHitParser { - override fun parse(searchHit: SearchHit): NotificationEventInfo { - val parser = XContentType.JSON.xContent().createParser( - NamedXContentRegistry.EMPTY, - LoggingDeprecationHandler.INSTANCE, - searchHit.sourceAsString - ) - parser.nextToken() - val doc = NotificationEventDoc.parse(parser) - return NotificationEventInfo( - searchHit.id, - doc.metadata.lastUpdateTime, - doc.metadata.createdTime, - doc.event - ) - } - } - - /** - * {@inheritDoc} - */ - fun initialize(client: Client, clusterService: ClusterService) { - NotificationEventIndex.client = SecureIndexClient(client) - NotificationEventIndex.clusterService = clusterService - } - - /** - * Create index using the mapping and settings defined in resource - */ - @Suppress("TooGenericExceptionCaught") - private fun createIndex() { - if (!isIndexExists()) { - val classLoader = NotificationEventIndex::class.java.classLoader - val indexMappingSource = classLoader.getResource(MAPPING_FILE_NAME)?.readText()!! - val indexMappingAsMap = XContentHelper.convertToMap(XContentType.YAML.xContent(), indexMappingSource, false) - val indexSettingsSource = classLoader.getResource(SETTINGS_FILE_NAME)?.readText()!! - val request = CreateIndexRequest(INDEX_NAME) - .mapping(indexMappingAsMap) - .settings(indexSettingsSource, XContentType.YAML) - try { - val actionFuture = client.admin().indices().create(request) - val response = actionFuture.actionGet(PluginSettings.operationTimeoutMs) - if (response.isAcknowledged) { - log.info("$LOG_PREFIX:Index $INDEX_NAME creation Acknowledged") - } else { - throw IllegalStateException("$LOG_PREFIX:Index $INDEX_NAME creation not Acknowledged") - } - } catch (exception: Exception) { - if (exception !is ResourceAlreadyExistsException && exception.cause !is ResourceAlreadyExistsException) { - throw exception - } - } - } - } - - /** - * Check if the index is created and available. - * @return true if index is available, false otherwise - */ - private fun isIndexExists(): Boolean { - val clusterState = clusterService.state() - return clusterState.routingTable.hasIndex(INDEX_NAME) - } - - /** - * {@inheritDoc} - */ - override fun createNotificationEvent(eventDoc: NotificationEventDoc, id: String?): String? { - createIndex() - val indexRequest = IndexRequest(INDEX_NAME) - .source(eventDoc.toXContent()) - .create(true) - if (id != null) { - indexRequest.id(id) - } - val actionFuture = client.index(indexRequest) - val response = actionFuture.actionGet(PluginSettings.operationTimeoutMs) - return if (response.result != DocWriteResponse.Result.CREATED) { - log.warn("$LOG_PREFIX:createNotificationEvent - response:$response") - null - } else { - response.id - } - } - - /** - * {@inheritDoc} - */ - override fun getNotificationEvents(ids: Set): List { - createIndex() - val getRequest = MultiGetRequest() - ids.forEach { getRequest.add(INDEX_NAME, it) } - val actionFuture = client.multiGet(getRequest) - val response = actionFuture.actionGet(PluginSettings.operationTimeoutMs) - return response.responses.mapNotNull { parseNotificationEventDoc(it.id, it.response) } - } - - /** - * {@inheritDoc} - */ - override fun getNotificationEvent(id: String): NotificationEventDocInfo? { - createIndex() - val getRequest = GetRequest(INDEX_NAME).id(id) - val actionFuture = client.get(getRequest) - val response = actionFuture.actionGet(PluginSettings.operationTimeoutMs) - return parseNotificationEventDoc(id, response) - } - - private fun parseNotificationEventDoc(id: String, response: GetResponse): NotificationEventDocInfo? { - return if (response.sourceAsString == null) { - log.warn("$LOG_PREFIX:getNotificationEvent - $id not found; response:$response") - null - } else { - val parser = XContentType.JSON.xContent().createParser( - NamedXContentRegistry.EMPTY, - LoggingDeprecationHandler.INSTANCE, - response.sourceAsString - ) - parser.nextToken() - val doc = NotificationEventDoc.parse(parser) - val info = DocInfo( - id = id, - version = response.version, - seqNo = response.seqNo, - primaryTerm = response.primaryTerm - ) - NotificationEventDocInfo(info, doc) - } - } - - /** - * {@inheritDoc} - */ - override fun getAllNotificationEvents( - access: List, - request: GetNotificationEventRequest - ): NotificationEventSearchResult { - createIndex() - val sourceBuilder = SearchSourceBuilder() - .timeout(TimeValue(PluginSettings.operationTimeoutMs, TimeUnit.MILLISECONDS)) - .sort(EventQueryHelper.getSortField(request.sortField), request.sortOrder ?: SortOrder.ASC) - .size(request.maxItems) - .from(request.fromIndex) - val query = QueryBuilders.boolQuery() - if (access.isNotEmpty()) { - query.filter(QueryBuilders.termsQuery("$METADATA_TAG.$ACCESS_LIST_TAG", access)) - } - EventQueryHelper.addQueryFilters(query, request.filterParams) - sourceBuilder.query(query) - val searchRequest = SearchRequest() - .indices(INDEX_NAME) - .source(sourceBuilder) - val actionFuture = client.search(searchRequest) - val response = actionFuture.actionGet(PluginSettings.operationTimeoutMs) - val result = NotificationEventSearchResult(request.fromIndex.toLong(), response, searchHitParser) - log.info( - "$LOG_PREFIX:getAllNotificationEvents from:${request.fromIndex}, maxItems:${request.maxItems}," + - " sortField:${request.sortField}, sortOrder=${request.sortOrder}, filters=${request.filterParams}" + - " retCount:${result.objectList.size}, totalCount:${result.totalHits}" - ) - return result - } - - /** - * {@inheritDoc} - */ - override fun updateNotificationEvent(id: String, notificationEventDoc: NotificationEventDoc): Boolean { - createIndex() - val updateRequest = UpdateRequest() - .index(INDEX_NAME) - .id(id) - .doc(notificationEventDoc.toXContent()) - .fetchSource(true) - val actionFuture = client.update(updateRequest) - val response = actionFuture.actionGet(PluginSettings.operationTimeoutMs) - if (response.result != DocWriteResponse.Result.UPDATED) { - log.warn("$LOG_PREFIX:updateNotificationEvent failed for $id; response:$response") - } - return response.result == DocWriteResponse.Result.UPDATED - } - - /** - * {@inheritDoc} - */ - override fun deleteNotificationEvent(id: String): Boolean { - createIndex() - val deleteRequest = DeleteRequest() - .index(INDEX_NAME) - .id(id) - val actionFuture = client.delete(deleteRequest) - val response = actionFuture.actionGet(PluginSettings.operationTimeoutMs) - if (response.result != DocWriteResponse.Result.DELETED) { - log.warn("$LOG_PREFIX:deleteNotificationEvent failed for $id; response:$response") - } - return response.result == DocWriteResponse.Result.DELETED - } - - /** - * {@inheritDoc} - */ - override fun deleteNotificationEvents(ids: Set): Map { - createIndex() - val bulkRequest = BulkRequest() - ids.forEach { - val deleteRequest = DeleteRequest() - .index(INDEX_NAME) - .id(it) - bulkRequest.add(deleteRequest) - } - val actionFuture = client.bulk(bulkRequest) - val response = actionFuture.actionGet(PluginSettings.operationTimeoutMs) - val mutableMap = mutableMapOf() - response.forEach { - mutableMap[it.id] = it.status() - if (it.isFailed) { - log.warn("$LOG_PREFIX:deleteNotificationEvent failed for ${it.id}; response:${it.failureMessage}") - } - } - return mutableMap - } -} diff --git a/notifications/notifications/src/main/kotlin/org/opensearch/notifications/metrics/Metrics.kt b/notifications/notifications/src/main/kotlin/org/opensearch/notifications/metrics/Metrics.kt index 146e71a0316..8a057326510 100644 --- a/notifications/notifications/src/main/kotlin/org/opensearch/notifications/metrics/Metrics.kt +++ b/notifications/notifications/src/main/kotlin/org/opensearch/notifications/metrics/Metrics.kt @@ -131,26 +131,6 @@ enum class Metrics(val metricName: String, val counter: Counter<*>) { NOTIFICATIONS_CONFIG_INFO_USER_ERROR_SET_NOT_FOUND( "notifications_config.info.user_error.set_not_found", RollingCounter() ), - NOTIFICATIONS_CONFIG_INFO_SYSTEM_ERROR( - "notifications_config.info.system_error", - RollingCounter() - ), - // Event Endpoints - // GET _plugins/_notifications/events/{configId} - NOTIFICATIONS_EVENTS_INFO_TOTAL( - "notifications_events.info.total", - BasicCounter() - ), - NOTIFICATIONS_EVENTS_INFO_INTERVAL_COUNT( - "notifications_events.info.count", - RollingCounter() - ), - NOTIFICATIONS_EVENTS_INFO_USER_ERROR_INVALID_CONFIG_ID( - "notifications_events.info.user_error.invalid_config_id", RollingCounter() - ), - NOTIFICATIONS_EVENTS_INFO_SYSTEM_ERROR( - "notifications_events.info.system_error", RollingCounter() - ), // Feature Channels Endpoints // GET _plugins/_notifications/channels NOTIFICATIONS_CHANNELS_INFO_TOTAL( @@ -160,9 +140,6 @@ enum class Metrics(val metricName: String, val counter: Counter<*>) { NOTIFICATIONS_CHANNELS_INFO_INTERVAL_COUNT( "notifications_channels.info.count", RollingCounter() ), - NOTIFICATIONS_CHANNELS_INFO_SYSTEM_ERROR( - "notifications_channels.info.system_error", RollingCounter() - ), // Features Endpoints // GET _plugins/_notifications/features NOTIFICATIONS_FEATURES_INFO_TOTAL( @@ -173,10 +150,6 @@ enum class Metrics(val metricName: String, val counter: Counter<*>) { "notifications_features.info.count", RollingCounter() ), - NOTIFICATIONS_FEATURES_INFO_SYSTEM_ERROR( - "notifications_features.info.system_error", - RollingCounter() - ), // Send Message Endpoints // POST _plugins/_notifications/send NOTIFICATIONS_SEND_MESSAGE_TOTAL( @@ -190,10 +163,6 @@ enum class Metrics(val metricName: String, val counter: Counter<*>) { NOTIFICATIONS_SEND_MESSAGE_USER_ERROR_NOT_FOUND( "notifications.send_message.user_error.not_found", RollingCounter() ), - NOTIFICATIONS_SEND_MESSAGE_SYSTEM_ERROR( - "notifications.send_message.system_error", - RollingCounter() - ), // Track message destinations NOTIFICATIONS_MESSAGE_DESTINATION_SLACK( "notifications.message_destination.slack", BasicCounter() diff --git a/notifications/notifications/src/main/kotlin/org/opensearch/notifications/model/NotificationEventDoc.kt b/notifications/notifications/src/main/kotlin/org/opensearch/notifications/model/NotificationEventDoc.kt deleted file mode 100644 index 89df79fbad7..00000000000 --- a/notifications/notifications/src/main/kotlin/org/opensearch/notifications/model/NotificationEventDoc.kt +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ -package org.opensearch.notifications.model - -import org.opensearch.common.xcontent.ToXContent -import org.opensearch.common.xcontent.XContentBuilder -import org.opensearch.common.xcontent.XContentFactory -import org.opensearch.common.xcontent.XContentParser -import org.opensearch.common.xcontent.XContentParserUtils -import org.opensearch.commons.notifications.NotificationConstants.EVENT_TAG -import org.opensearch.commons.notifications.model.NotificationEvent -import org.opensearch.commons.utils.logger -import org.opensearch.notifications.model.DocMetadata.Companion.METADATA_TAG -import java.io.IOException - -/** - * Data class representing Notification event with metadata. - */ -data class NotificationEventDoc( - val metadata: DocMetadata, - val event: NotificationEvent -) : ToXContent { - - companion object { - private val log by logger(NotificationEventDoc::class.java) - - /** - * Parse the data from parser and create object - * @param parser data referenced at parser - * @return created object - */ - @JvmStatic - @Throws(IOException::class) - fun parse(parser: XContentParser): NotificationEventDoc { - var metadata: DocMetadata? = null - var event: NotificationEvent? = null - - XContentParserUtils.ensureExpectedToken( - XContentParser.Token.START_OBJECT, - parser.currentToken(), - parser - ) - while (parser.nextToken() != XContentParser.Token.END_OBJECT) { - val fieldName = parser.currentName() - parser.nextToken() - when (fieldName) { - METADATA_TAG -> metadata = DocMetadata.parse(parser) - EVENT_TAG -> event = NotificationEvent.parse(parser) - else -> { - parser.skipChildren() - log.info("Unexpected field: $fieldName, while parsing event doc") - } - } - } - metadata ?: throw IllegalArgumentException("$METADATA_TAG field absent") - event ?: throw IllegalArgumentException("$EVENT_TAG field absent") - return NotificationEventDoc( - metadata, - event - ) - } - } - - /** - * create XContentBuilder from this object using [XContentFactory.jsonBuilder()] - * @param params XContent parameters - * @return created XContentBuilder object - */ - fun toXContent(params: ToXContent.Params = ToXContent.EMPTY_PARAMS): XContentBuilder { - return toXContent(XContentFactory.jsonBuilder(), params) - } - - /** - * {@inheritDoc} - */ - override fun toXContent(builder: XContentBuilder?, params: ToXContent.Params?): XContentBuilder { - builder!! - return builder.startObject() - .field(METADATA_TAG, metadata) - .field(EVENT_TAG, event) - .endObject() - } -} diff --git a/notifications/notifications/src/main/kotlin/org/opensearch/notifications/model/NotificationEventDocInfo.kt b/notifications/notifications/src/main/kotlin/org/opensearch/notifications/model/NotificationEventDocInfo.kt deleted file mode 100644 index 93ed7e7deda..00000000000 --- a/notifications/notifications/src/main/kotlin/org/opensearch/notifications/model/NotificationEventDocInfo.kt +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.opensearch.notifications.model - -/** - * Class to hold notification event document with information - */ -data class NotificationEventDocInfo( - val docInfo: DocInfo, - val eventDoc: NotificationEventDoc -) diff --git a/notifications/notifications/src/main/kotlin/org/opensearch/notifications/resthandler/NotificationEventRestHandler.kt b/notifications/notifications/src/main/kotlin/org/opensearch/notifications/resthandler/NotificationEventRestHandler.kt deleted file mode 100644 index 529c27ee54f..00000000000 --- a/notifications/notifications/src/main/kotlin/org/opensearch/notifications/resthandler/NotificationEventRestHandler.kt +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ -package org.opensearch.notifications.resthandler - -import org.opensearch.client.node.NodeClient -import org.opensearch.commons.notifications.NotificationConstants.DEFAULT_MAX_ITEMS -import org.opensearch.commons.notifications.NotificationConstants.EVENT_ID_LIST_TAG -import org.opensearch.commons.notifications.NotificationConstants.EVENT_ID_TAG -import org.opensearch.commons.notifications.NotificationConstants.FROM_INDEX_TAG -import org.opensearch.commons.notifications.NotificationConstants.MAX_ITEMS_TAG -import org.opensearch.commons.notifications.NotificationConstants.SORT_FIELD_TAG -import org.opensearch.commons.notifications.NotificationConstants.SORT_ORDER_TAG -import org.opensearch.commons.notifications.NotificationsPluginInterface -import org.opensearch.commons.notifications.action.GetNotificationEventRequest -import org.opensearch.commons.utils.logger -import org.opensearch.notifications.NotificationPlugin.Companion.LOG_PREFIX -import org.opensearch.notifications.NotificationPlugin.Companion.PLUGIN_BASE_URI -import org.opensearch.notifications.index.EventQueryHelper -import org.opensearch.notifications.metrics.Metrics -import org.opensearch.rest.BaseRestHandler.RestChannelConsumer -import org.opensearch.rest.BytesRestResponse -import org.opensearch.rest.RestHandler.Route -import org.opensearch.rest.RestRequest -import org.opensearch.rest.RestRequest.Method.GET -import org.opensearch.rest.RestStatus -import org.opensearch.rest.action.RestToXContentListener -import org.opensearch.search.sort.SortOrder - -/** - * Rest handler for notification events. - */ -internal class NotificationEventRestHandler : PluginBaseHandler() { - companion object { - private val log by logger(NotificationEventRestHandler::class.java) - - /** - * Base URL for this handler - */ - private const val REQUEST_URL = "$PLUGIN_BASE_URI/events" - } - - /** - * {@inheritDoc} - */ - override fun getName(): String { - return "notifications_event" - } - - /** - * {@inheritDoc} - */ - override fun routes(): List { - return listOf( - /** - * Get a notification event - * Request URL: GET [REQUEST_URL/{eventId}] - * Request body: Ref [org.opensearch.commons.notifications.action.GetNotificationEventRequest] - * Response body: [org.opensearch.commons.notifications.action.GetNotificationEventResponse] - */ - Route(GET, "$REQUEST_URL/{$EVENT_ID_TAG}"), - /** - * Get list of notification events - * Request URL: GET [REQUEST_URL?event_id=id] or [REQUEST_URL?] - * -> - * event_id_list=id1,id2,id3 (Other query_params ignored if this is not empty) - * from_index=20 - * max_items=10 - * sort_order=asc - * sort_field=event_source.severity - * last_updated_time_ms=from_time..to_time (Range filter field) - * created_time_ms=from_time..to_time (Range filter field) - * event_source.reference_id=abc,xyz (Keyword filter field) - * event_source.severity=info,high (Keyword filter field) - * event_source.tags=test,tags (Text filter field) - * event_source.title=sample title (Text filter field) - * status_list.config_id=abc,xyz (Keyword filter field) - * status_list.config_type=slack,chime (Keyword filter field) - * status_list.config_name=sample (Text filter field) - * status_list.delivery_status.status_code=400,503 (Keyword filter field) - * status_list.delivery_status.status_text=bad,request (Text filter field) - * status_list.email_recipient_status.recipient=abc,xyz (Text filter field) - * status_list.email_recipient_status.delivery_status.status_code=400,503 (Keyword filter field) - * status_list.email_recipient_status.delivery_status.status_text=bad,request (Text filter field) - * query=search all above keyword and text filter fields - * text_query=search text filter fields from above list - * Request body: Ref [org.opensearch.commons.notifications.action.GetNotificationEventRequest] - * Response body: [org.opensearch.commons.notifications.action.GetNotificationEventResponse] - */ - Route(GET, REQUEST_URL) - ) - } - - /** - * {@inheritDoc} - */ - override fun responseParams(): Set { - return setOf( - EVENT_ID_TAG, - EVENT_ID_LIST_TAG, - SORT_FIELD_TAG, - SORT_ORDER_TAG, - FROM_INDEX_TAG, - MAX_ITEMS_TAG - ) - } - - /** - * {@inheritDoc} - */ - override fun executeRequest(request: RestRequest, client: NodeClient): RestChannelConsumer { - return when (request.method()) { - GET -> executeGetRequest(request, client) - else -> RestChannelConsumer { - it.sendResponse(BytesRestResponse(RestStatus.METHOD_NOT_ALLOWED, "${request.method()} is not allowed")) - } - } - } - - private fun executeGetRequest( - request: RestRequest, - client: NodeClient - ): RestChannelConsumer { - Metrics.NOTIFICATIONS_EVENTS_INFO_TOTAL.counter.increment() - Metrics.NOTIFICATIONS_EVENTS_INFO_INTERVAL_COUNT.counter.increment() - val eventId: String? = request.param(EVENT_ID_TAG) - val eventIdList: String? = request.param(EVENT_ID_LIST_TAG) - val sortField: String? = request.param(SORT_FIELD_TAG) - val sortOrderString: String? = request.param(SORT_ORDER_TAG) - val sortOrder: SortOrder? = if (sortOrderString == null) { - null - } else { - SortOrder.fromString(sortOrderString) - } - val fromIndex = request.param(FROM_INDEX_TAG)?.toIntOrNull() ?: 0 - val maxItems = request.param(MAX_ITEMS_TAG)?.toIntOrNull() ?: DEFAULT_MAX_ITEMS - val filterParams = request.params() - .filter { EventQueryHelper.FILTER_PARAMS.contains(it.key) } - .map { Pair(it.key, request.param(it.key)) } - .toMap() - log.info( - "$LOG_PREFIX:executeGetRequest from:$fromIndex, maxItems:$maxItems," + - " sortField:$sortField, sortOrder=$sortOrder, filters=$filterParams" - ) - val eventRequest = GetNotificationEventRequest( - getEventIdSet(eventId, eventIdList), - fromIndex, - maxItems, - sortField, - sortOrder, - filterParams - ) - return RestChannelConsumer { - NotificationsPluginInterface.getNotificationEvent( - client, - eventRequest, - RestToXContentListener(it) - ) - } - } - - private fun getEventIdSet(eventId: String?, eventIdList: String?): Set { - var retIds: Set = setOf() - if (eventId != null) { - retIds = setOf(eventId) - } - if (eventIdList != null) { - retIds = eventIdList.split(",").union(retIds) - } - return retIds - } -} diff --git a/notifications/notifications/src/main/kotlin/org/opensearch/notifications/send/SendMessageActionHelper.kt b/notifications/notifications/src/main/kotlin/org/opensearch/notifications/send/SendMessageActionHelper.kt index acb65566658..c69b85667a6 100644 --- a/notifications/notifications/src/main/kotlin/org/opensearch/notifications/send/SendMessageActionHelper.kt +++ b/notifications/notifications/src/main/kotlin/org/opensearch/notifications/send/SendMessageActionHelper.kt @@ -39,11 +39,8 @@ import org.opensearch.commons.utils.logger import org.opensearch.notifications.CoreProvider import org.opensearch.notifications.NotificationPlugin.Companion.LOG_PREFIX import org.opensearch.notifications.index.ConfigOperations -import org.opensearch.notifications.index.EventOperations import org.opensearch.notifications.metrics.Metrics -import org.opensearch.notifications.model.DocMetadata import org.opensearch.notifications.model.NotificationConfigDocInfo -import org.opensearch.notifications.model.NotificationEventDoc import org.opensearch.notifications.security.UserAccess import org.opensearch.notifications.spi.model.DestinationMessageResponse import org.opensearch.notifications.spi.model.MessageContent @@ -56,7 +53,6 @@ import org.opensearch.notifications.spi.model.destination.SmtpDestination import org.opensearch.notifications.spi.model.destination.SnsDestination import org.opensearch.rest.RestStatus import java.io.ByteArrayOutputStream -import java.time.Instant /** * Helper function for send transport action. @@ -66,12 +62,10 @@ object SendMessageActionHelper { private val log by logger(SendMessageActionHelper::class.java) private lateinit var configOperations: ConfigOperations - private lateinit var eventOperations: EventOperations private lateinit var userAccess: UserAccess - fun initialize(configOperations: ConfigOperations, eventOperations: EventOperations, userAccess: UserAccess) { + fun initialize(configOperations: ConfigOperations, userAccess: UserAccess) { this.configOperations = configOperations - this.eventOperations = eventOperations this.userAccess = userAccess } @@ -84,25 +78,13 @@ object SendMessageActionHelper { val channelMessage = request.channelMessage val channelIds = request.channelIds.toSet() val user: User? = User.parse(request.threadContext) - val createdTime = Instant.now() userAccess.validateUser(user) val channelMap = getConfigs(channelIds) val childConfigMap = getConfigs(getChildConfigIds(channelMap.values.filterNotNull().toList())) val message = createMessageContent(eventSource, channelMessage) val eventStatusList = sendMessagesInParallel(user, eventSource, channelMap, childConfigMap, message) - val updatedTime = Instant.now() - val docMetadata = DocMetadata( - updatedTime, - createdTime, - userAccess.getAllAccessInfo(user) - ) val event = NotificationEvent(eventSource, eventStatusList) - val eventDoc = NotificationEventDoc(docMetadata, event) - val docId = eventOperations.createNotificationEvent(eventDoc) - ?: run { - Metrics.NOTIFICATIONS_SEND_MESSAGE_SYSTEM_ERROR.counter.increment() - throw OpenSearchStatusException("Indexing not Acknowledged", RestStatus.INSUFFICIENT_STORAGE) - } + // traverse status to determine HTTP status code var overallStatusCode = eventStatusList.first().deliveryStatus?.statusCode eventStatusList.forEach { eventStatus -> @@ -112,13 +94,13 @@ object SendMessageActionHelper { } val eventStatusListString = eventStatusList.joinToString(",", "[", "]") { getJsonString(it) } if (overallStatusCode != RestStatus.OK.status.toString()) { - val errorMessage = "{\"notification_id\": \"$docId\",\"event_status_list\": $eventStatusListString}" + val errorMessage = "{\"event_status_list\": $eventStatusListString}" throw OpenSearchStatusException( errorMessage, RestStatus.fromCode(overallStatusCode!!.toInt()) ) } - return SendNotificationResponse(docId) + return SendNotificationResponse(event) } /** diff --git a/notifications/notifications/src/test/kotlin/org/opensearch/integtest/config/QueryNotificationConfigIT.kt b/notifications/notifications/src/test/kotlin/org/opensearch/integtest/config/QueryNotificationConfigIT.kt index efb1ea8e13a..48720824e98 100644 --- a/notifications/notifications/src/test/kotlin/org/opensearch/integtest/config/QueryNotificationConfigIT.kt +++ b/notifications/notifications/src/test/kotlin/org/opensearch/integtest/config/QueryNotificationConfigIT.kt @@ -164,7 +164,7 @@ class QueryNotificationConfigIT : PluginRestTestCase() { fun `test Get all notification config`() { val configIds: Set = (1..20).map { createConfig() }.toSet() - Thread.sleep(1000) + refreshAllIndices() // Get all notification configs val getAllConfigResponse = executeRequest( diff --git a/notifications/notifications/src/test/kotlin/org/opensearch/integtest/event/QueryNotificationEventIT.kt b/notifications/notifications/src/test/kotlin/org/opensearch/integtest/event/QueryNotificationEventIT.kt deleted file mode 100644 index daf8609a766..00000000000 --- a/notifications/notifications/src/test/kotlin/org/opensearch/integtest/event/QueryNotificationEventIT.kt +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.opensearch.integtest.event - -import org.opensearch.integtest.PluginRestTestCase -import org.opensearch.notifications.NotificationPlugin.Companion.PLUGIN_BASE_URI -import org.opensearch.rest.RestRequest -import org.opensearch.rest.RestStatus - -class QueryNotificationEventIT : PluginRestTestCase() { - - fun `test Get single absent notification event should fail as part of path`() { - // Get notification event with absent id - executeRequest( - RestRequest.Method.GET.name, - "$PLUGIN_BASE_URI/events/unavailable_config_id", - "", - RestStatus.NOT_FOUND.status - ) - } -} diff --git a/notifications/notifications/src/test/kotlin/org/opensearch/integtest/send/SendTestMessageRestHandlerIT.kt b/notifications/notifications/src/test/kotlin/org/opensearch/integtest/send/SendTestMessageRestHandlerIT.kt index cd2d0b1d2b5..8cd8fcfd9ec 100644 --- a/notifications/notifications/src/test/kotlin/org/opensearch/integtest/send/SendTestMessageRestHandlerIT.kt +++ b/notifications/notifications/src/test/kotlin/org/opensearch/integtest/send/SendTestMessageRestHandlerIT.kt @@ -5,7 +5,6 @@ package org.opensearch.integtest.send -import com.google.gson.JsonParser import org.junit.Assert import org.opensearch.commons.notifications.model.MethodType import org.opensearch.commons.notifications.model.SmtpAccount @@ -52,21 +51,6 @@ internal class SendTestMessageRestHandlerIT : PluginRestTestCase() { // verify failure response is with message val error = sendResponse.get("error").asJsonObject Assert.assertNotNull(error.get("reason").asString) - - // verify event is created correctly with status - val eventId = JsonParser.parseString(error.get("reason").asString).asJsonObject.get("notification_id").asString - val getEventResponse = executeRequest( - RestRequest.Method.GET.name, - "$PLUGIN_BASE_URI/events/$eventId", - "", - RestStatus.OK.status - ) - val items = getEventResponse.get("event_list").asJsonArray - Assert.assertEquals(1, items.size()) - val getResponseItem = items[0].asJsonObject - Assert.assertEquals(eventId, getResponseItem.get("event_id").asString) - Assert.assertNotNull(getResponseItem.get("event").asJsonObject) - Thread.sleep(100) } @Suppress("EmptyFunctionBlock") @@ -106,22 +90,6 @@ internal class SendTestMessageRestHandlerIT : PluginRestTestCase() { // verify failure response is with message val error = sendResponse.get("error").asJsonObject Assert.assertNotNull(error.get("reason").asString) - - // verify event is created correctly with status - val eventId = JsonParser.parseString(error.get("reason").asString).asJsonObject.get("notification_id").asString - - val getEventResponse = executeRequest( - RestRequest.Method.GET.name, - "$PLUGIN_BASE_URI/events/$eventId", - "", - RestStatus.OK.status - ) - val items = getEventResponse.get("event_list").asJsonArray - Assert.assertEquals(1, items.size()) - val getResponseItem = items[0].asJsonObject - Assert.assertEquals(eventId, getResponseItem.get("event_id").asString) - Assert.assertNotNull(getResponseItem.get("event").asJsonObject) - Thread.sleep(100) } @Suppress("EmptyFunctionBlock") @@ -164,22 +132,6 @@ internal class SendTestMessageRestHandlerIT : PluginRestTestCase() { // verify failure response is with message val error = sendResponse.get("error").asJsonObject Assert.assertNotNull(error.get("reason").asString) - - // verify event is created correctly with status - val eventId = JsonParser.parseString(error.get("reason").asString).asJsonObject.get("notification_id").asString - - val getEventResponse = executeRequest( - RestRequest.Method.GET.name, - "$PLUGIN_BASE_URI/events/$eventId", - "", - RestStatus.OK.status - ) - val items = getEventResponse.get("event_list").asJsonArray - Assert.assertEquals(1, items.size()) - val getResponseItem = items[0].asJsonObject - Assert.assertEquals(eventId, getResponseItem.get("event_id").asString) - Assert.assertNotNull(getResponseItem.get("event").asJsonObject) - Thread.sleep(100) } @Suppress("EmptyFunctionBlock") @@ -256,21 +208,5 @@ internal class SendTestMessageRestHandlerIT : PluginRestTestCase() { // verify failure response is with message val error = sendResponse.get("error").asJsonObject Assert.assertNotNull(error.get("reason").asString) - - // verify event is created correctly with status - val eventId = JsonParser.parseString(error.get("reason").asString).asJsonObject.get("notification_id").asString - - val getEventResponse = executeRequest( - RestRequest.Method.GET.name, - "$PLUGIN_BASE_URI/events/$eventId", - "", - RestStatus.OK.status - ) - val items = getEventResponse.get("event_list").asJsonArray - Assert.assertEquals(1, items.size()) - val getResponseItem = items[0].asJsonObject - Assert.assertEquals(eventId, getResponseItem.get("event_id").asString) - Assert.assertNotNull(getResponseItem.get("event").asJsonObject) - Thread.sleep(100) } } diff --git a/notifications/notifications/src/test/kotlin/org/opensearch/notifications/action/PluginActionTests.kt b/notifications/notifications/src/test/kotlin/org/opensearch/notifications/action/PluginActionTests.kt index 8d7934a635e..5025f500c9f 100644 --- a/notifications/notifications/src/test/kotlin/org/opensearch/notifications/action/PluginActionTests.kt +++ b/notifications/notifications/src/test/kotlin/org/opensearch/notifications/action/PluginActionTests.kt @@ -27,8 +27,6 @@ import org.opensearch.commons.notifications.action.GetChannelListRequest import org.opensearch.commons.notifications.action.GetChannelListResponse import org.opensearch.commons.notifications.action.GetNotificationConfigRequest import org.opensearch.commons.notifications.action.GetNotificationConfigResponse -import org.opensearch.commons.notifications.action.GetNotificationEventRequest -import org.opensearch.commons.notifications.action.GetNotificationEventResponse import org.opensearch.commons.notifications.action.GetPluginFeaturesRequest import org.opensearch.commons.notifications.action.GetPluginFeaturesResponse import org.opensearch.commons.notifications.action.LegacyPublishNotificationRequest @@ -38,10 +36,14 @@ import org.opensearch.commons.notifications.action.SendNotificationResponse import org.opensearch.commons.notifications.action.UpdateNotificationConfigRequest import org.opensearch.commons.notifications.action.UpdateNotificationConfigResponse import org.opensearch.commons.notifications.model.ChannelList +import org.opensearch.commons.notifications.model.ConfigType +import org.opensearch.commons.notifications.model.DeliveryStatus +import org.opensearch.commons.notifications.model.EventSource +import org.opensearch.commons.notifications.model.EventStatus import org.opensearch.commons.notifications.model.NotificationConfigSearchResult -import org.opensearch.commons.notifications.model.NotificationEventSearchResult +import org.opensearch.commons.notifications.model.NotificationEvent +import org.opensearch.commons.notifications.model.SeverityType import org.opensearch.notifications.index.ConfigIndexingActions -import org.opensearch.notifications.index.EventIndexingActions import org.opensearch.notifications.send.SendMessageActionHelper import org.opensearch.rest.RestStatus import org.opensearch.tasks.Task @@ -131,23 +133,6 @@ internal class PluginActionTests { getNotificationConfigAction.execute(task, request, AssertionListener(response)) } - @Test - fun `Get notification event action should call back action listener`() { - val request = mock(GetNotificationEventRequest::class.java) - val response = GetNotificationEventResponse( - mock(NotificationEventSearchResult::class.java) - ) - - // Mock singleton's method by mockk framework - mockkObject(EventIndexingActions) - every { EventIndexingActions.get(request, any()) } returns response - - val getNotificationEventAction = GetNotificationEventAction( - transportService, client, actionFilters, xContentRegistry - ) - getNotificationEventAction.execute(task, request, AssertionListener(response)) - } - @Test fun `Get plugin features action should call back action listener`() { val allowedConfigTypes = listOf("type1") @@ -180,7 +165,22 @@ internal class PluginActionTests { fun `Send notification action should call back action listener`() { val notificationId = "notification-1" val request = mock(SendNotificationRequest::class.java) - val response = SendNotificationResponse(notificationId) + + val sampleEventSource = EventSource( + "title", + "reference_id", + severity = SeverityType.INFO + ) + val sampleStatus = EventStatus( + "config_id", + "name", + ConfigType.SLACK, + deliveryStatus = DeliveryStatus("404", "invalid recipient") + ) + + val sampleEvent = NotificationEvent(sampleEventSource, listOf(sampleStatus)) + + val response = SendNotificationResponse(sampleEvent) // Mock singleton's method by mockk framework mockkObject(SendMessageActionHelper) diff --git a/notifications/notifications/src/test/kotlin/org/opensearch/notifications/index/NotificationEventIndexTests.kt b/notifications/notifications/src/test/kotlin/org/opensearch/notifications/index/NotificationEventIndexTests.kt deleted file mode 100644 index ffed4d05dee..00000000000 --- a/notifications/notifications/src/test/kotlin/org/opensearch/notifications/index/NotificationEventIndexTests.kt +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ -package org.opensearch.notifications.index - -import com.nhaarman.mockitokotlin2.any -import com.nhaarman.mockitokotlin2.whenever -import io.mockk.every -import io.mockk.mockkObject -import org.junit.jupiter.api.Assertions -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Assertions.assertNull -import org.junit.jupiter.api.Test -import org.mockito.Mockito.anyLong -import org.mockito.Mockito.mock -import org.opensearch.action.ActionFuture -import org.opensearch.action.admin.indices.create.CreateIndexResponse -import org.opensearch.action.get.GetRequest -import org.opensearch.action.get.GetResponse -import org.opensearch.client.AdminClient -import org.opensearch.client.Client -import org.opensearch.client.IndicesAdminClient -import org.opensearch.cluster.ClusterState -import org.opensearch.cluster.routing.RoutingTable -import org.opensearch.cluster.service.ClusterService -import org.opensearch.common.util.concurrent.ThreadContext -import org.opensearch.common.util.concurrent.ThreadContext.StoredContext -import org.opensearch.commons.notifications.model.ConfigType -import org.opensearch.commons.notifications.model.DeliveryStatus -import org.opensearch.commons.notifications.model.EventSource -import org.opensearch.commons.notifications.model.EventStatus -import org.opensearch.commons.notifications.model.NotificationEvent -import org.opensearch.commons.notifications.model.SeverityType -import org.opensearch.notifications.model.DocInfo -import org.opensearch.notifications.model.DocMetadata -import org.opensearch.notifications.model.NotificationEventDoc -import org.opensearch.notifications.model.NotificationEventDocInfo -import org.opensearch.threadpool.ThreadPool -import java.time.Instant - -internal class NotificationEventIndexTests { - - private val client: Client = mock(Client::class.java, "client") - - private val indexName = ".opensearch-notifications-event" - - private val clusterService: ClusterService = mock(ClusterService::class.java, "clusterService") - - @Suppress("UNCHECKED_CAST") - @Test - fun `index operation to get single event`() { - NotificationEventIndex.initialize(client, clusterService) - // creating expected value - val id = "index-1" - val docInfo = DocInfo("index-1", 1, 1, 1) - val lastUpdatedTimeMs = Instant.ofEpochMilli(Instant.now().toEpochMilli()) - val createdTimeMs = lastUpdatedTimeMs.minusSeconds(1000) - val metadata = DocMetadata( - lastUpdatedTimeMs, - createdTimeMs, - listOf("br1", "br2", "br3") - ) - val sampleEventSource = EventSource( - "title", - "reference_id", - tags = listOf("tag1", "tag2"), - severity = SeverityType.INFO - ) - val status = EventStatus( - "config_id", - "name", - ConfigType.CHIME, - deliveryStatus = DeliveryStatus("200", "success") - ) - val sampleEvent = NotificationEvent(sampleEventSource, listOf(status)) - val eventDoc = NotificationEventDoc(metadata, sampleEvent) - val expectedEventDocInfo = NotificationEventDocInfo(docInfo, eventDoc) - - // mocking the dependencies for isIndexExists function - mockIsIndexExists() - - // creating a mock index response for client - val mockActionGet = mock(CreateIndexResponse::class.java) - mockCreateIndex(mockActionGet) - whenever(mockActionGet.isAcknowledged).thenReturn(true) - - // creating a get request - val getRequest = GetRequest(indexName).id(id) - - // mocking ActionFuture to create response for the get request - val mockActionFuture: ActionFuture = mock(ActionFuture::class.java) as ActionFuture - whenever(client.get(getRequest)).thenReturn(mockActionFuture) - - // mocking the get request of Secure Index client - mockGetSecureIndexClient(mockActionFuture) - - // create mock response for actionGet - val mockGetResponse = mock(GetResponse::class.java) - whenever(mockActionFuture.actionGet(anyLong())).thenReturn(mockGetResponse) - - whenever(mockGetResponse.sourceAsString).thenReturn(true.toString()) - whenever(mockGetResponse.version).thenReturn(1) - whenever(mockGetResponse.primaryTerm).thenReturn(1) - whenever(mockGetResponse.seqNo).thenReturn(1) - - // mock object - mockkObject(NotificationEventDoc) - every { NotificationEventDoc.parse(any()) } returns eventDoc - - val actualEventDocInfo = NotificationEventIndex.getNotificationEvent(id) - assertEquals(expectedEventDocInfo, actualEventDocInfo) - } - - @Suppress("UNCHECKED_CAST") - @Test - fun `NotificationEventIndex should safely return null if response source is null`() { - NotificationEventIndex.initialize(client, clusterService) - val id = "index-1" - // mocking the dependencies for isIndexExists function - mockIsIndexExists() - - // creating a mock index response for client - val mockActionGet = mock(CreateIndexResponse::class.java) - mockCreateIndex(mockActionGet) - whenever(mockActionGet.isAcknowledged).thenReturn(true) - - // creating a get request - val getRequest = GetRequest(indexName).id(id) - // mock ActionFuture to create response for the get request - val mockActionFuture: ActionFuture = mock(ActionFuture::class.java) as ActionFuture - whenever(client.get(getRequest)).thenReturn(mockActionFuture) - - // mocking the get request of Secure Index client - mockGetSecureIndexClient(mockActionFuture) - - // create mock response for actionGet - val mockGetResponse = mock(GetResponse::class.java) - whenever(mockActionFuture.actionGet(anyLong())).thenReturn(mockGetResponse) - - val actualEventDocInfo = NotificationEventIndex.getNotificationEvent(id) - assertNull(actualEventDocInfo) - } - - @Suppress("UNCHECKED_CAST") - @Test - fun `NotificationEventIndex should throw exception if response isn't acknowledged`() { - NotificationEventIndex.initialize(client, clusterService) - val id = "index-1" - // mocking the dependencies for isIndexExists function - mockIsIndexExists() - - // creating a mock index response for client - val mockActionGet = mock(CreateIndexResponse::class.java) - mockCreateIndex(mockActionGet) - Assertions.assertThrows(IllegalStateException::class.java) { - NotificationEventIndex.getNotificationEvent(id) - } - } - - @Suppress("UNCHECKED_CAST") - @Test - fun `NotificationEventIndex should throw exception if index couldn't be created`() { - NotificationEventIndex.initialize(client, clusterService) - val id = "index-1" - // mocking the dependencies for isIndexExists function - mockIsIndexExists() - Assertions.assertThrows(Exception::class.java) { - NotificationEventIndex.getNotificationEvent(id) - } - } - - private fun mockIsIndexExists() { - val clusterState = mock(ClusterState::class.java) - whenever(clusterService.state()).thenReturn(clusterState) - - val mockRoutingTable = mock(RoutingTable::class.java) - val mockHasIndex = mockRoutingTable.hasIndex(indexName) - whenever(clusterState.routingTable).thenReturn(mockRoutingTable) - whenever(mockRoutingTable.hasIndex(indexName)).thenReturn(mockHasIndex) - } - - @Suppress("UNCHECKED_CAST") - private fun mockCreateIndex(mockActionGet: CreateIndexResponse) { - val admin = mock(AdminClient::class.java) - val indices = mock(IndicesAdminClient::class.java) - val mockCreateClient: ActionFuture = - mock(ActionFuture::class.java) as ActionFuture - - whenever(client.admin()).thenReturn(admin) - whenever(admin.indices()).thenReturn(indices) - whenever(indices.create(any())).thenReturn(mockCreateClient) - whenever(mockCreateClient.actionGet(anyLong())).thenReturn(mockActionGet) - } - - private fun mockGetSecureIndexClient(mockActionFuture: ActionFuture) { - val mockThreadPool = mock(ThreadPool::class.java) - val mockThreadContext = mock(ThreadContext::class.java) - val mockStashContext = mock(StoredContext::class.java) - - whenever(client.threadPool()).thenReturn(mockThreadPool) - whenever(mockThreadPool.threadContext).thenReturn(mockThreadContext) - whenever(mockThreadContext.stashContext()).thenReturn(mockStashContext) - whenever(client.get(any())).thenReturn(mockActionFuture) - } -} diff --git a/notifications/notifications/src/test/kotlin/org/opensearch/notifications/model/NotificationEventDocTests.kt b/notifications/notifications/src/test/kotlin/org/opensearch/notifications/model/NotificationEventDocTests.kt deleted file mode 100644 index e49dba0a356..00000000000 --- a/notifications/notifications/src/test/kotlin/org/opensearch/notifications/model/NotificationEventDocTests.kt +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ -package org.opensearch.notifications.model - -import org.junit.jupiter.api.Assertions -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test -import org.opensearch.commons.notifications.model.ConfigType -import org.opensearch.commons.notifications.model.DeliveryStatus -import org.opensearch.commons.notifications.model.EventSource -import org.opensearch.commons.notifications.model.EventStatus -import org.opensearch.commons.notifications.model.NotificationEvent -import org.opensearch.commons.notifications.model.SeverityType -import org.opensearch.notifications.createObjectFromJsonString -import org.opensearch.notifications.getJsonString -import java.time.Instant - -internal class NotificationEventDocTests { - - @Test - fun `Event doc serialize and deserialize using json config object should be equal`() { - val lastUpdatedTimeMs = Instant.ofEpochMilli(Instant.now().toEpochMilli()) - val createdTimeMs = Instant.ofEpochMilli(Instant.now().minusSeconds(2000).toEpochMilli()) - val metadata = DocMetadata( - lastUpdatedTimeMs, - createdTimeMs, - listOf("br1", "br2", "br3") - ) - val sampleEventSource = EventSource( - "title", - "reference_id", - tags = listOf("tag1", "tag2"), - severity = SeverityType.INFO - ) - val status = EventStatus( - "config_id", - "name", - ConfigType.CHIME, - deliveryStatus = DeliveryStatus("200", "success") - ) - val sampleEvent = NotificationEvent(sampleEventSource, listOf(status)) - val eventDoc = NotificationEventDoc(metadata, sampleEvent) - val jsonString = getJsonString(eventDoc) - val recreatedObject = createObjectFromJsonString(jsonString) { NotificationEventDoc.parse(it) } - assertEquals(eventDoc, recreatedObject) - } - - @Test - fun `Event doc should safely ignore extra field in json object`() { - val lastUpdatedTimeMs = Instant.ofEpochMilli(Instant.now().toEpochMilli()) - val createdTimeMs = Instant.ofEpochMilli(Instant.now().minusSeconds(2000).toEpochMilli()) - val metadata = DocMetadata( - lastUpdatedTimeMs, - createdTimeMs, - listOf("br1", "br2", "br3") - ) - val eventSource = EventSource( - "title", - "reference_id", - tags = listOf("tag1", "tag2"), - severity = SeverityType.INFO - ) - val eventStatus = EventStatus( - "config_id", - "name", - ConfigType.CHIME, - deliveryStatus = DeliveryStatus("200", "success") - ) - val sampleEvent = NotificationEvent(eventSource, listOf(eventStatus)) - val eventDoc = NotificationEventDoc(metadata, sampleEvent) - val jsonString = """ - { - "metadata":{ - "last_updated_time_ms":"${lastUpdatedTimeMs.toEpochMilli()}", - "created_time_ms":"${createdTimeMs.toEpochMilli()}", - "access":["br1", "br2", "br3"] - }, - "event":{ - "event_source":{ - "title":"title", - "reference_id":"reference_id", - "severity":"info", - "tags":["tag1", "tag2"] - }, - "status_list":[ - { - "config_id":"config_id", - "config_type":"chime", - "config_name":"name", - "delivery_status": - { - "status_code":"200", - "status_text":"success" - } - } - ] - }, - "extra_field_1":["extra", "value"], - "extra_field_2":{"extra":"value"}, - "extra_field_3":"extra value 3" - } - """.trimIndent() - val recreatedObject = createObjectFromJsonString(jsonString) { NotificationEventDoc.parse(it) } - assertEquals(eventDoc, recreatedObject) - } - - @Test - fun `Event doc should throw exception if metadata is absent in json`() { - val jsonString = """ - { - "event":{ - "event_source":{ - "title":"title", - "reference_id":"reference_id", - "severity":"info", - "tags":["tag1", "tag2"] - }, - "status_list":[ - { - "config_id":"config_id", - "config_type":"chime", - "config_name":"name", - "delivery_status": - { - "status_code":"200", - "status_text":"success" - } - } - ] - } - } - """.trimIndent() - Assertions.assertThrows(IllegalArgumentException::class.java) { - createObjectFromJsonString(jsonString) { NotificationEventDoc.parse(it) } - } - } - - @Test - fun `Event doc should throw exception if event is absent in json`() { - val lastUpdatedTimeMs = Instant.ofEpochMilli(Instant.now().toEpochMilli()) - val createdTimeMs = Instant.ofEpochMilli(Instant.now().minusSeconds(2000).toEpochMilli()) - val jsonString = """ - { - "metadata":{ - "last_updated_time_ms":"${lastUpdatedTimeMs.toEpochMilli()}", - "created_time_ms":"${createdTimeMs.toEpochMilli()}", - "access":["br1", "br2", "br3"] - } - } - """.trimIndent() - Assertions.assertThrows(IllegalArgumentException::class.java) { - createObjectFromJsonString(jsonString) { NotificationEventDoc.parse(it) } - } - } -}