This is an automated email from the ASF dual-hosted git repository. btellier pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/james-project.git
The following commit(s) were added to refs/heads/master by this push: new 5ccc59b3e3 JAMES-4052 Add optional user property to OpenSearch index (#2363) 5ccc59b3e3 is described below commit 5ccc59b3e3582bf0d8fc86494f187e8f85ea52f8 Author: Benoit TELLIER <btell...@linagora.com> AuthorDate: Fri Jul 26 10:20:43 2024 +0200 JAMES-4052 Add optional user property to OpenSearch index (#2363) --- .../pages/distributed/configure/opensearch.adoc | 4 +++ .../mailbox/opensearch/MailboxMappingFactory.java | 3 ++ .../opensearch/OpenSearchMailboxConfiguration.java | 35 +++++++++++++++--- .../OpenSearchListeningMessageSearchIndex.java | 4 +-- .../mailbox/opensearch/json/IndexableMessage.java | 23 ++++++++++-- .../opensearch/json/JsonMessageConstants.java | 1 + .../opensearch/json/MessageToOpenSearchJson.java | 41 +++++++++++++++++++++- src/site/xdoc/server/config-opensearch.xml | 2 ++ upgrade-instructions.md | 26 +++++++++++++- 9 files changed, 128 insertions(+), 11 deletions(-) diff --git a/docs/modules/servers/pages/distributed/configure/opensearch.adoc b/docs/modules/servers/pages/distributed/configure/opensearch.adoc index 0b70fd3b43..fd061b4407 100644 --- a/docs/modules/servers/pages/distributed/configure/opensearch.adoc +++ b/docs/modules/servers/pages/distributed/configure/opensearch.adoc @@ -99,6 +99,10 @@ Default to false. | opensearch.indexBody | Indicates if you wish to index body or not (default: true). This can be used to decrease the performance cost associated with indexing. + +| opensearch.indexUser +| Indicates if you wish to index user or not (default: false). This can be used to have per user reports in OpenSearch Dashboards. + |=== === Quota search diff --git a/mailbox/opensearch/src/main/java/org/apache/james/mailbox/opensearch/MailboxMappingFactory.java b/mailbox/opensearch/src/main/java/org/apache/james/mailbox/opensearch/MailboxMappingFactory.java index 9d97e9fd9e..84a61f9b9b 100644 --- a/mailbox/opensearch/src/main/java/org/apache/james/mailbox/opensearch/MailboxMappingFactory.java +++ b/mailbox/opensearch/src/main/java/org/apache/james/mailbox/opensearch/MailboxMappingFactory.java @@ -251,6 +251,9 @@ public class MailboxMappingFactory { .put(JsonMessageConstants.MIME_MESSAGE_ID, new Property.Builder() .keyword(new KeywordProperty.Builder().build()) .build()) + .put(JsonMessageConstants.USER, new Property.Builder() + .keyword(new KeywordProperty.Builder().build()) + .build()) .put(JsonMessageConstants.TEXT_BODY, new Property.Builder() .text(new TextProperty.Builder().analyzer(STANDARD).build()) .build()) diff --git a/mailbox/opensearch/src/main/java/org/apache/james/mailbox/opensearch/OpenSearchMailboxConfiguration.java b/mailbox/opensearch/src/main/java/org/apache/james/mailbox/opensearch/OpenSearchMailboxConfiguration.java index 6fa68d0286..16958f6411 100644 --- a/mailbox/opensearch/src/main/java/org/apache/james/mailbox/opensearch/OpenSearchMailboxConfiguration.java +++ b/mailbox/opensearch/src/main/java/org/apache/james/mailbox/opensearch/OpenSearchMailboxConfiguration.java @@ -26,6 +26,7 @@ import org.apache.commons.configuration2.Configuration; import org.apache.james.backends.opensearch.IndexName; import org.apache.james.backends.opensearch.ReadAliasName; import org.apache.james.backends.opensearch.WriteAliasName; +import org.apache.james.mailbox.opensearch.json.MessageToOpenSearchJson.IndexUser; public class OpenSearchMailboxConfiguration { @@ -37,6 +38,7 @@ public class OpenSearchMailboxConfiguration { private Optional<IndexHeaders> indexHeaders; private Optional<Boolean> optimiseMoves; private Optional<IndexBody> indexBody; + private Optional<IndexUser> indexUser; Builder() { indexMailboxName = Optional.empty(); @@ -46,6 +48,7 @@ public class OpenSearchMailboxConfiguration { indexHeaders = Optional.empty(); optimiseMoves = Optional.empty(); indexBody = Optional.empty(); + indexUser = Optional.empty(); } public Builder indexMailboxName(Optional<IndexName> indexMailboxName) { @@ -83,6 +86,11 @@ public class OpenSearchMailboxConfiguration { return this; } + public Builder indexUser(IndexUser indexUser) { + this.indexUser = Optional.ofNullable(indexUser); + return this; + } + public OpenSearchMailboxConfiguration build() { return new OpenSearchMailboxConfiguration( indexMailboxName.orElse(MailboxOpenSearchConstants.DEFAULT_MAILBOX_INDEX), @@ -91,7 +99,8 @@ public class OpenSearchMailboxConfiguration { indexAttachment.orElse(IndexAttachments.YES), indexHeaders.orElse(IndexHeaders.YES), optimiseMoves.orElse(DEFAULT_OPTIMIZE_MOVES), - indexBody.orElse(IndexBody.YES)); + indexBody.orElse(IndexBody.YES), + indexUser.orElse(IndexUser.NO)); } } @@ -109,10 +118,12 @@ public class OpenSearchMailboxConfiguration { private static final String OPENSEARCH_INDEX_HEADERS = "opensearch.indexHeaders"; private static final String OPENSEARCH_MESSAGE_INDEX_OPTIMIZE_MOVE = "opensearch.message.index.optimize.move"; private static final String OPENSEARCH_INDEX_BODY = "opensearch.indexBody"; + private static final String OPENSEARCH_INDEX_USER = "opensearch.indexUser"; private static final boolean DEFAULT_INDEX_ATTACHMENTS = true; private static final boolean DEFAULT_INDEX_HEADERS = true; public static final boolean DEFAULT_OPTIMIZE_MOVES = false; public static final boolean DEFAULT_INDEX_BODY = true; + public static final boolean DEFAULT_INDEX_USER = false; public static final OpenSearchMailboxConfiguration DEFAULT_CONFIGURATION = builder().build(); public static OpenSearchMailboxConfiguration fromProperties(Configuration configuration) { @@ -124,6 +135,7 @@ public class OpenSearchMailboxConfiguration { .indexHeaders(provideIndexHeaders(configuration)) .optimiseMoves(configuration.getBoolean(OPENSEARCH_MESSAGE_INDEX_OPTIMIZE_MOVE, null)) .indexBody(provideIndexBody(configuration)) + .indexUser(provideIndexUser(configuration)) .build(); } @@ -169,6 +181,13 @@ public class OpenSearchMailboxConfiguration { return IndexBody.NO; } + private static IndexUser provideIndexUser(Configuration configuration) { + if (configuration.getBoolean(OPENSEARCH_INDEX_USER, DEFAULT_INDEX_USER)) { + return IndexUser.YES; + } + return IndexUser.NO; + } + private final IndexName indexMailboxName; private final ReadAliasName readAliasMailboxName; private final WriteAliasName writeAliasMailboxName; @@ -176,10 +195,12 @@ public class OpenSearchMailboxConfiguration { private final IndexHeaders indexHeaders; private final boolean optimiseMoves; private final IndexBody indexBody; + private final IndexUser indexUser; private OpenSearchMailboxConfiguration(IndexName indexMailboxName, ReadAliasName readAliasMailboxName, WriteAliasName writeAliasMailboxName, IndexAttachments indexAttachment, - IndexHeaders indexHeaders, boolean optimiseMoves, IndexBody indexBody) { + IndexHeaders indexHeaders, boolean optimiseMoves, IndexBody indexBody, + IndexUser indexUser) { this.indexMailboxName = indexMailboxName; this.readAliasMailboxName = readAliasMailboxName; this.writeAliasMailboxName = writeAliasMailboxName; @@ -187,6 +208,7 @@ public class OpenSearchMailboxConfiguration { this.indexHeaders = indexHeaders; this.optimiseMoves = optimiseMoves; this.indexBody = indexBody; + this.indexUser = indexUser; } public IndexName getIndexMailboxName() { @@ -217,6 +239,10 @@ public class OpenSearchMailboxConfiguration { return indexBody; } + public IndexUser getIndexUser() { + return indexUser; + } + @Override public final boolean equals(Object o) { if (o instanceof OpenSearchMailboxConfiguration) { @@ -228,7 +254,8 @@ public class OpenSearchMailboxConfiguration { && Objects.equals(this.readAliasMailboxName, that.readAliasMailboxName) && Objects.equals(this.optimiseMoves, that.optimiseMoves) && Objects.equals(this.writeAliasMailboxName, that.writeAliasMailboxName) - && Objects.equals(this.indexBody, that.indexBody); + && Objects.equals(this.indexBody, that.indexBody) + && Objects.equals(this.indexUser, that.indexUser); } return false; } @@ -236,6 +263,6 @@ public class OpenSearchMailboxConfiguration { @Override public final int hashCode() { return Objects.hash(indexMailboxName, readAliasMailboxName, writeAliasMailboxName, indexAttachment, indexHeaders, - writeAliasMailboxName, optimiseMoves, indexBody); + writeAliasMailboxName, optimiseMoves, indexBody, indexUser); } } diff --git a/mailbox/opensearch/src/main/java/org/apache/james/mailbox/opensearch/events/OpenSearchListeningMessageSearchIndex.java b/mailbox/opensearch/src/main/java/org/apache/james/mailbox/opensearch/events/OpenSearchListeningMessageSearchIndex.java index 1ec770400b..1dbc729adc 100644 --- a/mailbox/opensearch/src/main/java/org/apache/james/mailbox/opensearch/events/OpenSearchListeningMessageSearchIndex.java +++ b/mailbox/opensearch/src/main/java/org/apache/james/mailbox/opensearch/events/OpenSearchListeningMessageSearchIndex.java @@ -357,7 +357,7 @@ public class OpenSearchListeningMessageSearchIndex extends ListeningMessageSearc } private Mono<String> generateIndexedJson(Mailbox mailbox, MailboxMessage message, MailboxSession session) { - return messageToOpenSearchJson.convertToJson(message) + return messageToOpenSearchJson.convertToJson(message, session) .onErrorResume(e -> { LOGGER.warn("Indexing mailbox {}-{} of user {} on message {} without attachments ", mailbox.getName(), @@ -365,7 +365,7 @@ public class OpenSearchListeningMessageSearchIndex extends ListeningMessageSearc session.getUser().asString(), message.getUid(), e); - return messageToOpenSearchJson.convertToJsonWithoutAttachment(message); + return messageToOpenSearchJson.convertToJsonWithoutAttachment(message, session); }); } diff --git a/mailbox/opensearch/src/main/java/org/apache/james/mailbox/opensearch/json/IndexableMessage.java b/mailbox/opensearch/src/main/java/org/apache/james/mailbox/opensearch/json/IndexableMessage.java index f068801a5e..23569aed72 100644 --- a/mailbox/opensearch/src/main/java/org/apache/james/mailbox/opensearch/json/IndexableMessage.java +++ b/mailbox/opensearch/src/main/java/org/apache/james/mailbox/opensearch/json/IndexableMessage.java @@ -38,6 +38,7 @@ import org.apache.james.mailbox.store.mail.model.MailboxMessage; import org.apache.james.mailbox.store.search.SearchUtil; import org.apache.james.mime4j.MimeException; +import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; @@ -64,6 +65,7 @@ public class IndexableMessage { private MailboxMessage message; private TextExtractor textExtractor; private ZoneId zoneId; + private String user = null; private Builder() { } @@ -112,6 +114,13 @@ public class IndexableMessage { return this; } + public Builder user(MessageToOpenSearchJson.IndexUser indexUser, String user) { + if (indexUser == MessageToOpenSearchJson.IndexUser.YES) { + this.user = user; + } + return this; + } + private Mono<IndexableMessage> instantiateIndexedMessage() throws IOException, MimeException { String messageId = SearchUtil.getSerializedMessageIdIfSupportedByUnderlyingStorageOrNull(message); String threadId = SearchUtil.getSerializedThreadIdIfSupportedByUnderlyingStorageOrNull(message); @@ -175,6 +184,7 @@ public class IndexableMessage { mediaType, messageId, threadId, + user, modSeq, sentDate, saveDate, @@ -183,8 +193,7 @@ public class IndexableMessage { subType, to, uid, - userFlags, - mimeMessageID); + userFlags, mimeMessageID); }); } @@ -236,6 +245,7 @@ public class IndexableMessage { private final String mediaType; private final String messageId; private final String threadId; + private final String user; private final long modSeq; private final String sentDate; private final Optional<String> saveDate; @@ -264,7 +274,7 @@ public class IndexableMessage { boolean isUnRead, String mailboxId, String mediaType, String messageId, - String threadId, + String threadId, String user, ModSeq modSeq, String sentDate, Optional<String> saveDate, long size, @@ -293,6 +303,7 @@ public class IndexableMessage { this.mediaType = mediaType; this.messageId = messageId; this.threadId = threadId; + this.user = user; this.modSeq = modSeq.asLong(); this.sentDate = sentDate; this.saveDate = saveDate; @@ -445,6 +456,12 @@ public class IndexableMessage { return isUnRead; } + @JsonProperty(JsonMessageConstants.USER) + @JsonInclude(JsonInclude.Include.NON_NULL) + public String getUser() { + return user; + } + @JsonProperty(JsonMessageConstants.MIME_MESSAGE_ID) public Optional<String> getMimeMessageID() { return mimeMessageID; diff --git a/mailbox/opensearch/src/main/java/org/apache/james/mailbox/opensearch/json/JsonMessageConstants.java b/mailbox/opensearch/src/main/java/org/apache/james/mailbox/opensearch/json/JsonMessageConstants.java index 798f4e6455..65cef55261 100644 --- a/mailbox/opensearch/src/main/java/org/apache/james/mailbox/opensearch/json/JsonMessageConstants.java +++ b/mailbox/opensearch/src/main/java/org/apache/james/mailbox/opensearch/json/JsonMessageConstants.java @@ -47,6 +47,7 @@ public interface JsonMessageConstants { String ATTACHMENTS = "attachments"; String TEXT = "text"; String MIME_MESSAGE_ID = "mimeMessageID"; + String USER = "user"; String MODSEQ = "modSeq"; String USER_FLAGS = "userFlags"; diff --git a/mailbox/opensearch/src/main/java/org/apache/james/mailbox/opensearch/json/MessageToOpenSearchJson.java b/mailbox/opensearch/src/main/java/org/apache/james/mailbox/opensearch/json/MessageToOpenSearchJson.java index fa47d7ac87..4253dfa9dd 100644 --- a/mailbox/opensearch/src/main/java/org/apache/james/mailbox/opensearch/json/MessageToOpenSearchJson.java +++ b/mailbox/opensearch/src/main/java/org/apache/james/mailbox/opensearch/json/MessageToOpenSearchJson.java @@ -28,6 +28,7 @@ import java.util.Date; import jakarta.inject.Inject; import jakarta.mail.Flags; +import org.apache.james.mailbox.MailboxSession; import org.apache.james.mailbox.MessageUid; import org.apache.james.mailbox.ModSeq; import org.apache.james.mailbox.extractor.TextExtractor; @@ -49,6 +50,9 @@ import com.google.common.base.Preconditions; import reactor.core.publisher.Mono; public class MessageToOpenSearchJson { + public enum IndexUser { + YES, NO + } private final ObjectMapper mapper; private final TextExtractor textExtractor; @@ -56,12 +60,17 @@ public class MessageToOpenSearchJson { private final IndexAttachments indexAttachments; private final IndexHeaders indexHeaders; private final IndexBody indexBody; + private final IndexUser indexUser; public MessageToOpenSearchJson(TextExtractor textExtractor, ZoneId zoneId, IndexAttachments indexAttachments, IndexHeaders indexHeaders) { this(textExtractor, zoneId, indexAttachments, indexHeaders, IndexBody.YES); } public MessageToOpenSearchJson(TextExtractor textExtractor, ZoneId zoneId, IndexAttachments indexAttachments, IndexHeaders indexHeaders, IndexBody indexBody) { + this(textExtractor, zoneId, indexAttachments, indexHeaders, indexBody, IndexUser.YES); + } + + public MessageToOpenSearchJson(TextExtractor textExtractor, ZoneId zoneId, IndexAttachments indexAttachments, IndexHeaders indexHeaders, IndexBody indexBody, IndexUser indexUser) { this.textExtractor = textExtractor; this.zoneId = zoneId; this.indexAttachments = indexAttachments; @@ -70,11 +79,41 @@ public class MessageToOpenSearchJson { this.mapper.registerModule(new GuavaModule()); this.mapper.registerModule(new Jdk8Module()); this.indexBody = indexBody; + this.indexUser = indexUser; } @Inject public MessageToOpenSearchJson(TextExtractor textExtractor, OpenSearchMailboxConfiguration configuration) { - this(textExtractor, ZoneId.systemDefault(), configuration.getIndexAttachment(), configuration.getIndexHeaders(), configuration.getIndexBody()); + this(textExtractor, ZoneId.systemDefault(), configuration.getIndexAttachment(), configuration.getIndexHeaders(), + configuration.getIndexBody(), configuration.getIndexUser()); + } + + public Mono<String> convertToJson(MailboxMessage message, MailboxSession session) { + Preconditions.checkNotNull(message); + + return IndexableMessage.builder() + .message(message) + .extractor(textExtractor) + .zoneId(zoneId) + .indexAttachments(indexAttachments) + .indexHeaders(indexHeaders) + .indexBody(indexBody) + .user(indexUser, session.getUser().asString()) + .build() + .map(Throwing.function(mapper::writeValueAsString)); + } + + public Mono<String> convertToJsonWithoutAttachment(MailboxMessage message, MailboxSession session) { + return IndexableMessage.builder() + .message(message) + .extractor(textExtractor) + .zoneId(zoneId) + .indexAttachments(IndexAttachments.NO) + .indexHeaders(indexHeaders) + .user(indexUser, session.getUser().asString()) + .indexBody(indexBody) + .build() + .map(Throwing.function(mapper::writeValueAsString)); } public Mono<String> convertToJson(MailboxMessage message) { diff --git a/src/site/xdoc/server/config-opensearch.xml b/src/site/xdoc/server/config-opensearch.xml index 6dda770d3c..e6c5f95f3c 100644 --- a/src/site/xdoc/server/config-opensearch.xml +++ b/src/site/xdoc/server/config-opensearch.xml @@ -124,6 +124,8 @@ Default to false.</dd> <dt><strong>opensearch.indexBody</strong></dt> <dd>Indicates if you wish to index body or not (default: true). This can be used to decrease the performance cost associated with indexing.</dd> + <dt><strong>opensearch.indexUser</strong></dt> + <dd>Indicates if you wish to index user or not (default: false). This can be used to have per user reports in OpenSearch Dashboards.</dd> <dt><strong>opensearch.index.quota.ratio.name</strong></dt> <dd>Specify the OpenSearch alias name used for quotas</dd> <dt><strong>opensearch.alias.read.quota.ratio.name</strong></dt> diff --git a/upgrade-instructions.md b/upgrade-instructions.md index d1a308f9a2..2d9bfe8b52 100644 --- a/upgrade-instructions.md +++ b/upgrade-instructions.md @@ -32,8 +32,9 @@ Change list: - [Make all queues on RabbitMQ quorum queue when `quorum.queues.enable=true`](#make-all-queues-on-rabbitmq-quorum-queue-when-quorumqueuesenabletrue) - [Migrate RabbitMQ classic queues to version 2](#migrate-rabbitmq-classic-queues-to-version-2) - [JAMES-3946 White list removals](#james-3946-white-list-removals) + - [JAMES-4052 Details in quota index](#james-4052-details-in-quota-index) -### JAMES-4052 Details in quota index +### JAMES-4052 Details in quota index and mailbox user Date: 23/07/2024 @@ -80,6 +81,29 @@ opensearch.alias.read.quota.ratio.name=james-quota-ratio-read-v2 opensearch.alias.write.quota.ratio.name=james-quota-ratio-write-v2 ``` +We also added an optional field to the mailbox mapping for `user`. It allows doing per user analysis in OpenSearch dashboards. + +In order to activate that field, include in `opensearch.properties`: + +``` +opensearch.indexUser=true +``` + +And create the field: + +``` +curl -X PUT \ + http://ip:port/mailbox_v2/_mapping \ + -H 'Content-Type: application/json' \ + -d '{ + "properties": { + "user": { + "type": "keyword" + } + } +}' +``` + ### JAMES-3946 White list removals Date: 14/06/2024 --------------------------------------------------------------------- To unsubscribe, e-mail: notifications-unsubscr...@james.apache.org For additional commands, e-mail: notifications-h...@james.apache.org