Skip to content

Commit

Permalink
Merge pull request #366 from finos/poll-bot-teams
Browse files Browse the repository at this point in the history
Poll bot teams
  • Loading branch information
pankaj-a-khandelwal-db authored Sep 4, 2022
2 parents 6b3358d + 11c3026 commit f75185c
Show file tree
Hide file tree
Showing 47 changed files with 573 additions and 380 deletions.
8 changes: 3 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,15 +78,13 @@ This project contains various example bots that were (originally) written at Deu

### 🤖 Poll Bot

A bot for running polls in a Symphony chat room. 👍 _Production Ready_
A bot for running polls in a Symphony/Teams chat room. 👍 _Production Ready_


- [View the README](tools/poll-bot/README.md)

- [View the README](tools/poll-bot/README.md)

### 🤖 News (RSS) Bot

A bot for feeding news into a Symphony chat room. 👍 _Production Ready_
A bot for feeding news into a Symphony/Teams chat room. 👍 _Production Ready_

- [View the README](tools/rss-bot/README.md)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;

public class DataHandlerConfig {
Expand All @@ -43,6 +44,9 @@ public EntityJsonConverter entityJsonConverter() {
List<VersionSpace> workAnnotatedversionSpaces = scanForWorkClasses();

ObjectMapper om = new ObjectMapper();
TypeFactory tf = TypeFactory.defaultInstance()
.withClassLoader(ac.getClassLoader());
om.setTypeFactory(tf);
om.enable(SerializationFeature.INDENT_OUTPUT);
om.enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS);
om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import java.util.stream.Collectors;

import org.finos.springbot.workflow.content.Addressable;
import org.finos.springbot.workflow.content.Tag;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
Expand Down Expand Up @@ -40,7 +39,7 @@ public <X> Optional<X> getLastFromHistory(Class<X> type, Addressable address) {


@Override
public <X> Optional<X> getLastFromHistory(Class<X> type, Tag t, Addressable address) {
public <X> Optional<X> getLastFromHistory(Class<X> type, String t, Addressable address) {
return getDelegates().stream()
.filter(p -> p.isSupported(address))
.map(p -> p.getLastFromHistory(type, t, address))
Expand All @@ -58,7 +57,7 @@ public <X> List<X> getFromHistory(Class<X> type, Addressable address, Instant si


@Override
public <X> List<X> getFromHistory(Class<X> type, Tag t, Addressable address, Instant since) {
public <X> List<X> getFromHistory(Class<X> type, String t, Addressable address, Instant since) {
return getDelegates().stream()
.filter(p -> p.isSupported(address))
.flatMap(p -> p.getFromHistory(type, t, address, since).stream())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,18 @@ public interface History<A extends Addressable> {

public <X> Optional<X> getLastFromHistory(Class<X> type, A address);

public <X> Optional<X> getLastFromHistory(Class<X> type, Tag t, A address);
public <X> Optional<X> getLastFromHistory(Class<X> type, String tag, A address);

public <X> List<X> getFromHistory(Class<X> type, A address, Instant since);

public <X> List<X> getFromHistory(Class<X> type, Tag t, A address, Instant since);
public <X> List<X> getFromHistory(Class<X> type, String tag, A address, Instant since);

public default <X> Optional<X> getLastFromHistory(Class<X> type, Tag t, A address) {
return getLastFromHistory(type, t.getName(), address);
}

public default <X> List<X> getFromHistory(Class<X> type, Tag t, A address, Instant since) {
return getFromHistory(type, t.getName(), address, since);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ public HeaderDetails() {
super();
}

public HeaderDetails(List<String> tags) {
super();
this.tags = tags;
}

public HeaderDetails(String name, String description, List<String> tags) {
super();
this.name = name;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public X apply(Field ctx, Type t, boolean editMode, Variable variable) {

protected Map<String, String> generateEnumOptions(Class<Enum<?>> t) {
return Arrays.stream(t.getEnumConstants())
.collect(Collectors.toMap(e -> e.toString(), e -> e.toString()));
.collect(Collectors.toMap(e -> e.name(), e -> e.toString()));

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@
<version>9.0.1-SNAPSHOT</version>
<scope>test</scope>
</dependency>

</dependencies>

</project>
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,19 @@ public interface SymphonyHistory extends PlatformHistory<SymphonyAddressable> {

public <X> Optional<EntityJson> getLastEntityJsonFromHistory(Class<X> type, SymphonyAddressable address);

public <X> Optional<EntityJson> getLastEntityJsonFromHistory(Class<X> type, Tag t, SymphonyAddressable address);
public default <X> Optional<EntityJson> getLastEntityJsonFromHistory(Class<X> type, Tag t, SymphonyAddressable address) {
return getLastEntityJsonFromHistory(type, t.getName(), address);
}

public <X> Optional<EntityJson> getLastEntityJsonFromHistory(Class<X> type, String t, SymphonyAddressable address);

public <X> List<EntityJson> getEntityJsonFromHistory(Class<X> type, SymphonyAddressable address, Instant since);

public List<EntityJson> getEntityJsonFromHistory(Tag t, SymphonyAddressable address, Instant since);
public default List<EntityJson> getEntityJsonFromHistory(Tag t, SymphonyAddressable address, Instant since) {
return getEntityJsonFromHistory(t.getName(), address, since);
}

public List<EntityJson> getEntityJsonFromHistory(String t, SymphonyAddressable address, Instant since);

public <X> Optional<X> getFromEntityJson(EntityJson ej, Class<X> c);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,10 @@
import java.util.stream.Collectors;

import org.finos.springbot.entityjson.EntityJson;
import org.finos.springbot.symphony.content.CashTag;
import org.finos.springbot.symphony.content.HashTag;
import org.finos.springbot.symphony.content.SymphonyAddressable;
import org.finos.springbot.symphony.content.SymphonyUser;
import org.finos.springbot.symphony.conversations.StreamResolver;
import org.finos.springbot.symphony.tags.SymphonyTagSupport;
import org.finos.springbot.workflow.content.Addressable;
import org.finos.springbot.workflow.content.Tag;
import org.finos.springbot.workflow.data.EntityJsonConverter;

import com.symphony.bdk.core.service.message.MessageService;
Expand Down Expand Up @@ -70,20 +66,20 @@ protected <X> Optional<X> convertToOptionalInstance(Class<X> type, List<V4Messag
}

@Override
public <X> Optional<X> getLastFromHistory(Class<X> type, Tag t, SymphonyAddressable address) {
public <X> Optional<X> getLastFromHistory(Class<X> type, String t, SymphonyAddressable address) {
return getRelevantObject(getLastEntityJsonFromHistory(type, t, address), type);
}

@Override
public <X> Optional<EntityJson> getLastEntityJsonFromHistory(Class<X> type, Tag t, SymphonyAddressable address) {
public <X> Optional<EntityJson> getLastEntityJsonFromHistory(Class<X> type, String t, SymphonyAddressable address) {
MessageSearchQuery msq = createMessageSearchQuery(null, address, null, t);
PaginationAttribute pa = new PaginationAttribute(0, 1);
List<V4Message> out = messageApi.searchMessages(msq, pa);
return convertToOptionalEntityJson(out);
}

@Override
public <X> List<X> getFromHistory(Class<X> type, Tag t, SymphonyAddressable address, Instant since) {
public <X> List<X> getFromHistory(Class<X> type, String t, SymphonyAddressable address, Instant since) {
return getFromEntityJson(getEntityJsonFromHistory(t, address, since), type);
}

Expand All @@ -94,7 +90,7 @@ public <X> List<X> getFromEntityJson(List<EntityJson> ej, Class<X> type) {
}

@Override
public List<EntityJson> getEntityJsonFromHistory(Tag t, SymphonyAddressable address, Instant since) {
public List<EntityJson> getEntityJsonFromHistory(String t, SymphonyAddressable address, Instant since) {
MessageSearchQuery msq = createMessageSearchQuery(null, address, since, t);
PaginationAttribute pa = new PaginationAttribute(0, 50);
List<V4Message> out = messageApi.searchMessages(msq, pa);
Expand Down Expand Up @@ -150,7 +146,7 @@ public <X> List<EntityJson> getEntityJsonFromHistory(Class<X> type, SymphonyAddr
return out.stream().map(msg -> getEntityJson(msg)).filter(e -> e != null).collect(Collectors.toList());
}

private <X> MessageSearchQuery createMessageSearchQuery(Class<X> type, Addressable address, Instant since, Tag t) {
private <X> MessageSearchQuery createMessageSearchQuery(Class<X> type, Addressable address, Instant since, String t) {
MessageSearchQuery msq = new MessageSearchQuery();
if (address instanceof SymphonyAddressable) {
msq.setStreamId(sr.getStreamFor((SymphonyAddressable) address));
Expand All @@ -163,13 +159,7 @@ private <X> MessageSearchQuery createMessageSearchQuery(Class<X> type, Addressab
if (type != null) {
msq.setHashtag(SymphonyTagSupport.formatTag(type));
} else if (t != null) {
if (t instanceof CashTag) {
msq.setCashtag(t.getName());
} else if (t instanceof HashTag) {
msq.setHashtag(t.getName());
} else if (t instanceof SymphonyUser) {
msq.setMention(Long.parseLong(((SymphonyUser) t).getUserId()));
}
msq.setHashtag(t);
}

return msq;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,18 @@ public class SymphonyFormConverterTest extends AbstractFormConverterTest {

SymphonyConversations sc;

AllConversations ac = new AllConversations() {

@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
protected List<PlatformConversations<Chat, User>> getDelegates() {
return Collections.singletonList((PlatformConversations) sc);
}

};

@Override
protected void before() {
sc = Mockito.mock(SymphonyConversations.class);
AllConversations ac = new AllConversations() {

@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
protected List<PlatformConversations<Chat, User>> getDelegates() {
return Collections.singletonList((PlatformConversations) sc);
}

};
ObjectMapper om = new ObjectMapper();
om.registerModule(new SymphonyFormDeserializerModule(ac));
om.registerModule(new JavaTimeModule());
Expand Down
1 change: 0 additions & 1 deletion libs/teams/teams-chat-workflow-spring-boot-starter/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.finos.springbot</groupId>
<artifactId>chat-workflow-testing</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import java.io.IOException;
import java.util.List;

import javax.annotation.PostConstruct;

import org.finos.springbot.ChatWorkflowConfig;
import org.finos.springbot.teams.content.TeamsContentConfig;
import org.finos.springbot.teams.content.serialization.TeamsHTMLParser;
Expand All @@ -24,9 +26,9 @@
import org.finos.springbot.teams.templating.adaptivecard.AdaptiveCardTemplateProvider;
import org.finos.springbot.teams.templating.adaptivecard.AdaptiveCardTemplater;
import org.finos.springbot.teams.templating.thymeleaf.ThymeleafConverterConfig;
import org.finos.springbot.teams.templating.thymeleaf.ThymeleafEngineConfig;
import org.finos.springbot.teams.templating.thymeleaf.ThymeleafTemplateProvider;
import org.finos.springbot.teams.templating.thymeleaf.ThymeleafTemplater;
import org.finos.springbot.teams.templating.thymeleaf.ThymeleafEngineConfig;
import org.finos.springbot.teams.turns.CurrentTurnContext;
import org.finos.springbot.workflow.actions.consumers.ActionConsumer;
import org.finos.springbot.workflow.actions.consumers.AddressingChecker;
Expand All @@ -45,7 +47,7 @@
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Profile;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.validation.Validator;

import com.azure.storage.blob.BlobServiceClient;
Expand All @@ -70,10 +72,7 @@
ThymeleafEngineConfig.class,
AdaptiveCardConverterConfig.class,
ThymeleafConverterConfig.class,
TeamsConversationsConfig.class,
})


TeamsConversationsConfig.class})
@Profile("teams")
public class TeamsWorkflowConfig {

Expand All @@ -83,7 +82,7 @@ public class TeamsWorkflowConfig {
Validator validator;

@Autowired
ResourceLoader resourceLoader;
DefaultResourceLoader resourceLoader;

@Autowired
EntityJsonConverter ejc;
Expand Down Expand Up @@ -160,7 +159,7 @@ public TeamsStateStorage teamsAzureBlobStateStorage(
@ConditionalOnMissingBean
public TeamsStateStorage teamsInMemoryStateStorage() {
LOG.warn("Using Memory storage for Azure data - NOT FOR PRODUCTION");
return new MemoryStateStorage();
return new MemoryStateStorage(ejc);
}

@Bean
Expand Down Expand Up @@ -206,5 +205,15 @@ public AddressingChecker teamsAddressingChecker(TeamsConversations conv) {
return u;
}, true);
}

/**
* Templates don't load properly with a fat jar.
* @see https://github.com/finos/spring-bot/issues/340
*/
@PostConstruct
public void setResourceLoaderClassLoader() {
resourceLoader.setClassLoader(this.getClass().getClassLoader());
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import org.finos.springbot.teams.content.TeamsAddressable;
import org.finos.springbot.teams.conversations.TeamsConversations;
import org.finos.springbot.teams.history.StorageIDResponseHandler;
import org.finos.springbot.teams.history.TeamsHistory;
import org.finos.springbot.teams.response.templating.EntityMarkupTemplateProvider;
import org.finos.springbot.teams.response.templating.MarkupAndEntities;
import org.finos.springbot.teams.state.TeamsStateStorage;
Expand Down Expand Up @@ -153,6 +154,7 @@ private BiFunction<? super ResourceResponse, Throwable, ResourceResponse> handle
if (tt == TemplateType.BOTH) {
// we also need to send the buttons.
JsonNode buttonsJson = workTemplater.template(null);
wr.getData().put(AdaptiveCardTemplateProvider.FORMID_KEY, "just-buttons");
JsonNode expandedJson = workTemplater.applyTemplate(buttonsJson, wr);
return sendCardResponse(expandedJson, (TeamsAddressable) wr.getAddress(), wr.getData()).get();
} else {
Expand Down Expand Up @@ -184,7 +186,7 @@ private BiFunction<? super ResourceResponse, Throwable, ResourceResponse> handle
initErrorHandler();
eh.handleError(e);
} else {
performStorage(address, data);
performStorage(address, data, teamsState);
}

return null;
Expand All @@ -200,7 +202,7 @@ protected CompletableFuture<ResourceResponse> sendCardResponse(JsonNode json, Te
return teamsConversations.handleActivity(out, address);
}

protected void performStorage(TeamsAddressable address, Map<String, Object> data) {
public static void performStorage(TeamsAddressable address, Map<String, Object> data, TeamsStateStorage teamsState) {
String dataKey = (String) data.get(StorageIDResponseHandler.STORAGE_ID_KEY);
if (dataKey != null) {
// first, store data for message
Expand All @@ -210,14 +212,15 @@ protected void performStorage(TeamsAddressable address, Map<String, Object> data
}
}

protected Map<String, String> createStorageTags(Map<String, Object> data, TeamsAddressable address) {
public static Map<String, String> createStorageTags(Map<String, Object> data, TeamsAddressable address) {
Map<String, String> out = new HashMap<String, String>();
HeaderDetails h = (HeaderDetails) data.get(HeaderDetails.KEY);
if (h != null) {
h.getTags().forEach(t -> out.put(t, TeamsStateStorage.PRESENT));
}

out.put(TeamsStateStorage.ADDRESSABLE_KEY, address.getKey());
out.put(TeamsHistory.TIMESTAMP_KEY, ""+System.currentTimeMillis());
return out;
}

Expand Down
Loading

0 comments on commit f75185c

Please sign in to comment.