Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

APP-3073: Enhancements on MessageService #277

Merged
merged 18 commits into from
Oct 9, 2020
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
214d830
Add internal dependencies version to bom module
symphony-hong Oct 1, 2020
4220b9c
Update missing dependencies
symphony-hong Oct 2, 2020
a9c2f47
Merge branch 'master' of https://github.com/SymphonyPlatformSolutions…
symphony-hong Oct 5, 2020
b3ad26d
Adding plugin to http and template submodule
symphony-hong Oct 5, 2020
72bb76c
Ability to directly set privateKey and certificate content
symphony-hong Oct 6, 2020
ca67543
Merge branch 'master' of https://github.com/SymphonyPlatformSolutions…
symphony-hong Oct 6, 2020
8991945
Fix typo and add documentation
symphony-hong Oct 6, 2020
ba7cef6
Update documentation
symphony-hong Oct 6, 2020
ca0035c
Enhancements on message service
symphony-hong Oct 7, 2020
8503e8f
Merge branch 'master' of https://github.com/SymphonyPlatformSolutions…
symphony-hong Oct 7, 2020
98c3b3d
Merge branch 'master' into APP-3073
symphony-hong Oct 7, 2020
fbe6676
Create message and attachment model to use in MessageService
symphony-hong Oct 8, 2020
edd4225
Merge branch 'master' of https://github.com/SymphonyPlatformSolutions…
symphony-hong Oct 8, 2020
b99a085
Merge branch 'APP-3073' of github.com:symphony-hong/symphony-api-clie…
symphony-hong Oct 8, 2020
09b9aef
Refactor MessageService and update unittest
symphony-hong Oct 8, 2020
865bf89
Provide MessageService#builder to build a message instance from Messa…
symphony-hong Oct 9, 2020
5fba2fa
Merge branch 'master' into APP-3073
symphony-hong Oct 9, 2020
d837eda
Update handling attachments
symphony-hong Oct 9, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import com.symphony.bdk.core.client.ApiClientFactory;
import com.symphony.bdk.core.config.model.BdkConfig;
import com.symphony.bdk.core.retry.RetryWithRecoveryBuilder;
import com.symphony.bdk.core.service.MessageService;
import com.symphony.bdk.core.service.message.MessageService;
thibauult marked this conversation as resolved.
Show resolved Hide resolved
import com.symphony.bdk.core.service.SessionService;
import com.symphony.bdk.core.service.datafeed.DatafeedService;
import com.symphony.bdk.core.service.datafeed.DatafeedVersion;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import com.symphony.bdk.core.auth.exception.AuthUnauthorizedException;
import com.symphony.bdk.core.client.ApiClientFactory;
import com.symphony.bdk.core.config.model.BdkConfig;
import com.symphony.bdk.core.service.MessageService;
import com.symphony.bdk.core.service.message.MessageService;
import com.symphony.bdk.core.service.SessionService;
import com.symphony.bdk.core.service.datafeed.DatafeedService;
import com.symphony.bdk.core.service.stream.StreamService;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ private PrivateKey loadPrivateKeyFromAuthenticationConfig(BdkAuthenticationConfi
try {
String privateKey;
if (isNotEmpty(config.getPrivateKeyContent())) {
privateKey = new String(config.getPrivateKeyContent());
privateKey = new String(config.getPrivateKeyContent(), StandardCharsets.UTF_8);
} else {
String privateKeyPath = config.getPrivateKeyPath();
log.debug("Loading RSA privateKey from path : {}", privateKeyPath);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package com.symphony.bdk.core.service;
package com.symphony.bdk.core.service.message;


import com.symphony.bdk.core.auth.AuthSession;
import com.symphony.bdk.core.retry.RetryWithRecovery;
import com.symphony.bdk.core.retry.RetryWithRecoveryBuilder;
import com.symphony.bdk.core.service.message.exception.MessageCreationException;
import com.symphony.bdk.core.service.message.model.Attachment;
import com.symphony.bdk.core.service.message.model.Message;
import com.symphony.bdk.core.service.message.model.MessageBuilder;
import com.symphony.bdk.core.service.pagination.PaginatedApi;
import com.symphony.bdk.core.service.pagination.PaginatedService;
import com.symphony.bdk.core.service.stream.constant.AttachmentSort;
Expand All @@ -27,12 +31,14 @@
import com.symphony.bdk.gen.api.model.V4Stream;
import com.symphony.bdk.http.api.util.ApiUtils;
import com.symphony.bdk.template.api.TemplateEngine;
import com.symphony.bdk.template.api.TemplateException;
import com.symphony.bdk.template.api.TemplateResolver;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apiguardian.api.API;

import java.io.File;
import java.io.IOException;
import java.time.Instant;
import java.util.List;
import java.util.stream.Stream;
Expand Down Expand Up @@ -93,6 +99,10 @@ public TemplateEngine templates() {
return templateEngine;
}

public MessageBuilder builder() {
return new MessageBuilder(this);
}

/**
* Get messages from an existing stream. Additionally returns any attachments associated with the message.
*
Expand Down Expand Up @@ -164,60 +174,70 @@ public Stream<V4Message> getMessagesStream(@Nonnull String streamId, @Nonnull In
* @param message the message payload in MessageML
* @return a {@link V4Message} object containing the details of the sent message
* @see <a href="https://developers.symphony.com/restapi/reference#create-message-v4">Create Message v4</a>
* @deprecated this method will be replaced by {@link MessageService#send(V4Stream, Message)}
*/
@Deprecated
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use Javadoc @deprecated tag to mention that this method will be replaced by send(V4Stream, Message)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should not we remove it at once since we are in beta?

public V4Message send(@Nonnull V4Stream stream, @Nonnull String message) {
return send(stream.getStreamId(), message);
}

/**
* Sends a templated to the stream ID of the passed {@link V4Stream} object.
* Sends a message to the stream ID passed in parameter.
*
* @param stream the stream to send the message to
* @param template the template name to be used to produce the message
* @param parameters the parameters to pass to the template to produce the message to be sent
* @param streamId the ID of the stream to send the message to
* @param message the message payload in MessageML
symphony-hong marked this conversation as resolved.
Show resolved Hide resolved
* @return a {@link V4Message} object containing the details of the sent message
* @see <a href="https://developers.symphony.com/restapi/reference#create-message-v4">Create Message v4</a>
* @deprecated this method will be replaced by {@link MessageService#send(String, Message)}
*/
public V4Message send(@Nonnull V4Stream stream, @Nonnull String template, Object parameters)
throws TemplateException {
return send(stream.getStreamId(), templateResolver.resolve(template).process(parameters));
}

/**
* Sends a templated to the stream ID passed in parameter.
*
* @param streamId the ID of the stream to send the message to
* @param template the template name to be used to produce the message
* @param parameters the parameters to pass to the template to produce the message to be sent
* @return a {@link V4Message} object containing the details of the sent message
* @throws TemplateException
*/
public V4Message send(@Nonnull String streamId, @Nonnull String template, Object parameters)
throws TemplateException {
return send(streamId, templateResolver.resolve(template).process(parameters));
@Deprecated
public V4Message send(@Nonnull String streamId, @Nonnull String message) {
return executeAndRetry("send", () -> messagesApi.v4StreamSidMessageCreatePost(
streamId,
authSession.getSessionToken(),
authSession.getKeyManagerToken(),
message,
null,
null,
null,
null
));
}

/**
* Sends a message to the stream ID passed in parameter.
*
* @param streamId the ID of the stream to send the message to
* @param message the message payload in MessageML
* @param streamId the ID of the stream to send the message to
* @param message the message to send to the stream
* @return a {@link V4Message} object containing the details of the sent message
* @see <a href="https://developers.symphony.com/restapi/reference#create-message-v4">Create Message v4</a>
*/
public V4Message send(@Nonnull String streamId, @Nonnull String message) {
public V4Message send(@Nonnull String streamId, @Nonnull Message message) {
File tempFile = this.createTemporaryAttachmentFile(message.getAttachment());
return executeAndRetry("send", () -> messagesApi.v4StreamSidMessageCreatePost(
streamId,
authSession.getSessionToken(),
authSession.getKeyManagerToken(),
message,
null,
null,
message.getContent(),
message.getData(),
null,
tempFile,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this.createTemporaryAttachmentFile(message.getAttachment()) directly here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know why yet but if I use this.createTemporaryAttachmentFile(message.getAttachment()), method will throw a RuntimeException instead of MessageCreationException and the unittest will fail

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because the retry throws every Throwable as RuntimeException

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And I would create the temp file outside the retry to avoid new file created if the retry recovery is triggered

null
));
}

/**
* Sends a message to the stream ID passed in parameter.
*
* @param stream the stream to send the message to
* @param message the message to send to the stream
* @return a {@link V4Message} object containing the details of the sent message
* @see <a href="https://developers.symphony.com/restapi/reference#create-message-v4">Create Message v4</a>
*/
public V4Message send(@Nonnull V4Stream stream, @Nonnull Message message) {
return send(stream.getStreamId(), message);
}

/**
* Downloads the attachment body by the stream ID, message ID and attachment ID.
*
Expand Down Expand Up @@ -386,4 +406,22 @@ private static Long getEpochMillis(Instant instant) {
private <T> T executeAndRetry(String name, SupplierWithApiException<T> supplier) {
return RetryWithRecovery.executeAndRetry(retryBuilder, name, supplier);
}

private File createTemporaryAttachmentFile(Attachment attachment) {
if (attachment == null) {
return null;
}
try {
String[] fileNameArrays = attachment.filename().split("\\.");
if (fileNameArrays.length < 2) {
symphony-hong marked this conversation as resolved.
Show resolved Hide resolved
throw new MessageCreationException("Invalid attachment's file name.");
}
File tempFile = File.createTempFile(fileNameArrays[0], "." + fileNameArrays[fileNameArrays.length - 1]);
symphony-hong marked this conversation as resolved.
Show resolved Hide resolved
tempFile.deleteOnExit();
symphony-hong marked this conversation as resolved.
Show resolved Hide resolved
FileUtils.copyInputStreamToFile(attachment.inputStream(), tempFile);
return tempFile;
} catch (IOException e) {
throw new MessageCreationException("Cannot create attachment.", e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.symphony.bdk.core.service.message.exception;

import org.apiguardian.api.API;

/**
* Exception thrown when a {@link com.symphony.bdk.core.service.message.model.Message} is failed to create.
*/
@API(status = API.Status.STABLE)
public class MessageCreationException extends RuntimeException {

public MessageCreationException(String message, Exception e) {
super(message, e);
}

public MessageCreationException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.symphony.bdk.core.service.message.model;

import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import org.apiguardian.api.API;

import java.io.InputStream;

@Getter
symphony-hong marked this conversation as resolved.
Show resolved Hide resolved
@Setter
@Accessors(fluent = true)
@API(status = API.Status.EXPERIMENTAL)
public class Attachment {

private InputStream inputStream;
private String filename;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.symphony.bdk.core.service.message.model;

import com.symphony.bdk.gen.api.model.V4Stream;

import lombok.Getter;
import lombok.Setter;
import org.apiguardian.api.API;

/**
* Message model to be used in {@link com.symphony.bdk.core.service.message.MessageService#send(V4Stream, Message)}
*/
@Getter
@Setter
symphony-hong marked this conversation as resolved.
Show resolved Hide resolved
symphony-hong marked this conversation as resolved.
Show resolved Hide resolved
@API(status = API.Status.EXPERIMENTAL)
public class Message {
symphony-hong marked this conversation as resolved.
Show resolved Hide resolved

// The version of the MessageML format
private String version;
symphony-hong marked this conversation as resolved.
Show resolved Hide resolved
private String content;
private String data;
symphony-hong marked this conversation as resolved.
Show resolved Hide resolved
private Attachment attachment;

protected Message(MessageBuilder builder) {
this.version = builder.version();
this.content = builder.content();
this.data = builder.data();
this.attachment = builder.attachment();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
package com.symphony.bdk.core.service.message.model;

import static java.util.Collections.emptyMap;

import com.symphony.bdk.core.service.message.MessageService;
import com.symphony.bdk.core.service.message.exception.MessageCreationException;
import com.symphony.bdk.gen.api.model.V4Message;
import com.symphony.bdk.gen.api.model.V4Stream;
import com.symphony.bdk.template.api.Template;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.json.JsonMapper;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import org.apiguardian.api.API;

import java.io.InputStream;

/**
* Builder for {@link Message}.
* Allows configuring the field of the messages before constructing them with build()
*/
@Getter
@Setter
@Accessors(fluent = true)
@API(status = API.Status.EXPERIMENTAL)
public class MessageBuilder {

private static final ObjectMapper MAPPER = new JsonMapper();

private final MessageService messageService;
private String version = "2.0";
symphony-hong marked this conversation as resolved.
Show resolved Hide resolved
private String content;
private String data;
private Attachment attachment;

public MessageBuilder(MessageService messageService) {
this.messageService = messageService;
}

/**
* Add messageML content to the message.
*
* @param message messageML.
* @return this builder with the content configured.
*/
public MessageBuilder messageML(String message) {
symphony-hong marked this conversation as resolved.
Show resolved Hide resolved
this.content = message;
return this;
}

/**
* Add content from a template to the message.
*
* @param template a custom or built-in template.
* @param parameters parameters to be used in the template.
* @return this builder with the content configured.
*/
public MessageBuilder template(Template template, Object parameters) {
this.content = template.process(parameters);
return this;
}

/**
* Add content from a static template to the message.
*
* @param template a custom or built-in template.
* @return this builder with the content configured.
*/
public MessageBuilder template(Template template) {
this.content = template.process(emptyMap());
return this;
}

/**
* Add data to the message.
* @param data Serializable data object.
* @return this builder with the data configured.
*/
public MessageBuilder data(Object data) {
symphony-hong marked this conversation as resolved.
Show resolved Hide resolved
try {
this.data = MAPPER.writeValueAsString(data);
return this;
} catch (JsonProcessingException e) {
throw new MessageCreationException("Failed to parse data to Json string", e);
}
}

/**
* Add data to the message.
* @param data Data json node.
* @return this builder with the data configured.
*/
public MessageBuilder data(JsonNode data) {
try {
this.data = MAPPER.writeValueAsString(data);
return this;
} catch (JsonProcessingException e) {
throw new MessageCreationException("Failed to parse data to Json string", e);
}
}

public MessageBuilder attachment(InputStream inputStream, String filename) {
symphony-hong marked this conversation as resolved.
Show resolved Hide resolved
this.attachment = new Attachment().inputStream(inputStream).filename(filename);
return this;
}

/**
* Create a {@link Message} using the configuration within the builder.
* @return constructed {@link Message} using configuration within this builder.
*/
public Message build() {
return new Message(this);
}

/**
* Build and send a {@link Message} using the {@link MessageService}.
*
* @param stream the stream where the message is sent to.
* @return a {@link V4Message} object containing the details of the sent message.
*/
public V4Message send(V4Stream stream) {
return this.messageService.send(stream, this.build());
}

/**
* Build and send a {@link Message} using the {@link MessageService}.
*
* @param streamId the id of the stream where the message is sent to.
* @return a {@link V4Message} object containing the details of the sent message.
*/
public V4Message send(String streamId) {
return this.messageService.send(streamId, this.build());
}

}
Loading