Skip to content

Commit

Permalink
SYMPHONYP-1106 upload attachment feature in famework
Browse files Browse the repository at this point in the history
  • Loading branch information
vaibhav-db committed Apr 26, 2024
1 parent 9571941 commit 15451c9
Show file tree
Hide file tree
Showing 9 changed files with 239 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
import java.io.IOException;
import java.util.List;

import javax.annotation.PostConstruct;

import org.finos.springbot.ChatWorkflowConfig;
import org.finos.springbot.teams.bot.BotController;
import org.finos.springbot.teams.content.TeamsContentConfig;
Expand All @@ -15,12 +13,14 @@
import org.finos.springbot.teams.form.TeamsFormConverter;
import org.finos.springbot.teams.form.TeamsFormDeserializerModule;
import org.finos.springbot.teams.handlers.ActivityHandler;
import org.finos.springbot.teams.handlers.AttachmentHandler;
import org.finos.springbot.teams.handlers.SimpleActivityHandler;
import org.finos.springbot.teams.handlers.SimpleAttachmentHandler;
import org.finos.springbot.teams.handlers.TeamsResponseHandler;
import org.finos.springbot.teams.history.StateStorageBasedTeamsHistory;
import org.finos.springbot.teams.history.StorageIDResponseHandler;
import org.finos.springbot.teams.history.TeamsHistory;
import org.finos.springbot.teams.messages.MessageActivityHandler;
import org.finos.springbot.teams.messages.FileActivityHandler;
import org.finos.springbot.teams.response.templating.EntityMarkupTemplateProvider;
import org.finos.springbot.teams.state.AzureBlobStateStorage;
import org.finos.springbot.teams.state.FileStateStorage;
Expand Down Expand Up @@ -60,6 +60,7 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.microsoft.bot.builder.TurnContext;
import com.microsoft.bot.builder.teams.TeamsActivityHandler;
import com.microsoft.bot.integration.BotFrameworkHttpAdapter;
import com.microsoft.bot.schema.ChannelAccount;

Expand Down Expand Up @@ -122,16 +123,25 @@ public ThymeleafTemplateProvider thymeleafWorkTemplater(
return new ThymeleafTemplateProvider(prefix, suffix, defaultName, resourceLoader, formConverter);
}



@Bean
@ConditionalOnMissingBean
public AttachmentHandler attachmentHandler() {
return new SimpleAttachmentHandler();
}

