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

Release/0.26.0 #2153

Merged
merged 51 commits into from
Jul 13, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
fa98427
Merge branch 'release/0.25.0' into develop
Jun 29, 2021
975c765
Bump version to 0.26.0-alpha
Jun 29, 2021
bc6bdcb
Bump copy-webpack-plugin from 9.0.0 to 9.0.1 (#2059)
dependabot[bot] Jun 29, 2021
ea51661
[#1819] Add Airy favicon (#2067)
chrismatix Jun 29, 2021
671296f
[#2060] fixed edit of text templates (#2068)
AudreyKj Jun 30, 2021
f9cc6db
[#2056] Added first transition and animations (#2066)
Jun 30, 2021
4a87750
[#1391] Cypress test for suggested replies (#2057)
AudreyKj Jun 30, 2021
e1f0f35
[#1914] Cypress test for conversation state (#2061)
AudreyKj Jun 30, 2021
eda9c63
Bump terser-webpack-plugin from 5.1.3 to 5.1.4 (#2065)
dependabot[bot] Jun 30, 2021
489c78a
Bump prettier from 2.3.1 to 2.3.2 (#2053)
dependabot[bot] Jun 30, 2021
62bb04b
Bump core-js from 3.15.0 to 3.15.2 (#2064)
dependabot[bot] Jun 30, 2021
ba4a7e1
Bump @types/node from 15.12.2 to 15.12.5 (#2052)
dependabot[bot] Jul 1, 2021
df65861
Bump style-loader from 2.0.0 to 3.0.0 (#2040)
dependabot[bot] Jul 1, 2021
dc40d83
Bump @typescript-eslint/parser from 4.28.0 to 4.28.1 (#2081)
dependabot[bot] Jul 1, 2021
8641844
[#1916] Cypress test for display name edit (#2088)
AudreyKj Jul 1, 2021
708c8c4
[#2074] Added subtitle to Chatplugin (#2087)
Jul 1, 2021
1b1441f
[#2069] fix http client and chat plugin packages (#2070)
AudreyKj Jul 1, 2021
426f163
update conversations counter in real time (#2091)
AitorAlgorta Jul 1, 2021
9ae3109
[#2073] Added powered by airy to Chatplugin (#2089)
Jul 1, 2021
f93c6bd
[#661] Added possibility to connect Google via UI (#2092)
Jul 2, 2021
17fb6e6
Bump webpack from 5.40.0 to 5.41.1 (#2085)
dependabot[bot] Jul 2, 2021
2a20f48
Bump @typescript-eslint/eslint-plugin from 4.28.0 to 4.28.1 (#2084)
dependabot[bot] Jul 2, 2021
33f0fa3
[#2093] Fixed moving avatar (#2094)
Jul 2, 2021
7d3ef36
Bump webpack from 5.41.1 to 5.42.0 (#2098)
dependabot[bot] Jul 2, 2021
c89d330
Bump preact from 10.5.13 to 10.5.14 (#2099)
dependabot[bot] Jul 2, 2021
cc095a4
Bump camelcase-keys from 6.2.2 to 7.0.0 (#2083)
dependabot[bot] Jul 2, 2021
d8ed7c8
[#2071] Redesigned inputbar Chatplugin (#2095)
Jul 2, 2021
762ce08
Fixes #2100 - link to up-to-date Rasa connector (#2101)
armanjindal Jul 2, 2021
4ba969f
Bump @types/react from 17.0.11 to 17.0.13 (#2097)
dependabot[bot] Jul 2, 2021
4383e86
Bump @types/node from 15.12.5 to 15.14.0 (#2096)
dependabot[bot] Jul 5, 2021
b892861
Bump @types/node from 15.14.0 to 16.0.0 (#2103)
dependabot[bot] Jul 5, 2021
687e81e
Bump eslint from 7.29.0 to 7.30.0 (#2102)
dependabot[bot] Jul 5, 2021
5bb909a
[#1970] Add issue templates (#2106)
chrismatix Jul 5, 2021
dca9a94
[#2051] Add instagram source (#2082)
chrismatix Jul 5, 2021
5daf6ff
[#2014] Fix stable version file (#2107)
ljupcovangelski Jul 6, 2021
524fb60
[#2072] Chat Plugin: Emoji Selector (#2121)
AudreyKj Jul 7, 2021
3f5a69c
[#1590] Refactor: Split chatplugin into library and app targets (#2120)
chrismatix Jul 8, 2021
289df40
Bump @types/react-window-infinite-loader from 1.0.3 to 1.0.4 (#2130)
dependabot[bot] Jul 9, 2021
1392727
Bump @typescript-eslint/eslint-plugin from 4.28.1 to 4.28.2 (#2112)
dependabot[bot] Jul 9, 2021
7fa8987
Bump @types/resize-observer-browser from 0.1.5 to 0.1.6 (#2135)
dependabot[bot] Jul 9, 2021
53347d6
Change endpoint for webhook to /twilio (#2123)
armanjindal Jul 12, 2021
58dd61c
[#2132] Allow deleting messages (#2136)
chrismatix Jul 12, 2021
4ab1dbe
Bump @types/react-router-dom from 5.1.7 to 5.1.8 (#2137)
dependabot[bot] Jul 12, 2021
a3bdb6f
Bump @typescript-eslint/parser from 4.28.1 to 4.28.2 (#2113)
dependabot[bot] Jul 12, 2021
1ad290b
Bump sass from 1.35.1 to 1.35.2 (#2141)
dependabot[bot] Jul 12, 2021
8992af8
Bump @types/react-dom from 17.0.8 to 17.0.9 (#2146)
dependabot[bot] Jul 12, 2021
8e0fe26
Bump cypress from 7.6.0 to 7.7.0 (#2145)
dependabot[bot] Jul 13, 2021
56e7482
Bump @types/react-redux from 7.1.16 to 7.1.18 (#2138)
dependabot[bot] Jul 13, 2021
a88c16f
[#2078] Add endpoint for uploading media (#2147)
chrismatix Jul 13, 2021
eb717c8
[#2149] UI crashes when filtering with unknown source involved (#2152)
AudreyKj Jul 13, 2021
f431c5e
release 0.26.0
AudreyKj Jul 13, 2021
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
32 changes: 32 additions & 0 deletions .github/ISSUE_TEMPLATE/---bug-report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
name: "\U0001F41E Bug report"
about: Report a bug in Airy. If this doesn’t look right, [choose a different type](https://github.com/airyhq/airy/issues/new/choose).
title: ""
labels: bug
assignees: ""
---

**Describe the bug**
A clear and concise description of what the bug is.

**To Reproduce**
Steps to reproduce the behavior:

1. I installed Airy using '...'
2. When I ran command / did this in the UI
3. See error

**Expected behavior**
A clear and concise description of what you expected to happen.

**Screenshots**
If applicable, add screenshots to help explain your problem.

**Desktop (please complete the following information):**

- OS: [e.g. iOS]
- Browser [e.g. Chrome, Safari]
- Version [e.g. 22]

**Additional context**
Add any other context about the problem here.
19 changes: 19 additions & 0 deletions .github/ISSUE_TEMPLATE/---feature-request.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
name: "\U0001F680 Feature request"
about: Suggest an idea for Airy. If this doesn’t look right, [choose a different type](https://github.com/airyhq/airy/issues/new/choose).
title: ""
labels: feature, needs discussion
assignees: ""
---

**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

**Describe the solution you'd like**
A clear and concise description of what you want to happen.

**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.

**Additional context**
Add any other context or screenshots about the feature request here.
2 changes: 1 addition & 1 deletion .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "daily"
interval: "weekly"
5 changes: 4 additions & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,12 @@ jobs:
run: |
echo ${{ secrets.PAT }} | docker login ghcr.io -u airydevci --password-stdin
./scripts/push-images.sh

- name: Install aws cli
uses: chrislennon/action-aws-cli@v1.1
env:
ACTIONS_ALLOW_UNSECURE_COMMANDS: 'true'

- name: Upload airy binary to S3
if: ${{ github.ref == 'refs/heads/develop' || startsWith(github.ref, 'refs/heads/release') || github.ref == 'refs/heads/main' }}
run: |
Expand All @@ -69,6 +71,7 @@ jobs:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
GITHUB_BRANCH: ${{ github.ref }}

- name: Publish http-client library to npm
if: ${{ startsWith(github.ref, 'refs/heads/main') }}
run: |
Expand All @@ -78,6 +81,7 @@ jobs:
DEPLOY_NPM_USERNAME: ${{ secrets.DEPLOY_NPM_USERNAME }}
DEPLOY_NPM_PASSWORD: ${{ secrets.DEPLOY_NPM_PASSWORD }}
DEPLOY_NPM_EMAIL: ${{ secrets.DEPLOY_NPM_EMAIL }}

- name: Publish chat-plugin library to npm
if: ${{ startsWith(github.ref, 'refs/heads/main') }}
run: |
Expand All @@ -87,4 +91,3 @@ jobs:
DEPLOY_NPM_USERNAME: ${{ secrets.DEPLOY_NPM_USERNAME }}
DEPLOY_NPM_PASSWORD: ${{ secrets.DEPLOY_NPM_PASSWORD }}
DEPLOY_NPM_EMAIL: ${{ secrets.DEPLOY_NPM_EMAIL }}
GITHUB_BRANCH: ${{ github.ref }}
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.25.0
0.26.0
4 changes: 2 additions & 2 deletions WORKSPACE
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
# Airy Bazel tools
git_repository(
name = "com_github_airyhq_bazel_tools",
commit = "b4f52101d80c342ff5e4734fb4c4385af921ffec",
commit = "2b34bc2b2c6fed64e4bb223d4624e06eda390e4d",
remote = "https://github.com/airyhq/bazel-tools.git",
shallow_since = "1624959927 +0200",
shallow_since = "1625065439 +0200",
)

load("@com_github_airyhq_bazel_tools//:repositories.bzl", "airy_bazel_tools_dependencies", "airy_jvm_deps")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.apache.kafka.streams.KeyValue;
import org.apache.kafka.streams.StreamsBuilder;
import org.apache.kafka.streams.kstream.KGroupedStream;
import org.apache.kafka.streams.kstream.KGroupedTable;
import org.apache.kafka.streams.kstream.KStream;
import org.apache.kafka.streams.kstream.KTable;
import org.apache.kafka.streams.kstream.Materialized;
Expand Down Expand Up @@ -103,8 +104,8 @@ private void startStream() {
.mapValues(readReceipt -> CountAction.reset(readReceipt.getReadDate()));

// produce unread count metadata
messageStream.selectKey((messageId, message) -> message.getConversationId())
.filter((conversationId, message) -> message.getIsFromContact())
messageStream.filter((conversationId, message) -> message != null && message.getIsFromContact())
.selectKey((messageId, message) -> message.getConversationId())
.mapValues(message -> CountAction.increment(message.getSentAt()))
.merge(resetStream)
.groupByKey()
Expand All @@ -127,25 +128,26 @@ private void startStream() {
})
.to(applicationCommunicationMetadata);

final KGroupedStream<String, MessageContainer> messageGroupedStream = messageStream.toTable()
final KGroupedTable<String, MessageContainer> messageGroupedTable = messageStream.toTable()
.leftJoin(metadataTable, (message, metadataMap) -> MessageContainer.builder()
.message(message)
.metadataMap(Optional.ofNullable(metadataMap).orElse(new MetadataMap()))
.build(), Materialized.as(messagesByIdStore))
.toStream()
.filter((messageId, messageContainer) -> messageContainer != null)
.groupBy((messageId, messageContainer) -> messageContainer.getMessage().getConversationId());
.groupBy((messageId, messageContainer) -> KeyValue.pair(messageContainer.getMessage().getConversationId(), messageContainer));


// messages store
messageGroupedStream.aggregate(MessagesTreeSet::new,
((key, value, aggregate) -> {
messageGroupedTable.aggregate(MessagesTreeSet::new,
(key, value, aggregate) -> {
aggregate.update(value);
return aggregate;
}), Materialized.as(messagesStore));
}, (key, value, aggregate) -> {
aggregate.remove(value);
return aggregate;
}, Materialized.as(messagesStore));

// Conversation stores
messageGroupedStream
messageGroupedTable
.aggregate(Conversation::new,
(conversationId, container, aggregate) -> {
if (aggregate.getLastMessageContainer() == null) {
Expand All @@ -164,6 +166,10 @@ private void startStream() {
aggregate.setSourceConversationId(container.getMessage().getSenderId());
}

return aggregate;
}, (conversationId, container, aggregate) -> {
// If the deleted message was the last message we have no way of replacing it
// so we have no choice but to keep it
return aggregate;
})
.join(channelTable, Conversation::getChannelId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
import co.airy.kafka.test.junit.SharedKafkaTestResource;
import co.airy.spring.core.AirySpringBootApplication;
import co.airy.spring.test.WebTestHelper;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.avro.specific.SpecificRecordBase;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.junit.jupiter.api.AfterAll;
Expand Down Expand Up @@ -102,6 +104,33 @@ void canFetchMessages() throws Exception {
"/messages.list endpoint error");
}

@Test
void canDeleteMessages() throws Exception {
final String conversationId = UUID.randomUUID().toString();

int messageCount = 2;
final List<ProducerRecord<String, SpecificRecordBase>> records = TestConversation.generateRecords(conversationId, channel, 2);
kafkaTestHelper.produceRecords(records);

final String payload = "{\"conversation_id\":\"" + conversationId + "\"}";
retryOnException(
() -> {
final String content = webTestHelper.post("/messages.list", payload)
.andExpect(status().isOk())
.andExpect(jsonPath("$.data", hasSize(messageCount)))
.andReturn().getResponse().getContentAsString();

final JsonNode jsonNode = new ObjectMapper().readTree(content);
final String messageId = jsonNode.get("data").get(0).get("id").textValue();

kafkaTestHelper.produceRecord(new ProducerRecord<>(applicationCommunicationMessages.name(), messageId, null));

webTestHelper.post("/messages.list", payload)
.andExpect(status().isOk())
.andExpect(jsonPath("$.data", hasSize(messageCount - 1)));
}, "message was not deleted");
}

@Test
void canReturnMetadata() throws Exception {
final String conversationId = UUID.randomUUID().toString();
Expand Down Expand Up @@ -173,7 +202,6 @@ void canReturnTwilioMessagesUnparsed() throws Exception {
final String conversationId = UUID.randomUUID().toString();
final String messageId = UUID.randomUUID().toString();
final String sourceConversationId = "+491234567";
final String text = "Hello World";
final String sourceChannelId = "+497654321";
final String token = "token";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,13 @@
import static co.airy.core.api.communication.util.Topics.applicationCommunicationChannels;
import static co.airy.core.api.communication.util.Topics.applicationCommunicationMessages;
import static co.airy.core.api.communication.util.Topics.getTopics;
import static co.airy.test.Timing.retryOnException;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.collection.IsCollectionWithSize.hasSize;
import static org.hamcrest.core.Is.is;
import static org.junit.jupiter.api.Assertions.fail;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = AirySpringBootApplication.class)
Expand Down Expand Up @@ -86,6 +89,13 @@ void canSendTextMessages() throws Exception {
"\"message\":%s}",
conversationId, messagePayload);

retryOnException(
() -> webTestHelper.post("/conversations.info",
"{\"conversation_id\":\"" + conversationId + "\"}")
.andExpect(status().isOk()),
"Could not find conversation"
);

final String response = webTestHelper.post("/messages.send", requestPayload)
.andExpect(status().isOk())
.andReturn().getResponse().getContentAsString();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ public class WebSocketController {
}

public void onMessage(Message message) {
messagingTemplate.convertAndSend(QUEUE_EVENTS, MessageEvent.fromMessage(message));
if (message != null) {
messagingTemplate.convertAndSend(QUEUE_EVENTS, MessageEvent.fromMessage(message));
}
}

public void onChannel(Channel channel) {
Expand Down
6 changes: 2 additions & 4 deletions backend/media/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,9 @@ app_deps = [
"//backend:base_app",
"//backend/model/message",
"//backend/model/metadata",
"//lib/java/mapping",
"//lib/java/uuid",
"//lib/java/url",
"//lib/java/spring/kafka/core:spring-kafka-core",
"//lib/java/spring/kafka/streams:spring-kafka-streams",
"@maven//:javax_xml_bind_jaxb_api",
"//lib/java/spring/web:spring-web",
"@maven//:org_springframework_retry_spring_retry",
"@maven//:org_aspectj_aspectjweaver",
"@maven//:com_amazonaws_aws_java_sdk_core",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package co.airy.core.media;

import co.airy.core.media.services.MediaUpload;
import co.airy.log.AiryLoggerFactory;
import co.airy.spring.web.payload.RequestErrorResponsePayload;
import co.airy.uuid.UUIDv5;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.slf4j.Logger;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;

@RestController
public class MediaController {
private static final Logger log = AiryLoggerFactory.getLogger(MediaController.class);
private final MediaUpload mediaUpload;

public MediaController(MediaUpload mediaUpload) {
this.mediaUpload = mediaUpload;
}


@PostMapping(value = "/media.uploadFile", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<?> mediaUpload(@RequestParam("file") MultipartFile multipartFile) {
final String originalFileName = multipartFile.getOriginalFilename();

if (originalFileName == null) {
return ResponseEntity.unprocessableEntity().body(new RequestErrorResponsePayload("Request is missing original file name"));
}

try {
final InputStream is = multipartFile.getInputStream();
String fileName = UUIDv5.fromFile(is).toString();

final String originalFileExtension = originalFileName.contains(".") ? originalFileName.substring(originalFileName.lastIndexOf(".")) : "";
fileName = fileName.concat(originalFileExtension);

return ResponseEntity.ok(new MediaUploadResponsePayload(mediaUpload.uploadMedia(is, fileName)));
} catch (Exception e) {
log.error("Media upload failed:", e);
return ResponseEntity.badRequest().body(new RequestErrorResponsePayload(String.format("Media Upload failed with error: %s", e.getMessage())));
}
}
}

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class MediaUploadResponsePayload {
private String mediaUrl;
}

Loading