diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index ba09160463..15fc9e17d8 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -3,7 +3,7 @@ name: Release Drafter on: push: branches: - - develop + - release/* jobs: update_release_draft: diff --git a/VERSION b/VERSION index 19199bccac..0f1a7dfc7c 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.36.1 +0.37.0 diff --git a/backend/model/metadata/src/main/java/co/airy/model/metadata/MetadataKeys.java b/backend/model/metadata/src/main/java/co/airy/model/metadata/MetadataKeys.java index b70944781a..1c60e6d917 100644 --- a/backend/model/metadata/src/main/java/co/airy/model/metadata/MetadataKeys.java +++ b/backend/model/metadata/src/main/java/co/airy/model/metadata/MetadataKeys.java @@ -50,6 +50,7 @@ public static class MessageKeys { public static class Source { public static final String ID = "source.id"; public static final String DELIVERY_STATE = "source.delivery_state"; + public static final String ERROR = "source.error"; } public static class Reaction { diff --git a/backend/sources/facebook/connector/BUILD b/backend/sources/facebook/connector/BUILD index 98eaab38c2..081f5e65ca 100644 --- a/backend/sources/facebook/connector/BUILD +++ b/backend/sources/facebook/connector/BUILD @@ -35,6 +35,7 @@ springboot( ":app", "//backend:base_test", "//lib/java/kafka/test:kafka-test", + "//lib/java/spring/test:spring-test", ] + app_deps, ) for file in glob(["src/test/java/**/*Test.java"]) diff --git a/backend/sources/facebook/connector/src/main/java/co/airy/core/sources/facebook/ChannelsController.java b/backend/sources/facebook/connector/src/main/java/co/airy/core/sources/facebook/ChannelsController.java index 2ea23ea60a..3a2941326d 100644 --- a/backend/sources/facebook/connector/src/main/java/co/airy/core/sources/facebook/ChannelsController.java +++ b/backend/sources/facebook/connector/src/main/java/co/airy/core/sources/facebook/ChannelsController.java @@ -5,6 +5,7 @@ import co.airy.avro.communication.Metadata; import co.airy.core.sources.facebook.api.Api; import co.airy.core.sources.facebook.api.ApiException; +import co.airy.core.sources.facebook.api.model.FaceBookMetadataKeys; import co.airy.core.sources.facebook.api.model.PageWithConnectInfo; import co.airy.core.sources.facebook.payload.ConnectInstagramRequestPayload; import co.airy.core.sources.facebook.payload.ConnectPageRequestPayload; @@ -104,7 +105,9 @@ ResponseEntity connectFacebook(@RequestBody @Valid ConnectPageRequestPayload ) .metadataMap(MetadataMap.from(List.of( newChannelMetadata(channelId, MetadataKeys.ChannelKeys.NAME, Optional.ofNullable(payload.getName()).orElse(pageWithConnectInfo.getNameWithLocationDescriptor())), - newChannelMetadata(channelId, MetadataKeys.ChannelKeys.IMAGE_URL, Optional.ofNullable(payload.getImageUrl()).orElse(pageWithConnectInfo.getPicture().getData().getUrl())) + newChannelMetadata(channelId, MetadataKeys.ChannelKeys.IMAGE_URL, Optional.ofNullable(payload.getImageUrl()).orElse(pageWithConnectInfo.getPicture().getData().getUrl())), + newChannelMetadata(channelId, FaceBookMetadataKeys.ChannelKeys.PAGE_ID, payload.getPageId()), + newChannelMetadata(channelId, FaceBookMetadataKeys.ChannelKeys.PAGE_TOKEN, payload.getPageToken()) ))).build(); stores.storeChannelContainer(container); @@ -132,7 +135,10 @@ ResponseEntity connectInstagram(@RequestBody @Valid ConnectInstagramRequestPa api.connectPageToApp(pageWithConnectInfo.getAccessToken()); final MetadataMap metadataMap = MetadataMap.from(List.of( - newChannelMetadata(channelId, MetadataKeys.ChannelKeys.NAME, Optional.ofNullable(payload.getName()).orElse(String.format("%s Instagram account", pageWithConnectInfo.getNameWithLocationDescriptor()))) + newChannelMetadata(channelId, MetadataKeys.ChannelKeys.NAME, Optional.ofNullable(payload.getName()).orElse(String.format("%s Instagram account", pageWithConnectInfo.getNameWithLocationDescriptor()))), + newChannelMetadata(channelId, FaceBookMetadataKeys.ChannelKeys.PAGE_ID, payload.getPageId()), + newChannelMetadata(channelId, FaceBookMetadataKeys.ChannelKeys.PAGE_TOKEN, payload.getPageToken()), + newChannelMetadata(channelId, FaceBookMetadataKeys.ChannelKeys.ACCOUNT_ID, payload.getAccountId()) )); Optional.ofNullable(payload.getImageUrl()) diff --git a/backend/sources/facebook/connector/src/main/java/co/airy/core/sources/facebook/Connector.java b/backend/sources/facebook/connector/src/main/java/co/airy/core/sources/facebook/Connector.java index 4ce019e554..829758e9a0 100644 --- a/backend/sources/facebook/connector/src/main/java/co/airy/core/sources/facebook/Connector.java +++ b/backend/sources/facebook/connector/src/main/java/co/airy/core/sources/facebook/Connector.java @@ -78,13 +78,23 @@ public List> sendMessage(SendMessageRequest return List.of(KeyValue.pair(message.getId(), message), KeyValue.pair(getId(metadata).toString(), metadata)); } catch (ApiException e) { - log.error(String.format("Failed to send a message to Facebook \n SendMessageRequest: %s \n Error Message: %s \n", sendMessageRequest, e.getMessage()), e); + log.error(String.format("Failed to send a message to Facebook \n SendMessageRequest: %s \n Api Exception: %s \n", sendMessageRequest, e.getMessage()), e); + final ArrayList> results = new ArrayList<>(); + final Metadata error = newMessageMetadata(message.getId(), MetadataKeys.MessageKeys.ERROR, e.getMessage()); + results.add(KeyValue.pair(getId(error).toString(), error)); + + if (e.getErrorPayload() != null) { + final Metadata errorPayload = newMessageMetadata(message.getId(), MetadataKeys.MessageKeys.Source.ERROR, e.getErrorPayload()); + results.add(KeyValue.pair(getId(errorPayload).toString(), errorPayload)); + } + updateDeliveryState(message, DeliveryState.FAILED); + return results; } catch (Exception e) { log.error(String.format("Failed to send a message to Facebook \n SendMessageRequest: %s", sendMessageRequest), e); + final Metadata metadata = newMessageMetadata(message.getId(), MetadataKeys.MessageKeys.ERROR, e.getMessage()); + updateDeliveryState(message, DeliveryState.FAILED); + return List.of(KeyValue.pair(message.getId(), message), KeyValue.pair(getId(metadata).toString(), metadata)); } - - updateDeliveryState(message, DeliveryState.FAILED); - return List.of(KeyValue.pair(message.getId(), message)); } private boolean isMessageStale(Message message) { diff --git a/backend/sources/facebook/connector/src/main/java/co/airy/core/sources/facebook/api/Api.java b/backend/sources/facebook/connector/src/main/java/co/airy/core/sources/facebook/api/Api.java index 33cb25e559..4264715ff1 100644 --- a/backend/sources/facebook/connector/src/main/java/co/airy/core/sources/facebook/api/Api.java +++ b/backend/sources/facebook/connector/src/main/java/co/airy/core/sources/facebook/api/Api.java @@ -8,7 +8,12 @@ import co.airy.core.sources.facebook.api.model.SendMessagePayload; import co.airy.core.sources.facebook.api.model.SendMessageResponse; import co.airy.core.sources.facebook.api.model.UserProfile; +import co.airy.log.AiryLoggerFactory; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.PropertyNamingStrategy; +import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.boot.web.client.RestTemplateBuilder; @@ -39,6 +44,7 @@ */ @Service public class Api implements ApplicationListener { + private static final Logger log = AiryLoggerFactory.getLogger(Api.class); private final RestTemplateBuilder restTemplateBuilder; private final ObjectMapper objectMapper; private RestTemplate restTemplate; @@ -51,16 +57,15 @@ public class Api implements ApplicationListener { private final HttpHeaders httpHeaders = new HttpHeaders(); private final String appId; private final String apiSecret; - private static final String errorMessageTemplate = - "Exception while sending a message to Facebook: \n" + - "Http Status Code: %s \n" + - "Error Message: %s \n"; - public Api(ObjectMapper objectMapper, RestTemplateBuilder restTemplateBuilder, + public Api(RestTemplateBuilder restTemplateBuilder, @Value("${facebook.app-id}") String appId, @Value("${facebook.app-secret}") String apiSecret) { httpHeaders.setContentType(MediaType.APPLICATION_JSON); - this.objectMapper = objectMapper; + this.objectMapper = new ObjectMapper() + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + .configure(DeserializationFeature.FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY, false) + .setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE); this.restTemplateBuilder = restTemplateBuilder; this.appId = appId; this.apiSecret = apiSecret; @@ -111,11 +116,8 @@ public UserProfile getInstagramProfile(String sourceConversationId, String token public UserProfile getProfileFromContact(String sourceConversationId, String token) { String reqUrl = String.format(baseUrl + "/%s?fields=first_name,last_name,profile_pic&access_token=%s", sourceConversationId, token); - ResponseEntity responseEntity = restTemplate.getForEntity(reqUrl, UserProfile.class); - if (responseEntity.getStatusCode() != HttpStatus.OK) { - throw new ApiException("Call unsuccessful, received HTTP status " + responseEntity.getStatusCodeValue()); - } - return responseEntity.getBody(); + ResponseEntity response = restTemplate.getForEntity(reqUrl, UserProfile.class); + return response.getBody(); } // See https://developers.facebook.com/docs/graph-api/reference/v9.0/conversation#edges @@ -123,12 +125,12 @@ public UserProfile getProfileFromParticipants(String sourceConversationId, Strin String reqUrl = String.format(baseUrl + "/me/conversations?user_id=%s&fields=participants&access_token=%s", sourceConversationId, token); - ResponseEntity responseEntity = restTemplate.getForEntity(reqUrl, Participants.class); - if (responseEntity.getBody() == null || responseEntity.getStatusCode() != HttpStatus.OK) { - throw new ApiException("Call unsuccessful"); + ResponseEntity response = restTemplate.getForEntity(reqUrl, Participants.class); + if (response.getBody() == null) { + throw new ApiException(String.format("Response body was null, status code %s", response.getStatusCode())); } - return fromParticipants(responseEntity.getBody(), sourceConversationId); + return fromParticipants(response.getBody(), sourceConversationId); } public PageWithConnectInfo getPageForUser(final String pageId, final String accessToken) throws Exception { @@ -180,7 +182,18 @@ public boolean hasError(ClientHttpResponse response) throws IOException { @Override public void handleError(ClientHttpResponse response) throws IOException { - throw new ApiException(String.format(errorMessageTemplate, response.getRawStatusCode(), new String(response.getBody().readAllBytes()))); + final String errorPayload = new String(response.getBody().readAllBytes()); + final int statusCode = response.getRawStatusCode(); + + final JsonNode jsonNode = objectMapper.readTree(errorPayload); + final String errorMessage = Optional.of(jsonNode.get("error")) + .map((node) -> node.get("message")) + .map(JsonNode::textValue) + .orElseGet(() -> { + log.warn("Could not parse error message from response: {}", errorPayload); + return String.format("Api replied with status code %s and payload %s", statusCode, errorPayload); + }); + throw new ApiException(errorMessage, errorPayload); } }) .additionalMessageConverters(new MappingJackson2HttpMessageConverter(objectMapper)) diff --git a/backend/sources/facebook/connector/src/main/java/co/airy/core/sources/facebook/api/ApiException.java b/backend/sources/facebook/connector/src/main/java/co/airy/core/sources/facebook/api/ApiException.java index 69dbceff1a..01dae8f3e0 100644 --- a/backend/sources/facebook/connector/src/main/java/co/airy/core/sources/facebook/api/ApiException.java +++ b/backend/sources/facebook/connector/src/main/java/co/airy/core/sources/facebook/api/ApiException.java @@ -1,7 +1,15 @@ package co.airy.core.sources.facebook.api; +import lombok.Getter; + public class ApiException extends RuntimeException { - public ApiException(String msg) { - super(msg); + @Getter + private String errorPayload; + public ApiException(String message) { + super(message); + } + public ApiException(String message, String errorPayload) { + super(message); + this.errorPayload = errorPayload; } } diff --git a/backend/sources/facebook/connector/src/main/java/co/airy/core/sources/facebook/api/model/FaceBookMetadataKeys.java b/backend/sources/facebook/connector/src/main/java/co/airy/core/sources/facebook/api/model/FaceBookMetadataKeys.java new file mode 100644 index 0000000000..e3f18ee6a9 --- /dev/null +++ b/backend/sources/facebook/connector/src/main/java/co/airy/core/sources/facebook/api/model/FaceBookMetadataKeys.java @@ -0,0 +1,10 @@ +package co.airy.core.sources.facebook.api.model; + + +public class FaceBookMetadataKeys { + public static class ChannelKeys { + public static final String PAGE_ID = "page_id"; + public static final String PAGE_TOKEN = "page_token"; + public static final String ACCOUNT_ID = "account_id"; + } +} diff --git a/backend/sources/facebook/connector/src/test/java/co/airy/core/sources/facebook/ChannelsControllerTest.java b/backend/sources/facebook/connector/src/test/java/co/airy/core/sources/facebook/ChannelsControllerTest.java new file mode 100644 index 0000000000..b36c2a3173 --- /dev/null +++ b/backend/sources/facebook/connector/src/test/java/co/airy/core/sources/facebook/ChannelsControllerTest.java @@ -0,0 +1,256 @@ +package co.airy.core.sources.facebook; + +import co.airy.avro.communication.Channel; +import co.airy.avro.communication.ChannelConnectionState; +import co.airy.avro.communication.Metadata; +import co.airy.core.sources.facebook.api.Api; +import co.airy.core.sources.facebook.api.model.FaceBookMetadataKeys; +import co.airy.core.sources.facebook.api.model.PageWithConnectInfo; +import co.airy.core.sources.facebook.payload.ConnectInstagramRequestPayload; +import co.airy.core.sources.facebook.payload.ConnectPageRequestPayload; +import co.airy.core.sources.facebook.payload.ExploreRequestPayload; +import co.airy.kafka.schema.Topic; +import co.airy.kafka.schema.application.ApplicationCommunicationChannels; +import co.airy.kafka.schema.application.ApplicationCommunicationMessages; +import co.airy.kafka.schema.application.ApplicationCommunicationMetadata; +import co.airy.kafka.test.KafkaTestHelper; +import co.airy.kafka.test.junit.SharedKafkaTestResource; +import co.airy.model.metadata.MetadataKeys; +import co.airy.spring.core.AirySpringBootApplication; +import co.airy.spring.test.WebTestHelper; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mockito.InjectMocks; +import org.mockito.MockitoAnnotations; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import java.util.Arrays; +import java.util.List; +import java.util.UUID; + +import static co.airy.test.Timing.retryOnException; +import static org.apache.kafka.streams.KafkaStreams.State.RUNNING; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.IsEqual.equalTo; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@SpringBootTest(classes = AirySpringBootApplication.class) +@TestPropertySource(value = "classpath:test.properties") +@AutoConfigureMockMvc +@ExtendWith(SpringExtension.class) +class ChannelsControllerTest { + + @Autowired + private ObjectMapper objectMapper; + + @Autowired + private WebTestHelper webTestHelper; + + @RegisterExtension + public static final SharedKafkaTestResource sharedKafkaTestResource = new SharedKafkaTestResource(); + private static KafkaTestHelper kafkaTestHelper; + + private static final Topic applicationCommunicationChannels = new ApplicationCommunicationChannels(); + private static final Topic applicationCommunicationMessages = new ApplicationCommunicationMessages(); + private static final Topic applicationCommunicationMetadata = new ApplicationCommunicationMetadata(); + + @MockBean + private Api api; + + @Autowired + @InjectMocks + private Connector worker; + + @Autowired + private Stores stores; + + @BeforeAll + static void beforeAll() throws Exception { + kafkaTestHelper = new KafkaTestHelper(sharedKafkaTestResource, + applicationCommunicationChannels, + applicationCommunicationMessages, + applicationCommunicationMetadata + ); + + kafkaTestHelper.beforeAll(); + } + + @AfterAll + static void afterAll() throws Exception { + kafkaTestHelper.afterAll(); + } + + @BeforeEach + void beforeEach() throws Exception { + MockitoAnnotations.openMocks(this); + retryOnException(() -> assertEquals(stores.getStreamState(), RUNNING), "Failed to reach RUNNING state."); + } + + @Test + void canConnect() throws Exception { + canFacebookConnect(); + canInstagramConnect(); + } + + private void canFacebookConnect() throws Exception { + // Connect to facebook channel + final ConnectPageRequestPayload connectPayload = this.mockConnectPageRequestPayload(); + final PageWithConnectInfo pageWithConnectInfo = this.mockPageWithConnectInfo(); + + doReturn("new-long-live-token-string").when(api).exchangeToLongLivingUserAccessToken(connectPayload.getPageToken()); + doReturn(pageWithConnectInfo).when(api).getPageForUser(connectPayload.getPageId(), "new-long-live-token-string"); + doNothing().when(api).connectPageToApp(pageWithConnectInfo.getAccessToken()); + + + String content = webTestHelper.post( + "/channels.facebook.connect", + objectMapper.writeValueAsString(connectPayload)) + .andExpect(status().isOk()).andReturn().getResponse().getContentAsString(); + final String channelId = new ObjectMapper().readTree(content).get("id").textValue(); + + List channels = kafkaTestHelper.consumeValues(1, applicationCommunicationChannels.name()); + final Channel channel = channels.get(0); + + assertThat("facebook", equalTo(channel.getSource())); + assertThat(pageWithConnectInfo.getId(), equalTo(channel.getSourceChannelId())); + assertThat(ChannelConnectionState.CONNECTED, equalTo(channel.getConnectionState())); + + final List metadataList = kafkaTestHelper.consumeValues(4, applicationCommunicationMetadata.name()); + metadataList.stream().forEach((metadata) -> { + final String key = metadata.getKey(); + switch (key) { + case MetadataKeys.ChannelKeys.NAME: + assertThat(pageWithConnectInfo.getNameWithLocationDescriptor(), equalTo(metadata.getValue())); + break; + case MetadataKeys.ChannelKeys.IMAGE_URL: + assertThat(pageWithConnectInfo.getPicture().getData().getUrl(), equalTo(metadata.getValue())); + break; + case FaceBookMetadataKeys.ChannelKeys.PAGE_ID: + assertThat(connectPayload.getPageId(), equalTo(metadata.getValue())); + break; + case FaceBookMetadataKeys.ChannelKeys.PAGE_TOKEN: + assertThat(connectPayload.getPageToken(), equalTo(metadata.getValue())); + break; + default: + assertThat(String.format("unexpected key: %s", key), false); + } + }); + + // Explore facebook connected channels + final ExploreRequestPayload explorePayload = new ExploreRequestPayload("explore-token-string"); + + doReturn(Arrays.asList(pageWithConnectInfo)).when(api).getPagesInfo(explorePayload.getAuthToken()); + content = webTestHelper.post( + "/channels.facebook.explore", + objectMapper.writeValueAsString(explorePayload)) + .andExpect(status().isOk()).andReturn().getResponse().getContentAsString(); + + assertThat(content, equalTo("{\"data\":[{\"page_id\":\"7562107744668308\",\"name\":\"facebook app\",\"image_url\":\"facebook app image\",\"connected\":true}]}")); + + + // Disconnect from facebook channel + webTestHelper.post( + "/channels.facebook.disconnect", + String.format("{\"channel_id\":\"%s\"}", channelId)) + .andExpect(status().isNoContent()); + + channels = kafkaTestHelper.consumeValues(1, applicationCommunicationChannels.name()); + assertThat(ChannelConnectionState.DISCONNECTED, equalTo(channels.get(0).getConnectionState())); + } + + private void canInstagramConnect() throws Exception { + // Connect to instagram channel + final ConnectInstagramRequestPayload connectPayload = this.mockConnectInstagramRequestPayload(); + final PageWithConnectInfo pageWithConnectInfo = this.mockPageWithConnectInfo(); + + doReturn("new-long-live-token-string").when(api).exchangeToLongLivingUserAccessToken(connectPayload.getPageToken()); + doReturn(pageWithConnectInfo).when(api).getPageForUser(connectPayload.getPageId(), "new-long-live-token-string"); + doNothing().when(api).connectPageToApp(pageWithConnectInfo.getAccessToken()); + + + final String content = webTestHelper.post( + "/channels.instagram.connect", + objectMapper.writeValueAsString(connectPayload)) + .andExpect(status().isOk()).andReturn().getResponse().getContentAsString(); + final String channelId = new ObjectMapper().readTree(content).get("id").textValue(); + + List channels = kafkaTestHelper.consumeValues(1, applicationCommunicationChannels.name()); + final Channel channel = channels.get(0); + + assertThat("instagram", equalTo(channel.getSource())); + assertThat(connectPayload.getAccountId(), equalTo(channel.getSourceChannelId())); + assertThat(ChannelConnectionState.CONNECTED, equalTo(channel.getConnectionState())); + + final List metadataList = kafkaTestHelper.consumeValues(5, applicationCommunicationMetadata.name()); + metadataList.stream().forEach((metadata) -> { + final String key = metadata.getKey(); + switch (key) { + case MetadataKeys.ChannelKeys.NAME: + assertThat(pageWithConnectInfo.getNameWithLocationDescriptor(), equalTo(metadata.getValue())); + break; + case FaceBookMetadataKeys.ChannelKeys.ACCOUNT_ID: + assertThat(connectPayload.getAccountId(), equalTo(metadata.getValue())); + break; + case FaceBookMetadataKeys.ChannelKeys.PAGE_ID: + assertThat(connectPayload.getPageId(), equalTo(metadata.getValue())); + break; + case FaceBookMetadataKeys.ChannelKeys.PAGE_TOKEN: + assertThat(connectPayload.getPageToken(), equalTo(metadata.getValue())); + break; + case MetadataKeys.ChannelKeys.IMAGE_URL: + assertThat(connectPayload.getImageUrl(), equalTo(metadata.getValue())); + break; + default: + assertThat(String.format("unexpected key: %s", key), false); + } + }); + + // Disconnect from instagram channel + webTestHelper.post( + "/channels.instagram.disconnect", + String.format("{\"channel_id\":\"%s\"}", channelId)) + .andExpect(status().isNoContent()); + + channels = kafkaTestHelper.consumeValues(1, applicationCommunicationChannels.name()); + assertThat(ChannelConnectionState.DISCONNECTED, equalTo(channels.get(0).getConnectionState())); + } + + private ConnectPageRequestPayload mockConnectPageRequestPayload() { + return new ConnectPageRequestPayload( + "7562107744668308", + "token-string", + "facebook app", + "facebook app image"); + } + + private PageWithConnectInfo mockPageWithConnectInfo() { + return new PageWithConnectInfo( + "7562107744668308", + "facebook app", + "some-access-token", + new PageWithConnectInfo.PagePic(new PageWithConnectInfo.PagePic.PicData("facebook app image")), + true); + } + + private ConnectInstagramRequestPayload mockConnectInstagramRequestPayload() { + return new ConnectInstagramRequestPayload( + "7562107744668308", + "1847583685763736", + "some-access-token", + "facebook app", + "instagram-image-url"); + } +} diff --git a/backend/sources/facebook/connector/src/test/java/co/airy/core/sources/facebook/FetchMetadataTest.java b/backend/sources/facebook/connector/src/test/java/co/airy/core/sources/facebook/FetchMetadataTest.java index b39da7f3e2..77f40ef6d3 100644 --- a/backend/sources/facebook/connector/src/test/java/co/airy/core/sources/facebook/FetchMetadataTest.java +++ b/backend/sources/facebook/connector/src/test/java/co/airy/core/sources/facebook/FetchMetadataTest.java @@ -35,10 +35,13 @@ import java.util.List; import static co.airy.model.metadata.MetadataKeys.ConversationKeys; +import static org.apache.kafka.streams.KafkaStreams.State.RUNNING; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.collection.IsCollectionWithSize.hasSize; import static org.hamcrest.core.IsEqual.equalTo; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; +import static co.airy.test.Timing.retryOnException; @SpringBootTest(classes = AirySpringBootApplication.class) @TestPropertySource(value = "classpath:test.properties") @@ -56,6 +59,9 @@ class FetchMetadataTest { @MockBean private Api api; + @Autowired + private Stores stores; + @Autowired @InjectMocks private Connector worker; @@ -77,8 +83,9 @@ static void afterAll() throws Exception { } @BeforeEach - void beforeEach() { + void beforeEach() throws Exception { MockitoAnnotations.openMocks(this); + retryOnException(() -> assertEquals(stores.getStreamState(), RUNNING), "Failed to reach RUNNING state."); } @Test diff --git a/backend/sources/facebook/connector/src/test/java/co/airy/core/sources/facebook/SendMessageTest.java b/backend/sources/facebook/connector/src/test/java/co/airy/core/sources/facebook/SendMessageTest.java index cbbd660c34..dbb865a7d5 100644 --- a/backend/sources/facebook/connector/src/test/java/co/airy/core/sources/facebook/SendMessageTest.java +++ b/backend/sources/facebook/connector/src/test/java/co/airy/core/sources/facebook/SendMessageTest.java @@ -4,7 +4,9 @@ import co.airy.avro.communication.ChannelConnectionState; import co.airy.avro.communication.DeliveryState; import co.airy.avro.communication.Message; +import co.airy.avro.communication.Metadata; import co.airy.core.sources.facebook.api.Api; +import co.airy.core.sources.facebook.api.ApiException; import co.airy.core.sources.facebook.api.model.SendMessagePayload; import co.airy.core.sources.facebook.api.model.SendMessageResponse; import co.airy.kafka.schema.Topic; @@ -13,6 +15,7 @@ import co.airy.kafka.schema.application.ApplicationCommunicationMetadata; import co.airy.kafka.test.KafkaTestHelper; import co.airy.kafka.test.junit.SharedKafkaTestResource; +import co.airy.model.metadata.MetadataKeys; import co.airy.spring.core.AirySpringBootApplication; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -36,6 +39,7 @@ import java.util.List; import java.util.concurrent.TimeUnit; +import static co.airy.model.metadata.MetadataRepository.getSubject; import static co.airy.test.Timing.retryOnException; import static org.apache.kafka.streams.KafkaStreams.State.RUNNING; import static org.hamcrest.MatcherAssert.assertThat; @@ -89,17 +93,21 @@ void beforeEach() throws InterruptedException { } @Test - void canSendMessageViaTheFacebookApi() throws Exception { + void canSendMessage() throws Exception { final String conversationId = "conversationId"; final String messageId = "message-id"; + final String failingMessageId = "message-id-failing"; final String sourceConversationId = "source-conversation-id"; final String channelId = "channel-id"; final String token = "token"; final String text = "Hello World"; + final String errorMessage = "message delivery failed"; ArgumentCaptor payloadCaptor = ArgumentCaptor.forClass(SendMessagePayload.class); ArgumentCaptor tokenCaptor = ArgumentCaptor.forClass(String.class); - when(api.sendMessage(tokenCaptor.capture(), payloadCaptor.capture())).thenReturn(new SendMessageResponse("recipient id", "message id")); + when(api.sendMessage(tokenCaptor.capture(), payloadCaptor.capture())) + .thenReturn(new SendMessageResponse("recipient id", "message id")) + .thenThrow(new ApiException(errorMessage)); kafkaTestHelper.produceRecords(List.of( new ProducerRecord<>(applicationCommunicationChannels.name(), channelId, Channel.newBuilder() @@ -129,7 +137,7 @@ void canSendMessageViaTheFacebookApi() throws Exception { final ObjectMapper objectMapper = new ObjectMapper(); final JsonNode messagePayload = objectMapper.readTree("{\"text\":\"Hello Facebook\"}"); - kafkaTestHelper.produceRecord(new ProducerRecord<>(applicationCommunicationMessages.name(), messageId, + kafkaTestHelper.produceRecords(List.of(new ProducerRecord<>(applicationCommunicationMessages.name(), messageId, Message.newBuilder() .setId(messageId) .setSentAt(Instant.now().toEpochMilli()) @@ -140,8 +148,21 @@ void canSendMessageViaTheFacebookApi() throws Exception { .setSource("facebook") .setContent(objectMapper.writeValueAsString(messagePayload)) .setIsFromContact(false) - .build()) - ); + .build()), + // This message should fail + new ProducerRecord<>(applicationCommunicationMessages.name(), messageId, + Message.newBuilder() + .setId(failingMessageId) + .setSentAt(Instant.now().toEpochMilli()) + .setSenderId("user-id") + .setDeliveryState(DeliveryState.PENDING) + .setConversationId(conversationId) + .setChannelId(channelId) + .setSource("facebook") + .setContent(objectMapper.writeValueAsString(messagePayload)) + .setIsFromContact(false) + .build()) + )); retryOnException(() -> { final SendMessagePayload sendMessagePayload = payloadCaptor.getValue(); @@ -150,5 +171,13 @@ void canSendMessageViaTheFacebookApi() throws Exception { assertThat(tokenCaptor.getValue(), equalTo(token)); }, "Facebook API was not called"); + + final List metadataList = kafkaTestHelper.consumeValues(3, applicationCommunicationMetadata.name()); + + assertThat(metadataList.size(), equalTo(3)); + assertThat(metadataList.stream().anyMatch((metadata) -> + metadata.getKey().equals(MetadataKeys.MessageKeys.ERROR) + && metadata.getValue().equals(errorMessage) + && getSubject(metadata).getIdentifier().equals(failingMessageId)), equalTo(true)); } } diff --git a/backend/sources/facebook/connector/src/test/java/co/airy/core/sources/facebook/WebhookControllerTest.java b/backend/sources/facebook/connector/src/test/java/co/airy/core/sources/facebook/WebhookControllerTest.java index 44b65987f9..abf2bd4fae 100644 --- a/backend/sources/facebook/connector/src/test/java/co/airy/core/sources/facebook/WebhookControllerTest.java +++ b/backend/sources/facebook/connector/src/test/java/co/airy/core/sources/facebook/WebhookControllerTest.java @@ -7,9 +7,11 @@ import co.airy.spring.core.AirySpringBootApplication; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.RegisterExtension; +import org.mockito.MockitoAnnotations; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; @@ -40,6 +42,9 @@ class WebhookControllerTest { @Autowired private MockMvc mvc; + @Autowired + private Stores stores; + @BeforeAll static void beforeAll() throws Exception { kafkaTestHelper = new KafkaTestHelper(sharedKafkaTestResource, sourceFacebookEvents); @@ -52,6 +57,11 @@ static void afterAll() throws Exception { kafkaTestHelper.afterAll(); } + @BeforeEach + void beforeEach() { + MockitoAnnotations.openMocks(this); + } + @Test void canAcceptAnything() throws Exception { mvc.perform(post("/facebook").content("whatever")).andExpect(status().isOk()); diff --git a/backend/sources/google/connector/src/main/java/co/airy/core/sources/google/ApiException.java b/backend/sources/google/connector/src/main/java/co/airy/core/sources/google/ApiException.java index d112f46a0c..cd1a361a30 100644 --- a/backend/sources/google/connector/src/main/java/co/airy/core/sources/google/ApiException.java +++ b/backend/sources/google/connector/src/main/java/co/airy/core/sources/google/ApiException.java @@ -1,7 +1,15 @@ package co.airy.core.sources.google; +import lombok.Getter; + public class ApiException extends RuntimeException { - public ApiException(String msg) { - super(msg); + @Getter + private String errorPayload; + public ApiException(String message) { + super(message); + } + public ApiException(String message, String errorPayload) { + super(message); + this.errorPayload = errorPayload; } } diff --git a/backend/sources/google/connector/src/main/java/co/airy/core/sources/google/Connector.java b/backend/sources/google/connector/src/main/java/co/airy/core/sources/google/Connector.java index c1fe9459a6..bc4d98c100 100644 --- a/backend/sources/google/connector/src/main/java/co/airy/core/sources/google/Connector.java +++ b/backend/sources/google/connector/src/main/java/co/airy/core/sources/google/Connector.java @@ -2,14 +2,18 @@ import co.airy.avro.communication.DeliveryState; import co.airy.avro.communication.Message; +import co.airy.avro.communication.Metadata; import co.airy.core.sources.google.model.SendMessageRequest; import co.airy.core.sources.google.services.Api; import co.airy.core.sources.google.services.Mapper; import co.airy.log.AiryLoggerFactory; +import co.airy.model.metadata.MetadataKeys; import co.airy.spring.auth.IgnoreAuthPattern; import co.airy.spring.web.filters.RequestLoggingIgnorePatterns; import co.airy.tracking.RouteTracking; import com.fasterxml.jackson.databind.JsonNode; +import org.apache.avro.specific.SpecificRecordBase; +import org.apache.kafka.streams.KeyValue; import org.slf4j.Logger; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; @@ -22,6 +26,8 @@ import java.util.regex.Pattern; import static co.airy.model.message.MessageRepository.updateDeliveryState; +import static co.airy.model.metadata.MetadataRepository.getId; +import static co.airy.model.metadata.MetadataRepository.newMessageMetadata; @Component public class Connector { @@ -36,18 +42,18 @@ public class Connector { this.mapper = mapper; } - public Message sendMessage(SendMessageRequest sendMessageRequest) { + public List> sendMessage(SendMessageRequest sendMessageRequest) { final Message message = sendMessageRequest.getMessage(); if (isMessageStale(message)) { updateDeliveryState(message, DeliveryState.FAILED); - return message; + return List.of(KeyValue.pair(message.getId(), message)); } if (sendMessageRequest.getSourceConversationId() == null) { // Cannot start conversation for Google updateDeliveryState(message, DeliveryState.FAILED); - return message; + return List.of(KeyValue.pair(message.getId(), message)); } try { @@ -55,15 +61,22 @@ public Message sendMessage(SendMessageRequest sendMessageRequest) { api.sendMessage(sendMessageRequest.getSourceConversationId(), sendMessagePayload); updateDeliveryState(message, DeliveryState.DELIVERED); - return message; + return List.of(KeyValue.pair(message.getId(), message)); } catch (ApiException e) { - log.error(String.format("Google Api Exception for SendMessageRequest:\n%s", sendMessageRequest), e); + log.error(String.format("Failed to send a message to Facebook \n SendMessageRequest: %s \n Api Exception: %s \n", sendMessageRequest, e.getMessage()), e); + final Metadata error = newMessageMetadata(message.getId(), MetadataKeys.MessageKeys.ERROR, e.getMessage()); + final Metadata errorPayload = newMessageMetadata(message.getId(), MetadataKeys.MessageKeys.Source.ERROR, e.getErrorPayload()); + updateDeliveryState(message, DeliveryState.FAILED); + return List.of(KeyValue.pair(message.getId(), message), + KeyValue.pair(getId(error).toString(), errorPayload), + KeyValue.pair(getId(errorPayload).toString(), errorPayload) + ); } catch (Exception e) { - log.error(String.format("Failed to send a message to Google \nSendMessageRequest: %s", sendMessageRequest), e); + log.error(String.format("Failed to send a message to Facebook \n SendMessageRequest: %s", sendMessageRequest), e); + final Metadata metadata = newMessageMetadata(message.getId(), MetadataKeys.MessageKeys.ERROR, e.getMessage()); + updateDeliveryState(message, DeliveryState.FAILED); + return List.of(KeyValue.pair(message.getId(), message), KeyValue.pair(getId(metadata).toString(), metadata)); } - - updateDeliveryState(message, DeliveryState.FAILED); - return message; } private boolean isMessageStale(Message message) { diff --git a/backend/sources/google/connector/src/main/java/co/airy/core/sources/google/Stores.java b/backend/sources/google/connector/src/main/java/co/airy/core/sources/google/Stores.java index 0054ee1b51..da2b28fc53 100644 --- a/backend/sources/google/connector/src/main/java/co/airy/core/sources/google/Stores.java +++ b/backend/sources/google/connector/src/main/java/co/airy/core/sources/google/Stores.java @@ -14,7 +14,6 @@ import org.apache.kafka.clients.producer.KafkaProducer; import org.apache.kafka.clients.producer.ProducerRecord; import org.apache.kafka.streams.KafkaStreams; -import org.apache.kafka.streams.KeyValue; import org.apache.kafka.streams.StreamsBuilder; import org.apache.kafka.streams.kstream.KStream; import org.apache.kafka.streams.kstream.KTable; @@ -37,6 +36,7 @@ public class Stores implements ApplicationListener, Dispo private final String channelsStore = "channels-store"; private static final String applicationCommunicationChannels = new ApplicationCommunicationChannels().name(); private static final String applicationCommunicationMetadata = new ApplicationCommunicationMetadata().name(); + private static final String applicationCommunicationMessages = new ApplicationCommunicationMessages().name(); private final KafkaStreamsWrapper streams; private final KafkaProducer producer; @@ -70,11 +70,17 @@ public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) { messageStream.filter((conversationId, message) -> DeliveryState.PENDING.equals(message.getDeliveryState())) .join(contextTable, (message, sendMessageRequest) -> sendMessageRequest.toBuilder().message(message).build()) - .map((conversationId, sendMessageRequest) -> { - final Message message = connector.sendMessage(sendMessageRequest); - return KeyValue.pair(message.getId(), message); - }) - .to(new ApplicationCommunicationMessages().name()); + .flatMap((conversationId, sendMessageRequest) -> connector.sendMessage(sendMessageRequest)) + .to((recordId, record, context) -> { + if (record instanceof Metadata) { + return applicationCommunicationMetadata; + } + if (record instanceof Message) { + return applicationCommunicationMessages; + } + + throw new IllegalStateException("Unknown type for record " + record); + }); streams.start(builder.build(), appId); } diff --git a/backend/sources/google/connector/src/main/java/co/airy/core/sources/google/services/Api.java b/backend/sources/google/connector/src/main/java/co/airy/core/sources/google/services/Api.java index 86a462184d..85cfe73b0e 100644 --- a/backend/sources/google/connector/src/main/java/co/airy/core/sources/google/services/Api.java +++ b/backend/sources/google/connector/src/main/java/co/airy/core/sources/google/services/Api.java @@ -2,10 +2,12 @@ import co.airy.core.sources.google.ApiException; import co.airy.core.sources.google.model.GoogleServiceAccount; +import co.airy.log.AiryLoggerFactory; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.auth.oauth2.AccessToken; import com.google.auth.oauth2.GoogleCredentials; +import org.slf4j.Logger; import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.context.ApplicationListener; @@ -22,19 +24,17 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.util.List; +import java.util.Optional; @Service public class Api implements ApplicationListener { + private static final Logger log = AiryLoggerFactory.getLogger(Api.class); final RestTemplateBuilder restTemplateBuilder; final ObjectMapper objectMapper; final GoogleServiceAccount serviceAccount; private RestTemplate restTemplate; private static final String requestTemplate = "https://businessmessages.googleapis.com/v1/conversations/%s/messages"; - private static final String errorMessageTemplate = - "Exception while sending a message to Google: \n" + - "Http Status Code: %s \n" + - "Error Message: %s \n"; public Api(RestTemplateBuilder restTemplateBuilder, ObjectMapper objectMapper, GoogleServiceAccount serviceAccount) { this.restTemplateBuilder = restTemplateBuilder; @@ -67,7 +67,18 @@ public boolean hasError(ClientHttpResponse response) throws IOException { @Override public void handleError(ClientHttpResponse response) throws IOException { - throw new ApiException(String.format(errorMessageTemplate, response.getRawStatusCode(), new String(response.getBody().readAllBytes()))); + final String errorPayload = new String(response.getBody().readAllBytes()); + final int statusCode = response.getRawStatusCode(); + + final JsonNode jsonNode = objectMapper.readTree(errorPayload); + final String errorMessage = Optional.of(jsonNode.get("error")) + .map((node) -> node.get("message")) + .map(JsonNode::textValue) + .orElseGet(() -> { + log.warn("Could not parse error message from response: {}", errorPayload); + return String.format("Api replied with status code %s and payload %s", statusCode, errorPayload); + }); + throw new ApiException(errorMessage, errorPayload); } }) .additionalMessageConverters(new MappingJackson2HttpMessageConverter(objectMapper)) diff --git a/backend/sources/twilio/connector/src/main/java/co/airy/core/sources/twilio/Connector.java b/backend/sources/twilio/connector/src/main/java/co/airy/core/sources/twilio/Connector.java index 80bbfebf66..40b3a48e55 100644 --- a/backend/sources/twilio/connector/src/main/java/co/airy/core/sources/twilio/Connector.java +++ b/backend/sources/twilio/connector/src/main/java/co/airy/core/sources/twilio/Connector.java @@ -2,25 +2,32 @@ import co.airy.avro.communication.DeliveryState; import co.airy.avro.communication.Message; +import co.airy.avro.communication.Metadata; import co.airy.core.sources.twilio.dto.SendMessageRequest; import co.airy.core.sources.twilio.services.Api; +import co.airy.core.sources.twilio.services.ApiException; import co.airy.log.AiryLoggerFactory; +import co.airy.model.metadata.MetadataKeys; import co.airy.spring.auth.IgnoreAuthPattern; import co.airy.spring.web.filters.RequestLoggingIgnorePatterns; import co.airy.tracking.RouteTracking; -import com.twilio.exception.ApiException; +import org.apache.avro.specific.SpecificRecordBase; +import org.apache.kafka.streams.KeyValue; import org.slf4j.Logger; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; import java.time.Instant; import java.time.temporal.ChronoUnit; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Pattern; import static co.airy.model.message.MessageRepository.updateDeliveryState; +import static co.airy.model.metadata.MetadataRepository.getId; +import static co.airy.model.metadata.MetadataRepository.newMessageMetadata; @Component public class Connector { @@ -33,34 +40,44 @@ public class Connector { this.api = api; } - public Message sendMessage(SendMessageRequest sendMessageRequest) { + public List> sendMessage(SendMessageRequest sendMessageRequest) { final Message message = sendMessageRequest.getMessage(); final String from = sendMessageRequest.getChannel().getSourceChannelId(); final String to = sendMessageRequest.getSourceRecipientId(); if (isMessageStale(message)) { updateDeliveryState(message, DeliveryState.FAILED); - return message; + return List.of(KeyValue.pair(message.getId(), message)); } if (to == null) { // Tried to create a new conversation without providing source recipient id updateDeliveryState(message, DeliveryState.FAILED); - return message; + return List.of(KeyValue.pair(message.getId(), message)); } try { api.sendMessage(from, to, message.getContent()); updateDeliveryState(message, DeliveryState.DELIVERED); - return message; + return List.of(KeyValue.pair(message.getId(), message)); } catch (ApiException e) { - log.error(String.format("Twilio Api Exception for SendMessageRequest:\n%s", sendMessageRequest), e); + log.error(String.format("Failed to send a message to Facebook \n SendMessageRequest: %s \n Api Exception: %s \n", sendMessageRequest, e.getMessage()), e); + final ArrayList> results = new ArrayList<>(); + final Metadata error = newMessageMetadata(message.getId(), MetadataKeys.MessageKeys.ERROR, e.getMessage()); + results.add(KeyValue.pair(getId(error).toString(), error)); + + if (e.getErrorPayload() != null) { + final Metadata errorPayload = newMessageMetadata(message.getId(), MetadataKeys.MessageKeys.Source.ERROR, e.getErrorPayload()); + results.add(KeyValue.pair(getId(errorPayload).toString(), errorPayload)); + } + updateDeliveryState(message, DeliveryState.FAILED); + return results; } catch (Exception e) { - log.error(String.format("Failed to send a message to Twilio \n SendMessageRequest: %s", sendMessageRequest), e); + log.error(String.format("Failed to send a message to Facebook \n SendMessageRequest: %s", sendMessageRequest), e); + final Metadata metadata = newMessageMetadata(message.getId(), MetadataKeys.MessageKeys.ERROR, e.getMessage()); + updateDeliveryState(message, DeliveryState.FAILED); + return List.of(KeyValue.pair(message.getId(), message), KeyValue.pair(getId(metadata).toString(), metadata)); } - - updateDeliveryState(message, DeliveryState.FAILED); - return message; } private boolean isMessageStale(Message message) { diff --git a/backend/sources/twilio/connector/src/main/java/co/airy/core/sources/twilio/Stores.java b/backend/sources/twilio/connector/src/main/java/co/airy/core/sources/twilio/Stores.java index 6c62adcf28..b6f6ea1002 100644 --- a/backend/sources/twilio/connector/src/main/java/co/airy/core/sources/twilio/Stores.java +++ b/backend/sources/twilio/connector/src/main/java/co/airy/core/sources/twilio/Stores.java @@ -15,7 +15,6 @@ import org.apache.kafka.clients.producer.KafkaProducer; import org.apache.kafka.clients.producer.ProducerRecord; import org.apache.kafka.streams.KafkaStreams; -import org.apache.kafka.streams.KeyValue; import org.apache.kafka.streams.StreamsBuilder; import org.apache.kafka.streams.kstream.KStream; import org.apache.kafka.streams.kstream.KTable; @@ -37,6 +36,7 @@ public class Stores implements ApplicationListener, Dispo private static final String applicationCommunicationChannels = new ApplicationCommunicationChannels().name(); private static final String applicationCommunicationMetadata = new ApplicationCommunicationMetadata().name(); + private static final String applicationCommunicationMessages = new ApplicationCommunicationMessages().name(); private static final String appId = "sources.twilio.ConnectorStores"; private final String channelsStore = "channels-store"; @@ -89,11 +89,17 @@ public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) { }) .selectKey((conversationId, sendMessageRequest) -> sendMessageRequest.getChannelId()) .join(channelsTable, (sendMessageRequest, channel) -> sendMessageRequest.toBuilder().channel(channel).build()) - .map((channelId, sendMessageRequest) -> { - final Message message = connector.sendMessage(sendMessageRequest); - return KeyValue.pair(message.getId(), message); - }) - .to(new ApplicationCommunicationMessages().name()); + .flatMap((channelId, sendMessageRequest) -> connector.sendMessage(sendMessageRequest)) + .to((recordId, record, context) -> { + if (record instanceof Metadata) { + return applicationCommunicationMetadata; + } + if (record instanceof Message) { + return applicationCommunicationMessages; + } + + throw new IllegalStateException("Unknown type for record " + record); + }); streams.start(builder.build(), appId); } diff --git a/backend/sources/twilio/connector/src/main/java/co/airy/core/sources/twilio/services/ApiException.java b/backend/sources/twilio/connector/src/main/java/co/airy/core/sources/twilio/services/ApiException.java new file mode 100644 index 0000000000..21db62eb6d --- /dev/null +++ b/backend/sources/twilio/connector/src/main/java/co/airy/core/sources/twilio/services/ApiException.java @@ -0,0 +1,15 @@ +package co.airy.core.sources.twilio.services; + +import lombok.Getter; + +public class ApiException extends RuntimeException { + @Getter + private String errorPayload; + public ApiException(String message) { + super(message); + } + public ApiException(String message, String errorPayload) { + super(message); + this.errorPayload = errorPayload; + } +} diff --git a/backend/sources/twilio/connector/src/test/java/co/airy/core/sources/twilio/SendMessageTest.java b/backend/sources/twilio/connector/src/test/java/co/airy/core/sources/twilio/SendMessageTest.java index 1deece342e..a3e9a1cae0 100644 --- a/backend/sources/twilio/connector/src/test/java/co/airy/core/sources/twilio/SendMessageTest.java +++ b/backend/sources/twilio/connector/src/test/java/co/airy/core/sources/twilio/SendMessageTest.java @@ -2,12 +2,16 @@ import co.airy.avro.communication.DeliveryState; import co.airy.avro.communication.Message; +import co.airy.avro.communication.Metadata; import co.airy.core.sources.twilio.services.Api; +import co.airy.core.sources.twilio.services.ApiException; import co.airy.kafka.schema.Topic; import co.airy.kafka.schema.application.ApplicationCommunicationChannels; import co.airy.kafka.schema.application.ApplicationCommunicationMessages; +import co.airy.kafka.schema.application.ApplicationCommunicationMetadata; import co.airy.kafka.test.KafkaTestHelper; import co.airy.kafka.test.junit.SharedKafkaTestResource; +import co.airy.model.metadata.MetadataKeys; import co.airy.spring.core.AirySpringBootApplication; import co.airy.spring.test.WebTestHelper; import com.fasterxml.jackson.databind.JsonNode; @@ -32,11 +36,12 @@ import java.time.Instant; import java.util.List; import java.util.UUID; -import java.util.concurrent.TimeUnit; import static co.airy.test.Timing.retryOnException; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.IsEqual.equalTo; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doThrow; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @SpringBootTest(classes = AirySpringBootApplication.class) @@ -51,6 +56,7 @@ class SendMessageTest { private static final Topic applicationCommunicationChannels = new ApplicationCommunicationChannels(); private static final Topic applicationCommunicationMessages = new ApplicationCommunicationMessages(); + private static final Topic applicationCommunicationMetadata = new ApplicationCommunicationMetadata(); @MockBean Api api; @@ -69,7 +75,8 @@ class SendMessageTest { static void beforeAll() throws Exception { kafkaTestHelper = new KafkaTestHelper(sharedKafkaTestResource, applicationCommunicationChannels, - applicationCommunicationMessages + applicationCommunicationMessages, + applicationCommunicationMetadata ); kafkaTestHelper.beforeAll(); @@ -93,12 +100,13 @@ void canSendMessages() throws Exception { final String sourceConversationId = "+491234567"; final String sourceChannelId = "+497654321"; final String payload = "{\"Body\":\"Hello World\"}"; + final String errorMessage = "message delivery failed"; ArgumentCaptor payloadCaptor = ArgumentCaptor.forClass(String.class); ArgumentCaptor fromCaptor = ArgumentCaptor.forClass(String.class); ArgumentCaptor toCaptor = ArgumentCaptor.forClass(String.class); - doNothing().when(api).sendMessage(fromCaptor.capture(), toCaptor.capture(), payloadCaptor.capture()); + doThrow(new ApiException(errorMessage)).doNothing().when(api).sendMessage(fromCaptor.capture(), toCaptor.capture(), payloadCaptor.capture()); // Test that phone number input gets cleaned up final String channelPayload = "{\"phone_number\":\"+49 765 4321 \",\"name\":\"Blips and Chitz\"}"; @@ -123,10 +131,9 @@ void canSendMessages() throws Exception { .build()) )); - TimeUnit.SECONDS.sleep(5); - - kafkaTestHelper.produceRecord(new ProducerRecord<>(applicationCommunicationMessages.name(), messageId, - Message.newBuilder() + kafkaTestHelper.produceRecords(List.of( + // This message should fail + new ProducerRecord<>(applicationCommunicationMessages.name(), messageId, Message.newBuilder() .setId(messageId) .setSentAt(Instant.now().toEpochMilli()) .setSenderId("user-id") @@ -136,13 +143,32 @@ void canSendMessages() throws Exception { .setChannelId(channelId) .setSource("twilio.sms") .setContent(payload) - .build()) - ); + .build()), + new ProducerRecord<>(applicationCommunicationMessages.name(), messageId, + Message.newBuilder() + .setId(messageId) + .setSentAt(Instant.now().toEpochMilli()) + .setSenderId("user-id") + .setIsFromContact(false) + .setDeliveryState(DeliveryState.PENDING) + .setConversationId(conversationId) + .setChannelId(channelId) + .setSource("twilio.sms") + .setContent(payload) + .build()) + )); retryOnException(() -> { assertEquals(payload, payloadCaptor.getValue()); assertEquals(sourceConversationId, toCaptor.getValue()); assertEquals(sourceChannelId, fromCaptor.getValue()); }, "Twilio API was not called"); + + final List metadataList = kafkaTestHelper.consumeValues(2, applicationCommunicationMetadata.name()); + + assertThat(metadataList.size(), equalTo(2)); + assertThat(metadataList.stream().anyMatch((metadata) -> + metadata.getKey().equals(MetadataKeys.MessageKeys.ERROR) + && metadata.getValue().equals(errorMessage)), equalTo(true)); } } diff --git a/backend/sources/viber/connector/src/main/java/co/airy/core/sources/viber/Connector.java b/backend/sources/viber/connector/src/main/java/co/airy/core/sources/viber/Connector.java index 3277c26bd7..ddadd5a1e4 100644 --- a/backend/sources/viber/connector/src/main/java/co/airy/core/sources/viber/Connector.java +++ b/backend/sources/viber/connector/src/main/java/co/airy/core/sources/viber/Connector.java @@ -60,10 +60,10 @@ public List> sendMessage(SendMessageRequest return List.of(KeyValue.pair(message.getId(), message), KeyValue.pair(getId(metadata).toString(), metadata)); } catch (Exception e) { log.error(String.format("Failed to send a message to viber \n SendMessageRequest: %s", sendMessageRequest), e); + final Metadata metadata = newMessageMetadata(message.getId(), MetadataKeys.MessageKeys.ERROR, e.getMessage()); + updateDeliveryState(message, DeliveryState.FAILED); + return List.of(KeyValue.pair(message.getId(), message), KeyValue.pair(getId(metadata).toString(), metadata)); } - - updateDeliveryState(message, DeliveryState.FAILED); - return List.of(KeyValue.pair(message.getId(), message)); } private boolean isMessageStale(Message message) { diff --git a/backend/sources/viber/connector/src/main/java/co/airy/core/sources/viber/EventsRouter.java b/backend/sources/viber/connector/src/main/java/co/airy/core/sources/viber/EventsRouter.java index 9207d7e762..76206a2c1a 100644 --- a/backend/sources/viber/connector/src/main/java/co/airy/core/sources/viber/EventsRouter.java +++ b/backend/sources/viber/connector/src/main/java/co/airy/core/sources/viber/EventsRouter.java @@ -8,6 +8,7 @@ import com.viber.bot.Request; import com.viber.bot.event.incoming.IncomingConversationStartedEvent; import com.viber.bot.event.incoming.IncomingDeliveredEvent; +import com.viber.bot.event.incoming.IncomingErrorEvent; import com.viber.bot.event.incoming.IncomingMessageEvent; import com.viber.bot.event.incoming.IncomingSeenEvent; import com.viber.bot.profile.UserProfile; @@ -93,6 +94,13 @@ public List> onEvent(String key, String payload return List.of(KeyValue.pair(getId(metadata).toString(), metadata)); } + case ERROR: { + final IncomingErrorEvent event = (IncomingErrorEvent) request.getEvent(); + final String messageId = getMessageId(event.getToken()); + final Metadata metadata = newMessageMetadata(messageId, MetadataKeys.MessageKeys.ERROR, event.getDescription()); + return List.of(KeyValue.pair(getId(metadata).toString(), metadata)); + } + default: return List.of(); } } diff --git a/backend/sources/viber/connector/src/main/java/co/airy/core/sources/viber/services/Api.java b/backend/sources/viber/connector/src/main/java/co/airy/core/sources/viber/services/Api.java index a0436b645e..39a63b0439 100644 --- a/backend/sources/viber/connector/src/main/java/co/airy/core/sources/viber/services/Api.java +++ b/backend/sources/viber/connector/src/main/java/co/airy/core/sources/viber/services/Api.java @@ -4,6 +4,7 @@ import co.airy.core.sources.viber.dto.SendMessageResponse; import co.airy.log.AiryLoggerFactory; import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.JsonNodeFactory; import com.fasterxml.jackson.databind.node.ObjectNode; @@ -11,27 +12,37 @@ import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.context.ApplicationListener; import org.springframework.context.annotation.Bean; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.client.ClientHttpResponse; import org.springframework.lang.Nullable; import org.springframework.stereotype.Component; +import org.springframework.web.client.ResponseErrorHandler; import org.springframework.web.client.RestTemplate; +import java.io.IOException; import java.util.HashMap; import java.util.Map; +import java.util.Optional; @Component -public class Api { +public class Api implements ApplicationListener { private final Logger log = AiryLoggerFactory.getLogger(Api.class); private static final String API_URL = "https://chatapi.viber.com/pa"; - private final RestTemplate restTemplate = new RestTemplate(); + private RestTemplate restTemplate; + private final RestTemplateBuilder restTemplateBuilder; private final ObjectMapper objectMapper; private final HttpHeaders authHeaders; - public Api(@Value("${authToken}") String authToken, @Qualifier("viberObjectMapper") ObjectMapper objectMapper) { + public Api(RestTemplateBuilder restTemplateBuilder, @Value("${authToken}") String authToken, @Qualifier("viberObjectMapper") ObjectMapper objectMapper) { this.objectMapper = objectMapper; + this.restTemplateBuilder = restTemplateBuilder; authHeaders = new HttpHeaders(); authHeaders.set("X-Viber-Auth-Token", authToken); } @@ -81,4 +92,33 @@ public void setWebhook(String webhookUrl) throws Exception { public void removeWebhook() throws Exception { getApiResponse("/set_webhook", objectMapper.writeValueAsString(Map.of("url", ""))); } + + + @Override + public void onApplicationEvent(ApplicationReadyEvent event) { + restTemplate = restTemplateBuilder + .errorHandler(new ResponseErrorHandler() { + @Override + public boolean hasError(ClientHttpResponse response) throws IOException { + return response.getRawStatusCode() != HttpStatus.OK.value(); + } + + @Override + public void handleError(ClientHttpResponse response) throws IOException { + final String errorPayload = new String(response.getBody().readAllBytes()); + final int statusCode = response.getRawStatusCode(); + + final JsonNode jsonNode = objectMapper.readTree(errorPayload); + final String errorMessage = Optional.of(jsonNode.get("status_message")) + .map(JsonNode::textValue) + .orElseGet(() -> { + log.warn("Could not parse error message from response: {}", errorPayload); + return String.format("Api replied with status code %s and payload %s", statusCode, errorPayload); + }); + throw new ApiException(errorMessage, errorPayload); + } + }) + .build(); + } + } diff --git a/backend/sources/viber/connector/src/main/java/co/airy/core/sources/viber/services/ApiException.java b/backend/sources/viber/connector/src/main/java/co/airy/core/sources/viber/services/ApiException.java new file mode 100644 index 0000000000..4737df81ec --- /dev/null +++ b/backend/sources/viber/connector/src/main/java/co/airy/core/sources/viber/services/ApiException.java @@ -0,0 +1,15 @@ +package co.airy.core.sources.viber.services; + +import lombok.Getter; + +public class ApiException extends RuntimeException { + @Getter + private String errorPayload; + public ApiException(String message) { + super(message); + } + public ApiException(String message, String errorPayload) { + super(message); + this.errorPayload = errorPayload; + } +} diff --git a/backend/sources/viber/connector/src/test/java/co/airy/core/sources/viber/ChannelsTest.java b/backend/sources/viber/connector/src/test/java/co/airy/core/sources/viber/ChannelsTest.java index 808d764ef5..cde64917bc 100644 --- a/backend/sources/viber/connector/src/test/java/co/airy/core/sources/viber/ChannelsTest.java +++ b/backend/sources/viber/connector/src/test/java/co/airy/core/sources/viber/ChannelsTest.java @@ -73,7 +73,6 @@ class ChannelsTest { @BeforeAll static void beforeAll() throws Exception { kafkaTestHelper = new KafkaTestHelper(sharedKafkaTestResource, Topics.getTopics()); - kafkaTestHelper.beforeAll(); } @@ -93,7 +92,7 @@ void canConnectChannels() throws Exception { ArgumentCaptor webhookCaptor = ArgumentCaptor.forClass(String.class); doNothing().when(api).setWebhook(webhookCaptor.capture()); doNothing().when(api).removeWebhook(); - Thread.sleep(5000); + final String content = webTestHelper.post("/channels.viber.connect") .andExpect(status().isOk()).andReturn().getResponse().getContentAsString(); final JsonNode jsonNode = new ObjectMapper().readTree(content); diff --git a/backend/sources/viber/connector/src/test/java/co/airy/core/sources/viber/EventsRouterTest.java b/backend/sources/viber/connector/src/test/java/co/airy/core/sources/viber/EventsRouterTest.java index 07ac8fa3b4..692843c5fa 100644 --- a/backend/sources/viber/connector/src/test/java/co/airy/core/sources/viber/EventsRouterTest.java +++ b/backend/sources/viber/connector/src/test/java/co/airy/core/sources/viber/EventsRouterTest.java @@ -5,6 +5,7 @@ import co.airy.core.sources.viber.dto.AccountInfo; import co.airy.core.sources.viber.lib.MockAccountInfo; import co.airy.core.sources.viber.lib.Topics; +import co.airy.core.sources.viber.services.Api; import co.airy.kafka.test.KafkaTestHelper; import co.airy.kafka.test.junit.SharedKafkaTestResource; import co.airy.model.metadata.MetadataRepository; @@ -18,6 +19,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit.jupiter.SpringExtension; @@ -47,6 +49,9 @@ class EventsRouterTest { @Autowired private AccountInfo accountInfo; + @MockBean + private Api api; + @BeforeAll static void beforeAll() throws Exception { testHelper = new KafkaTestHelper(sharedKafkaTestResource, Topics.getTopics()); diff --git a/backend/sources/viber/connector/src/test/java/co/airy/core/sources/viber/SendMessageTest.java b/backend/sources/viber/connector/src/test/java/co/airy/core/sources/viber/SendMessageTest.java index 7ac5359cba..b3b16ad2c9 100644 --- a/backend/sources/viber/connector/src/test/java/co/airy/core/sources/viber/SendMessageTest.java +++ b/backend/sources/viber/connector/src/test/java/co/airy/core/sources/viber/SendMessageTest.java @@ -2,12 +2,15 @@ import co.airy.avro.communication.DeliveryState; import co.airy.avro.communication.Message; +import co.airy.avro.communication.Metadata; import co.airy.core.sources.viber.dto.SendMessageResponse; import co.airy.core.sources.viber.lib.MockAccountInfo; import co.airy.core.sources.viber.lib.Topics; import co.airy.core.sources.viber.services.Api; +import co.airy.core.sources.viber.services.ApiException; import co.airy.kafka.test.KafkaTestHelper; import co.airy.kafka.test.junit.SharedKafkaTestResource; +import co.airy.model.metadata.MetadataKeys; import co.airy.spring.core.AirySpringBootApplication; import org.apache.kafka.clients.producer.ProducerRecord; import org.junit.jupiter.api.AfterAll; @@ -30,6 +33,7 @@ import java.util.List; import java.util.concurrent.TimeUnit; +import static co.airy.model.metadata.MetadataRepository.getSubject; import static co.airy.test.Timing.retryOnException; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.IsEqual.equalTo; @@ -68,13 +72,16 @@ static void afterAll() throws Exception { void sendsMessage() throws Exception { final String conversationId = "conversationId"; final String messageId = "message-id"; + final String failingMessageId = "message-id-failing"; final Long messageToken = 123L; final String sourceConversationId = "9MVsH/2gRPr6pP72Eb6aXw=="; final String content = "{\"type\":\"text\",\"text\":\"Hello\"}"; + final String errorMessage = "failed to deliver message"; ArgumentCaptor receiverCaptor = ArgumentCaptor.forClass(String.class); when(api.sendMessage(receiverCaptor.capture(), Mockito.any(), eq(content))) - .thenReturn(new SendMessageResponse(0, "ok", messageToken)); + .thenReturn(new SendMessageResponse(0, "ok", messageToken)) + .thenThrow(new ApiException(errorMessage)); testHelper.produceRecords(List.of( new ProducerRecord<>(Topics.applicationCommunicationMessages.name(), "other-message-id", @@ -107,8 +114,31 @@ void sendsMessage() throws Exception { .build()) ); + // This message should fail + testHelper.produceRecord(new ProducerRecord<>(Topics.applicationCommunicationMessages.name(), messageId, + Message.newBuilder() + .setId(failingMessageId) + .setSentAt(Instant.now().toEpochMilli()) + .setSenderId("user-id") + .setDeliveryState(DeliveryState.PENDING) + .setConversationId(conversationId) + .setChannelId("channelId") + .setSource("viber") + .setContent(content) + .setIsFromContact(false) + .build()) + ); + retryOnException(() -> { assertThat(receiverCaptor.getValue(), equalTo(sourceConversationId)); }, "Viber API was not called"); + + final List metadataList = testHelper.consumeValues(2, Topics.applicationCommunicationMetadata.name()); + + assertThat(metadataList.size(), equalTo(2)); + assertThat(metadataList.stream().anyMatch((metadata) -> + metadata.getKey().equals(MetadataKeys.MessageKeys.ERROR) + && metadata.getValue().equals(errorMessage) + && getSubject(metadata).getIdentifier().equals(failingMessageId)), equalTo(true)); } } diff --git a/backend/sources/viber/connector/src/test/java/co/airy/core/sources/viber/WebhookControllerTest.java b/backend/sources/viber/connector/src/test/java/co/airy/core/sources/viber/WebhookControllerTest.java index ce4dbc2fd7..83a22c0914 100644 --- a/backend/sources/viber/connector/src/test/java/co/airy/core/sources/viber/WebhookControllerTest.java +++ b/backend/sources/viber/connector/src/test/java/co/airy/core/sources/viber/WebhookControllerTest.java @@ -3,6 +3,7 @@ import co.airy.core.sources.viber.dto.AccountInfo; import co.airy.core.sources.viber.lib.MockAccountInfo; import co.airy.core.sources.viber.lib.Topics; +import co.airy.core.sources.viber.services.Api; import co.airy.kafka.test.KafkaTestHelper; import co.airy.kafka.test.junit.SharedKafkaTestResource; import co.airy.spring.core.AirySpringBootApplication; @@ -15,6 +16,7 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; import org.springframework.http.MediaType; import org.springframework.test.context.TestPropertySource; @@ -48,6 +50,9 @@ class WebhookControllerTest { @Autowired private AccountInfo accountInfo; + @MockBean + private Api api; + @Value("${authToken}") private String authToken; diff --git a/docs/docs/api/endpoints/google-messages-send.mdx b/docs/docs/api/endpoints/google-messages-send.mdx deleted file mode 100644 index 1ddfa30ca7..0000000000 --- a/docs/docs/api/endpoints/google-messages-send.mdx +++ /dev/null @@ -1,46 +0,0 @@ -`POST /messages.send` - -Sends a message to a conversation from Google's Business Messages source and returns a payload. - -Note that when you send a message from this source, you must specify a representative, the individual or automation that composed the message. -Google's Business Messages supports "BOT" and "HUMAN" as the `representativeType`. Find more information on [Google's Business Messages guide](https://developers.google.com/business-communications/business-messages/guides/build/send). - -Whatever is put on the `message` field will be forwarded "as-is" to the source's message endpoint. - -**Sending a text message** - -```json5 -{ - conversation_id: 'a688d36c-a85e-44af-bc02-4248c2c97622', - message: { - text: 'Hello!', - representative: { - representativeType: 'HUMAN', - }, - }, -} -``` - -**Sample response** - -```json5 -{ - "id": '{UUID}', - "content": { - "text": 'Hello!', - "representative": { - "representativeType": 'HUMAN', - }, - }, - "delivery_state": 'pending|failed|delivered', - "from_contact": 'true|false', - // See glossary - "sent_at": '{string}', - //'yyyy-MM-dd'T'HH:mm:ss.SSSZ' date in UTC form, to be localized by clients - "source": 'google', - "metadata": { - "sentFrom": 'iPhone', - }, - // metadata object of the message -} -``` diff --git a/docs/docs/api/endpoints/messages.md b/docs/docs/api/endpoints/messages.md index 4df7925b72..21bf373a87 100644 --- a/docs/docs/api/endpoints/messages.md +++ b/docs/docs/api/endpoints/messages.md @@ -93,7 +93,7 @@ to see learn how to send text, media, and many more message types. } ``` -**Starting a conversation** +### Starting a conversation The previous flow covers the use cases of most messaging sources. However, some sources such as SMS or Whatsapp also allow you to send messages to contacts that did not previously message you first. This means that you can create new conversations with them if you know their `source recipient id`. @@ -116,7 +116,7 @@ allow you to send messages to contacts that did not previously message you first { "id": "{UUID}", "content": "{\"text\":\"Hello world\"}", // Source specific body - "state": "pending|failed|delivered", + "state": "pending", "from_contact": true, "sent_at": "{string}", "source": "{String}", @@ -124,11 +124,50 @@ allow you to send messages to contacts that did not previously message you first } ``` -## Send from Google's Business Messages source +### Error handling + +Since outbound messages are delegated to the source apps we don't implement synchronous error replies. Instead, errors are sent +asynchronously as updates to the message's metadata. Those updates can be consumed with either the [webhook](/api/webhook) or the [websocket](/api/websocket). + +The message's state will also change to `failed`. Both changes will be visible in the conversation and messages query endpoints. -import GoogleMessagesSend from './google-messages-send.mdx' +**Sample metadata websocket update** + +```json5 +{ + "type": "metadata.updated", + "payload": { + "subject": "message", + "identifier": "failed message id", + "metadata": { + "error": "Some error message" + } + } +} +``` - +**Sample message webhook update** + +```json5 +{ + "type": "message.updated", + "payload": { + "conversation_id": "conversation id", + "channel_id": "channel id", + "message": { + "id": "failed message id", + "content": {"text": "Hello World"}, // source message payload + "delivery_state": "failed", + "from_contact": false, + "sent_at": "2020-10-25T21:24:54.560Z", // ISO 8601 date string + "source": "facebook", // messaging source + "metadata": { + "error": "Some error message" + } + } + } +} +``` ## Suggest replies diff --git a/docs/docs/changelog.md b/docs/docs/changelog.md index ab4751d5ea..87d132973a 100644 --- a/docs/docs/changelog.md +++ b/docs/docs/changelog.md @@ -1,14 +1,58 @@ ---- -title: Changelog -sidebar_label: 📝 Changelog ---- - -## 0.36.1 +--- +title: Changelog +sidebar_label: 📝 Changelog +--- + +## 0.37.0 + +#### Changes + +- [[#2653](https://github.com/airyhq/airy/issues/2653)] upgrade slf4j and underlying log4j libraries [[#2654](https://github.com/airyhq/airy/pull/2654)] + +#### 🚀 Features + +- [[#2586](https://github.com/airyhq/airy/issues/2586)] Set min and max size for EKS node group [[#2648](https://github.com/airyhq/airy/pull/2648)] +- [[#2586](https://github.com/airyhq/airy/issues/2586)] Fix terraform [[#2643](https://github.com/airyhq/airy/pull/2643)] +- [[#2611](https://github.com/airyhq/airy/issues/2611)] Session timeout [[#2630](https://github.com/airyhq/airy/pull/2630)] + +#### 🐛 Bug Fixes + +- [[#2660](https://github.com/airyhq/airy/issues/2660)] Refactor fargate\_profiles in AWS EKS [[#2661](https://github.com/airyhq/airy/pull/2661)] +- [[#2649](https://github.com/airyhq/airy/issues/2649)] ui fix instagram messages [[#2650](https://github.com/airyhq/airy/pull/2650)] +- [[#2637](https://github.com/airyhq/airy/issues/2637)] Fix channel prefill [[#2639](https://github.com/airyhq/airy/pull/2639)] + +#### 📚 Documentation + +- [[#2550](https://github.com/airyhq/airy/issues/2550)] Docs for multiple providers using helm [[#2597](https://github.com/airyhq/airy/pull/2597)] + +#### 🧰 Maintenance + +- Bump terser-webpack-plugin from 5.2.4 to 5.2.5 [[#2655](https://github.com/airyhq/airy/pull/2655)] +- Bump prettier from 2.4.1 to 2.5.1 [[#2635](https://github.com/airyhq/airy/pull/2635)] +- Bump algoliasearch-helper from 3.4.4 to 3.6.2 in /docs [[#2614](https://github.com/airyhq/airy/pull/2614)] +- Bump @babel/preset-typescript from 7.15.0 to 7.16.0 [[#2599](https://github.com/airyhq/airy/pull/2599)] +- Bump webpack from 5.59.1 to 5.64.4 [[#2620](https://github.com/airyhq/airy/pull/2620)] + +#### Airy CLI + +You can download the Airy CLI for your operating system from the following links: + +[MacOS](https://airy-core-binaries.s3.amazonaws.com/0.36.2/darwin/amd64/airy) +[Linux](https://airy-core-binaries.s3.amazonaws.com/0.36.2/linux/amd64/airy) +[Windows](https://airy-core-binaries.s3.amazonaws.com/0.36.2/windows/amd64/airy.exe) + +## 0.36.1 + +#### Changes #### 🐛 Bug Fixes - [[#2434](https://github.com/airyhq/airy/issues/2434)] Fix instagram ingestion race condition [[#2625](https://github.com/airyhq/airy/pull/2625)] +#### 🧰 Maintenance + +- Bump webpack from 5.59.1 to 5.64.4 [[#2620](https://github.com/airyhq/airy/pull/2620)] + #### Airy CLI You can download the Airy CLI for your operating system from the following links: @@ -16,9 +60,9 @@ You can download the Airy CLI for your operating system from the following links [MacOS](https://airy-core-binaries.s3.amazonaws.com/0.36.1/darwin/amd64/airy) [Linux](https://airy-core-binaries.s3.amazonaws.com/0.36.1/linux/amd64/airy) [Windows](https://airy-core-binaries.s3.amazonaws.com/0.36.1/windows/amd64/airy.exe) - -## 0.36.0 - + +## 0.36.0 + #### 🚀 Features - [[#2604](https://github.com/airyhq/airy/issues/2604)] Added attachment toggle for chatplugin [[#2609](https://github.com/airyhq/airy/pull/2609)] @@ -39,9 +83,9 @@ You can download the Airy CLI for your operating system from the following links [MacOS](https://airy-core-binaries.s3.amazonaws.com/0.36.0/darwin/amd64/airy) [Linux](https://airy-core-binaries.s3.amazonaws.com/0.36.0/linux/amd64/airy) [Windows](https://airy-core-binaries.s3.amazonaws.com/0.36.0/windows/amd64/airy.exe) - -## 0.35.1 - + +## 0.35.1 + #### 🚀 Features - [[#2586](https://github.com/airyhq/airy/issues/2586)] Terraform core module has wrong Helm link [[#2587](https://github.com/airyhq/airy/pull/2587)] @@ -71,9 +115,9 @@ You can download the Airy CLI for your operating system from the following links [MacOS](https://airy-core-binaries.s3.amazonaws.com/0.35.1/darwin/amd64/airy) [Linux](https://airy-core-binaries.s3.amazonaws.com/0.35.1/linux/amd64/airy) [Windows](https://airy-core-binaries.s3.amazonaws.com/0.35.1/windows/amd64/airy.exe) - -## 0.35.0 - + +## 0.35.0 + #### 🚀 Features - [[#2455](https://github.com/airyhq/airy/issues/2455)] Custom message colors chatplugin [[#2585](https://github.com/airyhq/airy/pull/2585)] @@ -117,9 +161,9 @@ You can download the Airy CLI for your operating system from the following links [MacOS](https://airy-core-binaries.s3.amazonaws.com/0.35.0/darwin/amd64/airy) [Linux](https://airy-core-binaries.s3.amazonaws.com/0.35.0/linux/amd64/airy) [Windows](https://airy-core-binaries.s3.amazonaws.com/0.35.0/windows/amd64/airy.exe) - -## 0.34.0 - + +## 0.34.0 + #### Changes #### 🚀 Features @@ -152,9 +196,9 @@ You can download the Airy CLI for your operating system from the following links [MacOS](https://airy-core-binaries.s3.amazonaws.com/0.34.0/darwin/amd64/airy) [Linux](https://airy-core-binaries.s3.amazonaws.com/0.34.0/linux/amd64/airy) [Windows](https://airy-core-binaries.s3.amazonaws.com/0.34.0/windows/amd64/airy.exe) - -## 0.33.0 - + +## 0.33.0 + #### 🚀 Features - [[#2294](https://github.com/airyhq/airy/issues/2294)] Allow creating conversations with Twilio [[#2500](https://github.com/airyhq/airy/pull/2500)] @@ -223,9 +267,9 @@ This release has breaking changes in the structure of the airy.yaml file. When u from the version of the CLI and the - namespace is used from the workspace file cli.yaml - Rename the `ingress:` section to `ingress-controller:` - -## 0.32.0 - + +## 0.32.0 + #### Changes #### 🚀 Features @@ -251,9 +295,9 @@ You can download the Airy CLI for your operating system from the following links [MacOS](https://airy-core-binaries.s3.amazonaws.com/0.32.0/darwin/amd64/airy) [Linux](https://airy-core-binaries.s3.amazonaws.com/0.32.0/linux/amd64/airy) [Windows](https://airy-core-binaries.s3.amazonaws.com/0.32.0/windows/amd64/airy.exe) - -## 0.31.1 - + +## 0.31.1 + #### 🚀 Features - [[#2432](https://github.com/airyhq/airy/issues/2432)] Added more options to the UI [[#2433](https://github.com/airyhq/airy/pull/2433)] @@ -275,9 +319,9 @@ You can download the Airy CLI for your operating system from the following links [MacOS](https://airy-core-binaries.s3.amazonaws.com/0.31.1/darwin/amd64/airy) [Linux](https://airy-core-binaries.s3.amazonaws.com/0.31.1/linux/amd64/airy) [Windows](https://airy-core-binaries.s3.amazonaws.com/0.31.1/windows/amd64/airy.exe) - -## 0.31.0 - + +## 0.31.0 + #### 🚀 Features - [[#628](https://github.com/airyhq/airy/issues/628)] Make library compatible with node.js [[#2426](https://github.com/airyhq/airy/pull/2426)] @@ -317,9 +361,9 @@ You can download the Airy CLI for your operating system from the following links #### Upgrade notes In the `airy.yaml` file, `host` is moved from the `kubernetes` section, into the `ingress` section. - -## 0.29.0 - + +## 0.29.0 + #### Changes - [[#2304](https://github.com/airyhq/airy/issues/2304)] Fixed broken link from Messages Send Section to Sources. [[#2307](https://github.com/airyhq/airy/pull/2307)] @@ -367,9 +411,9 @@ You can download the Airy CLI for your operating system from the following links [MacOS](https://airy-core-binaries.s3.amazonaws.com/0.29.0/darwin/amd64/airy) [Linux](https://airy-core-binaries.s3.amazonaws.com/0.29.0/linux/amd64/airy) [Windows](https://airy-core-binaries.s3.amazonaws.com/0.29.0/windows/amd64/airy.exe) - -## 0.30.0 - + +## 0.30.0 + #### 🚀 Features - [[#2274](https://github.com/airyhq/airy/issues/2274)] Introduce the source API [[#2327](https://github.com/airyhq/airy/pull/2327)] @@ -418,9 +462,9 @@ You can download the Airy CLI for your operating system from the following links [MacOS](https://airy-core-binaries.s3.amazonaws.com/0.30.0/darwin/amd64/airy) [Linux](https://airy-core-binaries.s3.amazonaws.com/0.30.0/linux/amd64/airy) [Windows](https://airy-core-binaries.s3.amazonaws.com/0.30.0/windows/amd64/airy.exe) - -## 0.28.0 - + +## 0.28.0 + #### 🚀 Features - [[#1911](https://github.com/airyhq/airy/issues/1911)] Reorganize the helm charts [[#2241](https://github.com/airyhq/airy/pull/2241)] @@ -458,21 +502,21 @@ You can download the Airy CLI for your operating system from the following links [MacOS](https://airy-core-binaries.s3.amazonaws.com/0.28.0/darwin/amd64/airy) [Linux](https://airy-core-binaries.s3.amazonaws.com/0.28.0/linux/amd64/airy) [Windows](https://airy-core-binaries.s3.amazonaws.com/0.28.0/windows/amd64/airy.exe) - -## Hotfix 0.27.1 - -[[#2219](https://github.com/airyhq/airy/issues/2219)] fixed inbox ui overflow bug [[#2220](https://github.com/airyhq/airy/pull/2220)] -## Hotfix 0.26.3 - -[[#2192](https://github.com/airyhq/airy/issues/2192)] Inbox crashing when selecting conversations in filtered view [[#2193](https://github.com/airyhq/airy/pull/2193)] -## Hotfix 0.26.2 - -[[#2187](https://github.com/airyhq/airy/issues/2187)] Hotfix chat plugin async bundle loading failed on installed websites -## Hotfix 0.26.1 - -[[#2181](https://github.com/airyhq/airy/issues/2181)] Fixes chat plugin integration crashing with empty config -## 0.27.0 - + +## Hotfix 0.27.1 + +[[#2219](https://github.com/airyhq/airy/issues/2219)] fixed inbox ui overflow bug [[#2220](https://github.com/airyhq/airy/pull/2220)] +## Hotfix 0.26.3 + +[[#2192](https://github.com/airyhq/airy/issues/2192)] Inbox crashing when selecting conversations in filtered view [[#2193](https://github.com/airyhq/airy/pull/2193)] +## Hotfix 0.26.2 + +[[#2187](https://github.com/airyhq/airy/issues/2187)] Hotfix chat plugin async bundle loading failed on installed websites +## Hotfix 0.26.1 + +[[#2181](https://github.com/airyhq/airy/issues/2181)] Fixes chat plugin integration crashing with empty config +## 0.27.0 + #### Changes #### 🚀 Features @@ -515,9 +559,9 @@ You can download the Airy CLI for your operating system from the following links [MacOS](https://airy-core-binaries.s3.amazonaws.com/0.27.0/darwin/amd64/airy) [Linux](https://airy-core-binaries.s3.amazonaws.com/0.27.0/linux/amd64/airy) [Windows](https://airy-core-binaries.s3.amazonaws.com/0.27.0/windows/amd64/airy.exe) - -## 0.26.0 - + +## 0.26.0 + #### Changes - Change endpoint for webhook to /twilio [[#2123](https://github.com/airyhq/airy/pull/2123)] @@ -585,9 +629,9 @@ You can download the Airy CLI for your operating system from the following links [MacOS](https://airy-core-binaries.s3.amazonaws.com/0.25.1/darwin/amd64/airy) [Linux](https://airy-core-binaries.s3.amazonaws.com/0.25.1/linux/amd64/airy) [Windows](https://airy-core-binaries.s3.amazonaws.com/0.25.1/windows/amd64/airy.exe) - -## 0.25.0 - + +## 0.25.0 + #### 🚀 Features - [[#1752](https://github.com/airyhq/airy/issues/1752)] Add connect cluster chart [[#1961](https://github.com/airyhq/airy/pull/1961)] @@ -629,13 +673,13 @@ You can download the Airy CLI for your operating system from the following links [MacOS](https://airy-core-binaries.s3.amazonaws.com/0.24.1/darwin/amd64/airy) [Linux](https://airy-core-binaries.s3.amazonaws.com/0.24.1/linux/amd64/airy) [Windows](https://airy-core-binaries.s3.amazonaws.com/0.24.1/windows/amd64/airy.exe) - -## 0.23.1 Hotfix - + +## 0.23.1 Hotfix + [[#1921](https://github.com/airyhq/airy/issues/1921)] Hotfix: Facebook echo ingestion [[#1922](https://github.com/airyhq/airy/issues/1922)] - -## 0.24.0 - + +## 0.24.0 + #### Changes - [[#1956](https://github.com/airyhq/airy/issues/1956)] Fix link to installation page [[#1957](https://github.com/airyhq/airy/pull/1957)] @@ -688,9 +732,9 @@ You can download the Airy CLI for your operating system from the following links [MacOS](https://airy-core-binaries.s3.amazonaws.com/0.24.0/darwin/amd64/airy) [Linux](https://airy-core-binaries.s3.amazonaws.com/0.24.0/linux/amd64/airy) [Windows](https://airy-core-binaries.s3.amazonaws.com/0.24.0/windows/amd64/airy.exe) - -## 0.23.0 - + +## 0.23.0 + #### 🚀 Features - [[#1815](https://github.com/airyhq/airy/issues/1815)] Added emptyState for filtered items [[#1874](https://github.com/airyhq/airy/pull/1874)] @@ -762,9 +806,9 @@ You can download the Airy CLI for your operating system from the following links [MacOS](https://airy-core-binaries.s3.amazonaws.com/0.23.0/darwin/amd64/airy) [Linux](https://airy-core-binaries.s3.amazonaws.com/0.23.0/linux/amd64/airy) [Windows](https://airy-core-binaries.s3.amazonaws.com/0.23.0/windows/amd64/airy.exe) - -## 0.22.0 - + +## 0.22.0 + #### 🚀 Features - [[#1743](https://github.com/airyhq/airy/issues/1743)] Return proper status code for unauthorized access [[#1785](https://github.com/airyhq/airy/pull/1785)] @@ -804,9 +848,9 @@ You can download the Airy CLI for your operating system from the following links [MacOS](https://airy-core-binaries.s3.amazonaws.com/0.22.0/darwin/amd64/airy) [Linux](https://airy-core-binaries.s3.amazonaws.com/0.22.0/linux/amd64/airy) [Windows](https://airy-core-binaries.s3.amazonaws.com/0.22.0/windows/amd64/airy.exe) - -## 0.21.0 - + +## 0.21.0 + #### Changes - [[#1750](https://github.com/airyhq/airy/issues/1750)] Fix tags filter [[#1765](https://github.com/airyhq/airy/pull/1765)] @@ -861,9 +905,9 @@ You can download the Airy CLI for your operating system from the following links [MacOS](https://airy-core-binaries.s3.amazonaws.com/0.21.0/darwin/amd64/airy) [Linux](https://airy-core-binaries.s3.amazonaws.com/0.21.0/linux/amd64/airy) [Windows](https://airy-core-binaries.s3.amazonaws.com/0.21.0/windows/amd64/airy.exe) - -## 0.20.0 - + +## 0.20.0 + #### Changes - Bump @types/react from 16.9.34 to 17.0.4 [[#1658](https://github.com/airyhq/airy/pull/1658)] @@ -914,9 +958,9 @@ You can download the Airy CLI for your operating system from the following links [MacOS](https://airy-core-binaries.s3.amazonaws.com/0.20.0/darwin/amd64/airy) [Linux](https://airy-core-binaries.s3.amazonaws.com/0.20.0/linux/amd64/airy) [Windows](https://airy-core-binaries.s3.amazonaws.com/0.20.0/windows/amd64/airy.exe) - -## 0.19.0 - + +## 0.19.0 + #### Changes #### 🚀 Features @@ -969,9 +1013,9 @@ You can download the Airy CLI for your operating system from the following links [MacOS](https://airy-core-binaries.s3.amazonaws.com/0.19.0/darwin/amd64/airy) [Linux](https://airy-core-binaries.s3.amazonaws.com/0.19.0/linux/amd64/airy) [Windows](https://airy-core-binaries.s3.amazonaws.com/0.19.0/windows/amd64/airy.exe) - -## 0.18.0 - + +## 0.18.0 + #### 🚀 Features - [[#1524](https://github.com/airyhq/airy/issues/1524)] Added conversationState to conversationList [[#1560](https://github.com/airyhq/airy/pull/1560)] - [[#1515](https://github.com/airyhq/airy/issues/1515)] Create airy chat plugin library + use it in UI [[#1550](https://github.com/airyhq/airy/pull/1550)] @@ -1020,9 +1064,9 @@ You can download the Airy CLI for your operating system from the following links You can download the Airy CLI for your operating system from the following links: [MacOS](https://airy-core-binaries.s3.amazonaws.com/0.18.0/darwin/amd64/airy) [Linux](https://airy-core-binaries.s3.amazonaws.com/0.18.0/linux/amd64/airy) -[Windows](https://airy-core-binaries.s3.amazonaws.com/0.18.0/windows/amd64/airy.exe) -## 0.17.0 - +[Windows](https://airy-core-binaries.s3.amazonaws.com/0.18.0/windows/amd64/airy.exe) +## 0.17.0 + #### 🚀 Features - [[#929](https://github.com/airyhq/airy/issues/929)] Implement the option to end chat [[#1508](https://github.com/airyhq/airy/pull/1508)] @@ -1072,9 +1116,9 @@ You can download the Airy CLI for your operating system from the following links [MacOS](https://airy-core-binaries.s3.amazonaws.com/0.17.0/darwin/amd64/airy) [Linux](https://airy-core-binaries.s3.amazonaws.com/0.17.0/linux/amd64/airy) [Windows](https://airy-core-binaries.s3.amazonaws.com/0.17.0/windows/amd64/airy.exe) - -## 0.16.0 - + +## 0.16.0 + #### 🚀 Features - [[#1111](https://github.com/airyhq/airy/issues/1111)] Customize Chat Plugin [[#1456](https://github.com/airyhq/airy/pull/1456)] @@ -1126,57 +1170,4 @@ You can download the Airy CLI for your operating system from the following links [MacOS](https://airy-core-binaries.s3.amazonaws.com/0.16.0/darwin/amd64/airy) [Linux](https://airy-core-binaries.s3.amazonaws.com/0.16.0/linux/amd64/airy) [Windows](https://airy-core-binaries.s3.amazonaws.com/0.16.0/windows/amd64/airy.exe) - -## 0.15.1 Hotfix - -- [[#1427](https://github.com/airyhq/airy/issues/1427)] Fix broken UI pod config for AWS deployment - -## 0.15.0 - -#### 🚀 Features - -- [[#1299](https://github.com/airyhq/airy/issues/1299)] Video Fallback for the render library [[#1412](https://github.com/airyhq/airy/pull/1412)] -- [[#1357](https://github.com/airyhq/airy/issues/1357)] Rename the draft release with release.sh [[#1394](https://github.com/airyhq/airy/pull/1394)] -- [[#1018](https://github.com/airyhq/airy/issues/1018)] Introduce aws provider for airy create [[#1240](https://github.com/airyhq/airy/pull/1240)] -- [[#1182](https://github.com/airyhq/airy/issues/1182)] Added gifs and image to supported message types chat plugin [[#1365](https://github.com/airyhq/airy/pull/1365)] -- [[#1326](https://github.com/airyhq/airy/issues/1326)] Move Carousel to components lib [[#1364](https://github.com/airyhq/airy/pull/1364)] -- [[#1097](https://github.com/airyhq/airy/issues/1097)] Allow users to fetch a chat plugin resume token [[#1350](https://github.com/airyhq/airy/pull/1350)] -- [[#1325](https://github.com/airyhq/airy/issues/1325)] Move ListenOutsideClick to component lib [[#1345](https://github.com/airyhq/airy/pull/1345)] - -#### 🐛 Bug Fixes - -- [[#1392](https://github.com/airyhq/airy/issues/1392)] Cypress testing for filtering is false positive [[#1402](https://github.com/airyhq/airy/pull/1402)] -- [[#1097](https://github.com/airyhq/airy/issues/1097)] Fix CORS issue introduced by PR #1350 [[#1371](https://github.com/airyhq/airy/pull/1371)] -- [[#1369](https://github.com/airyhq/airy/issues/1369)] Improved filtering for channels [[#1375](https://github.com/airyhq/airy/pull/1375)] - -#### 📚 Documentation - -- [[#1363](https://github.com/airyhq/airy/issues/1363)] Added suggested replies doc [[#1381](https://github.com/airyhq/airy/pull/1381)] -- [[#1355](https://github.com/airyhq/airy/issues/1355)] Add debugging advices to all sources [[#1368](https://github.com/airyhq/airy/pull/1368)] -- [[#1318](https://github.com/airyhq/airy/issues/1318)] Improve components page [[#1360](https://github.com/airyhq/airy/pull/1360)] - -#### 🧰 Maintenance - -- [[#1045](https://github.com/airyhq/airy/issues/1045)] Automated testing of the web socket [[#1382](https://github.com/airyhq/airy/pull/1382)] -- Move CLI to root [[#1401](https://github.com/airyhq/airy/pull/1401)] -- Bump @babel/core from 7.8.4 to 7.13.10 [[#1186](https://github.com/airyhq/airy/pull/1186)] -- Bump webpack from 4.46.0 to 5.27.2 [[#1352](https://github.com/airyhq/airy/pull/1352)] -- Minor tweaks to titles and paragraphs [[#1379](https://github.com/airyhq/airy/pull/1379)] -- Bump @typescript-eslint/eslint-plugin from 4.18.0 to 4.19.0 [[#1376](https://github.com/airyhq/airy/pull/1376)] -- Bump css-loader from 5.1.3 to 5.2.0 [[#1378](https://github.com/airyhq/airy/pull/1378)] -- Bump html-webpack-plugin from 4.5.2 to 5.3.1 [[#1372](https://github.com/airyhq/airy/pull/1372)] -- Bump @bazel/typescript from 3.2.2 to 3.2.3 [[#1374](https://github.com/airyhq/airy/pull/1374)] -- Bump sass-loader from 10.1.1 to 11.0.1 [[#1373](https://github.com/airyhq/airy/pull/1373)] -- Bump copy-webpack-plugin from 6.4.1 to 8.1.0 [[#1366](https://github.com/airyhq/airy/pull/1366)] -- Bump eslint-plugin-react from 7.22.0 to 7.23.0 [[#1339](https://github.com/airyhq/airy/pull/1339)] -- Bump webpack from 5.27.2 to 5.28.0 [[#1361](https://github.com/airyhq/airy/pull/1361)] -- Update the release process [[#1358](https://github.com/airyhq/airy/pull/1358)] - -#### Airy CLI - -You can download the Airy CLI for your operating system from the following links: - -[MacOS](https://airy-core-binaries.s3.amazonaws.com/0.15.0/darwin/amd64/airy) -[Linux](https://airy-core-binaries.s3.amazonaws.com/0.15.0/linux/amd64/airy) -[Windows](https://airy-core-binaries.s3.amazonaws.com/0.15.0/windows/amd64/airy.exe) - + diff --git a/docs/docs/getting-started/installation/helm.md b/docs/docs/getting-started/installation/helm.md index 6f66a55688..90741ee47a 100644 --- a/docs/docs/getting-started/installation/helm.md +++ b/docs/docs/getting-started/installation/helm.md @@ -4,8 +4,11 @@ sidebar_label: Helm --- import TLDR from "@site/src/components/TLDR"; +import useBaseUrl from '@docusaurus/useBaseUrl'; import ButtonBox from "@site/src/components/ButtonBox"; import DiamondSVG from "@site/static/icons/diamond.svg"; +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; Deploy Airy with Helm, on an existing Kubernetes cluster. @@ -16,28 +19,194 @@ an already existing Kubernetes cluster [Helm](https://helm.sh/). ## Prerequisites -You would need an existing Kubernetes cluster and administrative access to it. For the purpose of this guide, we will use a Google (GKE) Kubernetes cluster. -The cluster that we will create will have two nodes with 2cpu and 16GB RAM each. +### Kubernetes + +You would need an existing Kubernetes cluster and administrative access to it. The size of the cluster depends on the number of connected sources and the number of messages flowing through the `Airy Core` platform. It is important that the pods are running stable and that they are not restarting. You can start with a simple setup of `two nodes`, each of them with `4 vCPUs` and `8GB RAM`. After that you can add or remove computing resources, so that the cluster is not under or over provisioned. + +In case you are not sure how to create a Kubernetes cluster, here is a small guide on setting up Kubernetes in different environments: + + + + + +For creating a Kubernetes cluster in Google, you can use either the [Google cloud dashboard](https://console.cloud.google.com) or the [gcloud](https://cloud.google.com/sdk/docs/install) command line tool which is part of the Google SDK. + +After you install the Google SDK and you have setup your Google account, you can create a Kubernetes cluster with one command: ```sh gcloud container clusters create awesomechat --num-nodes=2 --machine-type=e2-standard-4 ``` -You will also need the [Helm](https://helm.sh/docs/intro/quickstart/) and [Kubectl](https://kubernetes.io/docs/tasks/tools/) binaries, locally on your machine. +The command will also update your `kubeconfig` file. + +For more information refer to the [official Google Guide](https://cloud.google.com/kubernetes-engine/docs/quickstart) + + + + + +For creating a Kubernetes cluster on Microsoft Azure, you can use the [Microsoft Azure Portal](https://portal.azure.com), the [Azure PowerShell utility](https://docs.microsoft.com/en-us/powershell/azure/get-started-azureps) or the [Azure CLI](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli). + +The simplest way to create the cluster is using the Microsoft Azure Portal. Navigate to the `Kubernetes services` dashboard and click on `Create` -> `Create a Kubernetes cluster`. + +On the following screen make sure that you: + +- Select the default resource group or create a new one. +- Fill in the name of the cluster (ex. awesomechat). +- Select the number of nodes. + +Azure portal - Kubernetes services + +After the cluster is created, you can use the `az` Azure CLI to setup access to the cluster: + +```sh +az login +az aks list +az aks get-credentials --resource-group DefaultResourceGroup-EUS --name awesomechat +``` + +The last command will update your `kubeconfig` file with the proper credentials. + +For more information refer to the [official Microsoft Guide](https://docs.microsoft.com/en-us/azure/aks/kubernetes-walkthrough). + + + + + +A Kubernetes cluster can be created directly on the [DigitalOcean dashboard](https://cloud.digitalocean.com/kubernetes/clusters) by clicking `Create` -> `Kubernetes`. You can leave all the options default, except for the `Node plan` as the default nodes might be too small for running the `Airy Core` platform. + +DigitalOcean - Create Kubernetes + +After you create the cluster you need to go through a short guided cluster setup. + +DigitalOcean - Setup Kubernetes + +After you complete the setup you can `Download Config File` to save the `kubeconfig` file to your machine. With the `kubeconfig` file you can now access the kubernetes cluster. + +```sh +kubectl --kubeconfig ./awesomechat-kubeconfig.yaml get pods +``` + +For more information refer to the [official DigitalOcean Guide](https://docs.digitalocean.com/products/kubernetes/quickstart/) + + + + + +`Airy Core` can be created on Minikube with predefined settings, using the [Airy CLI](/cli/introduction). However, if you want to create your custom Minikube instance, for example with custom settings for CPU and RAM, you can also do that with the [Minikube](https://minikube.sigs.k8s.io/docs/start/) utility: + +```sh +minikube -p airy start --driver=virtualbox --cpus=4 --memory=7168 --extra-config=apiserver.service-nodeport-range=1-65535 +``` + +The `apiserver.service-nodeport-range` settings is needed if you want to use port 80 on the Minikube VM as the NodePort for the ingress controller service. + +For more information refer to the [official Minikube Documentation](https://minikube.sigs.k8s.io/docs/start/). + + + + + +`Airy Core` can be created on AWS using the [Airy CLI](/cli/introduction). However that approach doesn't allow you to customize parameters other then the `vpcId` and `instanceType`. To be fully in control of the creation of the [Kubernetes cluster on AWS](https://docs.aws.amazon.com/eks/latest/userguide/create-cluster.html) you can use create one using the [AWS Console](https://console.aws.amazon.com/) or the [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html). + +For creating a cluster you first need an AWS IAM Role and a VPC. Export your profile and your region: + +```sh +export AWS_PROFILE=my-aws-profile +export AWS_REGION=my-aws-region +``` + +Create a new AWS IAM Role and attach the appropriate policies: + +```sh +export POLICY='{"Version": "2012-10-17","Statement": [{"Effect": "Allow","Principal": {"Service": "eks.amazonaws.com"},"Action": "sts:AssumeRole"}]}' +``` + +```sh +aws iam create-role --role-name awesomechat --assume-role-policy-document "$POLICY" +``` + +```sh +aws iam attach-role-policy --role-name awesomechat --policy-arn \ + "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy" +``` + +```sh +aws iam attach-role-policy --role-name awesomechat --policy-arn \ + "arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy" +``` + +```sh +aws iam attach-role-policy --role-name awesomechat --policy-arn \ + "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly" +``` + +```sh +aws iam attach-role-policy --role-name awesomechat --policy-arn \ + "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy" +``` + +```sh +ROLE_ARN=$(aws iam get-role --role-name awesomechat --query 'Role.Arn' --output text) +``` -Make sure that you can access the cluster running: +Get the default VPC and the public subnets: ```sh -kubectl get pods -No resources found in default namespace. +VPC_ID=$(aws ec2 describe-vpcs --filters Name=is-default,Values=true --query 'Vpcs[0].VpcId' --output text) +``` + +```sh +SUBNETS=$(aws ec2 describe-subnets --filters Name=vpc-id,Values=${VPC_ID} \ + --query 'Subnets[?MapPublicIpOnLaunch==`true`].SubnetId' --output text | sed 's/\t/,/g') +``` + +You can modify the list of subnets according to your needs, but you must have at least two subnets with the property `MapPublicIpOnLaunch` set to true. + +Then create the Kubernetes cluster with the following command: -helm list -NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION +```sh +aws eks create-cluster --name awesomechat --role-arn ${ROLE_ARN} --resources-vpc-config subnetIds=${SUBNETS} +``` + +To update your `kubeconfig` file run: + +```sh +aws eks update-kubeconfig --name awesomechat --alias awesomechat ``` +For more information refer to the [official AWS Guide](https://docs.aws.amazon.com/eks/latest/userguide/getting-started-console.html). + + + + + +### Command line utilities + +You will also need the [Helm](https://helm.sh/docs/intro/quickstart/) and [Kubectl](https://kubernetes.io/docs/tasks/tools/) binaries, locally on your machine. + ## Install -Deploy Airy Core with the latest version. You can also configure a specific version +:::note + +Before you proceed with the Helm installation, make sure that you are connected to the correct Kubernetes cluster. +If you are not using your default `kubeconfig` file, you need to export an environment variable: + +export KUBECONFIG=./kube.conf + +::: + +Deploy Airy Core with the latest version. You can also configure a specific version. ```sh VERSION=$(curl -L -s https://airy-core-binaries.s3.amazonaws.com/stable.txt) @@ -49,7 +218,7 @@ By default `Airy Core` creates only a HTTP listener and when running in cloud en Get the address of your LoadBalancer: ```sh -kubectl -n kube-system get service ingress-nginx-controller -o jsonpath='{.status.loadBalancer.ingress[0].ip}' +kubectl -n kube-system get service ingress-nginx-controller -o jsonpath='{.status.loadBalancer.ingress[0].*}{"\n"}' ``` Configure your DNS so that your desired hostname points to the IP address of LoadBalancer. In this example we will be using the hostname `awesomechat.airy.co`. diff --git a/docs/docs/sources/google.md b/docs/docs/sources/google.md index 5b061dc048..84d1812424 100644 --- a/docs/docs/sources/google.md +++ b/docs/docs/sources/google.md @@ -116,6 +116,45 @@ import ConnectGoogle from '../api/endpoints/connect-google.mdx' After connecting the source to your instance, you will be able to send messages through the [Messages endpoint](/api/endpoints/messages#send). -import GoogleMessagesSend from '../api/endpoints/google-messages-send.mdx' +Note that when you send a message from this source, you must specify a representative, the individual or automation that composed the message. +Google's Business Messages supports "BOT" and "HUMAN" as the `representativeType`. Find more information on [Google's Business Messages guide](https://developers.google.com/business-communications/business-messages/guides/build/send). + +Whatever is put on the `message` field will be forwarded "as-is" to the source's message endpoint. + +**Sending a text message** + +```json5 +{ + conversation_id: "a688d36c-a85e-44af-bc02-4248c2c97622", + message: { + text: "Hello!", + representative: { + representativeType: "HUMAN" + } + } +} +``` - +**Sample response** + +```json5 +{ + "id": "{UUID}", + "content": { + "text": "Hello!", + "representative": { + "representativeType": "HUMAN" + } + }, + "delivery_state": "pending|failed|delivered", + "from_contact": "true|false", + // See glossary + "sent_at": "{string}", + //'yyyy-MM-dd'T'HH:mm:ss.SSSZ' date in UTC form, to be localized by clients + "source": "google", + "metadata": { + "sentFrom": "iPhone" + } + // metadata object of the message +} +``` diff --git a/docs/static/img/getting-started/installation/k8s-azure.jpg b/docs/static/img/getting-started/installation/k8s-azure.jpg new file mode 100644 index 0000000000..ab7db80fda Binary files /dev/null and b/docs/static/img/getting-started/installation/k8s-azure.jpg differ diff --git a/docs/static/img/getting-started/installation/k8s-digitalocean-setup.jpg b/docs/static/img/getting-started/installation/k8s-digitalocean-setup.jpg new file mode 100644 index 0000000000..f954d09ab1 Binary files /dev/null and b/docs/static/img/getting-started/installation/k8s-digitalocean-setup.jpg differ diff --git a/docs/static/img/getting-started/installation/k8s-digitalocean.jpg b/docs/static/img/getting-started/installation/k8s-digitalocean.jpg new file mode 100644 index 0000000000..8bad42f8d3 Binary files /dev/null and b/docs/static/img/getting-started/installation/k8s-digitalocean.jpg differ diff --git a/docs/yarn.lock b/docs/yarn.lock index 26db3e8608..531d8e0f7e 100644 --- a/docs/yarn.lock +++ b/docs/yarn.lock @@ -2040,9 +2040,9 @@ ajv@^6.1.0, ajv@^6.10.2, ajv@^6.12.4, ajv@^6.12.5: uri-js "^4.2.2" algoliasearch-helper@^3.3.4: - version "3.4.4" - resolved "https://registry.yarnpkg.com/algoliasearch-helper/-/algoliasearch-helper-3.4.4.tgz#f2eb46bc4d2f6fed82c7201b8ac4ce0a1988ae67" - integrity sha512-OjyVLjykaYKCMxxRMZNiwLp8CS310E0qAeIY2NaublcmLAh8/SL19+zYHp7XCLtMem2ZXwl3ywMiA32O9jszuw== + version "3.6.2" + resolved "https://registry.yarnpkg.com/algoliasearch-helper/-/algoliasearch-helper-3.6.2.tgz#45e19b12589cfa0c611b573287f65266ea2cc14a" + integrity sha512-Xx0NOA6k4ySn+R2l3UMSONAaMkyfmrZ3AP1geEMo32MxDJQJesZABZYsldO9fa6FKQxH91afhi4hO1G0Zc2opg== dependencies: events "^1.1.1" diff --git a/frontend/chat-plugin/lib/src/AiryChatPlugin.tsx b/frontend/chat-plugin/lib/src/AiryChatPlugin.tsx index c17588dcbe..339c6d942b 100644 --- a/frontend/chat-plugin/lib/src/AiryChatPlugin.tsx +++ b/frontend/chat-plugin/lib/src/AiryChatPlugin.tsx @@ -17,18 +17,23 @@ export const AiryChatPlugin = (props: AiryChatPluginProps) => { const [windowHeight, setWindowHeight] = useState(window.innerHeight); const [windowWidth, setWindowWidth] = useState(window.innerWidth); + const [windowBrowserHeight, setWindowBrowserHeight] = useState(window.outerHeight); + const [windowBrowserWidth, setWindowBrowserWidth] = useState(window.outerWidth); const handleResize = () => { setWindowHeight(window.innerHeight); setWindowWidth(window.innerWidth); + setWindowBrowserHeight(window.outerHeight); + setWindowBrowserWidth(window.outerWidth); }; window.addEventListener('resize', handleResize); const customStyle = { background: 'transparent', - width: Math.min(config.config?.width ?? defaultWidth, windowWidth), - height: Math.min(config.config?.height ?? defaultHeight, windowHeight), + width: windowBrowserWidth < 420 ? windowBrowserWidth : Math.min(config.config?.width ?? defaultWidth, windowWidth), + height: + windowBrowserHeight < 850 ? windowBrowserHeight : Math.min(config.config?.height ?? defaultHeight, windowHeight), ...(config.config?.primaryColor && { '--color-airy-blue': config.config?.primaryColor, '--color-airy-message-outbound': config.config?.primaryColor, diff --git a/frontend/chat-plugin/lib/src/airyRenderProps/AiryBubble/index.module.scss b/frontend/chat-plugin/lib/src/airyRenderProps/AiryBubble/index.module.scss index 801b218e03..fb7349063f 100644 --- a/frontend/chat-plugin/lib/src/airyRenderProps/AiryBubble/index.module.scss +++ b/frontend/chat-plugin/lib/src/airyRenderProps/AiryBubble/index.module.scss @@ -10,6 +10,10 @@ transition: all 0.2s ease-in-out; cursor: pointer; pointer-events: all; + @media only screen and (max-device-width: 420px) and (max-device-width: 820px) { + height: 0; + display: none; + } } .hideBubble:hover { diff --git a/frontend/chat-plugin/lib/src/airyRenderProps/AiryBubble/index.tsx b/frontend/chat-plugin/lib/src/airyRenderProps/AiryBubble/index.tsx index bd5a3ea7f3..4a90050e96 100644 --- a/frontend/chat-plugin/lib/src/airyRenderProps/AiryBubble/index.tsx +++ b/frontend/chat-plugin/lib/src/airyRenderProps/AiryBubble/index.tsx @@ -38,7 +38,12 @@ const AiryBubble = (props: Props) => { }; return ( -
props.toggleHideChat()} data-cy={props.dataCyId}> +
props.toggleHideChat()} + data-cy={props.dataCyId} + > {!props.isChatHidden ? ( diff --git a/frontend/chat-plugin/lib/src/airyRenderProps/AiryHeaderBar/index.module.scss b/frontend/chat-plugin/lib/src/airyRenderProps/AiryHeaderBar/index.module.scss index 161b910aa9..8ac2e4f1ff 100644 --- a/frontend/chat-plugin/lib/src/airyRenderProps/AiryHeaderBar/index.module.scss +++ b/frontend/chat-plugin/lib/src/airyRenderProps/AiryHeaderBar/index.module.scss @@ -7,6 +7,10 @@ border-top-right-radius: 8px; border-bottom: 1px solid #fff; height: 48px; + @media only screen and (max-device-width: 420px) and (max-device-width: 820px) { + border-top-left-radius: 0; + border-top-right-radius: 0; + } } .headerInfo { @@ -26,6 +30,8 @@ font-size: 20px; line-height: 24px; font-weight: bold; + text-align: left; + text-transform: none; padding: 0; margin: 0 8px 0 8px; cursor: none; diff --git a/frontend/chat-plugin/lib/src/airyRenderProps/AiryInputBar/index.module.scss b/frontend/chat-plugin/lib/src/airyRenderProps/AiryInputBar/index.module.scss index c4c625764f..988e49c396 100644 --- a/frontend/chat-plugin/lib/src/airyRenderProps/AiryInputBar/index.module.scss +++ b/frontend/chat-plugin/lib/src/airyRenderProps/AiryInputBar/index.module.scss @@ -198,7 +198,7 @@ svg { path { - fill: #4bb3fd; + fill: var(--color-airy-accent); } } @@ -208,7 +208,7 @@ } .fileInput { - display: none; + display: none !important; } .selectorLoader { diff --git a/frontend/chat-plugin/lib/src/airyRenderProps/AiryInputBar/index.tsx b/frontend/chat-plugin/lib/src/airyRenderProps/AiryInputBar/index.tsx index 72fb2f32a8..76528986cb 100644 --- a/frontend/chat-plugin/lib/src/airyRenderProps/AiryInputBar/index.tsx +++ b/frontend/chat-plugin/lib/src/airyRenderProps/AiryInputBar/index.tsx @@ -42,8 +42,10 @@ const AiryInputBar = (props: AiryInputBarProps) => { const isMobileDevice = window.innerHeight < 1200 || window.innerWidth < 1000; useEffect(() => { - textInputRef.current.selectionStart = props.messageString?.length ?? 0; - textInputRef.current.selectionEnd = props.messageString?.length ?? 0; + if (textInputRef.current) { + textInputRef.current.selectionStart = props.messageString?.length ?? 0; + textInputRef.current.selectionEnd = props.messageString?.length ?? 0; + } }, []); useEffect(() => { diff --git a/frontend/chat-plugin/lib/src/components/chat/index.module.scss b/frontend/chat-plugin/lib/src/components/chat/index.module.scss index 9d617cb796..691dfe6c4a 100644 --- a/frontend/chat-plugin/lib/src/components/chat/index.module.scss +++ b/frontend/chat-plugin/lib/src/components/chat/index.module.scss @@ -51,6 +51,11 @@ flex-direction: column; flex-grow: 1; pointer-events: all; + @media only screen and (max-device-width: 420px) and (max-device-width: 820px) { + margin: 0; + border-radius: 0; + box-shadow: none; + } } .containerAnimationOpen { diff --git a/frontend/chat-plugin/lib/src/websocket/index.ts b/frontend/chat-plugin/lib/src/websocket/index.ts index 1c18f0264f..15a403df3e 100644 --- a/frontend/chat-plugin/lib/src/websocket/index.ts +++ b/frontend/chat-plugin/lib/src/websocket/index.ts @@ -70,9 +70,13 @@ class WebSocket { this.client.onStompError = (frame: IFrame) => { console.error('Broker reported error: ' + frame.headers['message']); console.error('Additional details: ' + frame.body); - authenticate(this.channelId); + if (frame.headers['message'].includes('401')) { + this.client.deactivate(); + this.start(); + } else { + authenticate(this.channelId); + } }; - this.client.activate(); }; diff --git a/frontend/ui/src/AiryConfig.ts b/frontend/ui/src/AiryConfig.ts deleted file mode 100644 index 7abde61ad7..0000000000 --- a/frontend/ui/src/AiryConfig.ts +++ /dev/null @@ -1,4 +0,0 @@ -export class AiryConfig { - static NODE_ENV = process.env.NODE_ENV; - static FACEBOOK_APP_ID = 'CHANGE_ME'; -} diff --git a/frontend/ui/src/assets/scss/fonts.scss b/frontend/ui/src/assets/scss/fonts.scss index 7ee32d73a8..c9037aecc6 100644 --- a/frontend/ui/src/assets/scss/fonts.scss +++ b/frontend/ui/src/assets/scss/fonts.scss @@ -2,56 +2,56 @@ @mixin font-s { font-family: 'Lato', sans-serif; - font-size: 0.8rem; // 13px - line-height: 1rem; + font-size: 13px; + line-height: 16px; } @mixin font-base { font-family: 'Lato', sans-serif; - font-size: 1rem; // 16 px - line-height: 1.5rem; + font-size: 16px; + line-height: 24px; } @mixin font-m { font-family: 'Lato', sans-serif; - font-size: 1.25rem; // 20px - line-height: 2rem; + font-size: 20px; + line-height: 32px; } @mixin font-l { font-family: 'Lato', sans-serif; - font-size: 1.563rem; // 25px - line-height: 2rem; + font-size: 25px; + line-height: 32px; } @mixin font-xl { font-family: 'Lato', sans-serif; - font-size: 1.953rem; // 31px - line-height: 2.5rem; + font-size: 31px; + line-height: 42px; } @mixin font-xxl { font-family: 'Lato', sans-serif; - font-size: 2.441rem; // 39px - line-height: 3rem; + font-size: 39px; + line-height: 48px; } @mixin font-xxxl { font-family: 'Lato', sans-serif; - font-size: 3.052rem; // 48px - line-height: 3.5rem; + font-size: 48px; + line-height: 56px; } @mixin font-4l { font-family: 'Lato', sans-serif; - font-size: 3.815rem; // 61px - line-height: 4rem; + font-size: 61px; + line-height: 72px; } @mixin font-5l { font-family: 'Lato', sans-serif; - font-size: 4.815rem; // 76px - line-height: 4.5rem; + font-size: 76px; + line-height: 82px; } .font-s { diff --git a/frontend/ui/src/pages/Channels/Providers/Instagram/InstagramConnect.tsx b/frontend/ui/src/pages/Channels/Providers/Instagram/InstagramConnect.tsx index ea40363ccf..495eab0a2c 100644 --- a/frontend/ui/src/pages/Channels/Providers/Instagram/InstagramConnect.tsx +++ b/frontend/ui/src/pages/Channels/Providers/Instagram/InstagramConnect.tsx @@ -30,9 +30,9 @@ const connector = connect(mapStateToProps, mapDispatchToProps); const InstagramConnect = (props: InstagramProps) => { const {connectInstagramChannel, channel} = props; - const [id, setId] = useState(channel?.sourceChannelId || ''); + const [id, setId] = useState(''); const [token, setToken] = useState(''); - const [accountId, setAccountId] = useState(''); + const [accountId, setAccountId] = useState(channel?.sourceChannelId || ''); const [name, setName] = useState(channel?.metadata?.name || ''); const [image, setImage] = useState(channel?.metadata?.imageUrl || ''); const [buttonTitle, setButtonTitle] = useState('Connect Page'); diff --git a/frontend/ui/src/pages/Inbox/MessageInput/InputOptions.module.scss b/frontend/ui/src/pages/Inbox/MessageInput/InputOptions.module.scss index 86a9ac4a21..711a888167 100644 --- a/frontend/ui/src/pages/Inbox/MessageInput/InputOptions.module.scss +++ b/frontend/ui/src/pages/Inbox/MessageInput/InputOptions.module.scss @@ -105,7 +105,7 @@ } .fileInput { - display: none; + display: none !important; } .paperclipIcon { diff --git a/frontend/ui/src/pages/Inbox/MessageInput/index.tsx b/frontend/ui/src/pages/Inbox/MessageInput/index.tsx index 30eab89e4f..9fabd7b607 100644 --- a/frontend/ui/src/pages/Inbox/MessageInput/index.tsx +++ b/frontend/ui/src/pages/Inbox/MessageInput/index.tsx @@ -81,6 +81,7 @@ const MessageInput = (props: Props) => { const [uploadedFileUrl, setUploadedFileUrl] = useState(null); const [fileUploadErrorPopUp, setFileUploadErrorPopUp] = useState(''); const [loadingSelector, setLoadingSelector] = useState(false); + const [blockSpam, setBlockSpam] = useState(false); const prevConversationId = usePrevious(conversation.id); const textAreaRef = useRef(null); @@ -237,6 +238,7 @@ const MessageInput = (props: Props) => { if (canSendMessage()) { setSelectedSuggestedReply(null); setSelectedTemplate(null); + setBlockSpam(true); sendMessages( selectedTemplate || selectedSuggestedReply @@ -255,6 +257,7 @@ const MessageInput = (props: Props) => { } ).then(() => { setInput(''); + setBlockSpam(false); removeElementFromInput(); }); } @@ -267,7 +270,7 @@ const MessageInput = (props: Props) => { (event.ctrlKey && event.key === 'Enter') ) { event.preventDefault(); - if (input.trim().length > 0) { + if (input.trim().length > 0 && !blockSpam) { sendMessage(); } } @@ -453,7 +456,7 @@ const MessageInput = (props: Props) => { (input.trim().length != 0 || canSendMessage()) && styles.sendButtonActive }`} onClick={sendMessage} - disabled={input.trim().length == 0 && !canSendMessage()} + disabled={(input.trim().length == 0 && !canSendMessage()) || blockSpam} data-cy={cyMessageSendButton} >
diff --git a/infrastructure/helm-chart/charts/components/charts/api/charts/api-communication/templates/communication/deployment.yaml b/infrastructure/helm-chart/charts/components/charts/api/charts/api-communication/templates/communication/deployment.yaml index 86c0987e94..3013b66487 100644 --- a/infrastructure/helm-chart/charts/components/charts/api/charts/api-communication/templates/communication/deployment.yaml +++ b/infrastructure/helm-chart/charts/components/charts/api/charts/api-communication/templates/communication/deployment.yaml @@ -61,7 +61,7 @@ spec: {{ toYaml .Values.communication.resources | indent 10 }} initContainers: - name: wait - image: busybox + image: "{{ .Values.global.busyboxImage }}" imagePullPolicy: IfNotPresent command: ["/bin/sh", "/opt/provisioning/wait-for-minimum-kafkas.sh"] env: diff --git a/infrastructure/helm-chart/charts/components/charts/api/charts/api-communication/templates/websocket/deployment.yaml b/infrastructure/helm-chart/charts/components/charts/api/charts/api-communication/templates/websocket/deployment.yaml index 57120161a5..8ec1ad0cae 100644 --- a/infrastructure/helm-chart/charts/components/charts/api/charts/api-communication/templates/websocket/deployment.yaml +++ b/infrastructure/helm-chart/charts/components/charts/api/charts/api-communication/templates/websocket/deployment.yaml @@ -61,7 +61,7 @@ spec: {{ toYaml .Values.websocket.resources | indent 10 }} initContainers: - name: wait - image: busybox + image: "{{ .Values.global.busyboxImage }}" imagePullPolicy: IfNotPresent command: ["/bin/sh", "/opt/provisioning/wait-for-minimum-kafkas.sh"] env: diff --git a/infrastructure/helm-chart/charts/components/charts/integration/charts/source-api/templates/deployment.yaml b/infrastructure/helm-chart/charts/components/charts/integration/charts/source-api/templates/deployment.yaml index 33e6e3b398..259796ccd1 100644 --- a/infrastructure/helm-chart/charts/components/charts/integration/charts/source-api/templates/deployment.yaml +++ b/infrastructure/helm-chart/charts/components/charts/integration/charts/source-api/templates/deployment.yaml @@ -69,7 +69,7 @@ spec: {{ toYaml .Values.resources | indent 10 }} initContainers: - name: wait - image: busybox + image: "{{ .Values.global.busyboxImage }}" imagePullPolicy: IfNotPresent command: ["/bin/sh", "/opt/provisioning/wait-for-minimum-kafkas.sh"] env: diff --git a/infrastructure/helm-chart/charts/components/charts/integration/charts/webhook/templates/deployments.yaml b/infrastructure/helm-chart/charts/components/charts/integration/charts/webhook/templates/deployments.yaml index 94e58389d9..1cf0333a4c 100644 --- a/infrastructure/helm-chart/charts/components/charts/integration/charts/webhook/templates/deployments.yaml +++ b/infrastructure/helm-chart/charts/components/charts/integration/charts/webhook/templates/deployments.yaml @@ -71,7 +71,7 @@ spec: {{ toYaml .Values.consumer.resources | indent 10 }} initContainers: - name: wait - image: busybox + image: "{{ .Values.global.busyboxImage }}" imagePullPolicy: IfNotPresent command: ["/bin/sh", "/opt/provisioning/wait-for-service.sh"] env: @@ -168,7 +168,7 @@ spec: {{ toYaml .Values.publisher.resources | indent 10 }} initContainers: - name: wait - image: busybox + image: "{{ .Values.global.busyboxImage }}" imagePullPolicy: IfNotPresent command: ["/bin/sh", "/opt/provisioning/wait-for-minimum-kafkas.sh"] env: diff --git a/infrastructure/helm-chart/charts/components/charts/media/charts/resolver/templates/deployment.yaml b/infrastructure/helm-chart/charts/components/charts/media/charts/resolver/templates/deployment.yaml index 861a34ea58..e27369daa6 100644 --- a/infrastructure/helm-chart/charts/components/charts/media/charts/resolver/templates/deployment.yaml +++ b/infrastructure/helm-chart/charts/components/charts/media/charts/resolver/templates/deployment.yaml @@ -63,7 +63,7 @@ spec: {{ toYaml .Values.resources | indent 12 }} initContainers: - name: wait - image: busybox + image: "{{ .Values.global.busyboxImage }}" imagePullPolicy: IfNotPresent command: ["/bin/sh", "/opt/provisioning/wait-for-minimum-kafkas.sh"] env: diff --git a/infrastructure/helm-chart/charts/components/charts/sources/charts/chatplugin/templates/backend/deployment.yaml b/infrastructure/helm-chart/charts/components/charts/sources/charts/chatplugin/templates/backend/deployment.yaml index 43579183e7..a43ca3fdcb 100644 --- a/infrastructure/helm-chart/charts/components/charts/sources/charts/chatplugin/templates/backend/deployment.yaml +++ b/infrastructure/helm-chart/charts/components/charts/sources/charts/chatplugin/templates/backend/deployment.yaml @@ -71,7 +71,7 @@ spec: {{ toYaml .Values.backend.resources | indent 10 }} initContainers: - name: wait - image: busybox + image: "{{ .Values.global.busyboxImage }}" imagePullPolicy: IfNotPresent command: ["/bin/sh", "/opt/provisioning/wait-for-minimum-kafkas.sh"] env: diff --git a/infrastructure/helm-chart/charts/components/charts/sources/charts/chatplugin/templates/frontend/deployment.yaml b/infrastructure/helm-chart/charts/components/charts/sources/charts/chatplugin/templates/frontend/deployment.yaml index 556b9a7c88..bc39f9b92a 100644 --- a/infrastructure/helm-chart/charts/components/charts/sources/charts/chatplugin/templates/frontend/deployment.yaml +++ b/infrastructure/helm-chart/charts/components/charts/sources/charts/chatplugin/templates/frontend/deployment.yaml @@ -48,7 +48,7 @@ spec: {{ toYaml .Values.frontend.resources | indent 10 }} initContainers: - name: wait - image: busybox + image: "{{ .Values.global.busyboxImage }}" imagePullPolicy: IfNotPresent command: ["/bin/sh", "/opt/provisioning/wait-for-service.sh"] env: diff --git a/infrastructure/helm-chart/charts/components/charts/sources/charts/facebook/templates/deployments.yaml b/infrastructure/helm-chart/charts/components/charts/sources/charts/facebook/templates/deployments.yaml index 5e673e5888..7fb5613eb2 100644 --- a/infrastructure/helm-chart/charts/components/charts/sources/charts/facebook/templates/deployments.yaml +++ b/infrastructure/helm-chart/charts/components/charts/sources/charts/facebook/templates/deployments.yaml @@ -76,7 +76,7 @@ spec: {{ toYaml .Values.connector.resources | indent 12 }} initContainers: - name: wait - image: busybox + image: "{{ .Values.global.busyboxImage }}" imagePullPolicy: IfNotPresent command: ["/bin/sh", "/opt/provisioning/wait-for-minimum-kafkas.sh"] env: @@ -161,7 +161,7 @@ spec: {{ toYaml .Values.eventsRouter.resources | indent 10 }} initContainers: - name: wait - image: busybox + image: "{{ .Values.global.busyboxImage }}" imagePullPolicy: IfNotPresent command: ["/bin/sh", "/opt/provisioning/wait-for-minimum-kafkas.sh"] env: diff --git a/infrastructure/helm-chart/charts/components/charts/sources/charts/google/templates/deployments.yaml b/infrastructure/helm-chart/charts/components/charts/sources/charts/google/templates/deployments.yaml index 0fed5e8048..9316f06877 100644 --- a/infrastructure/helm-chart/charts/components/charts/sources/charts/google/templates/deployments.yaml +++ b/infrastructure/helm-chart/charts/components/charts/sources/charts/google/templates/deployments.yaml @@ -71,7 +71,7 @@ spec: {{ toYaml .Values.connector.resources | indent 12 }} initContainers: - name: wait - image: busybox + image: "{{ .Values.global.busyboxImage }}" imagePullPolicy: IfNotPresent command: ["/bin/sh", "/opt/provisioning/wait-for-minimum-kafkas.sh"] env: @@ -159,7 +159,7 @@ spec: {{ toYaml .Values.eventsRouter.resources | indent 10 }} initContainers: - name: wait - image: busybox + image: "{{ .Values.global.busyboxImage }}" imagePullPolicy: IfNotPresent command: ["/bin/sh", "/opt/provisioning/wait-for-minimum-kafkas.sh"] env: diff --git a/infrastructure/helm-chart/charts/components/charts/sources/charts/twilio/templates/deployments.yaml b/infrastructure/helm-chart/charts/components/charts/sources/charts/twilio/templates/deployments.yaml index f3a998f91b..b6f86cf3b9 100644 --- a/infrastructure/helm-chart/charts/components/charts/sources/charts/twilio/templates/deployments.yaml +++ b/infrastructure/helm-chart/charts/components/charts/sources/charts/twilio/templates/deployments.yaml @@ -71,7 +71,7 @@ spec: {{ toYaml .Values.connector.resources | indent 12 }} initContainers: - name: wait - image: busybox + image: "{{ .Values.global.busyboxImage }}" imagePullPolicy: IfNotPresent command: ["/bin/sh", "/opt/provisioning/wait-for-minimum-kafkas.sh"] env: @@ -161,7 +161,7 @@ spec: {{ toYaml .Values.eventsRouter.resources | indent 10 }} initContainers: - name: wait - image: busybox + image: "{{ .Values.global.busyboxImage }}" imagePullPolicy: IfNotPresent command: ["/bin/sh", "/opt/provisioning/wait-for-minimum-kafkas.sh"] env: diff --git a/infrastructure/helm-chart/charts/components/charts/sources/charts/viber/templates/deployments.yaml b/infrastructure/helm-chart/charts/components/charts/sources/charts/viber/templates/deployments.yaml index cd9c59ee8e..bc94277f3b 100644 --- a/infrastructure/helm-chart/charts/components/charts/sources/charts/viber/templates/deployments.yaml +++ b/infrastructure/helm-chart/charts/components/charts/sources/charts/viber/templates/deployments.yaml @@ -66,7 +66,7 @@ spec: {{ toYaml .Values.connector.resources | indent 12 }} initContainers: - name: wait - image: busybox + image: "{{ .Values.global.busyboxImage }}" imagePullPolicy: IfNotPresent command: [ "/bin/sh", "/opt/provisioning/wait-for-minimum-kafkas.sh" ] env: @@ -156,7 +156,7 @@ spec: {{ toYaml .Values.eventsRouter.resources | indent 12 }} initContainers: - name: wait - image: busybox + image: "{{ .Values.global.busyboxImage }}" imagePullPolicy: IfNotPresent command: [ "/bin/sh", "/opt/provisioning/wait-for-minimum-kafkas.sh" ] env: diff --git a/infrastructure/helm-chart/charts/components/templates/api/admin/deployment.yaml b/infrastructure/helm-chart/charts/components/templates/api/admin/deployment.yaml index b962f53dd5..a857d872e7 100644 --- a/infrastructure/helm-chart/charts/components/templates/api/admin/deployment.yaml +++ b/infrastructure/helm-chart/charts/components/templates/api/admin/deployment.yaml @@ -77,7 +77,7 @@ spec: {{ toYaml .Values.api.admin.resources | indent 10 }} initContainers: - name: wait - image: busybox + image: "{{ .Values.global.busyboxImage }}" imagePullPolicy: IfNotPresent command: ["/bin/sh", "/opt/provisioning/wait-for-minimum-kafkas.sh"] env: diff --git a/infrastructure/helm-chart/charts/prerequisites/charts/kafka/charts/kafka/templates/statefulset.yaml b/infrastructure/helm-chart/charts/prerequisites/charts/kafka/charts/kafka/templates/statefulset.yaml index 9623730be7..1521c11565 100644 --- a/infrastructure/helm-chart/charts/prerequisites/charts/kafka/charts/kafka/templates/statefulset.yaml +++ b/infrastructure/helm-chart/charts/prerequisites/charts/kafka/charts/kafka/templates/statefulset.yaml @@ -108,7 +108,7 @@ spec: {{- end }} initContainers: - name: wait - image: busybox + image: "{{ .Values.global.busyboxImage }}" imagePullPolicy: IfNotPresent command: ["/bin/sh", "/opt/provisioning/wait-for-service-url.sh"] env: @@ -118,7 +118,7 @@ spec: - name: kafka-helper-scripts mountPath: /opt/provisioning - name: fix-permissions - image: busybox + image: "{{ .Values.global.busyboxImage }}" imagePullPolicy: IfNotPresent command: - sh diff --git a/infrastructure/helm-chart/charts/prerequisites/charts/kafka/charts/schema-registry/templates/deployment.yaml b/infrastructure/helm-chart/charts/prerequisites/charts/kafka/charts/schema-registry/templates/deployment.yaml index 8489e443be..82fd26fae2 100644 --- a/infrastructure/helm-chart/charts/prerequisites/charts/kafka/charts/schema-registry/templates/deployment.yaml +++ b/infrastructure/helm-chart/charts/prerequisites/charts/kafka/charts/schema-registry/templates/deployment.yaml @@ -41,7 +41,7 @@ spec: value: {{ .Values.kafka.bootstrapServers }} initContainers: - name: wait - image: busybox + image: "{{ .Values.global.busyboxImage }}" imagePullPolicy: IfNotPresent command: ["/bin/sh", "/opt/provisioning/wait-for-minimum-kafkas.sh"] env: diff --git a/infrastructure/helm-chart/charts/prerequisites/charts/kafka/charts/zookeeper/templates/statefulset.yaml b/infrastructure/helm-chart/charts/prerequisites/charts/kafka/charts/zookeeper/templates/statefulset.yaml index e3e0fa887e..2356f50d97 100644 --- a/infrastructure/helm-chart/charts/prerequisites/charts/kafka/charts/zookeeper/templates/statefulset.yaml +++ b/infrastructure/helm-chart/charts/prerequisites/charts/kafka/charts/zookeeper/templates/statefulset.yaml @@ -47,7 +47,7 @@ spec: topologyKey: "kubernetes.io/hostname" initContainers: - name: fix-permissions - image: busybox + image: "{{ .Values.global.busyboxImage }}" imagePullPolicy: IfNotPresent command: - sh diff --git a/infrastructure/helm-chart/templates/provisioning/job-kafka.yaml b/infrastructure/helm-chart/templates/provisioning/job-kafka.yaml index 702c24be21..e7470ee1e2 100644 --- a/infrastructure/helm-chart/templates/provisioning/job-kafka.yaml +++ b/infrastructure/helm-chart/templates/provisioning/job-kafka.yaml @@ -31,7 +31,7 @@ spec: mountPath: /opt/provisioning initContainers: - name: wait - image: busybox + image: "{{ .Values.global.busyboxImage }}" imagePullPolicy: IfNotPresent command: ["/bin/sh", "/opt/provisioning/wait-for-minimum-kafkas.sh"] env: diff --git a/infrastructure/helm-chart/templates/provisioning/job-wait.yaml b/infrastructure/helm-chart/templates/provisioning/job-wait.yaml index 63ed7410fd..ea75c1c012 100644 --- a/infrastructure/helm-chart/templates/provisioning/job-wait.yaml +++ b/infrastructure/helm-chart/templates/provisioning/job-wait.yaml @@ -14,7 +14,7 @@ spec: spec: containers: - name: wait - image: busybox + image: "{{ .Values.global.busyboxImage }}" imagePullPolicy: IfNotPresent command: ["/bin/sh", "/opt/provisioning/wait-for-service-url.sh"] env: @@ -46,7 +46,7 @@ spec: spec: containers: - name: wait - image: busybox + image: "{{ .Values.global.busyboxImage }}" imagePullPolicy: IfNotPresent command: ["/bin/sh", "/opt/provisioning/wait-for-service-url.sh"] env: diff --git a/infrastructure/helm-chart/templates/upgrading/0.29.0.yaml b/infrastructure/helm-chart/templates/upgrading/0.29.0.yaml index e678ba5c79..3ae0798aea 100644 --- a/infrastructure/helm-chart/templates/upgrading/0.29.0.yaml +++ b/infrastructure/helm-chart/templates/upgrading/0.29.0.yaml @@ -126,7 +126,7 @@ spec: spec: initContainers: - name: wait-api-admin - image: busybox + image: "{{ .Values.global.busyboxImage }}" imagePullPolicy: IfNotPresent command: ["/bin/sh", "/opt/provisioning/wait-for-service.sh"] env: @@ -269,7 +269,7 @@ spec: spec: initContainers: - name: wait-api-admin - image: busybox + image: "{{ .Values.global.busyboxImage }}" imagePullPolicy: IfNotPresent command: ["/bin/sh", "/opt/provisioning/wait-for-service.sh"] env: diff --git a/infrastructure/helm-chart/values.yaml b/infrastructure/helm-chart/values.yaml index 5730dcbfe2..4ec647a456 100644 --- a/infrastructure/helm-chart/values.yaml +++ b/infrastructure/helm-chart/values.yaml @@ -1,5 +1,6 @@ global: containerRegistry: ghcr.io/airyhq + busyboxImage: ghcr.io/airyhq/infrastructure/busybox:latest security: systemToken: "" allowedOrigins: "*" diff --git a/infrastructure/images/busybox/Dockerfile b/infrastructure/images/busybox/Dockerfile new file mode 100644 index 0000000000..19c21f98a7 --- /dev/null +++ b/infrastructure/images/busybox/Dockerfile @@ -0,0 +1,3 @@ +FROM busybox + +LABEL maintainer "https://github.com/airyhq" diff --git a/infrastructure/images/busybox/Makefile b/infrastructure/images/busybox/Makefile new file mode 100644 index 0000000000..0942ee4758 --- /dev/null +++ b/infrastructure/images/busybox/Makefile @@ -0,0 +1,6 @@ +build: + docker build -t busybox . + +release: build + docker tag busybox ghcr.io/airyhq/infrastructure/busybox + docker push ghcr.io/airyhq/infrastructure/busybox diff --git a/infrastructure/terraform/main/files/defaultResourceLimits.yaml b/infrastructure/terraform/main/files/defaultResourceLimits.yaml new file mode 100644 index 0000000000..b8b4b3aa9d --- /dev/null +++ b/infrastructure/terraform/main/files/defaultResourceLimits.yaml @@ -0,0 +1,175 @@ +prerequisites: + kafka: + kafka: + resources: + requests: + cpu: "300m" + memory: "512Mi" + limits: + cpu: "2000m" + memory: "4096Mi" + zookeeper: + resources: + requests: + cpu: "100m" + memory: "128Mi" + limits: + cpu: "1000m" + memory: "2048Mi" +components: + api: + admin: + resources: + requests: + cpu: "100m" + memory: "128Mi" + limits: + cpu: "1000m" + memory: "1024Mi" + communication: + communication: + resources: + requests: + cpu: "200m" + memory: "128Mi" + limits: + cpu: "2000m" + memory: "2048Mi" + websocket: + resources: + requests: + cpu: "50m" + memory: "128Mi" + limits: + cpu: "1000m" + memory: "1024Mi" + frontend: + ui: + resources: + requests: + cpu: "50m" + memory: "64Mi" + limits: + cpu: "500m" + memory: "512Mi" + integration: + source-api: + resources: + requests: + cpu: "50m" + memory: "128Mi" + limits: + cpu: "1000m" + memory: "1024Mi" + webhook: + consumer: + resources: + requests: + cpu: "50m" + memory: "128Mi" + limits: + cpu: "1000m" + memory: "1024Mi" + publisher: + resources: + requests: + cpu: "50m" + memory: "128Mi" + limits: + cpu: "1000m" + memory: "2048Mi" + media: + resolver: + resources: + requests: + cpu: "50m" + memory: "128Mi" + limits: + cpu: "1000m" + memory: "1024Mi" + sources: + chatplugin: + backend: + resources: + requests: + cpu: "50m" + memory: "128Mi" + limits: + cpu: "1000m" + memory: "1024Mi" + frontend: + resources: + requests: + cpu: "50m" + memory: "64Mi" + limits: + cpu: "500m" + memory: "1024Mi" + facebook: + connector: + resources: + requests: + cpu: "100m" + memory: "128Mi" + limits: + cpu: "1000m" + memory: "1024Mi" + eventsRouter: + resources: + requests: + cpu: "50m" + memory: "128Mi" + limits: + cpu: "1000m" + memory: "1024Mi" + google: + connector: + resources: + requests: + cpu: "100m" + memory: "128Mi" + limits: + cpu: "1000m" + memory: "1024Mi" + eventsRouter: + resources: + requests: + cpu: "50m" + memory: "128Mi" + limits: + cpu: "1000m" + memory: "1024Mi" + twilio: + connector: + resources: + requests: + cpu: "100m" + memory: "128Mi" + limits: + cpu: "1000m" + memory: "1024Mi" + eventsRouter: + resources: + requests: + cpu: "50m" + memory: "128Mi" + limits: + cpu: "1000m" + memory: "1024Mi" + viber: + connector: + resources: + requests: + cpu: "100m" + memory: "128Mi" + limits: + cpu: "1000m" + memory: "1024Mi" + eventsRouter: + resources: + requests: + cpu: "50m" + memory: "128Mi" + limits: + cpu: "1000m" + memory: "1024Mi" diff --git a/infrastructure/terraform/main/main.tf b/infrastructure/terraform/main/main.tf index f142cb6a9d..f3fef1ae43 100644 --- a/infrastructure/terraform/main/main.tf +++ b/infrastructure/terraform/main/main.tf @@ -11,6 +11,7 @@ provider "kubernetes" { module "my-airy-core" { source = "../modules/core" values_yaml = data.template_file.airy_yaml.rendered + resources_yaml = file("${path.module}/files/defaultResourceLimits.yaml") } data "template_file" "airy_yaml" { diff --git a/infrastructure/terraform/modules/aws_eks/main.tf b/infrastructure/terraform/modules/aws_eks/main.tf index ce21ee0f2c..8494c6c05d 100644 --- a/infrastructure/terraform/modules/aws_eks/main.tf +++ b/infrastructure/terraform/modules/aws_eks/main.tf @@ -21,8 +21,12 @@ module "vpc" { private_subnets = var.private_subnets public_subnets = var.public_subnets - enable_nat_gateway = true - enable_vpn_gateway = true + enable_nat_gateway = true + enable_vpn_gateway = true + enable_dns_support = true + enable_dns_hostnames = true + + single_nat_gateway = true tags = { Terraform = "true" @@ -56,10 +60,13 @@ module "eks" { fargate_subnets = [local.vpc.private_subnets[0]] kubeconfig_output_path = var.kubeconfig_output_path write_kubeconfig = true + map_users = var.kubernetes_users node_groups = { default = { desired_capacity = var.node_group_size + min_capacity = var.node_group_size + max_capacity = (var.node_group_size + 1) instance_types = [var.instance_type] update_config = { @@ -93,21 +100,17 @@ module "eks" { } -resource "aws_eks_fargate_profile" "example" { - - +resource "aws_eks_fargate_profile" "namespaces" { + count = length(var.fargate_profiles) cluster_name = var.core_id - fargate_profile_name = "stateless" + fargate_profile_name = "stateless-${var.fargate_profiles[count.index]}" pod_execution_role_arn = module.eks.fargate_iam_role_arn subnet_ids = module.vpc.private_subnets - dynamic "selector" { - for_each = var.fargate_profiles - content { - namespace = selector.value + selector { + namespace = var.fargate_profiles[count.index] labels = { WorkerType = "fargate" } - } } } diff --git a/infrastructure/terraform/modules/aws_eks/outputs.tf b/infrastructure/terraform/modules/aws_eks/outputs.tf index 6c3e36bdb3..20edcf6723 100644 --- a/infrastructure/terraform/modules/aws_eks/outputs.tf +++ b/infrastructure/terraform/modules/aws_eks/outputs.tf @@ -17,3 +17,19 @@ output "cluster_iam_role_arn" { output "subnet_ids" { value = module.vpc.private_subnets } + +output "vpc_id" { + value = module.vpc.vpc_id +} + +output "vpc_cidr_block" { + value = module.vpc.vpc_cidr_block +} + +output "fargate_iam_role_arn" { + value = module.eks.fargate_iam_role_arn +} + +output "vpc_private_subnets" { + value = module.vpc.private_subnets +} \ No newline at end of file diff --git a/infrastructure/terraform/modules/aws_eks/variables.tf b/infrastructure/terraform/modules/aws_eks/variables.tf index 22630bff09..16124dcaa6 100644 --- a/infrastructure/terraform/modules/aws_eks/variables.tf +++ b/infrastructure/terraform/modules/aws_eks/variables.tf @@ -64,11 +64,17 @@ variable "kubeconfig_output_path" { default = "../main/.kubeconfig" } -variable "fargate_namespaces" { - type = list(string) - default = [ "default" ] +variable "fargate_profiles" { + type = list(string) + description = "List of Fargate namespaces (maximum of 10)" + default = [] } -variable "fargate_profiles" { - -} \ No newline at end of file +variable "kubernetes_users" { + type = list(object({ + userarn = string + username = string + groups = list(string) + })) + default = [] +} diff --git a/infrastructure/terraform/modules/core/main.tf b/infrastructure/terraform/modules/core/main.tf index 2155fabe48..30338845e1 100644 --- a/infrastructure/terraform/modules/core/main.tf +++ b/infrastructure/terraform/modules/core/main.tf @@ -1,13 +1,3 @@ -provider "helm" { - kubernetes { - config_path = var.kubeconfig_output_path - } -} - -provider "kubernetes" { - config_path = var.kubeconfig_output_path -} - resource "kubernetes_namespace" "core" { metadata { name = var.core_id @@ -32,7 +22,8 @@ resource "helm_release" "airy_core" { timeout = "600" values = [ - var.values_yaml + var.values_yaml, + var.resources_yaml ] namespace = var.namespace diff --git a/infrastructure/terraform/modules/core/variables.tf b/infrastructure/terraform/modules/core/variables.tf index 311361d4e1..e9e51d2185 100644 --- a/infrastructure/terraform/modules/core/variables.tf +++ b/infrastructure/terraform/modules/core/variables.tf @@ -17,6 +17,10 @@ variable "values_yaml" { description = "The helm values overrides" } +variable "resources_yaml" { + description = "Resource requests and limits for the components" +} + variable "core_version" { description = "Version of the Airy Core instance" type = string diff --git a/lib/java/log/BUILD b/lib/java/log/BUILD index 688c1dd32a..ca109e4d76 100644 --- a/lib/java/log/BUILD +++ b/lib/java/log/BUILD @@ -9,12 +9,14 @@ custom_java_library( "@maven//:org_apache_logging_log4j_log4j_api", "@maven//:org_apache_logging_log4j_log4j_slf4j_impl", "@maven//:org_slf4j_slf4j_api", + "@maven//:org_slf4j_slf4j_nop", ], deps = [ "@maven//:org_apache_logging_log4j_log4j_api", "@maven//:org_apache_logging_log4j_log4j_core", "@maven//:org_apache_logging_log4j_log4j_slf4j_impl", "@maven//:org_slf4j_slf4j_api", + "@maven//:org_slf4j_slf4j_nop", ], ) diff --git a/lib/java/spring/test/src/main/java/co/airy/spring/test/WebTestHelper.java b/lib/java/spring/test/src/main/java/co/airy/spring/test/WebTestHelper.java index 1f4e9ff59e..e0b5759276 100644 --- a/lib/java/spring/test/src/main/java/co/airy/spring/test/WebTestHelper.java +++ b/lib/java/spring/test/src/main/java/co/airy/spring/test/WebTestHelper.java @@ -1,9 +1,7 @@ package co.airy.spring.test; -import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpHeaders; -import org.springframework.stereotype.Component; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.ResultActions; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; @@ -13,12 +11,11 @@ import static org.springframework.http.MediaType.APPLICATION_JSON; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -@Component public class WebTestHelper { private final MockMvc mvc; private final String systemToken; - WebTestHelper(MockMvc mvc, @Value("${systemToken:#{null}}") String systemToken) { + WebTestHelper(MockMvc mvc, String systemToken) { this.mvc = mvc; this.systemToken = systemToken; } diff --git a/lib/java/spring/test/src/main/java/co/airy/spring/test/WebTestHelperConfig.java b/lib/java/spring/test/src/main/java/co/airy/spring/test/WebTestHelperConfig.java new file mode 100644 index 0000000000..0ffa33ac7c --- /dev/null +++ b/lib/java/spring/test/src/main/java/co/airy/spring/test/WebTestHelperConfig.java @@ -0,0 +1,17 @@ +package co.airy.spring.test; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Lazy; +import org.springframework.test.web.servlet.MockMvc; + +@Configuration +public class WebTestHelperConfig { + + @Bean + @Lazy + public WebTestHelper webTestHelper(MockMvc mvc, @Value("${systemToken:#{null}}") String systemToken) { + return new WebTestHelper(mvc, systemToken); + } +} diff --git a/lib/typescript/assets/scss/fonts.scss b/lib/typescript/assets/scss/fonts.scss index 7ee32d73a8..c9037aecc6 100644 --- a/lib/typescript/assets/scss/fonts.scss +++ b/lib/typescript/assets/scss/fonts.scss @@ -2,56 +2,56 @@ @mixin font-s { font-family: 'Lato', sans-serif; - font-size: 0.8rem; // 13px - line-height: 1rem; + font-size: 13px; + line-height: 16px; } @mixin font-base { font-family: 'Lato', sans-serif; - font-size: 1rem; // 16 px - line-height: 1.5rem; + font-size: 16px; + line-height: 24px; } @mixin font-m { font-family: 'Lato', sans-serif; - font-size: 1.25rem; // 20px - line-height: 2rem; + font-size: 20px; + line-height: 32px; } @mixin font-l { font-family: 'Lato', sans-serif; - font-size: 1.563rem; // 25px - line-height: 2rem; + font-size: 25px; + line-height: 32px; } @mixin font-xl { font-family: 'Lato', sans-serif; - font-size: 1.953rem; // 31px - line-height: 2.5rem; + font-size: 31px; + line-height: 42px; } @mixin font-xxl { font-family: 'Lato', sans-serif; - font-size: 2.441rem; // 39px - line-height: 3rem; + font-size: 39px; + line-height: 48px; } @mixin font-xxxl { font-family: 'Lato', sans-serif; - font-size: 3.052rem; // 48px - line-height: 3.5rem; + font-size: 48px; + line-height: 56px; } @mixin font-4l { font-family: 'Lato', sans-serif; - font-size: 3.815rem; // 61px - line-height: 4rem; + font-size: 61px; + line-height: 72px; } @mixin font-5l { font-family: 'Lato', sans-serif; - font-size: 4.815rem; // 76px - line-height: 4.5rem; + font-size: 76px; + line-height: 82px; } .font-s { diff --git a/lib/typescript/model/Channel.ts b/lib/typescript/model/Channel.ts index e87dbc25ae..2b371ad198 100644 --- a/lib/typescript/model/Channel.ts +++ b/lib/typescript/model/Channel.ts @@ -3,6 +3,9 @@ import {Metadata} from './Metadata'; export type ChannelMetadata = Metadata & { name: string; imageUrl?: string; + pageId?: string; + pageToken?: string; + accountId?: string; }; export interface Channel { diff --git a/lib/typescript/render/SourceMessagePreview.tsx b/lib/typescript/render/SourceMessagePreview.tsx index 84a290a741..df0b4b5d8c 100644 --- a/lib/typescript/render/SourceMessagePreview.tsx +++ b/lib/typescript/render/SourceMessagePreview.tsx @@ -37,7 +37,9 @@ export const SourceMessagePreview = (props: SourceMessagePreviewProps) => { const {conversation} = props; const lastMessageIsText = (conversation: Conversation) => { - const lastMessageContent = conversation.lastMessage.content; + const lastMessageContent = conversation?.lastMessage?.content?.message || conversation?.lastMessage?.content; + + //google const googleLiveAgentRequest = lastMessageContent?.userStatus?.requestedLiveAgent; const googleSurveyResponse = lastMessageContent?.surveyResponse; @@ -57,6 +59,31 @@ export const SourceMessagePreview = (props: SourceMessagePreviewProps) => { ); } + //instagram + const instagramStoryMention = + lastMessageContent?.attachment?.type === 'story_mention' || + lastMessageContent?.attachments?.[0]?.type === 'story_mention'; + const instagramStoryReplies = lastMessageContent?.reply_to; + const instagramDeletedMessage = lastMessageContent?.is_deleted; + const instagramShare = + lastMessageContent?.attachment?.type === 'share' || lastMessageContent?.attachments?.[0]?.type === 'share'; + + if (instagramStoryMention) { + return <>story mention; + } + + if (instagramStoryReplies) { + return <>story reply; + } + + if (instagramDeletedMessage) { + return <>deleted message; + } + + if (instagramShare) { + return <>shared post; + } + if (typeof lastMessageContent === 'string') { let text; diff --git a/lib/typescript/render/providers/facebook/components/InstagramStoryReplies/index.module.scss b/lib/typescript/render/providers/facebook/components/InstagramStoryReplies/index.module.scss index c2cffb6d17..3ef586f173 100644 --- a/lib/typescript/render/providers/facebook/components/InstagramStoryReplies/index.module.scss +++ b/lib/typescript/render/providers/facebook/components/InstagramStoryReplies/index.module.scss @@ -1,6 +1,22 @@ @import 'assets/scss/fonts.scss'; @import 'assets/scss/colors.scss'; +.container { + margin-top: 10px; +} + +.containerMemberContent { + display: flex; + flex-direction: column; + align-items: flex-end; +} + +.containerContactContent { + display: flex; + flex-direction: column; + align-items: flex-start; +} + .textMessage { display: inline-block; overflow-wrap: break-word; @@ -25,10 +41,6 @@ color: white; } -.container { - margin-top: 10px; -} - .storyReply { display: flex; align-items: center; diff --git a/lib/typescript/render/providers/facebook/components/InstagramStoryReplies/index.tsx b/lib/typescript/render/providers/facebook/components/InstagramStoryReplies/index.tsx index 63872cf557..44950d70a1 100644 --- a/lib/typescript/render/providers/facebook/components/InstagramStoryReplies/index.tsx +++ b/lib/typescript/render/providers/facebook/components/InstagramStoryReplies/index.tsx @@ -13,7 +13,9 @@ type InstagramRepliesProps = { export const StoryReplies = ({url, text, sentAt, fromContact}: InstagramRepliesProps) => { return ( -
+
In response to a  {timeElapsedInHours(sentAt) <= 24 ? ( diff --git a/maven_install.json b/maven_install.json index f44c3c961b..817b282297 100644 --- a/maven_install.json +++ b/maven_install.json @@ -1,11 +1,10 @@ { "dependency_tree": { - "__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": -376988728, + "__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": 1389523621, "conflict_resolution": { "com.fasterxml.jackson.core:jackson-annotations:2.10.0": "com.fasterxml.jackson.core:jackson-annotations:2.11.4", "com.fasterxml.jackson.core:jackson-core:2.10.0": "com.fasterxml.jackson.core:jackson-core:2.11.4", "com.fasterxml.jackson.core:jackson-databind:2.10.0": "com.fasterxml.jackson.core:jackson-databind:2.11.4", - "com.segment.analytics.java:analytics-core:2.1.0": "com.segment.analytics.java:analytics-core:3.1.3", "io.jsonwebtoken:jjwt-api:0.10.5": "io.jsonwebtoken:jjwt-api:0.10.7", "io.jsonwebtoken:jjwt-impl:0.10.5": "io.jsonwebtoken:jjwt-impl:0.10.7", "io.jsonwebtoken:jjwt-jackson:0.10.5": "io.jsonwebtoken:jjwt-jackson:0.10.7", @@ -17,7 +16,6 @@ "org.junit.platform:junit-platform-engine:1.7.0": "org.junit.platform:junit-platform-engine:1.7.1", "org.mockito:mockito-core:2.28.2": "org.mockito:mockito-core:3.6.28", "org.rocksdb:rocksdbjni:5.18.3": "org.rocksdb:rocksdbjni:5.18.4", - "org.slf4j:slf4j-api:1.7.29": "org.slf4j:slf4j-api:1.7.30", "org.springframework:spring-aop:4.1.4.RELEASE": "org.springframework:spring-aop:5.3.6" }, "dependencies": [ @@ -43,10 +41,10 @@ { "coord": "com.101tec:zkclient:0.11", "dependencies": [ - "org.slf4j:slf4j-api:1.7.30" + "org.slf4j:slf4j-api:1.7.32" ], "directDependencies": [ - "org.slf4j:slf4j-api:1.7.30" + "org.slf4j:slf4j-api:1.7.32" ], "exclusions": [ "org.slf4j:slf4j-log4j12", @@ -227,13 +225,13 @@ { "coord": "com.dinstone:beanstalkc:2.3.0", "dependencies": [ - "org.slf4j:slf4j-api:1.7.30", - "org.slf4j:slf4j-api:jar:1.7.30", - "org.apache.mina:mina-core:2.1.3" + "org.apache.mina:mina-core:2.1.3", + "org.slf4j:slf4j-api:jar:1.7.32", + "org.slf4j:slf4j-api:1.7.32" ], "directDependencies": [ "org.apache.mina:mina-core:2.1.3", - "org.slf4j:slf4j-api:jar:1.7.30" + "org.slf4j:slf4j-api:jar:1.7.32" ], "exclusions": [ "ch.qos.logback:logback-classic", @@ -1202,14 +1200,14 @@ { "coord": "com.jayway.jsonpath:json-path:2.4.0", "dependencies": [ - "org.slf4j:slf4j-api:1.7.30", "net.minidev:json-smart:2.3", "org.ow2.asm:asm:9.0", - "net.minidev:accessors-smart:1.2" + "net.minidev:accessors-smart:1.2", + "org.slf4j:slf4j-api:1.7.32" ], "directDependencies": [ "net.minidev:json-smart:2.3", - "org.slf4j:slf4j-api:1.7.30" + "org.slf4j:slf4j-api:1.7.32" ], "exclusions": [ "ch.qos.logback:logback-classic", @@ -1235,9 +1233,9 @@ "com.fasterxml.jackson.core:jackson-core:2.11.4", "com.fasterxml.jackson.core:jackson-annotations:2.11.4", "org.jetbrains.kotlin:kotlin-scripting-compiler-impl-embeddable:1.3.50", - "org.slf4j:slf4j-api:1.7.30", "org.scala-lang:scala-library:2.13.3", "com.fasterxml.jackson.core:jackson-databind:2.11.4", + "org.slf4j:slf4j-api:1.7.32", "org.jetbrains.kotlin:kotlin-scripting-jvm:1.3.50", "org.jetbrains.kotlin:kotlin-scripting-common:1.3.50", "org.jetbrains.kotlin:kotlin-reflect:1.3.50", @@ -1247,9 +1245,9 @@ "directDependencies": [ "io.github.classgraph:classgraph:4.8.21", "org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:1.3.50", - "org.slf4j:slf4j-api:1.7.30", "org.scala-lang:scala-library:2.13.3", "com.fasterxml.jackson.core:jackson-databind:2.11.4", + "org.slf4j:slf4j-api:1.7.32", "javax.validation:validation-api:2.0.1.Final" ], "exclusions": [ @@ -1863,13 +1861,13 @@ "coord": "com.typesafe.scala-logging:scala-logging_2.13:3.9.2", "dependencies": [ "org.scala-lang:scala-library:2.13.3", - "org.slf4j:slf4j-api:1.7.30", - "org.scala-lang:scala-reflect:2.13.3" + "org.scala-lang:scala-reflect:2.13.3", + "org.slf4j:slf4j-api:1.7.32" ], "directDependencies": [ "org.scala-lang:scala-library:2.13.3", "org.scala-lang:scala-reflect:2.13.3", - "org.slf4j:slf4j-api:1.7.30" + "org.slf4j:slf4j-api:1.7.32" ], "exclusions": [ "org.slf4j:slf4j-log4j12", @@ -1919,11 +1917,11 @@ "com.fasterxml.jackson.core:jackson-core:2.11.4", "com.fasterxml.jackson.core:jackson-annotations:2.11.4", "com.google.code.findbugs:jsr305:3.0.2", - "org.slf4j:slf4j-api:1.7.30", "org.jetbrains.kotlin:kotlin-stdlib-common:1.4.21", "com.squareup.okio:okio:2.8.0", "com.fasterxml.jackson.core:jackson-databind:2.11.4", "com.google.errorprone:error_prone_annotations:2.3.4", + "org.slf4j:slf4j-api:1.7.32", "com.google.guava:failureaccess:1.0.1", "com.google.guava:guava:29.0-jre", "org.jetbrains.kotlin:kotlin-stdlib:1.4.21", @@ -1933,8 +1931,8 @@ ], "directDependencies": [ "com.google.code.findbugs:jsr305:3.0.2", - "org.slf4j:slf4j-api:1.7.30", "com.fasterxml.jackson.core:jackson-databind:2.11.4", + "org.slf4j:slf4j-api:1.7.32", "com.google.guava:guava:29.0-jre", "com.squareup.okhttp3:okhttp:4.9.1" ], @@ -1956,10 +1954,10 @@ { "coord": "com.yammer.metrics:metrics-core:2.2.0", "dependencies": [ - "org.slf4j:slf4j-api:1.7.30" + "org.slf4j:slf4j-api:1.7.32" ], "directDependencies": [ - "org.slf4j:slf4j-api:1.7.30" + "org.slf4j:slf4j-api:1.7.32" ], "exclusions": [ "org.slf4j:slf4j-log4j12", @@ -2155,10 +2153,10 @@ { "coord": "io.confluent:common-utils:6.1.1", "dependencies": [ - "org.slf4j:slf4j-api:1.7.30" + "org.slf4j:slf4j-api:1.7.32" ], "directDependencies": [ - "org.slf4j:slf4j-api:1.7.30" + "org.slf4j:slf4j-api:1.7.32" ], "exclusions": [ "ch.qos.logback:logback-classic", @@ -2202,7 +2200,6 @@ "com.fasterxml.jackson.core:jackson-annotations:2.11.4", "io.confluent:common-utils:6.1.1", "org.glassfish.hk2.external:jakarta.inject:2.6.1", - "org.slf4j:slf4j-api:1.7.30", "org.glassfish.hk2:osgi-resource-locator:1.0.3", "org.apache.commons:commons-compress:1.20", "io.swagger:swagger-annotations:1.6.2", @@ -2210,6 +2207,7 @@ "com.fasterxml.jackson.core:jackson-databind:2.11.4", "com.sun.activation:jakarta.activation:1.2.2", "org.apache.avro:avro:1.10.0", + "org.slf4j:slf4j-api:1.7.32", "jakarta.annotation:jakarta.annotation-api:1.3.5", "org.xerial.snappy:snappy-java:1.1.7.7", "org.apache.kafka:kafka-clients:6.1.1-ccs", @@ -2259,7 +2257,6 @@ "io.confluent:common-utils:6.1.1", "org.jetbrains.kotlin:kotlin-scripting-compiler-impl-embeddable:1.3.50", "org.glassfish.hk2.external:jakarta.inject:2.6.1", - "org.slf4j:slf4j-api:1.7.30", "org.scala-lang:scala-library:2.13.3", "org.glassfish.hk2:osgi-resource-locator:1.0.3", "com.fasterxml.jackson.datatype:jackson-datatype-joda:2.10.5", @@ -2274,6 +2271,7 @@ "com.sun.activation:jakarta.activation:1.2.2", "org.apache.avro:avro:1.10.0", "com.google.errorprone:error_prone_annotations:2.3.4", + "org.slf4j:slf4j-api:1.7.32", "jakarta.annotation:jakarta.annotation-api:1.3.5", "org.xerial.snappy:snappy-java:1.1.7.7", "org.apache.kafka:kafka-clients:6.1.1-ccs", @@ -2336,7 +2334,6 @@ "com.google.code.findbugs:jsr305:3.0.2", "io.confluent:common-utils:6.1.1", "org.glassfish.hk2.external:jakarta.inject:2.6.1", - "org.slf4j:slf4j-api:1.7.30", "org.glassfish.hk2:osgi-resource-locator:1.0.3", "org.jetbrains.kotlin:kotlin-stdlib-common:1.4.21", "org.apache.commons:commons-compress:1.20", @@ -2348,6 +2345,7 @@ "com.sun.activation:jakarta.activation:1.2.2", "org.apache.avro:avro:1.10.0", "com.google.errorprone:error_prone_annotations:2.3.4", + "org.slf4j:slf4j-api:1.7.32", "jakarta.annotation:jakarta.annotation-api:1.3.5", "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.10", "org.xerial.snappy:snappy-java:1.1.7.7", @@ -2396,7 +2394,6 @@ "com.fasterxml.jackson.core:jackson-annotations:2.11.4", "io.confluent:common-utils:6.1.1", "org.glassfish.hk2.external:jakarta.inject:2.6.1", - "org.slf4j:slf4j-api:1.7.30", "org.glassfish.hk2:osgi-resource-locator:1.0.3", "org.apache.commons:commons-compress:1.20", "io.swagger:swagger-annotations:1.6.2", @@ -2404,6 +2401,7 @@ "com.fasterxml.jackson.core:jackson-databind:2.11.4", "com.sun.activation:jakarta.activation:1.2.2", "org.apache.avro:avro:1.10.0", + "org.slf4j:slf4j-api:1.7.32", "jakarta.annotation:jakarta.annotation-api:1.3.5", "org.xerial.snappy:snappy-java:1.1.7.7", "org.apache.kafka:kafka-clients:6.1.1-ccs", @@ -2472,7 +2470,6 @@ "javax.activation:javax.activation-api:1.2.0", "org.jetbrains.kotlin:kotlin-scripting-compiler-impl-embeddable:1.3.50", "org.glassfish.hk2.external:jakarta.inject:2.6.1", - "org.slf4j:slf4j-api:1.7.30", "org.scala-lang:scala-library:2.13.3", "org.eclipse.jetty:jetty-http:9.4.39.v20210325", "org.eclipse.jetty:jetty-webapp:9.4.39.v20210325", @@ -2516,6 +2513,7 @@ "com.fasterxml.jackson.module:jackson-module-jaxb-annotations:2.10.5", "com.google.errorprone:error_prone_annotations:2.3.4", "org.eclipse.jetty:jetty-server:9.4.39.v20210325", + "org.slf4j:slf4j-api:1.7.32", "jakarta.annotation:jakarta.annotation-api:1.3.5", "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.10", "io.netty:netty-buffer:4.1.50.Final", @@ -2632,7 +2630,6 @@ "com.fasterxml.jackson.core:jackson-annotations:2.11.4", "io.confluent:common-utils:6.1.1", "org.glassfish.hk2.external:jakarta.inject:2.6.1", - "org.slf4j:slf4j-api:1.7.30", "org.glassfish.hk2:osgi-resource-locator:1.0.3", "org.apache.commons:commons-compress:1.20", "io.swagger:swagger-annotations:1.6.2", @@ -2640,6 +2637,7 @@ "com.fasterxml.jackson.core:jackson-databind:2.11.4", "com.sun.activation:jakarta.activation:1.2.2", "org.apache.avro:avro:1.10.0", + "org.slf4j:slf4j-api:1.7.32", "jakarta.annotation:jakarta.annotation-api:1.3.5", "org.xerial.snappy:snappy-java:1.1.7.7", "org.apache.kafka:kafka-clients:6.1.1-ccs", @@ -2674,7 +2672,6 @@ "com.fasterxml.jackson.core:jackson-annotations:2.11.4", "io.confluent:common-utils:6.1.1", "org.glassfish.hk2.external:jakarta.inject:2.6.1", - "org.slf4j:slf4j-api:1.7.30", "org.glassfish.hk2:osgi-resource-locator:1.0.3", "org.apache.commons:commons-compress:1.20", "io.swagger:swagger-annotations:1.6.2", @@ -2682,6 +2679,7 @@ "com.fasterxml.jackson.core:jackson-databind:2.11.4", "com.sun.activation:jakarta.activation:1.2.2", "org.apache.avro:avro:1.10.0", + "org.slf4j:slf4j-api:1.7.32", "jakarta.annotation:jakarta.annotation-api:1.3.5", "org.xerial.snappy:snappy-java:1.1.7.7", "org.apache.kafka:kafka-clients:6.1.1-ccs", @@ -2732,7 +2730,6 @@ "io.confluent:common-utils:6.1.1", "javax.activation:javax.activation-api:1.2.0", "org.glassfish.hk2.external:jakarta.inject:2.6.1", - "org.slf4j:slf4j-api:1.7.30", "org.eclipse.jetty:jetty-http:9.4.39.v20210325", "org.eclipse.jetty:jetty-webapp:9.4.39.v20210325", "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:2.10.5", @@ -2758,6 +2755,7 @@ "com.fasterxml.jackson.module:jackson-module-jaxb-annotations:2.10.5", "com.google.errorprone:error_prone_annotations:2.3.4", "org.eclipse.jetty:jetty-server:9.4.39.v20210325", + "org.slf4j:slf4j-api:1.7.32", "jakarta.annotation:jakarta.annotation-api:1.3.5", "org.xerial.snappy:snappy-java:1.1.7.7", "org.glassfish.jersey.core:jersey-client:2.31", @@ -3362,10 +3360,10 @@ "com.fasterxml.jackson.core:jackson-core:2.11.4", "com.fasterxml.jackson.core:jackson-annotations:2.11.4", "com.google.code.findbugs:jsr305:3.0.2", - "org.slf4j:slf4j-api:1.7.30", "io.swagger:swagger-annotations:1.6.2", "com.fasterxml.jackson.core:jackson-databind:2.11.4", "com.google.errorprone:error_prone_annotations:2.3.4", + "org.slf4j:slf4j-api:1.7.32", "com.google.guava:failureaccess:1.0.1", "com.google.guava:guava:29.0-jre", "org.apache.commons:commons-lang3:3.9", @@ -3376,8 +3374,8 @@ "directDependencies": [ "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.11.1", "com.fasterxml.jackson.core:jackson-annotations:2.11.4", - "org.slf4j:slf4j-api:1.7.30", "com.fasterxml.jackson.core:jackson-databind:2.11.4", + "org.slf4j:slf4j-api:1.7.32", "com.google.guava:guava:29.0-jre", "org.apache.commons:commons-lang3:3.9", "io.swagger:swagger-models:1.6.2" @@ -3402,13 +3400,13 @@ "coord": "io.swagger:swagger-models:1.6.2", "dependencies": [ "io.swagger:swagger-annotations:1.6.2", - "org.slf4j:slf4j-api:1.7.30", - "com.fasterxml.jackson.core:jackson-annotations:2.11.4" + "com.fasterxml.jackson.core:jackson-annotations:2.11.4", + "org.slf4j:slf4j-api:1.7.32" ], "directDependencies": [ "com.fasterxml.jackson.core:jackson-annotations:2.11.4", "io.swagger:swagger-annotations:1.6.2", - "org.slf4j:slf4j-api:1.7.30" + "org.slf4j:slf4j-api:1.7.32" ], "exclusions": [ "org.slf4j:slf4j-log4j12", @@ -3949,18 +3947,18 @@ "dependencies": [ "com.fasterxml.jackson.core:jackson-core:2.11.4", "com.fasterxml.jackson.core:jackson-annotations:2.11.4", - "org.slf4j:slf4j-api:1.7.30", "org.apache.commons:commons-compress:1.20", "com.fasterxml.jackson.core:jackson-databind:2.11.4", "org.apache.velocity:velocity-engine-core:2.2", "org.apache.avro:avro:1.10.0", + "org.slf4j:slf4j-api:1.7.32", "org.apache.commons:commons-lang3:3.9" ], "directDependencies": [ - "org.slf4j:slf4j-api:1.7.30", "com.fasterxml.jackson.core:jackson-databind:2.11.4", "org.apache.velocity:velocity-engine-core:2.2", "org.apache.avro:avro:1.10.0", + "org.slf4j:slf4j-api:1.7.32", "org.apache.commons:commons-lang3:3.9" ], "exclusions": [ @@ -3984,13 +3982,13 @@ "com.fasterxml.jackson.core:jackson-core:2.11.4", "com.fasterxml.jackson.core:jackson-annotations:2.11.4", "org.apache.avro:avro-ipc:1.10.0", - "org.slf4j:slf4j-api:1.7.30", "org.eclipse.jetty:jetty-http:9.4.39.v20210325", "org.apache.commons:commons-compress:1.20", "com.fasterxml.jackson.core:jackson-databind:2.11.4", "org.apache.velocity:velocity-engine-core:2.2", "org.apache.avro:avro:1.10.0", "org.eclipse.jetty:jetty-server:9.4.39.v20210325", + "org.slf4j:slf4j-api:1.7.32", "org.xerial.snappy:snappy-java:1.1.7.7", "org.tukaani:xz:1.8", "javax.annotation:javax.annotation-api:1.3.2", @@ -4005,9 +4003,9 @@ ], "directDependencies": [ "org.apache.avro:avro-ipc:1.10.0", - "org.slf4j:slf4j-api:1.7.30", "org.apache.avro:avro:1.10.0", "org.eclipse.jetty:jetty-server:9.4.39.v20210325", + "org.slf4j:slf4j-api:1.7.32", "org.eclipse.jetty:jetty-servlet:9.4.39.v20210325", "org.eclipse.jetty:jetty-util:9.4.39.v20210325" ], @@ -4031,11 +4029,11 @@ "dependencies": [ "com.fasterxml.jackson.core:jackson-core:2.11.4", "com.fasterxml.jackson.core:jackson-annotations:2.11.4", - "org.slf4j:slf4j-api:1.7.30", "org.apache.commons:commons-compress:1.20", "com.fasterxml.jackson.core:jackson-databind:2.11.4", "org.apache.velocity:velocity-engine-core:2.2", "org.apache.avro:avro:1.10.0", + "org.slf4j:slf4j-api:1.7.32", "org.xerial.snappy:snappy-java:1.1.7.7", "org.tukaani:xz:1.8", "javax.annotation:javax.annotation-api:1.3.2", @@ -4044,10 +4042,10 @@ ], "directDependencies": [ "com.fasterxml.jackson.core:jackson-core:2.11.4", - "org.slf4j:slf4j-api:1.7.30", "com.fasterxml.jackson.core:jackson-databind:2.11.4", "org.apache.velocity:velocity-engine-core:2.2", "org.apache.avro:avro:1.10.0", + "org.slf4j:slf4j-api:1.7.32", "org.xerial.snappy:snappy-java:1.1.7.7", "org.tukaani:xz:1.8", "javax.annotation:javax.annotation-api:1.3.2", @@ -4075,13 +4073,13 @@ "com.fasterxml.jackson.core:jackson-core:2.11.4", "com.fasterxml.jackson.core:jackson-annotations:2.11.4", "org.apache.avro:avro-ipc:1.10.0", - "org.slf4j:slf4j-api:1.7.30", "org.eclipse.jetty:jetty-http:9.4.39.v20210325", "org.apache.commons:commons-compress:1.20", "com.fasterxml.jackson.core:jackson-databind:2.11.4", "org.apache.velocity:velocity-engine-core:2.2", "org.apache.avro:avro:1.10.0", "org.eclipse.jetty:jetty-server:9.4.39.v20210325", + "org.slf4j:slf4j-api:1.7.32", "org.xerial.snappy:snappy-java:1.1.7.7", "org.tukaani:xz:1.8", "javax.annotation:javax.annotation-api:1.3.2", @@ -4098,7 +4096,7 @@ "com.fasterxml.jackson.core:jackson-core:2.11.4", "org.apache.avro:avro-ipc:1.10.0", "org.apache.avro:avro-ipc-jetty:1.10.0", - "org.slf4j:slf4j-api:1.7.30" + "org.slf4j:slf4j-api:1.7.32" ], "exclusions": [ "ch.qos.logback:logback-classic", @@ -4122,7 +4120,6 @@ "com.fasterxml.jackson.core:jackson-core:2.11.4", "com.fasterxml.jackson.core:jackson-annotations:2.11.4", "org.apache.avro:avro-ipc:1.10.0", - "org.slf4j:slf4j-api:1.7.30", "org.eclipse.jetty:jetty-http:9.4.39.v20210325", "org.apache.commons:commons-compress:1.20", "com.fasterxml.jackson.core:jackson-databind:2.11.4", @@ -4130,6 +4127,7 @@ "org.apache.avro:trevni-core:1.10.0", "org.apache.avro:avro:1.10.0", "org.eclipse.jetty:jetty-server:9.4.39.v20210325", + "org.slf4j:slf4j-api:1.7.32", "org.xerial.snappy:snappy-java:1.1.7.7", "org.tukaani:xz:1.8", "javax.annotation:javax.annotation-api:1.3.2", @@ -4152,9 +4150,9 @@ "directDependencies": [ "com.fasterxml.jackson.core:jackson-core:2.11.4", "org.apache.avro:avro-ipc:1.10.0", - "org.slf4j:slf4j-api:1.7.30", "org.apache.avro:trevni-core:1.10.0", "org.apache.avro:avro:1.10.0", + "org.slf4j:slf4j-api:1.7.32", "org.xerial.snappy:snappy-java:1.1.7.7", "org.tukaani:xz:1.8", "org.apache.avro:trevni-core:jar:tests:1.10.0", @@ -4186,15 +4184,15 @@ "dependencies": [ "com.fasterxml.jackson.core:jackson-core:2.11.4", "com.fasterxml.jackson.core:jackson-annotations:2.11.4", - "org.slf4j:slf4j-api:1.7.30", "org.apache.commons:commons-compress:1.20", - "com.fasterxml.jackson.core:jackson-databind:2.11.4" + "com.fasterxml.jackson.core:jackson-databind:2.11.4", + "org.slf4j:slf4j-api:1.7.32" ], "directDependencies": [ "com.fasterxml.jackson.core:jackson-core:2.11.4", "com.fasterxml.jackson.core:jackson-databind:2.11.4", "org.apache.commons:commons-compress:1.20", - "org.slf4j:slf4j-api:1.7.30" + "org.slf4j:slf4j-api:1.7.32" ], "exclusions": [ "ch.qos.logback:logback-classic", @@ -4218,7 +4216,6 @@ "com.fasterxml.jackson.core:jackson-core:2.11.4", "com.fasterxml.jackson.core:jackson-annotations:2.11.4", "org.apache.avro:avro-ipc:1.10.0", - "org.slf4j:slf4j-api:1.7.30", "org.eclipse.jetty:jetty-http:9.4.39.v20210325", "org.apache.commons:commons-compress:1.20", "com.fasterxml.jackson.core:jackson-databind:2.11.4", @@ -4226,6 +4223,7 @@ "org.apache.avro:trevni-core:1.10.0", "org.apache.avro:avro:1.10.0", "org.eclipse.jetty:jetty-server:9.4.39.v20210325", + "org.slf4j:slf4j-api:1.7.32", "org.xerial.snappy:snappy-java:1.1.7.7", "org.tukaani:xz:1.8", "javax.annotation:javax.annotation-api:1.3.2", @@ -4243,7 +4241,7 @@ "org.apache.avro:avro:1.10.0", "org.apache.avro:avro-mapred:1.10.0", "org.apache.avro:trevni-core:1.10.0", - "org.slf4j:slf4j-api:1.7.30" + "org.slf4j:slf4j-api:1.7.32" ], "exclusions": [ "ch.qos.logback:logback-classic", @@ -4267,7 +4265,6 @@ "com.fasterxml.jackson.core:jackson-core:2.11.4", "com.fasterxml.jackson.core:jackson-annotations:2.11.4", "org.apache.avro:avro-ipc:1.10.0", - "org.slf4j:slf4j-api:1.7.30", "org.eclipse.jetty:jetty-http:9.4.39.v20210325", "org.apache.commons:commons-compress:1.20", "com.fasterxml.jackson.core:jackson-databind:2.11.4", @@ -4275,6 +4272,7 @@ "org.apache.avro:trevni-core:1.10.0", "org.apache.avro:avro:1.10.0", "org.eclipse.jetty:jetty-server:9.4.39.v20210325", + "org.slf4j:slf4j-api:1.7.32", "org.xerial.snappy:snappy-java:1.1.7.7", "org.tukaani:xz:1.8", "javax.annotation:javax.annotation-api:1.3.2", @@ -4292,7 +4290,7 @@ "org.apache.avro:avro:1.10.0", "org.apache.avro:avro-mapred:1.10.0", "org.apache.avro:trevni-core:1.10.0", - "org.slf4j:slf4j-api:1.7.30" + "org.slf4j:slf4j-api:1.7.32" ], "exclusions": [ "ch.qos.logback:logback-classic", @@ -4313,12 +4311,12 @@ "coord": "org.apache.avro:trevni-core:1.10.0", "dependencies": [ "org.apache.commons:commons-compress:1.20", - "org.slf4j:slf4j-api:1.7.30", + "org.slf4j:slf4j-api:1.7.32", "org.xerial.snappy:snappy-java:1.1.7.7" ], "directDependencies": [ "org.apache.commons:commons-compress:1.20", - "org.slf4j:slf4j-api:1.7.30", + "org.slf4j:slf4j-api:1.7.32", "org.xerial.snappy:snappy-java:1.1.7.7" ], "exclusions": [ @@ -4340,12 +4338,12 @@ "coord": "org.apache.avro:trevni-core:jar:tests:1.10.0", "dependencies": [ "org.apache.commons:commons-compress:1.20", - "org.slf4j:slf4j-api:1.7.30", + "org.slf4j:slf4j-api:1.7.32", "org.xerial.snappy:snappy-java:1.1.7.7" ], "directDependencies": [ "org.apache.commons:commons-compress:1.20", - "org.slf4j:slf4j-api:1.7.30", + "org.slf4j:slf4j-api:1.7.32", "org.xerial.snappy:snappy-java:1.1.7.7" ], "exclusions": [ @@ -4407,7 +4405,6 @@ "com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava", "com.google.j2objc:j2objc-annotations:1.3", "com.google.code.findbugs:jsr305:3.0.2", - "org.slf4j:slf4j-api:1.7.30", "io.netty:netty-codec:4.1.50.Final", "io.netty:netty-transport-native-unix-common:4.1.50.Final", "log4j:log4j:1.2.17", @@ -4415,6 +4412,7 @@ "io.netty:netty-handler:4.1.50.Final", "io.netty:netty-common:4.1.50.Final", "com.google.errorprone:error_prone_annotations:2.3.4", + "org.slf4j:slf4j-api:1.7.32", "io.netty:netty-buffer:4.1.50.Final", "org.apache.yetus:audience-annotations:0.5.0", "org.apache.zookeeper:zookeeper:3.5.9", @@ -4493,8 +4491,8 @@ { "coord": "org.apache.kafka:connect-api:2.7.0", "dependencies": [ - "org.slf4j:slf4j-api:1.7.30", "org.lz4:lz4-java:1.7.1", + "org.slf4j:slf4j-api:1.7.32", "org.xerial.snappy:snappy-java:1.1.7.7", "org.apache.kafka:kafka-clients:6.1.1-ccs", "javax.ws.rs:javax.ws.rs-api:2.1.1", @@ -4503,7 +4501,7 @@ "directDependencies": [ "javax.ws.rs:javax.ws.rs-api:2.1.1", "org.apache.kafka:kafka-clients:6.1.1-ccs", - "org.slf4j:slf4j-api:1.7.30" + "org.slf4j:slf4j-api:1.7.32" ], "exclusions": [ "ch.qos.logback:logback-classic", @@ -4525,10 +4523,10 @@ "dependencies": [ "com.fasterxml.jackson.core:jackson-core:2.11.4", "com.fasterxml.jackson.core:jackson-annotations:2.11.4", - "org.slf4j:slf4j-api:1.7.30", "org.lz4:lz4-java:1.7.1", "com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.11.4", "com.fasterxml.jackson.core:jackson-databind:2.11.4", + "org.slf4j:slf4j-api:1.7.32", "org.xerial.snappy:snappy-java:1.1.7.7", "org.apache.kafka:kafka-clients:6.1.1-ccs", "org.apache.kafka:connect-api:2.7.0", @@ -4538,7 +4536,7 @@ "com.fasterxml.jackson.core:jackson-databind:2.11.4", "com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.11.4", "org.apache.kafka:connect-api:2.7.0", - "org.slf4j:slf4j-api:1.7.30" + "org.slf4j:slf4j-api:1.7.32" ], "exclusions": [ "org.slf4j:slf4j-log4j12", @@ -4559,8 +4557,8 @@ { "coord": "org.apache.kafka:connect-transforms:2.7.0", "dependencies": [ - "org.slf4j:slf4j-api:1.7.30", "org.lz4:lz4-java:1.7.1", + "org.slf4j:slf4j-api:1.7.32", "org.xerial.snappy:snappy-java:1.1.7.7", "org.apache.kafka:kafka-clients:6.1.1-ccs", "javax.ws.rs:javax.ws.rs-api:2.1.1", @@ -4569,7 +4567,7 @@ ], "directDependencies": [ "org.apache.kafka:connect-api:2.7.0", - "org.slf4j:slf4j-api:1.7.30" + "org.slf4j:slf4j-api:1.7.32" ], "exclusions": [ "ch.qos.logback:logback-classic", @@ -4589,15 +4587,15 @@ { "coord": "org.apache.kafka:kafka-clients:6.1.1-ccs", "dependencies": [ - "org.slf4j:slf4j-api:1.7.30", "com.github.luben:zstd-jni:1.4.5-6", "org.lz4:lz4-java:1.7.1", + "org.slf4j:slf4j-api:1.7.32", "org.xerial.snappy:snappy-java:1.1.7.7" ], "directDependencies": [ "com.github.luben:zstd-jni:1.4.5-6", "org.lz4:lz4-java:1.7.1", - "org.slf4j:slf4j-api:1.7.30", + "org.slf4j:slf4j-api:1.7.32", "org.xerial.snappy:snappy-java:1.1.7.7" ], "exclusions": [ @@ -4624,15 +4622,15 @@ { "coord": "org.apache.kafka:kafka-clients:jar:test:6.1.1-ccs", "dependencies": [ - "org.slf4j:slf4j-api:1.7.30", "com.github.luben:zstd-jni:1.4.5-6", "org.lz4:lz4-java:1.7.1", + "org.slf4j:slf4j-api:1.7.32", "org.xerial.snappy:snappy-java:1.1.7.7" ], "directDependencies": [ "com.github.luben:zstd-jni:1.4.5-6", "org.lz4:lz4-java:1.7.1", - "org.slf4j:slf4j-api:1.7.30", + "org.slf4j:slf4j-api:1.7.32", "org.xerial.snappy:snappy-java:1.1.7.7" ], "exclusions": [ @@ -4655,9 +4653,9 @@ "dependencies": [ "com.fasterxml.jackson.core:jackson-core:2.11.4", "com.fasterxml.jackson.core:jackson-annotations:2.11.4", - "org.slf4j:slf4j-api:1.7.30", "org.lz4:lz4-java:1.7.1", "com.fasterxml.jackson.core:jackson-databind:2.11.4", + "org.slf4j:slf4j-api:1.7.32", "org.xerial.snappy:snappy-java:1.1.7.7", "org.apache.kafka:kafka-clients:6.1.1-ccs", "com.github.luben:zstd-jni:1.4.5-6" @@ -4665,7 +4663,7 @@ "directDependencies": [ "com.fasterxml.jackson.core:jackson-databind:2.11.4", "org.apache.kafka:kafka-clients:6.1.1-ccs", - "org.slf4j:slf4j-api:1.7.30" + "org.slf4j:slf4j-api:1.7.32" ], "exclusions": [ "org.slf4j:slf4j-log4j12", @@ -4694,11 +4692,11 @@ "org.apache.kafka:connect-json:2.7.0", "com.fasterxml.jackson.core:jackson-core:2.11.4", "com.fasterxml.jackson.core:jackson-annotations:2.11.4", - "org.slf4j:slf4j-api:1.7.30", "org.rocksdb:rocksdbjni:5.18.4", "org.lz4:lz4-java:1.7.1", "com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.11.4", "com.fasterxml.jackson.core:jackson-databind:2.11.4", + "org.slf4j:slf4j-api:1.7.32", "org.xerial.snappy:snappy-java:1.1.7.7", "org.apache.kafka:kafka-clients:6.1.1-ccs", "org.apache.kafka:connect-api:2.7.0", @@ -4708,7 +4706,7 @@ "org.apache.kafka:connect-json:2.7.0", "org.apache.kafka:kafka-clients:6.1.1-ccs", "org.rocksdb:rocksdbjni:5.18.4", - "org.slf4j:slf4j-api:1.7.30" + "org.slf4j:slf4j-api:1.7.32" ], "exclusions": [ "ch.qos.logback:logback-classic", @@ -4735,7 +4733,6 @@ "com.fasterxml.jackson.core:jackson-core:2.11.4", "com.fasterxml.jackson.core:jackson-annotations:2.11.4", "org.scala-lang.modules:scala-collection-compat_2.13:2.2.0", - "org.slf4j:slf4j-api:1.7.30", "org.scala-lang:scala-library:2.13.3", "io.netty:netty-codec:4.1.50.Final", "io.netty:netty-transport-native-unix-common:4.1.50.Final", @@ -4745,6 +4742,7 @@ "com.fasterxml.jackson.core:jackson-databind:2.11.4", "io.netty:netty-handler:4.1.50.Final", "io.netty:netty-common:4.1.50.Final", + "org.slf4j:slf4j-api:1.7.32", "io.netty:netty-buffer:4.1.50.Final", "org.xerial.snappy:snappy-java:1.1.7.7", "org.apache.kafka:kafka-raft:6.1.1-ccs", @@ -4767,10 +4765,10 @@ "com.typesafe.scala-logging:scala-logging_2.13:3.9.2", "org.scala-lang:scala-reflect:2.13.3", "org.scala-lang.modules:scala-collection-compat_2.13:2.2.0", - "org.slf4j:slf4j-api:1.7.30", "org.scala-lang:scala-library:2.13.3", "com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.11.4", "com.fasterxml.jackson.core:jackson-databind:2.11.4", + "org.slf4j:slf4j-api:1.7.32", "org.apache.kafka:kafka-raft:6.1.1-ccs", "org.apache.kafka:kafka-clients:6.1.1-ccs", "com.yammer.metrics:metrics-core:2.2.0", @@ -4796,7 +4794,7 @@ "url": "https://packages.confluent.io/maven/org/apache/kafka/kafka_2.13/6.1.1-ccs/kafka_2.13-6.1.1-ccs.jar" }, { - "coord": "org.apache.logging.log4j:log4j-api:2.12.1", + "coord": "org.apache.logging.log4j:log4j-api:2.15.0", "dependencies": [], "directDependencies": [], "exclusions": [ @@ -4805,22 +4803,22 @@ "org.springframework.boot:spring-boot-starter-logging", "org.slf4j:slf4j-log4j12" ], - "file": "v1/https/repo1.maven.org/maven2/org/apache/logging/log4j/log4j-api/2.12.1/log4j-api-2.12.1.jar", + "file": "v1/https/repo1.maven.org/maven2/org/apache/logging/log4j/log4j-api/2.15.0/log4j-api-2.15.0.jar", "mirror_urls": [ - "https://packages.confluent.io/maven/org/apache/logging/log4j/log4j-api/2.12.1/log4j-api-2.12.1.jar", - "https://repo1.maven.org/maven2/org/apache/logging/log4j/log4j-api/2.12.1/log4j-api-2.12.1.jar", - "https://jitpack.io/org/apache/logging/log4j/log4j-api/2.12.1/log4j-api-2.12.1.jar" + "https://packages.confluent.io/maven/org/apache/logging/log4j/log4j-api/2.15.0/log4j-api-2.15.0.jar", + "https://repo1.maven.org/maven2/org/apache/logging/log4j/log4j-api/2.15.0/log4j-api-2.15.0.jar", + "https://jitpack.io/org/apache/logging/log4j/log4j-api/2.15.0/log4j-api-2.15.0.jar" ], - "sha256": "429534d03bdb728879ab551d469e26f6f7ff4c8a8627f59ac68ab6ef26063515", - "url": "https://repo1.maven.org/maven2/org/apache/logging/log4j/log4j-api/2.12.1/log4j-api-2.12.1.jar" + "sha256": "c8c33e7e8e05496dae69cf0caac8c3092cffd937a164526e92922d2d566d0a55", + "url": "https://repo1.maven.org/maven2/org/apache/logging/log4j/log4j-api/2.15.0/log4j-api-2.15.0.jar" }, { - "coord": "org.apache.logging.log4j:log4j-core:2.12.1", + "coord": "org.apache.logging.log4j:log4j-core:2.15.0", "dependencies": [ - "org.apache.logging.log4j:log4j-api:2.12.1" + "org.apache.logging.log4j:log4j-api:2.15.0" ], "directDependencies": [ - "org.apache.logging.log4j:log4j-api:2.12.1" + "org.apache.logging.log4j:log4j-api:2.15.0" ], "exclusions": [ "ch.qos.logback:logback-classic", @@ -4828,26 +4826,26 @@ "org.springframework.boot:spring-boot-starter-logging", "org.slf4j:slf4j-log4j12" ], - "file": "v1/https/repo1.maven.org/maven2/org/apache/logging/log4j/log4j-core/2.12.1/log4j-core-2.12.1.jar", + "file": "v1/https/repo1.maven.org/maven2/org/apache/logging/log4j/log4j-core/2.15.0/log4j-core-2.15.0.jar", "mirror_urls": [ - "https://packages.confluent.io/maven/org/apache/logging/log4j/log4j-core/2.12.1/log4j-core-2.12.1.jar", - "https://repo1.maven.org/maven2/org/apache/logging/log4j/log4j-core/2.12.1/log4j-core-2.12.1.jar", - "https://jitpack.io/org/apache/logging/log4j/log4j-core/2.12.1/log4j-core-2.12.1.jar" + "https://packages.confluent.io/maven/org/apache/logging/log4j/log4j-core/2.15.0/log4j-core-2.15.0.jar", + "https://repo1.maven.org/maven2/org/apache/logging/log4j/log4j-core/2.15.0/log4j-core-2.15.0.jar", + "https://jitpack.io/org/apache/logging/log4j/log4j-core/2.15.0/log4j-core-2.15.0.jar" ], - "sha256": "885e31a14fc71cb4849e93564d26a221c685a789379ef63cb2d082cedf3c2235", - "url": "https://repo1.maven.org/maven2/org/apache/logging/log4j/log4j-core/2.12.1/log4j-core-2.12.1.jar" + "sha256": "419a8512895971b7b4f4f33e620d361254e5c9552b904b0474b09ddd4a6a220b", + "url": "https://repo1.maven.org/maven2/org/apache/logging/log4j/log4j-core/2.15.0/log4j-core-2.15.0.jar" }, { - "coord": "org.apache.logging.log4j:log4j-slf4j-impl:2.12.1", + "coord": "org.apache.logging.log4j:log4j-slf4j-impl:2.15.0", "dependencies": [ - "org.apache.logging.log4j:log4j-core:2.12.1", - "org.slf4j:slf4j-api:1.7.30", - "org.apache.logging.log4j:log4j-api:2.12.1" + "org.apache.logging.log4j:log4j-core:2.15.0", + "org.apache.logging.log4j:log4j-api:2.15.0", + "org.slf4j:slf4j-api:1.7.32" ], "directDependencies": [ - "org.apache.logging.log4j:log4j-api:2.12.1", - "org.apache.logging.log4j:log4j-core:2.12.1", - "org.slf4j:slf4j-api:1.7.30" + "org.apache.logging.log4j:log4j-api:2.15.0", + "org.apache.logging.log4j:log4j-core:2.15.0", + "org.slf4j:slf4j-api:1.7.32" ], "exclusions": [ "ch.qos.logback:logback-classic", @@ -4855,14 +4853,14 @@ "org.springframework.boot:spring-boot-starter-logging", "org.slf4j:slf4j-log4j12" ], - "file": "v1/https/repo1.maven.org/maven2/org/apache/logging/log4j/log4j-slf4j-impl/2.12.1/log4j-slf4j-impl-2.12.1.jar", + "file": "v1/https/repo1.maven.org/maven2/org/apache/logging/log4j/log4j-slf4j-impl/2.15.0/log4j-slf4j-impl-2.15.0.jar", "mirror_urls": [ - "https://packages.confluent.io/maven/org/apache/logging/log4j/log4j-slf4j-impl/2.12.1/log4j-slf4j-impl-2.12.1.jar", - "https://repo1.maven.org/maven2/org/apache/logging/log4j/log4j-slf4j-impl/2.12.1/log4j-slf4j-impl-2.12.1.jar", - "https://jitpack.io/org/apache/logging/log4j/log4j-slf4j-impl/2.12.1/log4j-slf4j-impl-2.12.1.jar" + "https://packages.confluent.io/maven/org/apache/logging/log4j/log4j-slf4j-impl/2.15.0/log4j-slf4j-impl-2.15.0.jar", + "https://repo1.maven.org/maven2/org/apache/logging/log4j/log4j-slf4j-impl/2.15.0/log4j-slf4j-impl-2.15.0.jar", + "https://jitpack.io/org/apache/logging/log4j/log4j-slf4j-impl/2.15.0/log4j-slf4j-impl-2.15.0.jar" ], - "sha256": "3d9620afc3cd58527a182b70e7c111b7289046989c0d04a50e46b0ec31dc138a", - "url": "https://repo1.maven.org/maven2/org/apache/logging/log4j/log4j-slf4j-impl/2.12.1/log4j-slf4j-impl-2.12.1.jar" + "sha256": "fd654a1aa0b34196be41aa9e1e53362493f1a89109ff931c79ad2d58cc90eaa6", + "url": "https://repo1.maven.org/maven2/org/apache/logging/log4j/log4j-slf4j-impl/2.15.0/log4j-slf4j-impl-2.15.0.jar" }, { "coord": "org.apache.lucene:lucene-analyzers-common:8.7.0", @@ -4976,10 +4974,10 @@ { "coord": "org.apache.mina:mina-core:2.1.3", "dependencies": [ - "org.slf4j:slf4j-api:1.7.30" + "org.slf4j:slf4j-api:1.7.32" ], "directDependencies": [ - "org.slf4j:slf4j-api:1.7.30" + "org.slf4j:slf4j-api:1.7.32" ], "exclusions": [ "ch.qos.logback:logback-classic", @@ -4999,12 +4997,12 @@ { "coord": "org.apache.velocity:velocity-engine-core:2.2", "dependencies": [ - "org.slf4j:slf4j-api:1.7.30", - "org.apache.commons:commons-lang3:3.9" + "org.apache.commons:commons-lang3:3.9", + "org.slf4j:slf4j-api:1.7.32" ], "directDependencies": [ "org.apache.commons:commons-lang3:3.9", - "org.slf4j:slf4j-api:1.7.30" + "org.slf4j:slf4j-api:1.7.32" ], "exclusions": [ "ch.qos.logback:logback-classic", @@ -5080,12 +5078,12 @@ { "coord": "org.apache.zookeeper:zookeeper:3.5.9", "dependencies": [ - "org.slf4j:slf4j-api:1.7.30", "io.netty:netty-codec:4.1.50.Final", "io.netty:netty-transport-native-unix-common:4.1.50.Final", "io.netty:netty-transport-native-epoll:4.1.50.Final", "io.netty:netty-handler:4.1.50.Final", "io.netty:netty-common:4.1.50.Final", + "org.slf4j:slf4j-api:1.7.32", "io.netty:netty-buffer:4.1.50.Final", "org.apache.yetus:audience-annotations:0.5.0", "org.apache.zookeeper:zookeeper-jute:3.5.9", @@ -5093,9 +5091,9 @@ "io.netty:netty-transport:4.1.50.Final" ], "directDependencies": [ - "org.slf4j:slf4j-api:1.7.30", "io.netty:netty-transport-native-epoll:4.1.50.Final", "io.netty:netty-handler:4.1.50.Final", + "org.slf4j:slf4j-api:1.7.32", "org.apache.yetus:audience-annotations:0.5.0", "org.apache.zookeeper:zookeeper-jute:3.5.9" ], @@ -7448,23 +7446,53 @@ "url": "https://repo1.maven.org/maven2/org/skyscreamer/jsonassert/1.5.0/jsonassert-1.5.0.jar" }, { - "coord": "org.slf4j:slf4j-api:1.7.30", + "coord": "org.slf4j:slf4j-api:1.7.32", "dependencies": [], "directDependencies": [], + "exclusions": [ + "*:jline", + "*:jmxtools", + "*:log4j", + "*:slf4j-log4j12", + "*:jms", + "*:mail", + "*:javax", + "*:jmxri", + "org.springframework.boot:spring-boot-starter-tomcat", + "ch.qos.logback:logback-classic", + "org.springframework.boot:spring-boot-starter-logging" + ], + "file": "v1/https/repo1.maven.org/maven2/org/slf4j/slf4j-api/1.7.32/slf4j-api-1.7.32.jar", + "mirror_urls": [ + "https://packages.confluent.io/maven/org/slf4j/slf4j-api/1.7.32/slf4j-api-1.7.32.jar", + "https://repo1.maven.org/maven2/org/slf4j/slf4j-api/1.7.32/slf4j-api-1.7.32.jar", + "https://jitpack.io/org/slf4j/slf4j-api/1.7.32/slf4j-api-1.7.32.jar" + ], + "sha256": "3624f8474c1af46d75f98bc097d7864a323c81b3808aa43689a6e1c601c027be", + "url": "https://repo1.maven.org/maven2/org/slf4j/slf4j-api/1.7.32/slf4j-api-1.7.32.jar" + }, + { + "coord": "org.slf4j:slf4j-nop:1.7.32", + "dependencies": [ + "org.slf4j:slf4j-api:1.7.32" + ], + "directDependencies": [ + "org.slf4j:slf4j-api:1.7.32" + ], "exclusions": [ "ch.qos.logback:logback-classic", "org.springframework.boot:spring-boot-starter-tomcat", "org.springframework.boot:spring-boot-starter-logging", "org.slf4j:slf4j-log4j12" ], - "file": "v1/https/repo1.maven.org/maven2/org/slf4j/slf4j-api/1.7.30/slf4j-api-1.7.30.jar", + "file": "v1/https/repo1.maven.org/maven2/org/slf4j/slf4j-nop/1.7.32/slf4j-nop-1.7.32.jar", "mirror_urls": [ - "https://packages.confluent.io/maven/org/slf4j/slf4j-api/1.7.30/slf4j-api-1.7.30.jar", - "https://repo1.maven.org/maven2/org/slf4j/slf4j-api/1.7.30/slf4j-api-1.7.30.jar", - "https://jitpack.io/org/slf4j/slf4j-api/1.7.30/slf4j-api-1.7.30.jar" + "https://packages.confluent.io/maven/org/slf4j/slf4j-nop/1.7.32/slf4j-nop-1.7.32.jar", + "https://repo1.maven.org/maven2/org/slf4j/slf4j-nop/1.7.32/slf4j-nop-1.7.32.jar", + "https://jitpack.io/org/slf4j/slf4j-nop/1.7.32/slf4j-nop-1.7.32.jar" ], - "sha256": "cdba07964d1bb40a0761485c6b1e8c2f8fd9eb1d19c53928ac0d7f9510105c57", - "url": "https://repo1.maven.org/maven2/org/slf4j/slf4j-api/1.7.30/slf4j-api-1.7.30.jar" + "sha256": "f2d20b60b2b87fc1ca904e0cd4520deeeebf8a9c5b723ae2493cc4219389d782", + "url": "https://repo1.maven.org/maven2/org/slf4j/slf4j-nop/1.7.32/slf4j-nop-1.7.32.jar" }, { "coord": "org.springframework.boot:spring-boot-actuator-autoconfigure:2.4.5", @@ -7831,7 +7859,6 @@ "org.springframework.boot:spring-boot-autoconfigure:2.4.5", "org.ow2.asm:asm:9.0", "org.springframework:spring-beans:5.3.6", - "org.slf4j:slf4j-api:1.7.30", "org.springframework:spring-jcl:5.3.6", "org.objenesis:objenesis:3.1", "org.mockito:mockito-junit-jupiter:3.6.28", @@ -7840,6 +7867,7 @@ "org.springframework:spring-context:5.3.6", "org.opentest4j:opentest4j:1.2.0", "org.springframework:spring-expression:5.3.6", + "org.slf4j:slf4j-api:1.7.32", "jakarta.annotation:jakarta.annotation-api:1.3.5", "org.junit.jupiter:junit-jupiter-api:5.7.1", "org.springframework.boot:spring-boot-test:2.4.5", diff --git a/package.json b/package.json index f888188381..2e3e2d6a84 100644 --- a/package.json +++ b/package.json @@ -37,12 +37,12 @@ }, "devDependencies": { "@babel/core": "7.16.0", - "@babel/plugin-proposal-class-properties": "^7.14.5", + "@babel/plugin-proposal-class-properties": "^7.16.5", "@babel/plugin-proposal-object-rest-spread": "^7.14.5", "@babel/plugin-transform-spread": "^7.16.0", "@babel/preset-env": "^7.15.8", "@babel/preset-react": "^7.14.5", - "@babel/preset-typescript": "^7.15.0", + "@babel/preset-typescript": "^7.16.0", "@bazel/typescript": "^4.3.0", "@svgr/webpack": "^5.5.0", "@types/lodash-es": "^4.17.5", @@ -59,15 +59,15 @@ "file-loader": "^6.2.0", "html-webpack-plugin": "^5.5.0", "minimist": "^1.2.5", - "prettier": "^2.4.1", + "prettier": "^2.5.1", "react-hot-loader": "^4.13.0", "sass": "^1.43.4", "sass-loader": "^12.3.0", "style-loader": "^3.3.1", - "terser-webpack-plugin": "^5.2.4", + "terser-webpack-plugin": "^5.2.5", "typescript": "4.3.5", "url-loader": "^4.1.1", - "webpack": "5.59.1", + "webpack": "5.64.4", "webpack-bundle-analyzer": "^4.5.0", "webpack-cli": "^4.8.0", "webpack-dev-server": "^3.11.2" diff --git a/repositories.bzl b/repositories.bzl index 1a7cb00cc0..e09300a127 100644 --- a/repositories.bzl +++ b/repositories.bzl @@ -22,9 +22,10 @@ airy_jvm_deps = [ "javax.activation:javax.activation-api:1.2.0", "javax.validation:validation-api:2.0.1.Final", "javax.xml.bind:jaxb-api:2.3.1", - "org.apache.logging.log4j:log4j-core:2.12.1", - "org.apache.logging.log4j:log4j-slf4j-impl:2.12.1", - "org.slf4j:slf4j-api:1.7.29", + "org.apache.logging.log4j:log4j-core:2.15.0", + "org.apache.logging.log4j:log4j-slf4j-impl:2.15.0", + "org.slf4j:slf4j-nop:1.7.32", + "org.slf4j:slf4j-api:1.7.32", "org.apache.avro:avro-tools:1.10.0", "org.apache.avro:avro:1.10.0", "org.apache.curator:curator-test:4.2.0", @@ -63,6 +64,8 @@ airy_jvm_deps = [ "org.springframework:spring-websocket:5.3.6", "org.springframework.security:spring-security-core:5.4.6", "org.rocksdb:rocksdbjni:5.18.3", + "com.segment.analytics.java:analytics-core:3.1.3", + "com.segment.analytics.java:analytics:3.1.3", ] excluded_artifacts = [ diff --git a/yarn.lock b/yarn.lock index 418a3a2598..cce60fa8b8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -56,6 +56,15 @@ jsesc "^2.5.1" source-map "^0.5.0" +"@babel/generator@^7.16.5": + version "7.16.5" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.16.5.tgz#26e1192eb8f78e0a3acaf3eede3c6fc96d22bedf" + integrity sha512-kIvCdjZqcdKqoDbVVdt5R99icaRtrtYhYK/xux5qiWCBmfdvEYMFZ68QCrpE5cbFM1JsuArUNs1ZkuKtTtUcZA== + dependencies: + "@babel/types" "^7.16.0" + jsesc "^2.5.1" + source-map "^0.5.0" + "@babel/helper-annotate-as-pure@^7.14.5", "@babel/helper-annotate-as-pure@^7.15.4": version "7.15.4" resolved "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.15.4.tgz" @@ -63,6 +72,13 @@ dependencies: "@babel/types" "^7.15.4" +"@babel/helper-annotate-as-pure@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.0.tgz#9a1f0ebcda53d9a2d00108c4ceace6a5d5f1f08d" + integrity sha512-ItmYF9vR4zA8cByDocY05o0LGUkp1zhbTQOH1NFyl5xXEqlTJQCEJjieriw+aFpxo16swMxUnUiKS7a/r4vtHg== + dependencies: + "@babel/types" "^7.16.0" + "@babel/helper-builder-binary-assignment-operator-visitor@^7.14.5": version "7.14.5" resolved "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.14.5.tgz" @@ -81,17 +97,18 @@ browserslist "^4.17.5" semver "^6.3.0" -"@babel/helper-create-class-features-plugin@^7.14.5", "@babel/helper-create-class-features-plugin@^7.15.4": - version "7.15.4" - resolved "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.15.4.tgz" - integrity sha512-7ZmzFi+DwJx6A7mHRwbuucEYpyBwmh2Ca0RvI6z2+WLZYCqV0JOaLb+u0zbtmDicebgKBZgqbYfLaKNqSgv5Pw== +"@babel/helper-create-class-features-plugin@^7.14.5", "@babel/helper-create-class-features-plugin@^7.15.4", "@babel/helper-create-class-features-plugin@^7.16.0", "@babel/helper-create-class-features-plugin@^7.16.5": + version "7.16.5" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.16.5.tgz#5d1bcd096792c1ebec6249eebc6358eec55d0cad" + integrity sha512-NEohnYA7mkB8L5JhU7BLwcBdU3j83IziR9aseMueWGeAjblbul3zzb8UvJ3a1zuBiqCMObzCJHFqKIQE6hTVmg== dependencies: - "@babel/helper-annotate-as-pure" "^7.15.4" - "@babel/helper-function-name" "^7.15.4" - "@babel/helper-member-expression-to-functions" "^7.15.4" - "@babel/helper-optimise-call-expression" "^7.15.4" - "@babel/helper-replace-supers" "^7.15.4" - "@babel/helper-split-export-declaration" "^7.15.4" + "@babel/helper-annotate-as-pure" "^7.16.0" + "@babel/helper-environment-visitor" "^7.16.5" + "@babel/helper-function-name" "^7.16.0" + "@babel/helper-member-expression-to-functions" "^7.16.5" + "@babel/helper-optimise-call-expression" "^7.16.0" + "@babel/helper-replace-supers" "^7.16.5" + "@babel/helper-split-export-declaration" "^7.16.0" "@babel/helper-create-regexp-features-plugin@^7.14.5": version "7.14.5" @@ -115,6 +132,13 @@ resolve "^1.14.2" semver "^6.1.2" +"@babel/helper-environment-visitor@^7.16.5": + version "7.16.5" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.5.tgz#f6a7f38b3c6d8b07c88faea083c46c09ef5451b8" + integrity sha512-ODQyc5AnxmZWm/R2W7fzhamOk1ey8gSguo5SGvF0zcB3uUzRpTRmM/jmLSm9bDMyPlvbyJ+PwPEK0BWIoZ9wjg== + dependencies: + "@babel/types" "^7.16.0" + "@babel/helper-explode-assignable-expression@^7.14.5": version "7.14.5" resolved "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.14.5.tgz" @@ -182,6 +206,13 @@ dependencies: "@babel/types" "^7.16.0" +"@babel/helper-member-expression-to-functions@^7.16.5": + version "7.16.5" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.16.5.tgz#1bc9f7e87354e86f8879c67b316cb03d3dc2caab" + integrity sha512-7fecSXq7ZrLE+TWshbGT+HyCLkxloWNhTbU2QM1NTI/tDqyf0oZiMcEfYtDuUDCo528EOlt39G1rftea4bRZIw== + dependencies: + "@babel/types" "^7.16.0" + "@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.14.5": version "7.15.4" resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.15.4.tgz" @@ -224,10 +255,10 @@ dependencies: "@babel/types" "^7.16.0" -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.13.0", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": - version "7.14.5" - resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz" - integrity sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ== +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.13.0", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.5", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": + version "7.16.5" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.5.tgz#afe37a45f39fce44a3d50a7958129ea5b1a5c074" + integrity sha512-59KHWHXxVA9K4HNF4sbHCf+eJeFe0Te/ZFGqBT4OjXhrwvA04sGfaEGsVTdsjoszq0YTP49RC9UKe5g8uN2RwQ== "@babel/helper-remap-async-to-generator@^7.14.5", "@babel/helper-remap-async-to-generator@^7.15.4": version "7.15.4" @@ -258,6 +289,17 @@ "@babel/traverse" "^7.16.0" "@babel/types" "^7.16.0" +"@babel/helper-replace-supers@^7.16.5": + version "7.16.5" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.16.5.tgz#96d3988bd0ab0a2d22c88c6198c3d3234ca25326" + integrity sha512-ao3seGVa/FZCMCCNDuBcqnBFSbdr8N2EW35mzojx3TwfIbdPmNK+JV6+2d5bR0Z71W5ocLnQp9en/cTF7pBJiQ== + dependencies: + "@babel/helper-environment-visitor" "^7.16.5" + "@babel/helper-member-expression-to-functions" "^7.16.5" + "@babel/helper-optimise-call-expression" "^7.16.0" + "@babel/traverse" "^7.16.5" + "@babel/types" "^7.16.0" + "@babel/helper-simple-access@^7.15.4": version "7.15.4" resolved "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.15.4.tgz" @@ -345,6 +387,11 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.16.4.tgz#d5f92f57cf2c74ffe9b37981c0e72fee7311372e" integrity sha512-6V0qdPUaiVHH3RtZeLIsc+6pDhbYzHR8ogA8w+f+Wc77DuXto19g2QUwveINoS34Uw+W8/hQDGJCx+i4n7xcng== +"@babel/parser@^7.16.5": + version "7.16.5" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.16.5.tgz#beb3af702e54d24796341ab9420fb329131ad658" + integrity sha512-+Ce7T5iPNWzfu9C1aB5tN3Lyafs5xb3Ic7vBWyZL2KXT3QSdD1dD3CvgOzPmQKoNNRt6uauc0XwNJTQtXC2/Mw== + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.15.4": version "7.15.4" resolved "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.15.4.tgz" @@ -363,13 +410,13 @@ "@babel/helper-remap-async-to-generator" "^7.15.4" "@babel/plugin-syntax-async-generators" "^7.8.4" -"@babel/plugin-proposal-class-properties@^7.14.5": - version "7.14.5" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.14.5.tgz" - integrity sha512-q/PLpv5Ko4dVc1LYMpCY7RVAAO4uk55qPwrIuJ5QJ8c6cVuAmhu7I/49JOppXL6gXf7ZHzpRVEUZdYoPLM04Gg== +"@babel/plugin-proposal-class-properties@^7.14.5", "@babel/plugin-proposal-class-properties@^7.16.5": + version "7.16.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.5.tgz#3269f44b89122110f6339806e05d43d84106468a" + integrity sha512-pJD3HjgRv83s5dv1sTnDbZOaTjghKEz8KUn1Kbh2eAIRhGuyQ1XSeI4xVXU3UlIEVA3DAyIdxqT1eRn7Wcn55A== dependencies: - "@babel/helper-create-class-features-plugin" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-create-class-features-plugin" "^7.16.5" + "@babel/helper-plugin-utils" "^7.16.5" "@babel/plugin-proposal-class-static-block@^7.15.4": version "7.15.4" @@ -587,10 +634,10 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-syntax-typescript@^7.14.5": - version "7.14.5" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.14.5.tgz" - integrity sha512-u6OXzDaIXjEstBRRoBCQ/uKQKlbuaeE5in0RvWdA4pN6AhqxTIwUsnHPU1CFZA/amYObMsuWhYfRl3Ch90HD0Q== +"@babel/plugin-syntax-typescript@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.16.0.tgz#2feeb13d9334cc582ea9111d3506f773174179bb" + integrity sha512-Xv6mEXqVdaqCBfJFyeab0fH2DnUoMsDmhamxsSi4j8nLd4Vtw213WMJr55xxqipC/YVWyPY3K0blJncPYji+dQ== dependencies: "@babel/helper-plugin-utils" "^7.14.5" @@ -867,14 +914,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-typescript@^7.15.0": - version "7.15.4" - resolved "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.15.4.tgz" - integrity sha512-sM1/FEjwYjXvMwu1PJStH11kJ154zd/lpY56NQJ5qH2D0mabMv1CAy/kdvS9RP4Xgfj9fBBA3JiSLdDHgXdzOA== +"@babel/plugin-transform-typescript@^7.16.0": + version "7.16.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.16.1.tgz#cc0670b2822b0338355bc1b3d2246a42b8166409" + integrity sha512-NO4XoryBng06jjw/qWEU2LhcLJr1tWkhpMam/H4eas/CDKMX/b2/Ylb6EI256Y7+FVPCawwSM1rrJNOpDiz+Lg== dependencies: - "@babel/helper-create-class-features-plugin" "^7.15.4" + "@babel/helper-create-class-features-plugin" "^7.16.0" "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-typescript" "^7.14.5" + "@babel/plugin-syntax-typescript" "^7.16.0" "@babel/plugin-transform-unicode-escapes@^7.14.5": version "7.14.5" @@ -993,14 +1040,14 @@ "@babel/plugin-transform-react-jsx-development" "^7.14.5" "@babel/plugin-transform-react-pure-annotations" "^7.14.5" -"@babel/preset-typescript@^7.15.0": - version "7.15.0" - resolved "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.15.0.tgz" - integrity sha512-lt0Y/8V3y06Wq/8H/u0WakrqciZ7Fz7mwPDHWUJAXlABL5hiUG42BNlRXiELNjeWjO5rWmnNKlx+yzJvxezHow== +"@babel/preset-typescript@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.16.0.tgz#b0b4f105b855fb3d631ec036cdc9d1ffd1fa5eac" + integrity sha512-txegdrZYgO9DlPbv+9QOVpMnKbOtezsLHWsnsRF4AjbSIsVaujrq1qg8HK0mxQpWv0jnejt0yEoW1uWpvbrDTg== dependencies: "@babel/helper-plugin-utils" "^7.14.5" "@babel/helper-validator-option" "^7.14.5" - "@babel/plugin-transform-typescript" "^7.15.0" + "@babel/plugin-transform-typescript" "^7.16.0" "@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.13", "@babel/runtime@^7.14.5", "@babel/runtime@^7.15.4", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2": version "7.16.0" @@ -1033,6 +1080,22 @@ debug "^4.1.0" globals "^11.1.0" +"@babel/traverse@^7.16.5": + version "7.16.5" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.16.5.tgz#d7d400a8229c714a59b87624fc67b0f1fbd4b2b3" + integrity sha512-FOCODAzqUMROikDYLYxl4nmwiLlu85rNqBML/A5hKRVXG2LV8d0iMqgPzdYTcIpjZEBB7D6UDU9vxRZiriASdQ== + dependencies: + "@babel/code-frame" "^7.16.0" + "@babel/generator" "^7.16.5" + "@babel/helper-environment-visitor" "^7.16.5" + "@babel/helper-function-name" "^7.16.0" + "@babel/helper-hoist-variables" "^7.16.0" + "@babel/helper-split-export-declaration" "^7.16.0" + "@babel/parser" "^7.16.5" + "@babel/types" "^7.16.0" + debug "^4.1.0" + globals "^11.1.0" + "@babel/types@^7.12.6", "@babel/types@^7.14.5", "@babel/types@^7.15.4", "@babel/types@^7.15.6", "@babel/types@^7.16.0", "@babel/types@^7.4.4": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.16.0.tgz#db3b313804f96aadd0b776c4823e127ad67289ba" @@ -2244,18 +2307,7 @@ braces@^3.0.1, braces@~3.0.2: dependencies: fill-range "^7.0.1" -browserslist@^4.14.5, browserslist@^4.17.3: - version "4.17.4" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.17.4.tgz#72e2508af2a403aec0a49847ef31bd823c57ead4" - integrity sha512-Zg7RpbZpIJRW3am9Lyckue7PLytvVxxhJj1CaJVlCWENsGEAOlnlt8X0ZxGRPp7Bt9o8tIRM5SEXy4BCPMJjLQ== - dependencies: - caniuse-lite "^1.0.30001265" - electron-to-chromium "^1.3.867" - escalade "^3.1.1" - node-releases "^2.0.0" - picocolors "^1.0.0" - -browserslist@^4.17.5: +browserslist@^4.14.5, browserslist@^4.17.3, browserslist@^4.17.5: version "4.18.1" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.18.1.tgz#60d3920f25b6860eb917c6c7b185576f4d8b017f" integrity sha512-8ScCzdpPwR2wQh8IT82CA2VgDwjHyqMovPBZSNH54+tm4Jk2pCuv90gmAdH6J84OCRWi0b4gMe6O6XPXuJnjgQ== @@ -2352,11 +2404,6 @@ camelcase@^6.2.0: resolved "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz" integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== -caniuse-lite@^1.0.30001265: - version "1.0.30001267" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001267.tgz#b1cf2937175afc0570e4615fc2d2f9069fa0ed30" - integrity sha512-r1mjTzAuJ9W8cPBGbbus8E0SKcUP7gn03R14Wk8FlAlqhH9hroy9nLqmpuXlfKEw/oILW+FGz47ipXV2O7x8lg== - caniuse-lite@^1.0.30001280: version "1.0.30001282" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001282.tgz#38c781ee0a90ccfe1fe7fefd00e43f5ffdcb96fd" @@ -3143,11 +3190,6 @@ ee-first@1.1.1: resolved "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz" integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= -electron-to-chromium@^1.3.867: - version "1.3.870" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.870.tgz#c15c921e66a46985181b261f8093b91c2abb6604" - integrity sha512-PiJMshfq6PL+i1V+nKLwhHbCKeD8eAz8rvO9Cwk/7cChOHJBtufmjajLyYLsSRHguRFiOCVx3XzJLeZsIAYfSA== - electron-to-chromium@^1.3.896: version "1.3.900" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.900.tgz#5be2c5818a2a012c511b4b43e87b6ab7a296d4f5" @@ -5611,11 +5653,6 @@ node-forge@^0.10.0: resolved "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz" integrity sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA== -node-releases@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.0.tgz#67dc74903100a7deb044037b8a2e5f453bb05400" - integrity sha512-aA87l0flFYMzCHpTM3DERFSYxc6lv/BltdbRTOMZuxZ0cwZCD3mejE5n9vLhSJCN++/eOqr77G1IO5uXxlQYWA== - node-releases@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.1.tgz#3d1d395f204f1f2f29a54358b9fb678765ad2fc5" @@ -6127,10 +6164,10 @@ prelude-ls@^1.2.1: resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== -prettier@^2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.4.1.tgz#671e11c89c14a4cfc876ce564106c4a6726c9f5c" - integrity sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA== +prettier@^2.5.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.5.1.tgz#fff75fa9d519c54cf0fce328c1017d94546bc56a" + integrity sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg== pretty-bytes@^5.6.0: version "5.6.0" @@ -7398,13 +7435,12 @@ tapable@^2.0.0, tapable@^2.1.1, tapable@^2.2.0: resolved "https://registry.npmjs.org/tapable/-/tapable-2.2.0.tgz" integrity sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw== -terser-webpack-plugin@^5.1.3, terser-webpack-plugin@^5.2.4: - version "5.2.4" - resolved "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.2.4.tgz" - integrity sha512-E2CkNMN+1cho04YpdANyRrn8CyN4yMy+WdFKZIySFZrGXZxJwJP6PMNGGc/Mcr6qygQHUUqRxnAPmi0M9f00XA== +terser-webpack-plugin@^5.1.3, terser-webpack-plugin@^5.2.5: + version "5.2.5" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.2.5.tgz#ce65b9880a0c36872555c4874f45bbdb02ee32c9" + integrity sha512-3luOVHku5l0QBeYS8r4CdHYWEGMmIj3H1U64jgkdZzECcSOJAyJ9TjuqcQZvw1Y+4AOBN9SeYJPJmFn2cM4/2g== dependencies: jest-worker "^27.0.6" - p-limit "^3.1.0" schema-utils "^3.1.1" serialize-javascript "^6.0.0" source-map "^0.6.1" @@ -7878,10 +7914,10 @@ warning@^4.0.3: dependencies: loose-envify "^1.0.0" -watchpack@^2.2.0: - version "2.2.0" - resolved "https://registry.npmjs.org/watchpack/-/watchpack-2.2.0.tgz" - integrity sha512-up4YAn/XHgZHIxFBVCdlMiWDj6WaLKpwVeGQk2I5thdYxF/KmF0aaz6TfJZ/hfl1h/XlcDr7k1KH7ThDagpFaA== +watchpack@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.3.0.tgz#a41bca3da6afaff31e92a433f4c856a0c25ea0c4" + integrity sha512-MnN0Q1OsvB/GGHETrFeZPQaOelWh/7O+EiFlj8sM9GPjtQkis7k01aAxrg/18kTfoIVcLL+haEVFlXDaSRwKRw== dependencies: glob-to-regexp "^0.4.1" graceful-fs "^4.1.2" @@ -7998,15 +8034,15 @@ webpack-merge@^5.7.3: clone-deep "^4.0.1" wildcard "^2.0.0" -webpack-sources@^3.2.0: - version "3.2.1" - resolved "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.1.tgz" - integrity sha512-t6BMVLQ0AkjBOoRTZgqrWm7xbXMBzD+XDq2EZ96+vMfn3qKgsvdXZhbPZ4ElUOpdv4u+iiGe+w3+J75iy/bYGA== +webpack-sources@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.2.tgz#d88e3741833efec57c4c789b6010db9977545260" + integrity sha512-cp5qdmHnu5T8wRg2G3vZZHoJPN14aqQ89SyQ11NpGH5zEMDCclt49rzo+MaRazk7/UeILhAI+/sEtcM+7Fr0nw== -webpack@5.59.1: - version "5.59.1" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.59.1.tgz#60c77e9aad796252153d4d7ab6b2d4c11f0e548c" - integrity sha512-I01IQV9K96FlpXX3V0L4nvd7gb0r7thfuu1IfT2P4uOHOA77nKARAKDYGe/tScSHKnffNIyQhLC8kRXzY4KEHQ== +webpack@5.64.4: + version "5.64.4" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.64.4.tgz#e1454b6a13009f57cc2c78e08416cd674622937b" + integrity sha512-LWhqfKjCLoYJLKJY8wk2C3h77i8VyHowG3qYNZiIqD6D0ZS40439S/KVuc/PY48jp2yQmy0mhMknq8cys4jFMw== dependencies: "@types/eslint-scope" "^3.7.0" "@types/estree" "^0.0.50" @@ -8030,8 +8066,8 @@ webpack@5.59.1: schema-utils "^3.1.0" tapable "^2.1.1" terser-webpack-plugin "^5.1.3" - watchpack "^2.2.0" - webpack-sources "^3.2.0" + watchpack "^2.3.0" + webpack-sources "^3.2.2" websocket-driver@>=0.5.1, websocket-driver@^0.7.4: version "0.7.4"