@Bean
@ConditionalOnMissingBean
public TeamsResponseHandler teamsResponseHandler(
AttachmentHandler attachmentHandler,
EntityMarkupTemplateProvider markupTemplater,
AdaptiveCardTemplateProvider formTemplater,
ThymeleafTemplateProvider displayTemplater,
TeamsStateStorage th,
ActivityHandler ah) {
return new TeamsResponseHandler(
null, // attachment handler
attachmentHandler, // attachment handler
markupTemplater,
formTemplater,
displayTemplater,
Expand Down Expand Up @@ -200,21 +210,33 @@ public TeamsFormConverter teamsFormConverter(AllConversations tc) {
return new TeamsFormConverter(om);
}

// @Bean
// @ConditionalOnMissingBean
// public MessageActivityHandler teamsMessageActivityHandler(
// List<ActionConsumer> messageConsumers,
// TeamsHTMLParser parser,
// FormValidationProcessor fvp,
// TeamsConversations tc,
// TeamsStateStorage teamsStateStorage,
// TeamsFormConverter fc) {
// return new MessageActivityHandler(messageConsumers, tc, teamsStateStorage, parser, fc, fvp);
// }

@Bean
@ConditionalOnMissingBean
public MessageActivityHandler teamsMessageActivityHandler(
public TeamsActivityHandler teamsActivityHandler(
List<ActionConsumer> messageConsumers,
TeamsHTMLParser parser,
FormValidationProcessor fvp,
TeamsConversations tc,
TeamsStateStorage teamsStateStorage,
TeamsFormConverter fc) {
return new MessageActivityHandler(messageConsumers, tc, teamsStateStorage, parser, fc, fvp);
return new FileActivityHandler(messageConsumers, tc, teamsStateStorage, parser, fc, fvp);
}

@Bean
@ConditionalOnMissingBean
public BotController teamsBotController(MessageActivityHandler mah, BotFrameworkHttpAdapter bfa) {
public BotController teamsBotController(TeamsActivityHandler mah, BotFrameworkHttpAdapter bfa) {
return new BotController(bfa, mah);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

import org.finos.springbot.workflow.response.AttachmentResponse;

import com.microsoft.bot.schema.Attachment;

public interface AttachmentHandler {

public Object formatAttachment(AttachmentResponse ar);
public Attachment formatAttachment(AttachmentResponse ar);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package org.finos.springbot.teams.handlers;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.HashMap;
import java.util.Map;

import org.finos.springbot.workflow.response.AttachmentResponse;

import com.microsoft.bot.schema.Attachment;
import com.microsoft.bot.schema.teams.FileConsentCard;

public class SimpleAttachmentHandler implements AttachmentHandler {

public Attachment formatAttachment(AttachmentResponse ar) {

try {
Attachment a = new Attachment();
String fileName = ar.getName().replaceAll("\\s+", "_") + "." + ar.getExtension();
File f = null;
f = File.createTempFile("temp", fileName);
Files.write(f.toPath(), ar.getAttachment());

Map<String, String> consentContext = new HashMap<>();
consentContext.put("filename", fileName);
consentContext.put("filepath", f.getAbsolutePath());

FileConsentCard fileCard = new FileConsentCard();
fileCard.setDescription("This is the file I want to send you");
fileCard.setSizeInBytes(ar.getAttachment().length);
fileCard.setAcceptContext(consentContext);
fileCard.setDeclineContext(consentContext);

a.setContent(fileCard);
a.setName(fileName);
a.setContentType(FileConsentCard.CONTENT_TYPE);
return a;
} catch (IOException e) {
throw new RuntimeException(e);
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.function.BiFunction;
Expand Down Expand Up @@ -93,7 +94,7 @@ public ResourceResponse apply(Response t) {
try {
if (t instanceof MessageResponse) {
MessageResponse mr = (MessageResponse)t;
Object attachment = null;
Attachment attachment = null;
MarkupAndEntities mae = messageTemplater.template(mr);
String content = mae.getContents();
List<Entity> entities = mae.getEntities();
Expand Down Expand Up @@ -149,10 +150,14 @@ protected TemplateType getTemplateType(WorkResponse wr) {
return tt;
}

protected CompletableFuture<ResourceResponse> sendXMLResponse(String xml, Object attachment, TeamsAddressable address, List<Entity> entities, Map<String, Object> data) throws Exception {
protected CompletableFuture<ResourceResponse> sendXMLResponse(String xml, Attachment attachment, TeamsAddressable address, List<Entity> entities, Map<String, Object> data) throws Exception {
Activity out = Activity.createMessageActivity();
out.setEntities(entities);
out.setTextFormat(TextFormatTypes.XML);
if(Objects.nonNull(attachment)) {
out.getAttachments().add(attachment);
}else {
out.setEntities(entities);
out.setTextFormat(TextFormatTypes.XML);
}
out.setText(xml);
return ah.handleActivity(out, address);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
*/
public class StateStorageBasedTeamsHistory implements TeamsHistory {

private static final Logger LOG = LoggerFactory.getLogger(MessageActivityHandler.class);
private static final Logger LOG = LoggerFactory.getLogger(StateStorageBasedTeamsHistory.class);

public final TeamsStateStorage tss;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
package org.finos.springbot.teams.messages;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicReference;

import org.finos.springbot.teams.content.serialization.TeamsHTMLParser;
import org.finos.springbot.teams.conversations.TeamsConversations;
import org.finos.springbot.teams.state.TeamsStateStorage;
import org.finos.springbot.workflow.actions.consumers.ActionConsumer;
import org.finos.springbot.workflow.form.FormConverter;
import org.finos.springbot.workflow.form.FormValidationProcessor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.microsoft.bot.builder.MessageFactory;
import com.microsoft.bot.builder.TurnContext;
import com.microsoft.bot.schema.Activity;
import com.microsoft.bot.schema.Attachment;
import com.microsoft.bot.schema.ResultPair;
import com.microsoft.bot.schema.TextFormatTypes;
import com.microsoft.bot.schema.teams.FileConsentCardResponse;
import com.microsoft.bot.schema.teams.FileInfoCard;

public class FileActivityHandler extends MessageActivityHandler {

private static final Logger LOG = LoggerFactory.getLogger(FileActivityHandler.class);


public FileActivityHandler(List<ActionConsumer> messageConsumers, TeamsConversations teamsConversations,
TeamsStateStorage teamsStateStorage, TeamsHTMLParser parser, FormConverter formConverter,
FormValidationProcessor validationProcessor) {
super(messageConsumers, teamsConversations, teamsStateStorage, parser, formConverter, validationProcessor);
}

@Override
protected CompletableFuture<Void> onTeamsFileConsentAccept(TurnContext turnContext,
FileConsentCardResponse fileConsentCardResponse) {

return upload(fileConsentCardResponse)
.thenCompose(result -> !result.result() ? fileUploadFailed(turnContext, result.value())
: fileUploadCompleted(turnContext, fileConsentCardResponse));
}

@Override
protected CompletableFuture<Void> onTeamsFileConsentDecline(TurnContext turnContext,
FileConsentCardResponse fileConsentCardResponse) {
Map<String, String> context = (Map<String, String>) fileConsentCardResponse.getContext();

Activity reply = MessageFactory
.text(String.format("Declined. We won't upload file <b>%s</b>.", context.get("filename")));
reply.setTextFormat(TextFormatTypes.XML);

return turnContext.sendActivityBlind(reply);
}

private CompletableFuture<ResultPair<String>> upload(FileConsentCardResponse fileConsentCardResponse) {
AtomicReference<ResultPair<String>> result = new AtomicReference<>();

return CompletableFuture.runAsync(() -> {
Map<String, String> context = (Map<String, String>) fileConsentCardResponse.getContext();
LOG.info("filepath - {}", context.get("filepath"));
LOG.info("File upload endpoint : {}", fileConsentCardResponse.getUploadInfo().getUploadUrl());
File filePath = new File(context.get("filepath"));

HttpURLConnection connection = null;
try {
URL url = new URL(fileConsentCardResponse.getUploadInfo().getUploadUrl());
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("PUT");
connection.setDoOutput(true);
connection.setRequestProperty("Content-Length", Long.toString(filePath.length()));
connection.setRequestProperty("Content-Range",
String.format("bytes 0-%d/%d", filePath.length() - 1, filePath.length()));

try (FileInputStream fileStream = new FileInputStream(filePath);
OutputStream uploadStream = connection.getOutputStream()) {
byte[] buffer = new byte[4096];
int bytes_read;
while ((bytes_read = fileStream.read(buffer)) != -1) {
uploadStream.write(buffer, 0, bytes_read);
}

uploadStream.flush();
}

try {
BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String inputLine;
while ((inputLine = in.readLine()) != null)
LOG.info(inputLine);
in.close();
} catch (Exception e) {
LOG.error("Exception occured while reading steam.. ignore this error " + e);
}
result.set(new ResultPair<String>(true, null));
} catch (Throwable t) {
result.set(new ResultPair<String>(false, t.getLocalizedMessage()));
} finally {
if (connection != null) {
connection.disconnect();
}
}
}).thenApply(aVoid -> result.get());
}

private CompletableFuture<Void> fileUploadFailed(TurnContext turnContext, String error) {
Activity reply = MessageFactory.text("<b>File upload failed.</b> Error: <pre>" + error + "</pre>");
reply.setTextFormat(TextFormatTypes.XML);
return turnContext.sendActivityBlind(reply);
}

private CompletableFuture<Void> fileDownloadCompleted(TurnContext turnContext, Attachment attachment) {
Activity reply = MessageFactory.text(String.format("<b>%s</b> received and saved.", attachment.getName()));
reply.setTextFormat(TextFormatTypes.XML);

return turnContext.sendActivityBlind(reply);
}

private CompletableFuture<Void> fileUploadCompleted(TurnContext turnContext,
FileConsentCardResponse fileConsentCardResponse) {
FileInfoCard downloadCard = new FileInfoCard();
downloadCard.setUniqueId(fileConsentCardResponse.getUploadInfo().getUniqueId());
downloadCard.setFileType(fileConsentCardResponse.getUploadInfo().getFileType());

Attachment asAttachment = new Attachment();
asAttachment.setContent(downloadCard);
asAttachment.setContentType(FileInfoCard.CONTENT_TYPE);
asAttachment.setName(fileConsentCardResponse.getUploadInfo().getName());
asAttachment.setContentUrl(fileConsentCardResponse.getUploadInfo().getContentUrl());

Activity reply = MessageFactory
.text(String.format("<b>File uploaded.</b> Your file <b>%s</b> is ready to download",
fileConsentCardResponse.getUploadInfo().getName()));
reply.setTextFormat(TextFormatTypes.XML);
reply.setAttachment(asAttachment);

return turnContext.sendActivityBlind(reply);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;

import com.microsoft.bot.builder.ActivityHandler;
import com.microsoft.bot.builder.TurnContext;
import com.microsoft.bot.builder.teams.TeamsActivityHandler;
import com.microsoft.bot.schema.Activity;
import com.microsoft.bot.schema.Attachment;

public class MessageActivityHandler extends ActivityHandler {
public class MessageActivityHandler extends TeamsActivityHandler {

private static final Logger LOG = LoggerFactory.getLogger(MessageActivityHandler.class);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
*/
public class AzureBlobStateStorage extends AbstractStateStorage {

private static final Logger LOG = LoggerFactory.getLogger(MessageActivityHandler.class);
private static final Logger LOG = LoggerFactory.getLogger(AzureBlobStateStorage.class);

private final BlobContainerClient bcc;
private final BlobServiceClient bsc;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
import org.finos.springbot.teams.content.TeamsMultiwayChat;
import org.finos.springbot.teams.content.TeamsUser;
import org.finos.springbot.teams.conversations.TeamsConversations;
import org.finos.springbot.teams.messages.MessageActivityHandler;
import org.finos.springbot.teams.messages.FileActivityHandler;
import org.finos.springbot.teams.state.TeamsStateStorage;
import org.finos.springbot.teams.turns.CurrentTurnContext;
import org.finos.springbot.tests.controller.AbstractHandlerMappingTest;
Expand Down Expand Up @@ -72,7 +72,7 @@ public class TeamsHandlerMappingTest extends AbstractHandlerMappingTest {
TurnContext tc;

@Autowired
MessageActivityHandler mah;
FileActivityHandler mah;

@Autowired
ChatRequestChatHandlerMapping hm;
Expand Down

0 comments on commit 15451c9

Please sign in to comment.