diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 23676203f0..7a175e0394 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -62,3 +62,13 @@ jobs: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} GITHUB_BRANCH: ${{ github.ref }} + - name: Publish http-client library to npm + if: ${{ startsWith(github.ref, 'refs/tags/npm-v') }} + run: | + sudo apt-get install -y expect + bazel run //lib/typescript/httpclient:publish-npm release + env: + DEPLOY_NPM_USERNAME: ${{ secrets.DEPLOY_NPM_USERNAME }} + DEPLOY_NPM_PASSWORD: ${{ secrets.DEPLOY_NPM_PASSWORD }} + DEPLOY_NPM_EMAIL: ${{ secrets.DEPLOY_NPM_EMAIL }} + GITHUB_BRANCH: ${{ github.ref }} diff --git a/BUILD b/BUILD index ad2a647e9d..7ee9f23277 100644 --- a/BUILD +++ b/BUILD @@ -180,6 +180,7 @@ exports_files( ".prettierrc.json", ".prettierignore", "yarn.lock", + "VERSION", ], ) diff --git a/VERSION b/VERSION index c5523bd09b..66333910a4 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.17.0 +0.18.0 diff --git a/WORKSPACE b/WORKSPACE index 3e8db0582d..6aa0c2a34a 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -9,9 +9,9 @@ load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository") # Airy Bazel tools git_repository( name = "com_github_airyhq_bazel_tools", - commit = "2577f95b79aeef4c70a6aad1805b19ec707dbfa2", + commit = "d18b5a4418a8b69c0a7177f2831f8aa62da038c1", remote = "https://github.com/airyhq/bazel-tools.git", - shallow_since = "1617890651 +0200", + shallow_since = "1618558833 +0200", ) load("@com_github_airyhq_bazel_tools//:repositories.bzl", "airy_bazel_tools_dependencies", "airy_jvm_deps") diff --git a/backend/api/admin/src/main/java/co/airy/core/api/admin/ChannelsController.java b/backend/api/admin/src/main/java/co/airy/core/api/admin/ChannelsController.java index 1ea8e99108..5946befbee 100644 --- a/backend/api/admin/src/main/java/co/airy/core/api/admin/ChannelsController.java +++ b/backend/api/admin/src/main/java/co/airy/core/api/admin/ChannelsController.java @@ -86,7 +86,7 @@ ResponseEntity connect(@RequestBody @Valid ConnectChannelRequestPayload reque final String sourceChannelId = requestPayload.getName(); final String sourceIdentifier = "chatplugin"; - final String channelId = UUIDv5.fromNamespaceAndName(sourceIdentifier, sourceChannelId).toString(); + final String channelId = UUID.randomUUID().toString(); List metadataList = new ArrayList<>(); metadataList.add(newChannelMetadata(channelId, MetadataKeys.ChannelKeys.NAME, requestPayload.getName())); diff --git a/backend/api/admin/src/main/java/co/airy/core/api/config/ClientConfigController.java b/backend/api/admin/src/main/java/co/airy/core/api/config/ClientConfigController.java index 1bd872f298..5c4d35c666 100644 --- a/backend/api/admin/src/main/java/co/airy/core/api/config/ClientConfigController.java +++ b/backend/api/admin/src/main/java/co/airy/core/api/config/ClientConfigController.java @@ -4,8 +4,6 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RestController; -import java.util.Map; - @RestController public class ClientConfigController { private final ServiceDiscovery serviceDiscovery; @@ -18,7 +16,6 @@ public ClientConfigController(ServiceDiscovery serviceDiscovery) { public ResponseEntity getConfig() { return ResponseEntity.ok(ClientConfigResponsePayload.builder() .components(serviceDiscovery.getComponents()) - .features(Map.of()) .build()); } } diff --git a/backend/api/admin/src/main/java/co/airy/core/api/config/ClientConfigResponsePayload.java b/backend/api/admin/src/main/java/co/airy/core/api/config/ClientConfigResponsePayload.java index bd11534e48..56f96a52b6 100644 --- a/backend/api/admin/src/main/java/co/airy/core/api/config/ClientConfigResponsePayload.java +++ b/backend/api/admin/src/main/java/co/airy/core/api/config/ClientConfigResponsePayload.java @@ -13,5 +13,4 @@ @AllArgsConstructor public class ClientConfigResponsePayload { private Map> components; - private Map features; } diff --git a/backend/api/admin/src/main/java/co/airy/core/api/config/ComponentResponsePayload.java b/backend/api/admin/src/main/java/co/airy/core/api/config/ComponentResponsePayload.java new file mode 100644 index 0000000000..19a56f14c0 --- /dev/null +++ b/backend/api/admin/src/main/java/co/airy/core/api/config/ComponentResponsePayload.java @@ -0,0 +1,17 @@ +package co.airy.core.api.config; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode +public class ComponentResponsePayload { + private String name; + private Boolean enabled; +} diff --git a/backend/api/admin/src/main/java/co/airy/core/api/config/ComponentsResponsePayload.java b/backend/api/admin/src/main/java/co/airy/core/api/config/ComponentsResponsePayload.java new file mode 100644 index 0000000000..34e69af59d --- /dev/null +++ b/backend/api/admin/src/main/java/co/airy/core/api/config/ComponentsResponsePayload.java @@ -0,0 +1,17 @@ +package co.airy.core.api.config; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.List; + +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Data +public class ComponentsResponsePayload implements Serializable { + private List components; +} diff --git a/backend/api/admin/src/main/java/co/airy/core/api/config/ServiceDiscovery.java b/backend/api/admin/src/main/java/co/airy/core/api/config/ServiceDiscovery.java index 90e5ef6f0f..c369dd7fff 100644 --- a/backend/api/admin/src/main/java/co/airy/core/api/config/ServiceDiscovery.java +++ b/backend/api/admin/src/main/java/co/airy/core/api/config/ServiceDiscovery.java @@ -1,29 +1,23 @@ package co.airy.core.api.config; import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import org.springframework.web.client.RestTemplate; -import java.util.List; +import java.util.HashSet; import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArraySet; @Component public class ServiceDiscovery { private final String namespace; private final RestTemplate restTemplate; - private final Map> components = new ConcurrentHashMap<>(); - - private static final List services = List.of( - "sources-chatplugin", - "sources-facebook-connector", - "sources-twilio-connector", - "sources-google-connector" - ); + private Map> components = new ConcurrentHashMap<>(); public ServiceDiscovery(@Value("${kubernetes.namespace}") String namespace, RestTemplate restTemplate) { this.namespace = namespace; @@ -36,13 +30,12 @@ public Map> getComponents() { @Scheduled(fixedRate = 1_000) private void updateComponentsStatus() { - for (String service : services) { - try { - ResponseEntity response = restTemplate.exchange(String.format("http://%s.%s/actuator/health", service, namespace), HttpMethod.GET, null, Object.class); - components.put(service.replace("-connector", ""), Map.of("enabled", response.getStatusCode().is2xxSuccessful())); - } catch (Exception e) { - components.put(service.replace("-connector", ""), Map.of("enabled",false)); - } + final ResponseEntity response = restTemplate.getForEntity("http://airy-controller.default/components", ComponentsResponsePayload.class); + Map> newComponents = new ConcurrentHashMap<>(); + for (String component: response.getBody().getComponents()) { + newComponents.put(component, Map.of("enabled", true)); } + components.clear(); + components.putAll(newComponents); } } diff --git a/backend/api/admin/src/main/resources/application.properties b/backend/api/admin/src/main/resources/application.properties index 444ce06a02..a146bae54b 100644 --- a/backend/api/admin/src/main/resources/application.properties +++ b/backend/api/admin/src/main/resources/application.properties @@ -2,5 +2,5 @@ kafka.brokers=${KAFKA_BROKERS} kafka.schema-registry-url=${KAFKA_SCHEMA_REGISTRY_URL} kafka.cleanup=${KAFKA_CLEANUP:false} kafka.commit-interval-ms=${KAFKA_COMMIT_INTERVAL_MS} -auth.jwt-secret=${JWT_SECRET} +auth.jwt-secret=${jwtSecret} kubernetes.namespace=${KUBERNETES_NAMESPACE} \ No newline at end of file diff --git a/backend/api/admin/src/test/java/co/airy/core/api/admin/WebhooksControllerTest.java b/backend/api/admin/src/test/java/co/airy/core/api/admin/WebhooksControllerTest.java index 68a9213406..0826e91297 100644 --- a/backend/api/admin/src/test/java/co/airy/core/api/admin/WebhooksControllerTest.java +++ b/backend/api/admin/src/test/java/co/airy/core/api/admin/WebhooksControllerTest.java @@ -28,7 +28,7 @@ @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = AirySpringBootApplication.class) @TestPropertySource(value = "classpath:test.properties", properties = { - "ALLOWED_ORIGINS=origin1,origin2", + "allowedOrigins=origin1,origin2", }) @AutoConfigureMockMvc @ExtendWith(SpringExtension.class) diff --git a/backend/api/admin/src/test/java/co/airy/core/api/config/ClientConfigControllerTest.java b/backend/api/admin/src/test/java/co/airy/core/api/config/ClientConfigControllerTest.java index e6d0654942..d8437bb616 100644 --- a/backend/api/admin/src/test/java/co/airy/core/api/config/ClientConfigControllerTest.java +++ b/backend/api/admin/src/test/java/co/airy/core/api/config/ClientConfigControllerTest.java @@ -20,7 +20,7 @@ import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.HttpMethod; -import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.web.client.MockRestServiceServer; @@ -31,11 +31,12 @@ import static co.airy.test.Timing.retryOnException; import static org.hamcrest.CoreMatchers.everyItem; import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.Matchers.hasKey; import static org.hamcrest.collection.IsCollectionWithSize.hasSize; -import static org.springframework.test.web.client.ExpectedCount.min; +import static org.springframework.test.web.client.ExpectedCount.once; import static org.springframework.test.web.client.match.MockRestRequestMatchers.method; import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo; -import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus; +import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -91,25 +92,16 @@ void beforeEach() throws Exception { @Test public void canReturnConfig() throws Exception { - mockServer.expect(min(1), requestTo(new URI("http://sources-chatplugin.default/actuator/health"))) + mockServer.expect(once(), requestTo(new URI("http://airy-controller.default/components"))) .andExpect(method(HttpMethod.GET)) - .andRespond(withStatus(HttpStatus.OK)); - - mockServer.expect(min(1), requestTo(new URI("http://sources-facebook-connector.default/actuator/health"))) - .andExpect(method(HttpMethod.GET)) - .andRespond(withStatus(HttpStatus.OK)); - - mockServer.expect(min(1), requestTo(new URI("http://sources-twilio-connector.default/actuator/health"))) - .andExpect(method(HttpMethod.GET)) - .andRespond(withStatus(HttpStatus.OK)); - - mockServer.expect(min(1), requestTo(new URI("http://sources-google-connector.default/actuator/health"))) - .andExpect(method(HttpMethod.GET)) - .andRespond(withStatus(HttpStatus.OK)); + .andRespond( + withSuccess("{\"components\": [\"api-communication\"]}", MediaType.APPLICATION_JSON) + ); retryOnException(() -> webTestHelper.post("/client.config", "{}", "user-id") .andExpect(status().isOk()) - .andExpect(jsonPath("$.components.*", hasSize(4))) + .andExpect(jsonPath("$.components.*", hasSize(1))) + .andExpect(jsonPath("$.components", hasKey("api-communication"))) .andExpect(jsonPath("$.components.*.enabled", everyItem(is(true)))), "client.config call failed"); } diff --git a/backend/api/auth/src/main/resources/application.properties b/backend/api/auth/src/main/resources/application.properties index dde95dc526..7cdbd9af75 100644 --- a/backend/api/auth/src/main/resources/application.properties +++ b/backend/api/auth/src/main/resources/application.properties @@ -4,7 +4,7 @@ spring.datasource.username=${DB_USERNAME} spring.datasource.password=${DB_PASSWORD} spring.flyway.enabled=true -auth.jwt-secret=${JWT_SECRET} +auth.jwt-secret=${jwtSecret} mail.host.url=${MAIL_URL} mail.host.port=${MAIL_PORT} diff --git a/backend/api/communication/src/main/java/co/airy/core/api/communication/ConversationsController.java b/backend/api/communication/src/main/java/co/airy/core/api/communication/ConversationsController.java index 0dd50f8e27..543aee6cb4 100644 --- a/backend/api/communication/src/main/java/co/airy/core/api/communication/ConversationsController.java +++ b/backend/api/communication/src/main/java/co/airy/core/api/communication/ConversationsController.java @@ -12,6 +12,7 @@ import co.airy.core.api.communication.payload.ConversationListRequestPayload; import co.airy.core.api.communication.payload.ConversationListResponsePayload; import co.airy.core.api.communication.payload.ConversationResponsePayload; +import co.airy.core.api.communication.payload.ConversationSetStateRequestPayload; import co.airy.core.api.communication.payload.ConversationTagRequestPayload; import co.airy.core.api.communication.payload.PaginationData; import co.airy.model.metadata.MetadataKeys; @@ -39,6 +40,7 @@ import java.util.Set; import java.util.stream.Collectors; +import static co.airy.model.metadata.MetadataRepository.newConversationMetadata; import static co.airy.model.metadata.MetadataRepository.newConversationTag; import static java.util.Comparator.comparing; import static java.util.stream.Collectors.toList; @@ -230,4 +232,46 @@ ResponseEntity conversationUntag(@RequestBody @Valid ConversationTagRequestPa return ResponseEntity.noContent().build(); } + + @PostMapping("/conversations.setState") + ResponseEntity conversationSetState(@RequestBody @Valid ConversationSetStateRequestPayload requestPayload) { + final String conversationId = requestPayload.getConversationId().toString(); + final String state = requestPayload.getState(); + final ReadOnlyKeyValueStore store = stores.getConversationsStore(); + final Conversation conversation = store.get(conversationId); + + if (conversation == null) { + return ResponseEntity.notFound().build(); + } + + final Metadata metadata = newConversationMetadata(conversationId, MetadataKeys.ConversationKeys.STATE, state); + + try { + stores.storeMetadata(metadata); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new RequestErrorResponsePayload(e.getMessage())); + } + + return ResponseEntity.noContent().build(); + } + + @PostMapping("/conversations.removeState") + ResponseEntity conversationRemoveState(@RequestBody @Valid ConversationByIdRequestPayload requestPayload) { + final String conversationId = requestPayload.getConversationId().toString(); + final ReadOnlyKeyValueStore store = stores.getConversationsStore(); + final Conversation conversation = store.get(conversationId); + + if (conversation == null) { + return ResponseEntity.notFound().build(); + } + + try { + final Subject subject = new Subject("conversation", conversationId); + stores.deleteMetadata(subject, MetadataKeys.ConversationKeys.STATE); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new RequestErrorResponsePayload(e.getMessage())); + } + + return ResponseEntity.noContent().build(); + } } diff --git a/backend/api/communication/src/main/java/co/airy/core/api/communication/payload/ConversationSetStateRequestPayload.java b/backend/api/communication/src/main/java/co/airy/core/api/communication/payload/ConversationSetStateRequestPayload.java new file mode 100644 index 0000000000..646a374704 --- /dev/null +++ b/backend/api/communication/src/main/java/co/airy/core/api/communication/payload/ConversationSetStateRequestPayload.java @@ -0,0 +1,18 @@ +package co.airy.core.api.communication.payload; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotNull; +import java.util.UUID; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ConversationSetStateRequestPayload { + @NotNull + private UUID conversationId; + @NotNull + private String state; +} diff --git a/backend/api/communication/src/main/resources/application.properties b/backend/api/communication/src/main/resources/application.properties index cba3b56051..1b7c23b19f 100644 --- a/backend/api/communication/src/main/resources/application.properties +++ b/backend/api/communication/src/main/resources/application.properties @@ -3,4 +3,4 @@ kafka.schema-registry-url=${KAFKA_SCHEMA_REGISTRY_URL} kafka.cleanup=${KAFKA_CLEANUP:false} kafka.commit-interval-ms=${KAFKA_COMMIT_INTERVAL_MS} -auth.jwt-secret=${JWT_SECRET} +auth.jwt-secret=${jwtSecret} diff --git a/backend/api/communication/src/test/java/co/airy/core/api/communication/ConversationsStateTest.java b/backend/api/communication/src/test/java/co/airy/core/api/communication/ConversationsStateTest.java new file mode 100644 index 0000000000..372c8c0972 --- /dev/null +++ b/backend/api/communication/src/test/java/co/airy/core/api/communication/ConversationsStateTest.java @@ -0,0 +1,110 @@ +package co.airy.core.api.communication; + +import co.airy.avro.communication.Channel; +import co.airy.avro.communication.ChannelConnectionState; +import co.airy.core.api.communication.util.TestConversation; +import co.airy.kafka.test.KafkaTestHelper; +import co.airy.kafka.test.junit.SharedKafkaTestResource; +import co.airy.spring.core.AirySpringBootApplication; +import co.airy.spring.test.WebTestHelper; +import org.apache.kafka.clients.producer.ProducerRecord; +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.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import java.util.UUID; + +import static co.airy.core.api.communication.util.Topics.applicationCommunicationChannels; +import static co.airy.core.api.communication.util.Topics.getTopics; +import static co.airy.test.Timing.retryOnException; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.nullValue; +import static org.hamcrest.core.Is.is; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = AirySpringBootApplication.class) +@TestPropertySource(value = "classpath:test.properties") +@ExtendWith(SpringExtension.class) +@AutoConfigureMockMvc +class ConversationsStateTest { + @RegisterExtension + public static final SharedKafkaTestResource sharedKafkaTestResource = new SharedKafkaTestResource(); + + private static KafkaTestHelper kafkaTestHelper; + + @Autowired + private WebTestHelper webTestHelper; + + @BeforeAll + static void beforeAll() throws Exception { + kafkaTestHelper = new KafkaTestHelper(sharedKafkaTestResource, getTopics()); + kafkaTestHelper.beforeAll(); + } + + @AfterAll + static void afterAll() throws Exception { + kafkaTestHelper.afterAll(); + } + + @BeforeEach + void beforeEach() throws Exception { + webTestHelper.waitUntilHealthy(); + } + + @Test + void canSetandRemoveStateFromConversations() throws Exception { + final String userId = "user-id"; + final Channel channel = Channel.newBuilder() + .setConnectionState(ChannelConnectionState.CONNECTED) + .setId(UUID.randomUUID().toString()) + .setSource("facebook") + .setSourceChannelId("ps-id") + .build(); + + kafkaTestHelper.produceRecord(new ProducerRecord<>(applicationCommunicationChannels.name(), channel.getId(), channel)); + final String conversationId = UUID.randomUUID().toString(); + kafkaTestHelper.produceRecords(TestConversation.generateRecords(conversationId, channel, 1)); + + retryOnException(() -> webTestHelper.post("/conversations.info", + "{\"conversation_id\":\"" + conversationId + "\"}", userId) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.id", is(conversationId))), "conversation was not created"); + + final String state = "open"; + + webTestHelper.post("/conversations.setState", + "{\"conversation_id\":\"" + conversationId + "\",\"state\":\"" + state + "\"}", userId) + .andExpect(status().isNoContent()); + + retryOnException( + () -> webTestHelper.post("/conversations.info", + "{\"conversation_id\":\"" + conversationId + "\"}", userId) + .andExpect(status().isOk()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.id", is(conversationId))) + .andExpect(jsonPath("$.metadata.state", is(state))), + "conversation state was not set"); + + webTestHelper.post("/conversations.removeState", + "{\"conversation_id\":\"" + conversationId + "\"}", userId) + .andExpect(status().isNoContent()); + + retryOnException( + () -> webTestHelper.post("/conversations.info", + "{\"conversation_id\":\"" + conversationId + "\"}", userId) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.id", is(conversationId))) + .andExpect(jsonPath("$.metadata.state").doesNotExist()), + "conversation state was not removed"); + } + +} diff --git a/backend/api/websocket/src/main/resources/application.properties b/backend/api/websocket/src/main/resources/application.properties index cba3b56051..1b7c23b19f 100644 --- a/backend/api/websocket/src/main/resources/application.properties +++ b/backend/api/websocket/src/main/resources/application.properties @@ -3,4 +3,4 @@ kafka.schema-registry-url=${KAFKA_SCHEMA_REGISTRY_URL} kafka.cleanup=${KAFKA_CLEANUP:false} kafka.commit-interval-ms=${KAFKA_COMMIT_INTERVAL_MS} -auth.jwt-secret=${JWT_SECRET} +auth.jwt-secret=${jwtSecret} 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 00f89927fd..1bc2ebe9c2 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 @@ -8,6 +8,7 @@ public class MetadataKeys { public static class ConversationKeys { public static final String TAGS = "tags"; public static final String UNREAD_COUNT = "unread_count"; + public static final String STATE = "state"; public static final String CONTACT = "contact"; diff --git a/backend/model/metadata/src/main/java/co/airy/model/metadata/dto/MetadataNode.java b/backend/model/metadata/src/main/java/co/airy/model/metadata/dto/MetadataNode.java index 483f272a5a..4c6957243d 100644 --- a/backend/model/metadata/src/main/java/co/airy/model/metadata/dto/MetadataNode.java +++ b/backend/model/metadata/src/main/java/co/airy/model/metadata/dto/MetadataNode.java @@ -2,11 +2,13 @@ import lombok.AllArgsConstructor; import lombok.Data; +import lombok.NoArgsConstructor; import java.io.Serializable; @Data @AllArgsConstructor +@NoArgsConstructor public class MetadataNode implements Serializable { private String key; private String value; diff --git a/backend/sources/chat-plugin/src/main/java/co/airy/core/chat_plugin/ChatController.java b/backend/sources/chat-plugin/src/main/java/co/airy/core/chat_plugin/ChatController.java index 6351765b75..08045895c3 100644 --- a/backend/sources/chat-plugin/src/main/java/co/airy/core/chat_plugin/ChatController.java +++ b/backend/sources/chat-plugin/src/main/java/co/airy/core/chat_plugin/ChatController.java @@ -41,7 +41,7 @@ public class ChatController { private final Jwt jwt; private final String apiToken; - public ChatController(Stores stores, Jwt jwt, ObjectMapper objectMapper, @Value("${system_token:#{null}}") String apiToken) { + public ChatController(Stores stores, Jwt jwt, ObjectMapper objectMapper, @Value("${systemToken:#{null}}") String apiToken) { this.stores = stores; this.jwt = jwt; this.objectMapper = objectMapper; diff --git a/backend/sources/chat-plugin/src/main/java/co/airy/core/chat_plugin/config/AuthConfig.java b/backend/sources/chat-plugin/src/main/java/co/airy/core/chat_plugin/config/AuthConfig.java index 1453207266..bf29f44e88 100644 --- a/backend/sources/chat-plugin/src/main/java/co/airy/core/chat_plugin/config/AuthConfig.java +++ b/backend/sources/chat-plugin/src/main/java/co/airy/core/chat_plugin/config/AuthConfig.java @@ -26,7 +26,7 @@ public class AuthConfig extends WebSecurityConfigurerAdapter { private final Jwt jwt; private final String systemToken; - public AuthConfig(Jwt jwt, @Value("${system_token:#{null}}") String systemToken) { + public AuthConfig(Jwt jwt, @Value("${systemToken:#{null}}") String systemToken) { this.jwt = jwt; this.systemToken = systemToken; } @@ -46,7 +46,7 @@ protected void configure(final HttpSecurity http) throws Exception { @Bean CorsConfigurationSource corsConfigurationSource(final Environment environment) { - final String allowed = environment.getProperty("ALLOWED_ORIGINS", ""); + final String allowed = environment.getProperty("allowedOrigins", ""); CorsConfiguration config = new CorsConfiguration(); config.setAllowCredentials(true); config.addAllowedOrigin(allowed); diff --git a/backend/sources/chat-plugin/src/main/resources/application.properties b/backend/sources/chat-plugin/src/main/resources/application.properties index a51befc8ca..095c989191 100644 --- a/backend/sources/chat-plugin/src/main/resources/application.properties +++ b/backend/sources/chat-plugin/src/main/resources/application.properties @@ -1,4 +1,4 @@ kafka.brokers=${KAFKA_BROKERS} kafka.schema-registry-url=${KAFKA_SCHEMA_REGISTRY_URL} kafka.commit-interval-ms=${KAFKA_COMMIT_INTERVAL_MS} -chat-plugin.auth.jwt-secret=${JWT_SECRET} +chat-plugin.auth.jwt-secret=${jwtSecret} diff --git a/backend/sources/chat-plugin/src/test/java/co/airy/core/chat_plugin/ChatControllerTest.java b/backend/sources/chat-plugin/src/test/java/co/airy/core/chat_plugin/ChatControllerTest.java index ee81b51575..9a00df87b5 100644 --- a/backend/sources/chat-plugin/src/test/java/co/airy/core/chat_plugin/ChatControllerTest.java +++ b/backend/sources/chat-plugin/src/test/java/co/airy/core/chat_plugin/ChatControllerTest.java @@ -69,7 +69,7 @@ public class ChatControllerTest { @Value("${local.server.port}") private int port; - @Value("${system_token}") + @Value("${systemToken}") private String systemToken; private static KafkaTestHelper kafkaTestHelper; diff --git a/backend/sources/chat-plugin/src/test/resources/test.properties b/backend/sources/chat-plugin/src/test/resources/test.properties index 12ef627866..73d040ce86 100644 --- a/backend/sources/chat-plugin/src/test/resources/test.properties +++ b/backend/sources/chat-plugin/src/test/resources/test.properties @@ -1,4 +1,4 @@ kafka.cleanup=true kafka.commit-interval-ms=100 chat-plugin.auth.jwt-secret=this-needs-to-be-replaced-in-production-buffer:424242424242424242424242424242 -system_token=system-user-token +systemToken=system-user-token diff --git a/backend/sources/facebook/connector/src/main/resources/application.properties b/backend/sources/facebook/connector/src/main/resources/application.properties index fe8ecd4e38..58bc4666ad 100644 --- a/backend/sources/facebook/connector/src/main/resources/application.properties +++ b/backend/sources/facebook/connector/src/main/resources/application.properties @@ -1,4 +1,4 @@ -auth.jwt-secret=${JWT_SECRET} +auth.jwt-secret=${jwtSecret} facebook.webhook-secret=${FACEBOOK_WEBHOOK_SECRET} kafka.brokers=${KAFKA_BROKERS} kafka.cleanup=${KAFKA_CLEANUP:false} diff --git a/backend/sources/google/connector/src/main/resources/application.properties b/backend/sources/google/connector/src/main/resources/application.properties index c700759594..887baf50fa 100644 --- a/backend/sources/google/connector/src/main/resources/application.properties +++ b/backend/sources/google/connector/src/main/resources/application.properties @@ -1,4 +1,4 @@ -auth.jwt-secret=${JWT_SECRET} +auth.jwt-secret=${jwtSecret} google.auth.sa=${GOOGLE_SA_FILE} google.partner-key=${GOOGLE_PARTNER_KEY} kafka.brokers=${KAFKA_BROKERS} diff --git a/backend/sources/twilio/connector/src/main/resources/application.properties b/backend/sources/twilio/connector/src/main/resources/application.properties index a121db2adb..b859320ad4 100644 --- a/backend/sources/twilio/connector/src/main/resources/application.properties +++ b/backend/sources/twilio/connector/src/main/resources/application.properties @@ -1,4 +1,4 @@ -auth.jwt-secret=${JWT_SECRET} +auth.jwt-secret=${jwtSecret} kafka.brokers=${KAFKA_BROKERS} kafka.schema-registry-url=${KAFKA_SCHEMA_REGISTRY_URL} kafka.commit-interval-ms=${KAFKA_COMMIT_INTERVAL_MS} diff --git a/bazel.tsconfig.json b/bazel.tsconfig.json index e5684c8ea8..940fdf242b 100644 --- a/bazel.tsconfig.json +++ b/bazel.tsconfig.json @@ -72,6 +72,12 @@ ], "chat-plugin-handles": [ "./frontend/chat-plugin/handles" + ], + "chat-plugin": [ + "./frontend/chat-plugin" + ], + "chat-plugin/*": [ + "./frontend/chat-plugin/*" ] } } diff --git a/cli/pkg/cmd/config/BUILD b/cli/pkg/cmd/config/BUILD index a2fd4676ae..082b603f97 100644 --- a/cli/pkg/cmd/config/BUILD +++ b/cli/pkg/cmd/config/BUILD @@ -5,7 +5,6 @@ go_library( name = "config", srcs = [ "config.go", - "configmaps.go", "parser.go", ], importpath = "cli/pkg/cmd/config", @@ -16,9 +15,7 @@ go_library( "//cli/pkg/workspace", "@com_github_spf13_cobra//:cobra", "@in_gopkg_yaml_v2//:yaml_v2", - "@io_k8s_api//core/v1:go_default_library", "@io_k8s_apimachinery//pkg/apis/meta/v1:go_default_library", - "@io_k8s_client_go//kubernetes:go_default_library", ], ) diff --git a/cli/pkg/cmd/config/config.go b/cli/pkg/cmd/config/config.go index b667f5c137..2b18eb71f4 100644 --- a/cli/pkg/cmd/config/config.go +++ b/cli/pkg/cmd/config/config.go @@ -4,8 +4,11 @@ import ( "cli/pkg/console" "cli/pkg/kube" "cli/pkg/workspace" + "context" "fmt" + "github.com/spf13/cobra" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) var configFile string @@ -31,23 +34,45 @@ func applyConfig(cmd *cobra.Command, args []string) { kubeCtx := kube.Load() clientset, err := kubeCtx.GetClientSet() if err != nil { - console.Exit("Could not find an installation of Airy Core. Get started here https://airy.co/docs/core/getting-started/installation/introduction") - } - - if twilioApply(conf, clientset) { - fmt.Println("Twilio configuration applied.") + console.Exit("could not find an installation of Airy Core. Get started here https://airy.co/docs/core/getting-started/installation/introduction") } - if facebookApply(conf, clientset) { - fmt.Println("Facebook configuration applied.") + if len(conf.Security) != 0 { + applyErr := kube.ApplyConfigMap("security", conf.Kubernetes.Namespace, conf.Security, map[string]string{}, clientset) + if applyErr != nil { + fmt.Printf("unable to apply configuration for \"security\"\n Error:\n %v\n", applyErr) + } else { + fmt.Printf("applied configuration for \"security\"\n") + } } - if googleApply(conf, clientset) { - fmt.Println("Google configuration applied.") + configuredComponents := make(map[string]bool) + for componentType, _ := range conf.Components { + for componentName, componentValues := range conf.Components[componentType] { + configmapName := componentType + "-" + componentName + labels := map[string]string{ + "core.airy.co/component": configmapName, + } + applyErr := kube.ApplyConfigMap(configmapName, conf.Kubernetes.Namespace, componentValues, labels, clientset) + configuredComponents[configmapName] = true + if applyErr != nil { + fmt.Printf("unable to apply configuration for component: \"%s-%s\"\n Error:\n %v\n", componentType, componentName, applyErr) + } else { + fmt.Printf("applied configuration for component: \"%s-%s\"\n", componentType, componentName) + } + } } - if webhooksApply(conf, clientset) { - fmt.Println("Webhooks configuration applied.") + configmapList, _ := clientset.CoreV1().ConfigMaps(conf.Kubernetes.Namespace).List(context.TODO(), v1.ListOptions{LabelSelector: "core.airy.co/component"}) + for _, configmap := range configmapList.Items { + if !configuredComponents[configmap.ObjectMeta.Name] { + deleteErr := kube.DeleteConfigMap(configmap.ObjectMeta.Name, conf.Kubernetes.Namespace, clientset) + if deleteErr != nil { + fmt.Printf("unable to remove configuration for component %s.\n", configmap.ObjectMeta.Name) + } else { + fmt.Printf("removed configuration for component \"%s\".\n", configmap.ObjectMeta.Name) + } + } } } diff --git a/cli/pkg/cmd/config/configmaps.go b/cli/pkg/cmd/config/configmaps.go deleted file mode 100644 index c00fc3a1ad..0000000000 --- a/cli/pkg/cmd/config/configmaps.go +++ /dev/null @@ -1,111 +0,0 @@ -package config - -import ( - "context" - "fmt" - "os" - - corev1 "k8s.io/api/core/v1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes" -) - -func applyConfigMap(configMapName string, newCmData map[string]string, clientset *kubernetes.Clientset, namespace string) error { - cm, _ := clientset.CoreV1().ConfigMaps(namespace).Get(context.TODO(), configMapName, v1.GetOptions{}) - - if cm.GetName() == "" { - _, err := clientset.CoreV1().ConfigMaps(namespace).Create(context.TODO(), - &corev1.ConfigMap{ - ObjectMeta: v1.ObjectMeta{ - Name: configMapName, - Namespace: namespace, - }, - Data: newCmData, - }, v1.CreateOptions{}) - return err - } else { - cm.Data = newCmData - _, err := clientset.CoreV1().ConfigMaps(namespace).Update(context.TODO(), cm, v1.UpdateOptions{}) - return err - } - -} - -func facebookApply(airyConf airyConf, clientset *kubernetes.Clientset) bool { - facebookConfig := airyConf.Apps.Sources.Facebook - if facebookConfig.AppID != "" || facebookConfig.AppSecret != "" || facebookConfig.WebhookSecret != "" { - configMapData := make(map[string]string, 0) - configMapData["FACEBOOK_APP_ID"] = facebookConfig.AppID - configMapData["FACEBOOK_APP_SECRET"] = facebookConfig.AppSecret - configMapData["FACEBOOK_WEBHOOK_SECRET"] = facebookConfig.WebhookSecret - err := applyConfigMap("sources-facebook", configMapData, clientset, airyConf.Global.Namespace) - - if err != nil { - fmt.Println("unable to update configMap: ", err) - os.Exit(1) - } - - return true - } - - return false -} - -func googleApply(airyConf airyConf, clientset *kubernetes.Clientset) bool { - googleConfig := airyConf.Apps.Sources.Google - if googleConfig.PartnerKey != "" || googleConfig.SaFile != "" { - configMapData := make(map[string]string, 0) - configMapData["GOOGLE_PARTNER_KEY"] = googleConfig.PartnerKey - configMapData["GOOGLE_SA_FILE"] = googleConfig.SaFile - - err := applyConfigMap("sources-google", configMapData, clientset, airyConf.Global.Namespace) - - if err != nil { - fmt.Println("unable to update configMap: ", err) - os.Exit(1) - } - - return true - } - - return false -} - -func twilioApply(airyConf airyConf, clientset *kubernetes.Clientset) bool { - twilioConfig := airyConf.Apps.Sources.Twilio - if twilioConfig.AccountSid != "" || twilioConfig.AuthToken != "" { - configMapData := make(map[string]string, 0) - configMapData["TWILIO_ACCOUNT_SID"] = twilioConfig.AccountSid - configMapData["TWILIO_AUTH_TOKEN"] = twilioConfig.AuthToken - - err := applyConfigMap("sources-twilio", configMapData, clientset, airyConf.Global.Namespace) - - if err != nil { - fmt.Println("unable to update configMap: ", err) - os.Exit(1) - } - - return true - } - - return false -} - -func webhooksApply(airyConf airyConf, clientset *kubernetes.Clientset,) bool { - webhooksConfig := airyConf.Apps.Webhooks - if webhooksConfig.Name != "" { - configMapData := make(map[string]string, 0) - configMapData["NAME"] = webhooksConfig.Name - - err := applyConfigMap("webhooks-config", configMapData, clientset, airyConf.Global.Namespace) - - if err != nil { - fmt.Println("unable to update configMap: ", err) - os.Exit(1) - } - - return true - } - - return false -} diff --git a/cli/pkg/cmd/config/parser.go b/cli/pkg/cmd/config/parser.go index 5ab41dbf8b..bc58b70b34 100644 --- a/cli/pkg/cmd/config/parser.go +++ b/cli/pkg/cmd/config/parser.go @@ -1,40 +1,24 @@ package config import ( - "gopkg.in/yaml.v2" "io/ioutil" + + "gopkg.in/yaml.v2" ) -type globalConf struct { +type kubernetesConf struct { AppImageTag string `yaml:"appImageTag"` ContainerRegistry string `yaml:"containerRegistry"` Namespace string `yaml:"namespace"` + NgrokEnabled string `yaml:"ngrokEnabled"` } -type appsConf struct { - Sources struct { - Twilio struct { - AuthToken string `yaml:"authToken"` - AccountSid string `yaml:"accountSid"` - } - Facebook struct { - AppID string `yaml:"appId"` - AppSecret string `yaml:"appSecret"` - WebhookSecret string `yaml:"webhookSecret"` - } - Google struct { - PartnerKey string `yaml:"partnerKey"` - SaFile string `yaml:"saFile"` - } - } - Webhooks struct { - Name string `yaml:"name"` - } -} +type componentsConf map[string]map[string]string type airyConf struct { - Global globalConf - Apps appsConf + Kubernetes kubernetesConf + Security map[string]string + Components map[string]componentsConf } func parseConf(configFile string) (airyConf, error) { @@ -42,14 +26,7 @@ func parseConf(configFile string) (airyConf, error) { if err != nil { return airyConf{}, err } - - conf := airyConf{ - Global: globalConf{ - Namespace: "default", - }, - } - + conf := airyConf{} err = yaml.Unmarshal(data, &conf) - return conf, err } diff --git a/cli/pkg/cmd/create/helm.go b/cli/pkg/cmd/create/helm.go index 2234466baa..1196e5a0b3 100644 --- a/cli/pkg/cmd/create/helm.go +++ b/cli/pkg/cmd/create/helm.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "io/ioutil" + batchv1 "k8s.io/api/batch/v1" corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" @@ -75,7 +76,12 @@ func (h *Helm) Setup() error { } func (h *Helm) InstallCharts(overrides []string) error { - return h.runHelm(append([]string{"install", "--values", "/apps/config/airy-config-map.yaml", "core", "/apps/helm-chart/"}, overrides...)) + return h.runHelm(append([]string{"install", + "--values", "/apps/config/airy-config-map.yaml", + "--set", "global.appImageTag=" + h.version, + "--set", "global.namespace=" + h.namespace, + "--timeout", "10m0s", + "core", "/apps/helm-chart/"}, overrides...)) } func (h *Helm) runHelm(args []string) error { diff --git a/cli/pkg/kube/BUILD b/cli/pkg/kube/BUILD index 3e45dd2693..23685fd4a5 100644 --- a/cli/pkg/kube/BUILD +++ b/cli/pkg/kube/BUILD @@ -3,6 +3,7 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library") go_library( name = "kube", srcs = [ + "configmaps.go", "context.go", "hosts.go", ], @@ -10,6 +11,7 @@ go_library( visibility = ["//visibility:public"], deps = [ "@com_github_spf13_viper//:viper", + "@io_k8s_api//core/v1:go_default_library", "@io_k8s_apimachinery//pkg/apis/meta/v1:go_default_library", "@io_k8s_client_go//kubernetes:go_default_library", "@io_k8s_client_go//tools/clientcmd:go_default_library", diff --git a/cli/pkg/kube/configmaps.go b/cli/pkg/kube/configmaps.go new file mode 100644 index 0000000000..461905e0f5 --- /dev/null +++ b/cli/pkg/kube/configmaps.go @@ -0,0 +1,37 @@ +package kube + +import ( + "context" + corev1 "k8s.io/api/core/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" +) + +func ApplyConfigMap(configmapName string, namespace string, cmData map[string]string, labels map[string]string, clientset *kubernetes.Clientset) error { + cm, _ := clientset.CoreV1().ConfigMaps(namespace).Get(context.TODO(), configmapName, v1.GetOptions{}) + if cm.GetName() == "" { + _, err := clientset.CoreV1().ConfigMaps(namespace).Create(context.TODO(), + &corev1.ConfigMap{ + ObjectMeta: v1.ObjectMeta{ + Name: configmapName, + Namespace: namespace, + Labels: labels, + }, + Data: cmData, + }, v1.CreateOptions{}) + return err + } else { + cm.Data = cmData + _, err := clientset.CoreV1().ConfigMaps(namespace).Update(context.TODO(), cm, v1.UpdateOptions{}) + return err + } +} + +func DeleteConfigMap(configmapName string, namespace string, clientset *kubernetes.Clientset) error { + cm, _ := clientset.CoreV1().ConfigMaps(namespace).Get(context.TODO(), configmapName, v1.GetOptions{}) + if cm.GetName() != "" { + err := clientset.CoreV1().ConfigMaps(namespace).Delete(context.TODO(),configmapName, v1.DeleteOptions{}) + return err + } + return nil +} diff --git a/cli/pkg/workspace/template/airy.yaml b/cli/pkg/workspace/template/airy.yaml index fd647b5d90..f2cbaf3fcf 100644 --- a/cli/pkg/workspace/template/airy.yaml +++ b/cli/pkg/workspace/template/airy.yaml @@ -1,5 +1,4 @@ -global: - appImageTag: develop +kubernetes: containerRegistry: ghcr.io/airyhq namespace: default ngrokEnabled: false diff --git a/docs/docs/api/endpoints/chatplugin.md b/docs/docs/api/endpoints/chatplugin.md index 29d04e1433..ecefde4b54 100644 --- a/docs/docs/api/endpoints/chatplugin.md +++ b/docs/docs/api/endpoints/chatplugin.md @@ -48,8 +48,7 @@ previous conversation using the [resume endpoint](#get-a-resume-token). // source message payload "state": "{String}", // delivery state of message, one of PENDING, FAILED, DELIVERED - "sender_type": "{string/enum}", - // See glossary + "from_contact": true, "sent_at": "{string}", //'yyyy-MM-dd'T'HH:mm:ss.SSSZ' date in UTC form, to be localized by clients "metadata": { @@ -114,8 +113,7 @@ header. // source message payload "state": "{String}", // delivery state of message, one of PENDING, FAILED, DELIVERED - "sender_type": "{string/enum}", - // See glossary + "from_contact": true, "sent_at": "{string}", //'yyyy-MM-dd'T'HH:mm:ss.SSSZ' date in UTC form, to be localized by clients "metadata": { @@ -147,8 +145,7 @@ The WebSocket connection endpoint is at `/ws.chatplugin`. // source message payload "state": "{String}", // delivery state of message, one of PENDING, FAILED, DELIVERED - "sender_type": "{string/enum}", - // See glossary + "from_contact": true, "sent_at": "{string}", //'yyyy-MM-dd'T'HH:mm:ss.SSSZ' date in UTC form, to be localized by clients "metadata": { diff --git a/docs/docs/api/endpoints/conversations.md b/docs/docs/api/endpoints/conversations.md index 03ff2fe231..eebfc6d56c 100644 --- a/docs/docs/api/endpoints/conversations.md +++ b/docs/docs/api/endpoints/conversations.md @@ -72,8 +72,7 @@ Find users whose name ends with "Lovelace": // typed source message model state: "{String}", // delivery state of message, one of PENDING, FAILED, DELIVERED - sender_type: "{string/enum}", - // See glossary + "from_contact": true, sent_at: "{string}", //'yyyy-MM-dd'T'HH:mm:ss.SSSZ' date in UTC form, to be localized by clients "source": "{String}" @@ -133,8 +132,7 @@ Find users whose name ends with "Lovelace": // typed source message model "delivery_state": "{String}", // delivery state of message, one of PENDING, FAILED, DELIVERED - "sender_type": "{string/enum}", - // See glossary + "from_contact": true, "sent_at": "{string}" //'yyyy-MM-dd'T'HH:mm:ss.SSSZ' date in UTC form, to be localized by clients } @@ -189,3 +187,32 @@ tag](/api/endpoints/tags.md#create). Returns status code `200` if successful. ``` **Empty response (204)** + +## Set the state of a conversation + +`POST /conversations.setState` + +**Sample request** + +```json5 +{ + "conversation_id": "CONVERSATION_ID", + "state": "open" +} +``` + +**Empty response (204)** + +## Remove the state of a conversation + +`POST /conversations.removeState` + +**Sample request** + +```json5 +{ + "conversation_id": "CONVERSATION_ID" +} +``` + +**Empty response (204)** diff --git a/docs/docs/api/endpoints/google-messages-send.mdx b/docs/docs/api/endpoints/google-messages-send.mdx index f398b07c58..cd306095dd 100644 --- a/docs/docs/api/endpoints/google-messages-send.mdx +++ b/docs/docs/api/endpoints/google-messages-send.mdx @@ -11,13 +11,13 @@ Whatever is put on the `message` field will be forwarded "as-is" to the source's ```json5 { - conversation_id: 'a688d36c-a85e-44af-bc02-4248c2c97622', - message: { - text: 'Hello World Agent!', - representative: { - displayName: 'Hello World from Agent', - avatarImage: 'REPRESENTATIVE_AVATAR_URL', - representativeType: 'HUMAN', + "conversation_id": "a688d36c-a85e-44af-bc02-4248c2c97622", + "message": { + "text": "Hello World Agent!", + "representative": { + "displayName": "Hello World from Agent", + "avatarImage": "REPRESENTATIVE_AVATAR_URL", + "representativeType": "HUMAN", }, }, } @@ -27,17 +27,17 @@ Whatever is put on the `message` field will be forwarded "as-is" to the source's ```json5 { - id: '{UUID}', - content: '{"text":"Hello"}', - state: 'pending|failed|delivered', - sender_type: '{string/enum}', + "id": "{UUID}", + "content": "{\"text\":\"Hello\"}", + "state": "pending|failed|delivered", + "from_contact": true, // See glossary - sent_at: '{string}', + "sent_at": "{string}", //'yyyy-MM-dd'T'HH:mm:ss.SSSZ' date in UTC form, to be localized by clients - source: '{String}', + "source": "{String}", // one of the possible sources - metadata: { - sentFrom: 'iPhone', + "metadata": { + "sentFrom": "iPhone", }, // metadata object of the message } diff --git a/docs/docs/api/endpoints/messages-send.mdx b/docs/docs/api/endpoints/messages-send.mdx index 8caa442c4f..f3c23bfc61 100644 --- a/docs/docs/api/endpoints/messages-send.mdx +++ b/docs/docs/api/endpoints/messages-send.mdx @@ -19,9 +19,9 @@ Sends a message to a conversation and returns a payload. Whatever is put on the ```json5 { "id": "{UUID}", - "content": '{"text":"Hello"}', + "content": "{\"text\":\"Hello\"}", "state": "pending|failed|delivered", - "sender_type": "{string/enum}", + "from_contact": true, // See glossary "sent_at": "{string}", //'yyyy-MM-dd'T'HH:mm:ss.SSSZ' date in UTC form, to be localized by clients @@ -32,4 +32,4 @@ Sends a message to a conversation and returns a payload. Whatever is put on the } // metadata object of the message } -``` \ No newline at end of file +``` diff --git a/docs/docs/api/endpoints/messages.md b/docs/docs/api/endpoints/messages.md index 81547029b3..340adc2eca 100644 --- a/docs/docs/api/endpoints/messages.md +++ b/docs/docs/api/endpoints/messages.md @@ -34,8 +34,7 @@ latest. // source message payload "state": "{String}", // delivery state of message, one of PENDING, FAILED, DELIVERED - "sender_type": "{string/enum}", - // See glossary + "from_contact": true, "sent_at": "{string}", //'yyyy-MM-dd'T'HH:mm:ss.SSSZ' date in UTC form, to be localized by clients "source": "{String}", diff --git a/docs/docs/api/endpoints/suggest-replies.mdx b/docs/docs/api/endpoints/suggest-replies.mdx index 86b7c48cd1..4bd3554193 100644 --- a/docs/docs/api/endpoints/suggest-replies.mdx +++ b/docs/docs/api/endpoints/suggest-replies.mdx @@ -6,13 +6,13 @@ Suggest a set of replies for a given message id. UI clients can then show these ```json5 { - message_id: 'uuid', - suggestions: { - 'suggestion-id-1': { - content: {text: 'Great that it worked. Is there anything else you need?'}, + "message_id": "uuid", + "suggestions": { + "suggestion-id-1": { + "content": {"text": "Great that it worked. Is there anything else you need?"}, }, - 'suggestion-id-2': { - content: {text: 'Have a nice day!'}, + "suggestion-id-2": { + "content": {"text": "Have a nice day!"}, }, }, } @@ -24,20 +24,20 @@ The updated message including the suggested replies. ```json5 { - id: '{UUID}', - content: {text: 'Hello World'}, - state: '{String}', - sender_type: '{string/enum}', - sent_at: '{string}', - source: '{String}', - metadata: { - suggestions: { - 'suggestion-id-1': { - content: {text: 'Great that it worked. Is there anything else you need?'}, + "id": "{UUID}", + "content": {"text": "Hello World"}, + "state": "{String}", + "from_contact": true, + "sent_at": "{string}", + "source": "{String}", + "metadata": { + "suggestions": { + "suggestion-id-1": { + "content": {"text": "Great that it worked. Is there anything else you need?"}, // source specific content field (same as message content) }, - 'suggestion-id-2': { - content: {text: 'Have a nice day!'}, + "suggestion-id-2": { + "content": {"text": "Have a nice day!"}, }, }, }, diff --git a/docs/docs/api/event-payloads.mdx b/docs/docs/api/event-payloads.mdx index d7faed2e45..b0a7abc7ea 100644 --- a/docs/docs/api/event-payloads.mdx +++ b/docs/docs/api/event-payloads.mdx @@ -12,8 +12,7 @@ // source message payload "delivery_state": "pending|failed|delivered", // delivery state of message, one of pending, failed, delivered - "sender_type": "{string/enum}", - // See glossary + "from_contact": true, "sent_at": "{string}", //'yyyy-MM-dd'T'HH:mm:ss.SSSZ' date in UTC form, to be localized by clients "source": "{String}", diff --git a/docs/docs/api/webhook.md b/docs/docs/api/webhook.md index 9a7a9b2b0e..af57b46321 100644 --- a/docs/docs/api/webhook.md +++ b/docs/docs/api/webhook.md @@ -106,8 +106,7 @@ request with one the following payloads: // source message payload "delivery_state": "pending|failed|delivered", // delivery state of message, one of pending, failed, delivered - "sender_type": "{string/enum}", - // See glossary + "from_contact": true, "sent_at": "{string}", //'yyyy-MM-dd'T'HH:mm:ss.SSSZ' date in UTC form, to be localized by clients "source": "{String}" diff --git a/docs/docs/api/websocket.md b/docs/docs/api/websocket.md index 006a661579..0af79384bd 100644 --- a/docs/docs/api/websocket.md +++ b/docs/docs/api/websocket.md @@ -39,8 +39,7 @@ field informs the client of the kind of update that is encoded in the payload. // source message payload "delivery_state": "pending|failed|delivered", // delivery state of message, one of pending, failed, delivered - "sender_type": "{string/enum}", - // See glossary + "from_contact": true, "sent_at": "{string}", //'yyyy-MM-dd'T'HH:mm:ss.SSSZ' date in UTC form, to be localized by clients "source": "{String}" diff --git a/docs/docs/changelog.md b/docs/docs/changelog.md index 387f111b31..71c5453c1d 100644 --- a/docs/docs/changelog.md +++ b/docs/docs/changelog.md @@ -3,6 +3,68 @@ title: Changelog sidebar_label: πŸ“ Changelog --- +## 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)] +- [[#1577](https://github.com/airyhq/airy/issues/1577)] Conversations.setState Returns 404 [[#1578](https://github.com/airyhq/airy/pull/1578)] +- [[#1526](https://github.com/airyhq/airy/issues/1526)] Added conversation count in inbox [[#1572](https://github.com/airyhq/airy/pull/1572)] +- [[#1566](https://github.com/airyhq/airy/issues/1566)] Add state endpoints [[#1568](https://github.com/airyhq/airy/pull/1568)] +- [[#1537](https://github.com/airyhq/airy/issues/1537)] AWS Uninstall Docs - Remove reference to… [[#1564](https://github.com/airyhq/airy/pull/1564)] +- [[#1502](https://github.com/airyhq/airy/issues/1502)] Improve model lib [[#1547](https://github.com/airyhq/airy/pull/1547)] +- [[#740](https://github.com/airyhq/airy/issues/740)] Uses components endpoint on service discovery [[#1549](https://github.com/airyhq/airy/pull/1549)] +- [[#1503](https://github.com/airyhq/airy/issues/1503)] Cypress test to end a conversation in chatplugin [[#1543](https://github.com/airyhq/airy/pull/1543)] +- [[#740](https://github.com/airyhq/airy/issues/740)] Adding k8s endpoint to airy controller [[#1546](https://github.com/airyhq/airy/pull/1546)] +- [[#740](https://github.com/airyhq/airy/issues/740)] Label and introspect components [[#1510](https://github.com/airyhq/airy/pull/1510)] +- [[#740](https://github.com/airyhq/airy/issues/740)] Refactor config apply [[#1544](https://github.com/airyhq/airy/pull/1544)] + +#### πŸ› Bug Fixes + +- [[#1590](https://github.com/airyhq/airy/issues/1590)] Fix api host variable injection in chatplugin [[#1591](https://github.com/airyhq/airy/pull/1591)] +- [[#740](https://github.com/airyhq/airy/issues/740)] Fix env variables [[#1583](https://github.com/airyhq/airy/pull/1583)] +- [[#1581](https://github.com/airyhq/airy/issues/1581)] Prevent page from crashing when adding a channel [[#1582](https://github.com/airyhq/airy/pull/1582)] +- [[#1570](https://github.com/airyhq/airy/issues/1570)] Fixed confikey chat plugin [[#1571](https://github.com/airyhq/airy/pull/1571)] +- [[#1565](https://github.com/airyhq/airy/issues/1565)] Fixed github variable [[#1565](https://github.com/airyhq/airy/pull/1565)] +- [[#635](https://github.com/airyhq/airy/issues/635)] Fix deployment of the library to npm [[#1411](https://github.com/airyhq/airy/pull/1411)] +- [[#1555](https://github.com/airyhq/airy/issues/1555)] Fixed template button [[#1556](https://github.com/airyhq/airy/pull/1556)] +- [[#1540](https://github.com/airyhq/airy/issues/1540)] Added return to messageBubble [[#1542](https://github.com/airyhq/airy/pull/1542)] +- [[#1535](https://github.com/airyhq/airy/issues/1535)] Release version uses correct app image tag [[#1538](https://github.com/airyhq/airy/pull/1538)] + +#### πŸ“š Documentation + +- [[#1399](https://github.com/airyhq/airy/issues/1399)] Add Rasa suggested reply guide [[#1548](https://github.com/airyhq/airy/pull/1548)] +- [[#1532](https://github.com/airyhq/airy/issues/1532)] Remove step 4 of airy cli installation docs [[#1534](https://github.com/airyhq/airy/pull/1534)] + +#### 🧰 Maintenance + +- Bump css-loader from 5.2.2 to 5.2.4 [[#1587](https://github.com/airyhq/airy/pull/1587)] +- Bump webpack from 5.33.2 to 5.34.0 [[#1586](https://github.com/airyhq/airy/pull/1586)] +- Bump sass from 1.32.10 to 1.32.11 [[#1585](https://github.com/airyhq/airy/pull/1585)] +- Bump core-js from 3.10.1 to 3.10.2 [[#1584](https://github.com/airyhq/airy/pull/1584)] +- Bump @bazel/typescript from 3.3.0 to 3.4.0 [[#1552](https://github.com/airyhq/airy/pull/1552)] +- Bump css-loader from 5.2.1 to 5.2.2 [[#1574](https://github.com/airyhq/airy/pull/1574)] +- Bump sass from 1.32.8 to 1.32.10 [[#1573](https://github.com/airyhq/airy/pull/1573)] +- Bump @types/node from 14.14.40 to 14.14.41 [[#1561](https://github.com/airyhq/airy/pull/1561)] +- Bump @types/node from 14.14.39 to 14.14.40 [[#1559](https://github.com/airyhq/airy/pull/1559)] +- Bump react-markdown from 5.0.3 to 6.0.0 [[#1554](https://github.com/airyhq/airy/pull/1554)] +- Bump @types/node from 14.14.37 to 14.14.39 [[#1553](https://github.com/airyhq/airy/pull/1553)] +- Bump webpack from 5.32.0 to 5.33.2 [[#1551](https://github.com/airyhq/airy/pull/1551)] +- Bump react-modal from 3.12.1 to 3.13.1 [[#1545](https://github.com/airyhq/airy/pull/1545)] +- Bump @typescript-eslint/parser from 4.21.0 to 4.22.0 [[#1528](https://github.com/airyhq/airy/pull/1528)] +- Bump cypress from 7.0.1 to 7.1.0 [[#1529](https://github.com/airyhq/airy/pull/1529)] +- Bump @typescript-eslint/eslint-plugin from 4.21.0 to 4.22.0 [[#1530](https://github.com/airyhq/airy/pull/1530)] +- Bump webpack from 5.31.2 to 5.32.0 [[#1527](https://github.com/airyhq/airy/pull/1527)] + +#### 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.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 #### πŸš€ Features diff --git a/docs/docs/cli/introduction.md b/docs/docs/cli/introduction.md index eea7be2a75..ddb8971a3e 100644 --- a/docs/docs/cli/introduction.md +++ b/docs/docs/cli/introduction.md @@ -26,7 +26,6 @@ steps for a quick setup: - [Step 1: Check the requirements](introduction.md#step-1-check-the-requirements) - [Step 2: Install the Airy CLI](introduction.md#step-2-install-the-airy-cli) - [Step 3: Verify your installation](introduction.md#step-3-verify-your-installation) -- [Step 4: Initialize the setup](introduction.md#step-4-initialize-the-setup) ### Step 1: Check the requirements @@ -133,15 +132,6 @@ Make sure the output matches the version number you expect: airy version ``` -### Step 4: Initialize the setup - -The [airy init](cli/usage.md#init) will create a `cli.yaml` file which stores -your `apiHost` and `apiJwtToken`: - -```bash -airy init -``` - :tada: Congratulations! diff --git a/docs/docs/getting-started/components.md b/docs/docs/getting-started/components.md index c675a38df1..1cd60eeacc 100644 --- a/docs/docs/getting-started/components.md +++ b/docs/docs/getting-started/components.md @@ -65,6 +65,6 @@ Airy Core contains the following core components: iconInvertible={true} title='Integrations' description="Pre-made integrations into popular conversational tools, for example NLP tools like Rasa" - link='/integrations/rasa' + link='/integrations/rasa-assistant' /> diff --git a/docs/docs/getting-started/installation/aws.md b/docs/docs/getting-started/installation/aws.md index 4ce8f46d19..c5a071f971 100644 --- a/docs/docs/getting-started/installation/aws.md +++ b/docs/docs/getting-started/installation/aws.md @@ -155,10 +155,11 @@ For more details please see our [Configuration Section](configuration.md). You can remove the Airy Core AWS installation by deleting the Airy Core AWS resources with the [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html). -Retrieve the ID of the installation: +Retrieve the ID of the installation, in this case `my-airy` is the name of the installation that was passed on the creation process: ```sh -id=$(cat ~/.airy/cli.yaml | grep contextname | awk '{ print $2; }') +cd my-airy +id=$(cat cli.yaml | grep contextname | awk '{ print $2; }') echo ${id} ``` diff --git a/docs/docs/getting-started/installation/configuration.md b/docs/docs/getting-started/installation/configuration.md index 6b0a5d2dd8..a5d571d2e9 100644 --- a/docs/docs/getting-started/installation/configuration.md +++ b/docs/docs/getting-started/installation/configuration.md @@ -25,7 +25,7 @@ is enough not to provide any facebook specific configuration. Now let's have a look at the different sections so you can make the changes you are looking for. -### Global +### Kubernetes - `appImageTag` the image tag of the container images for the **Airy Components** @@ -61,13 +61,13 @@ cluster, PostgreSQL, and Redis. - `username` these credentials will be passed to the **API Auth Component** - `password` and they will be used to create the Postgres database -### Components +### Security -- `api` +- `jwtSecret` must be set to a long secure secret in production environments (default: random generated) +- `token` set to a long secure secret to use for machine [API authentication](api/authentication.md) (default: random generated) +- `allowedOrigins` your sites origin to prevent CORS-based attacks (default: "\*") - - `jwtSecret` must be set to a long secure secret in production environments - - `token` set to a long secure secret to use for machine [API authentication](api/authentication.md) - - `allowedOrigins` your sites origin to prevent CORS-based attacks +### Components - `sources` @@ -78,11 +78,16 @@ cluster, PostgreSQL, and Redis. The **Airy Controller** only starts configured sources. To keep system load to a minimum, only add the sources you are using. -- `webhooks` - - `name` -- `media-resolver` - - `storage` - - `s3` set these to your AWS S3 config to store source specific user data +- `integration` + - `webhook` + - `name` set this to the name of your webhook integration +- `media` + - `resolver` + - `s3Key` set this to your AWS S3 access key id + - `s3Secret` set this to your AWS S3 secret access key + - `s3Bucket` set this to your AWS S3 bucket + - `s3Region` set this to your AWS region + - `s3Path` set this to your AWS S3 path ### Tools diff --git a/docs/docs/guides/operations.md b/docs/docs/guides/operations.md index 8ad07d2262..b3ef55fb38 100644 --- a/docs/docs/guides/operations.md +++ b/docs/docs/guides/operations.md @@ -55,7 +55,7 @@ be used for all sorts of dashboard and monitoring requirements. `k edit cm prometheus-grafana` -```yaml +```toml [server] domain = root_url = /grafana diff --git a/docs/docs/integrations/rasa.md b/docs/docs/integrations/rasa-assistant.md similarity index 91% rename from docs/docs/integrations/rasa.md rename to docs/docs/integrations/rasa-assistant.md index bcdfbf423e..0bb94a9c0d 100644 --- a/docs/docs/integrations/rasa.md +++ b/docs/docs/integrations/rasa-assistant.md @@ -1,6 +1,6 @@ --- -title: Rasa integration -sidebar_label: Rasa +title: Rasa Chat Assistant +sidebar_label: Rasa Assistant --- import useBaseUrl from '@docusaurus/useBaseUrl'; @@ -32,14 +32,14 @@ array of messaging channels and service them in a single inbox. For Rasa, you can think of it as a forward messaging router that will persist your data and make it available for export to anywhere within your organization. -This guide covers how to configure your Rasa installation so that it can use the +This guide covers how to configure your Rasa installation so that it can use Airy Core to send and receive messages. :::note Prerequisites - A running Airy Core installation [Get Started](getting-started/installation/introduction.md) -- A local Rasa setup: For convenience, we recommend [the Docker setup](https://rasa.com/docs/rasa/docker/building-in-docker/) or [a demo repository](https://github.com/airyhq/rasa-demo) we created for this guide +- A local Rasa setup: For convenience, we recommend [the Docker setup](https://rasa.com/docs/rasa/docker/building-in-docker/) or [the demo repository](https://github.com/airyhq/rasa-demo) we created for this guide ::: @@ -84,16 +84,15 @@ copy this [connector file](https://github.com/airyhq/rasa-demo/blob/master/channels/airy.py) into it. The connector requires the following configuration values: -- `auth_token` the Airy Core JWT token you used - to connect the webhook. +- `system_token` the Airy Core system token used for authenticating with the API. - `api_host` The url where you host your Airy Core API. Add them to your existing Rasa `credentials.yml`: ```yaml channels.airy.AiryInput: - api_host: "your JWT authentication token" - auth_token: "http://airy.core" + api_host: "http://airy.core" + system_token: "the system api token for Airy Core" ``` Now you should have a working integration πŸŽ‰. diff --git a/docs/docs/integrations/rasa-suggested-replies.md b/docs/docs/integrations/rasa-suggested-replies.md new file mode 100644 index 0000000000..4915f4a8b9 --- /dev/null +++ b/docs/docs/integrations/rasa-suggested-replies.md @@ -0,0 +1,100 @@ +--- +title: Suggested replies with Rasa +sidebar_label: Rasa Suggested Replies +--- + +import useBaseUrl from '@docusaurus/useBaseUrl'; + +:::note Prerequisites + +This guide assumes that you completed the [Rasa Chat assistant guide](/integrations/rasa-assistant.md), which means you have: + +- a running Airy Core instance +- a Rasa setup connected to that instance with a custom channel (see the [demo repository](https://github.com/airyhq/rasa-demo)) + +::: + +## How it works + +see suggested replies in the Airy inbox when receiving a contact greeting + +Chatbots can serve a wide variety of use cases like answering frequently asked questions or booking flows. +Customer support however often requires a human agent to serve user questions with a high degree of quality. With Airy +Core you can get the best of both worlds by using NLP frameworks like Rasa to suggest a set of replies to the agent. +This way agents can handle the vast majority of use cases with the click of a button (see screenshot). + +## Configuring Rasa + +- [Step 1: Add a custom response type](#step-1-add-a-custom-response-type) +- [Step 2: Update the user stories](#step-2-update-the-user-stories) +- [Step 3: Extend the Airy connector](#step-3-extend-the-airy-connector) +- [Step 4: Retrain and restart](#step-4-consume-directly-from-apache-kafka) + +### Step 1: Add a custom response type + +The easiest way to instruct Rasa to suggest replies for a given user messages is by adding them as a [custom response type](https://rasa.com/docs/rasa/responses/#custom-output-payloads). To do this we add the following block to the `responses` section in our `domain.yaml`: + +```yaml +responses: + utter_suggest_greet: + - custom: + suggest-informal: + content: + text: "Hey, what's up?" + suggest-formal: + content: + text: "Hi, what can I help you with?" +``` + +### Step 2: Update the user stories + +Now we can use this new response type in our `stories.yaml` to let the bot know when to suggest replies: + +```yaml +stories: + - story: happy path + steps: + - intent: greet + - action: utter_suggest_greet + - intent: mood_great + - action: utter_happy +``` + +### Step 3: Extend the Airy connector + +Now we need to update our [custom Rasa connector](https://rasa.com/docs/rasa/connectors/custom-connectors/) for Airy Core to this response type. For +this we extend the [send_response method](https://github.com/airyhq/rasa-demo/blob/4f2fdd6063385cea805f2d70755733de347e8792/channels/airy.py#L32) in the Airy connector so that it calls the [suggest replies API](/api/endpoints/messages#suggested-replies) whenever +it encounters a custom response payload: + +```python +async def send_response(self, recipient_id: Text, message: Dict[Text, Any]) -> None: + headers = { + "Authorization": self.system_token + } + if message.get("custom"): + body = { + "message_id": self.last_message_id, + "suggestions": message.get("custom") + } + requests.post("{}/messages.suggestReplies".format(self.api_host), headers=headers, json=body) + elif message.get("text"): + body = { + "conversation_id": recipient_id, + "message": { + "text": message.get("text") + } + } + requests.post("{}/messages.send".format(self.api_host), headers=headers, json=body) +``` + +### Step 4: Retrain and restart + +Now we need to stop the server and retrain the model: + +```shell script +rasa train +``` + +Finally, we start the Rasa server, open the Airy Inbox at (`http://airy.core` for local deployments), where we should +see the suggested replies whenever a contact greets us (see gif above). diff --git a/docs/docs/ui/suggestedReplies.md b/docs/docs/ui/suggestedReplies.md index 7656f1fc5d..5f3a6c0058 100644 --- a/docs/docs/ui/suggestedReplies.md +++ b/docs/docs/ui/suggestedReplies.md @@ -42,5 +42,5 @@ You can integrate the Open Source machine learning framework Rasa to automate se icon={} title='Rasa integration' description='Configure Rasa to receive and reply to messages using Airy' -link='integrations/rasa' +link='integrations/rasa-assistant' /> diff --git a/docs/sidebars.js b/docs/sidebars.js index 94a6909d83..f285de2943 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -66,7 +66,7 @@ module.exports = { { 'πŸ› οΈ Integrations': [ { - 'Conversational AI /NLP': ['integrations/rasa'], + 'Conversational AI /NLP': ['integrations/rasa-assistant', 'integrations/rasa-suggested-replies'], }, ], }, diff --git a/docs/static/img/integrations/rasa/suggested-replies.gif b/docs/static/img/integrations/rasa/suggested-replies.gif new file mode 100644 index 0000000000..b54d1fe453 Binary files /dev/null and b/docs/static/img/integrations/rasa/suggested-replies.gif differ diff --git a/frontend/chat-plugin/BUILD b/frontend/chat-plugin/BUILD index 4d6d0d6b20..f52ba9f0cc 100644 --- a/frontend/chat-plugin/BUILD +++ b/frontend/chat-plugin/BUILD @@ -8,6 +8,8 @@ load("@rules_pkg//:pkg.bzl", "pkg_tar") load("//tools/build:container_release.bzl", "container_release") load("@io_bazel_rules_docker//container:container.bzl", "container_image") +package(default_visibility = ["//visibility:public"]) + module_deps = [ "//lib/typescript/assets", "//lib/typescript/components", @@ -24,7 +26,7 @@ web_app( "react": "preact/compat", "react-dom": "preact/compat", }, - app_lib = ":app", + app_lib = ":chat-plugin", entry = "frontend/chat-plugin/src/iframe", index = ":development.html", module_deps = module_deps, @@ -39,7 +41,7 @@ web_library( "react": "preact/compat", "react-dom": "preact/compat", }, - app_lib = ":app", + app_lib = ":chat-plugin", entry = "frontend/chat-plugin/src/defaultScript.js", module_deps = module_deps, output = { @@ -50,7 +52,7 @@ web_library( ) ts_web_library( - name = "app", + name = "chat-plugin", tsconfig = ":widget_tsconfig", deps = module_deps + [ "@npm//@stomp/stompjs", diff --git a/frontend/chat-plugin/handles/index.ts b/frontend/chat-plugin/handles/index.ts index 0f7a74aedc..5dda6f6533 100644 --- a/frontend/chat-plugin/handles/index.ts +++ b/frontend/chat-plugin/handles/index.ts @@ -2,3 +2,6 @@ export const cyBubble = 'bubble'; export const cyInputbarTextarea = 'inputbarTextarea'; export const cyInputbarButton = 'inputbarButton'; export const cyChatPluginMessageList = 'chatPluginMessageList'; +export const cyChatPluginHeaderBarCloseButton = 'chatPluginHeaderBarCloseButton'; +export const cyChatPluginEndChatModalButton = 'chatPluginEndChatModalButton'; +export const cyChatPluginStartNewConversation = 'chatPluginStartNewConversation'; diff --git a/frontend/chat-plugin/index.ts b/frontend/chat-plugin/index.ts new file mode 100644 index 0000000000..44ca2a2613 --- /dev/null +++ b/frontend/chat-plugin/index.ts @@ -0,0 +1,2 @@ +export * from './src/AiryChatPlugin'; +export * from './src/config'; diff --git a/frontend/chat-plugin/src/AiryChatPlugin.module.scss b/frontend/chat-plugin/src/AiryChatPlugin.module.scss new file mode 100644 index 0000000000..cd0b1e4135 --- /dev/null +++ b/frontend/chat-plugin/src/AiryChatPlugin.module.scss @@ -0,0 +1,12 @@ +.chatpluginWrapper { + position: relative; + width: 450px; + height: 506px; + display: flex; + flex-flow: column-reverse; + background-color: var(--color-background-gray); + border: 1px solid var(--color-dark-elements-gray); + border-radius: 10px; + padding: 0 24px 0 24px; + margin: 32px 0 0 24px; +} diff --git a/frontend/chat-plugin/src/AiryChatPlugin.tsx b/frontend/chat-plugin/src/AiryChatPlugin.tsx new file mode 100644 index 0000000000..c3b597c31b --- /dev/null +++ b/frontend/chat-plugin/src/AiryChatPlugin.tsx @@ -0,0 +1,42 @@ +import React, {useEffect, createRef, CSSProperties} from 'react'; +import {AiryChatPluginConfiguration} from './config'; +import AiryWidget from './AiryWidget'; + +import styles from './AiryChatPlugin.module.scss'; + +type AiryChatPluginProps = { + config: AiryChatPluginConfiguration; + customCSS?: CSSProperties; +}; + +export const AiryChatPlugin = (props: AiryChatPluginProps) => { + const {config, customCSS} = props; + + const chatPlugin: AiryWidget = new AiryWidget({...config}); + const anchorRef: React.RefObject = createRef(); + + useEffect(() => { + chatPlugin.config = config; + const chatpluginContainer = document.getElementById('chatpluginContainerId'); + chatPlugin.render(anchorRef.current); + chatpluginContainer.appendChild(anchorRef.current); + }, [config]); + + const customStyle = { + background: 'transparent', + ...(config.config?.primaryColor && { + '--color-airy-blue': config.config?.primaryColor, + }), + ...(config.config?.accentColor && { + '--color-airy-accent': config.config?.accentColor, + '--color-airy-blue-hover': config.config?.accentColor, + '--color-airy-blue-pressed': config.config?.accentColor, + }), + }; + + return ( +
+
+
+ ); +}; diff --git a/frontend/chat-plugin/src/AiryWidget.tsx b/frontend/chat-plugin/src/AiryWidget.tsx index e08a1f6e70..d306af5b10 100644 --- a/frontend/chat-plugin/src/AiryWidget.tsx +++ b/frontend/chat-plugin/src/AiryWidget.tsx @@ -1,12 +1,12 @@ import React from 'react'; import {render} from 'react-dom'; import Chat from './components/chat'; -import {AiryWidgetConfiguration} from './config'; +import {AiryChatPluginConfiguration} from './config'; export default class { - config: AiryWidgetConfiguration; + config: AiryChatPluginConfiguration; - constructor(config: AiryWidgetConfiguration) { + constructor(config: AiryChatPluginConfiguration) { this.config = config; } diff --git a/frontend/chat-plugin/src/App.tsx b/frontend/chat-plugin/src/App.tsx index 94fabd4879..b88ea86e79 100644 --- a/frontend/chat-plugin/src/App.tsx +++ b/frontend/chat-plugin/src/App.tsx @@ -1,6 +1,16 @@ import React, {Component} from 'react'; import Chat from './components/chat'; import style from './App.module.scss'; +import {Config} from './config'; + +declare global { + interface Window { + airy: { + host: string; + channelId: string; + }; + } +} export default class App extends Component { render() { @@ -19,10 +29,18 @@ export default class App extends Component { }), }; + const apiHost: string = window.airy ? window.airy.host : process.env.API_HOST; + return (
{channelId ? ( - + ) : ( Widget authorization failed. Please check your installation. )} @@ -31,18 +49,6 @@ export default class App extends Component { } } -export type Config = { - welcomeMessage?: {}; - headerText?: string; - headerTextColor?: string; - backgroundColor?: string; - primaryColor?: string; - accentColor?: string; - bubbleIcon?: string; - sendMessageIcon?: string; - showMode: boolean; -}; - export const config: Config = { welcomeMessage: { fallback: 'Hello!\n\nWelcome to Airy!', diff --git a/frontend/chat-plugin/src/airyRenderProps/AiryBubble/index.tsx b/frontend/chat-plugin/src/airyRenderProps/AiryBubble/index.tsx index c50ec79c61..748aadf333 100644 --- a/frontend/chat-plugin/src/airyRenderProps/AiryBubble/index.tsx +++ b/frontend/chat-plugin/src/airyRenderProps/AiryBubble/index.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import {Config} from '../../App'; +import {Config} from '../../config'; import style from './index.module.scss'; type Props = { diff --git a/frontend/chat-plugin/src/airyRenderProps/AiryHeaderBar/index.module.scss b/frontend/chat-plugin/src/airyRenderProps/AiryHeaderBar/index.module.scss index 2b8d5a4975..e9548ba1c2 100644 --- a/frontend/chat-plugin/src/airyRenderProps/AiryHeaderBar/index.module.scss +++ b/frontend/chat-plugin/src/airyRenderProps/AiryHeaderBar/index.module.scss @@ -54,7 +54,7 @@ border: none; height: 32px; width: 32px; - background: #1578d4; + background: transparent; border-radius: 100%; cursor: pointer; diff --git a/frontend/chat-plugin/src/airyRenderProps/AiryHeaderBar/index.tsx b/frontend/chat-plugin/src/airyRenderProps/AiryHeaderBar/index.tsx index bbeaf665cf..0b2d2b187e 100644 --- a/frontend/chat-plugin/src/airyRenderProps/AiryHeaderBar/index.tsx +++ b/frontend/chat-plugin/src/airyRenderProps/AiryHeaderBar/index.tsx @@ -1,8 +1,9 @@ import React from 'react'; -import {Config} from '../../App'; +import {Config} from '../../config'; import style from './index.module.scss'; import {ReactComponent as CloseButton} from 'assets/images/icons/close.svg'; import {ReactComponent as MinimizeButton} from 'assets/images/icons/minimize-button.svg'; +import {cyChatPluginHeaderBarCloseButton} from 'chat-plugin-handles'; type AiryHeaderBarProps = { toggleHideChat: () => void; @@ -35,7 +36,11 @@ const AiryHeaderBar = (props: AiryHeaderBarProps) => { -
diff --git a/frontend/chat-plugin/src/airyRenderProps/AiryInputBar/index.tsx b/frontend/chat-plugin/src/airyRenderProps/AiryInputBar/index.tsx index 3876ef9f54..869982540f 100644 --- a/frontend/chat-plugin/src/airyRenderProps/AiryInputBar/index.tsx +++ b/frontend/chat-plugin/src/airyRenderProps/AiryInputBar/index.tsx @@ -1,7 +1,7 @@ import React, {ChangeEvent, FormEvent, KeyboardEvent, createRef, useEffect} from 'react'; import style from './index.module.scss'; import {cyInputbarTextarea, cyInputbarButton} from 'chat-plugin-handles'; -import {Config} from '../../App'; +import {Config} from '../../config'; type AiryInputBarProps = { sendMessage: (text: string) => void; @@ -65,7 +65,7 @@ const AiryInputBar = (props: AiryInputBarProps) => { ref={textInputRef} className={style.textArea} placeholder={'Enter a message...'} - autoFocus={true} + autoFocus={!config.showMode ? true : false} onChange={handleChange} onKeyDown={handleKeyDown} value={props.messageString} diff --git a/frontend/chat-plugin/src/api/index.tsx b/frontend/chat-plugin/src/api/index.tsx index 4a559442f4..2880a91aed 100644 --- a/frontend/chat-plugin/src/api/index.tsx +++ b/frontend/chat-plugin/src/api/index.tsx @@ -1,17 +1,13 @@ import {QuickReplyCommand, SuggestionResponse, TextContent} from 'render/providers/chatplugin/chatPluginModel'; import {setResumeTokenInStorage} from '../storage'; -declare const window: { - airy: { - host: string; - channelId: string; - }; +let host; +export const setApiHost = apiHost => { + host = apiHost; }; -const API_HOST = window.airy ? window.airy.host : process.env.API_HOST; - export const sendMessage = (message: TextContent | SuggestionResponse | QuickReplyCommand, token: string) => { - return fetch(`${API_HOST}/chatplugin.send`, { + return fetch(`${host}/chatplugin.send`, { method: 'POST', body: JSON.stringify(convertToBody(message)), headers: { @@ -39,7 +35,7 @@ const convertToBody = (message: TextContent | SuggestionResponse | QuickReplyCom }; export const getResumeToken = async (channelId: string, authToken: string) => { - const resumeChat = await fetch(`${API_HOST}/chatplugin.resumeToken`, { + const resumeChat = await fetch(`${host}/chatplugin.resumeToken`, { method: 'POST', body: JSON.stringify({}), headers: { @@ -53,7 +49,7 @@ export const getResumeToken = async (channelId: string, authToken: string) => { export const start = async (channelId: string, resumeToken: string) => { try { - const response = await fetch(`${API_HOST}/chatplugin.authenticate`, { + const response = await fetch(`${host}/chatplugin.authenticate`, { method: 'POST', body: JSON.stringify({ channel_id: channelId, diff --git a/frontend/chat-plugin/src/components/chat/index.tsx b/frontend/chat-plugin/src/components/chat/index.tsx index 188227dd96..5ab67c02d0 100644 --- a/frontend/chat-plugin/src/components/chat/index.tsx +++ b/frontend/chat-plugin/src/components/chat/index.tsx @@ -2,39 +2,45 @@ import React from 'react'; import {useState, useEffect} from 'react'; import {IMessage} from '@stomp/stompjs'; +import {DeliveryState, Message} from 'model'; + import WebSocket, {ConnectionState} from '../../websocket'; import MessageProp from '../../components/message'; import InputBarProp from '../../components/inputBar'; import AiryInputBar from '../../airyRenderProps/AiryInputBar'; -import style from './index.module.scss'; import HeaderBarProp from '../../components/headerBar'; import AiryHeaderBar from '../../airyRenderProps/AiryHeaderBar'; -import {AiryWidgetConfiguration} from '../../config'; + +import {AiryChatPluginConfiguration} from '../../config'; + import BubbleProp from '../bubble'; import AiryBubble from '../../airyRenderProps/AiryBubble'; -import {MessageState, isFromContact, Message} from 'model'; + import {SourceMessage, CommandUnion} from 'render'; import {MessageInfoWrapper} from 'render/components/MessageInfoWrapper'; + /* eslint-disable @typescript-eslint/no-var-requires */ const camelcaseKeys = require('camelcase-keys'); -import {cyBubble, cyChatPluginMessageList} from 'chat-plugin-handles'; +import {cyBubble, cyChatPluginMessageList, cyChatPluginEndChatModalButton} from 'chat-plugin-handles'; import {getResumeTokenFromStorage, resetStorage} from '../../storage'; import {ModalDialogue} from '../../components/modal'; import NewConversation from '../../components/newConversation'; -import {start} from '../../api'; +import {setApiHost, start} from '../../api'; + +import style from './index.module.scss'; let ws: WebSocket; const defaultWelcomeMessage: Message = { id: '19527d24-9b47-4e18-9f79-fd1998b95059', content: {text: 'Hello! How can we help you?'}, - deliveryState: MessageState.delivered, + deliveryState: DeliveryState.delivered, fromContact: false, sentAt: new Date(), }; -type Props = AiryWidgetConfiguration; +type Props = AiryChatPluginConfiguration; const Chat = (props: Props) => { const {config} = props; @@ -43,9 +49,15 @@ const Chat = (props: Props) => { defaultWelcomeMessage.content = config.welcomeMessage; } + const chatHiddenInitialState = (): boolean => { + if (config.showMode === true) return false; + if (getResumeTokenFromStorage(props.channelId)) return true; + return false; + }; + const [installError, setInstallError] = useState(''); const [animation, setAnimation] = useState(''); - const [isChatHidden, setIsChatHidden] = useState(getResumeTokenFromStorage(props.channelId) ? false : true); + const [isChatHidden, setIsChatHidden] = useState(chatHiddenInitialState()); const [messages, setMessages] = useState([defaultWelcomeMessage]); const [messageString, setMessageString] = useState(''); const [connectionState, setConnectionState] = useState(null); @@ -54,15 +66,20 @@ const Chat = (props: Props) => { useEffect(() => { if (config.showMode) return; + setApiHost(props.apiHost); - ws = new WebSocket(props.channelId, onReceive, setInitialMessages, (state: ConnectionState) => { + ws = new WebSocket(props.apiHost, props.channelId, onReceive, setInitialMessages, (state: ConnectionState) => { setConnectionState(state); }); ws.start().catch(error => { console.error(error); setInstallError(error.message); }); - }, []); + }, [props.apiHost, props.channelId]); + + useEffect(() => { + setAnimation(''); + }, [config]); useEffect(() => { updateScroll(); @@ -197,9 +214,9 @@ const Chat = (props: Props) => {
- {messages.map((message, index: number) => { + {messages.map((message: Message, index: number) => { const nextMessage = messages[index + 1]; - const lastInGroup = nextMessage ? isFromContact(message) !== isFromContact(nextMessage) : true; + const lastInGroup = nextMessage ? message.fromContact !== nextMessage.fromContact : true; return ( { ? () => props.airyMessageProp(ctrl) : () => ( { {' '} Cancel - diff --git a/frontend/chat-plugin/src/components/newConversation/index.tsx b/frontend/chat-plugin/src/components/newConversation/index.tsx index 8031cd07d0..52d25883e2 100644 --- a/frontend/chat-plugin/src/components/newConversation/index.tsx +++ b/frontend/chat-plugin/src/components/newConversation/index.tsx @@ -1,5 +1,6 @@ import React from 'react'; import style from './index.module.scss'; +import {cyChatPluginStartNewConversation} from 'chat-plugin-handles'; type newConversationProps = { reAuthenticate: () => void; @@ -14,7 +15,11 @@ const NewConversation = (props: newConversationProps) => {
diff --git a/frontend/chat-plugin/src/config.ts b/frontend/chat-plugin/src/config.ts index cbd0907596..535eb97c0f 100644 --- a/frontend/chat-plugin/src/config.ts +++ b/frontend/chat-plugin/src/config.ts @@ -1,18 +1,29 @@ -import {Config} from './App'; - export type RenderCtrl = { toggleHideChat: () => void; }; export type RenderProp = (ctrl?: RenderCtrl) => JSX.Element; +export type Config = { + welcomeMessage?: {}; + headerText?: string; + headerTextColor?: string; + backgroundColor?: string; + primaryColor?: string; + accentColor?: string; + bubbleIcon?: string; + sendMessageIcon?: string; + showMode: boolean; +}; + export type AuthConfiguration = { channelId: string; resumeToken?: string; - config?: Config; }; -export type AiryWidgetConfiguration = AuthConfiguration & { +export type AiryChatPluginConfiguration = AuthConfiguration & { + apiHost: string; + config?: Config; headerBarProp?: RenderProp; inputBarProp?: RenderProp; airyMessageProp?: RenderProp; diff --git a/frontend/chat-plugin/src/defaultScript.tsx b/frontend/chat-plugin/src/defaultScript.tsx index 9f19b5c626..fc6118b6d7 100644 --- a/frontend/chat-plugin/src/defaultScript.tsx +++ b/frontend/chat-plugin/src/defaultScript.tsx @@ -1,5 +1,5 @@ import AiryWidget from './AiryWidget'; -import {Config} from './App'; +import {Config} from './config'; const body = document.getElementsByTagName('body')[0]; @@ -35,6 +35,7 @@ declare const window: { if (window.airy.channelId) { new AiryWidget({ + apiHost: window.airy.host, channelId: window.airy.channelId, resumeToken: window.airy.resumeToken, config: window.airy.config, diff --git a/frontend/chat-plugin/src/websocket/index.ts b/frontend/chat-plugin/src/websocket/index.ts index 0b8caccbc3..09d3e97ed9 100644 --- a/frontend/chat-plugin/src/websocket/index.ts +++ b/frontend/chat-plugin/src/websocket/index.ts @@ -8,17 +8,6 @@ import {getResumeTokenFromStorage, resetStorage} from '../storage'; /* eslint-disable @typescript-eslint/no-var-requires */ const camelcaseKeys = require('camelcase-keys'); -declare global { - interface Window { - airy: { - host: string; - channelId: string; - }; - } -} - -const API_HOST = window.airy ? window.airy.host : process.env.API_HOST; -const host = new URL(API_HOST).host; // https: -> wss: and http: -> ws: const protocol = location.protocol.replace('http', 'ws'); @@ -29,6 +18,7 @@ export enum ConnectionState { class WebSocket { client: Client; + apiHost: string; channelId: string; token: string; setInitialMessages: (messages: Array) => void; @@ -38,11 +28,13 @@ class WebSocket { updateConnectionState: (state: ConnectionState) => void; constructor( + apiHost: string, channelId: string, onReceive: messageCallbackType, setInitialMessages: (messages: Array) => void, updateConnectionState: (state: ConnectionState) => void ) { + this.apiHost = new URL(apiHost).host; this.channelId = channelId; this.onReceive = onReceive; this.setInitialMessages = setInitialMessages; @@ -54,13 +46,10 @@ class WebSocket { this.token = token; this.client = new Client({ - brokerURL: `${protocol}//${host}/ws.chatplugin`, + brokerURL: `${protocol}//${this.apiHost}/ws.chatplugin`, connectHeaders: { Authorization: `Bearer ${token}`, }, - debug: function (str) { - console.info(str); - }, reconnectDelay: 0, heartbeatIncoming: 4000, heartbeatOutgoing: 4000, diff --git a/frontend/ui/BUILD b/frontend/ui/BUILD index 27c21c4d05..3f10282157 100644 --- a/frontend/ui/BUILD +++ b/frontend/ui/BUILD @@ -18,6 +18,8 @@ module_deps = [ "//lib/typescript/dates", "//lib/typescript/websocketclient", "//frontend/ui/handles", + "//frontend/chat-plugin/handles:chat-plugin-handles", + "//frontend/chat-plugin", ] ts_web_library( diff --git a/frontend/ui/src/actions/conversations/index.ts b/frontend/ui/src/actions/conversations/index.ts index f5e899fd6b..6ef760da13 100644 --- a/frontend/ui/src/actions/conversations/index.ts +++ b/frontend/ui/src/actions/conversations/index.ts @@ -13,6 +13,7 @@ const CONVERSATION_ADD_ERROR = '@@conversations/ADD_ERROR_TO_CONVERSATION'; const CONVERSATION_REMOVE_ERROR = '@@conversations/REMOVE_ERROR_FROM_CONVERSATION'; const CONVERSATION_REMOVE_TAG = '@@conversations/CONVERSATION_REMOVE_TAG'; const CONVERSATION_UPDATE_PAGINATION_DATA = '@@conversation/UPDATE_PAGINATION_DATA'; +const CONVERSATION_SET_STATE = '@@conversations/CONVERSATION_SET_STATE'; export const loadingConversationAction = createAction( CONVERSATION_LOADING, @@ -48,6 +49,11 @@ export const updateMessagesPaginationDataAction = createAction( (conversationId: string, paginationData: Pagination) => ({conversationId, paginationData}) )<{conversationId: string; paginationData: Pagination}>(); +export const setStateConversationAction = createAction( + CONVERSATION_SET_STATE, + (conversationId: string, state: string) => ({conversationId, state}) +)<{conversationId: string; state: string}>(); + export const listConversations = () => async (dispatch: Dispatch) => { dispatch(loadingConversationsAction()); return HttpClientInstance.listConversations({page_size: 10}).then((response: PaginatedResponse) => { @@ -99,6 +105,12 @@ export const readConversations = (conversationId: string) => (dispatch: Dispatch ); }; +export const conversationState = (conversationId: string, state: string) => (dispatch: Dispatch) => { + HttpClientInstance.setStateConversation({conversationId, state}).then(() => + dispatch(setStateConversationAction(conversationId, state)) + ); +}; + export const addTagToConversation = (conversationId: string, tagId: string) => (dispatch: Dispatch) => { HttpClientInstance.tagConversation({conversationId, tagId}).then(() => dispatch( diff --git a/frontend/ui/src/pages/Channels/MainPage/index.module.scss b/frontend/ui/src/pages/Channels/MainPage/index.module.scss index cec845d859..bb94f1da5a 100644 --- a/frontend/ui/src/pages/Channels/MainPage/index.module.scss +++ b/frontend/ui/src/pages/Channels/MainPage/index.module.scss @@ -5,7 +5,6 @@ @include font-base; display: flex; justify-content: space-between; - margin-bottom: 20px; color: var(--color-text-contrast); font-size: 39px; font-weight: 900; diff --git a/frontend/ui/src/pages/Channels/MainPage/index.tsx b/frontend/ui/src/pages/Channels/MainPage/index.tsx index be9710681d..5436c2270f 100644 --- a/frontend/ui/src/pages/Channels/MainPage/index.tsx +++ b/frontend/ui/src/pages/Channels/MainPage/index.tsx @@ -61,7 +61,7 @@ const SourcesInfo: SourceInfo[] = [ image: , newChannelRoute: CHANNELS_CHAT_PLUGIN_ROUTE + '/new', channelsListRoute: CHANNELS_CONNECTED_ROUTE + '/chatplugin', - configKey: 'sources-chatplugin', + configKey: 'sources-chat-plugin', channelsToShow: 4, itemInfoString: 'channels', dataCyAddChannelButton: cyChannelsChatPluginAddButton, @@ -128,17 +128,14 @@ const MainPage = (props: MainPageProps & RouteComponentProps) => { const OpenRequirementsDialog = ({source}: {source: string}): JSX.Element => { switch (source) { case Source.facebook: - return setDisplayDialogFromSource('')} />; case Source.google: return setDisplayDialogFromSource('')} />; - break; - case Source.chatPlugin: - break; case Source.twilioSMS: - return setDisplayDialogFromSource('')} />; case Source.twilioWhatsapp: return setDisplayDialogFromSource('')} />; } + + return null; }; const channelsBySource = (Source: Source) => channels.filter((channel: Channel) => channel.source === Source); @@ -163,7 +160,7 @@ const MainPage = (props: MainPageProps & RouteComponentProps) => { sourceInfo={infoItem} displayButton={!channelsBySource(infoItem.type).length} addChannelAction={() => { - if (config.components[infoItem.configKey].enabled) { + if (config.components[infoItem.configKey] && config.components[infoItem.configKey].enabled) { props.history.push(infoItem.newChannelRoute); } else { setDisplayDialogFromSource(infoItem.type); diff --git a/frontend/ui/src/pages/Channels/Providers/Airy/ChatPlugin/sections/CustomiseSection.tsx b/frontend/ui/src/pages/Channels/Providers/Airy/ChatPlugin/sections/CustomiseSection.tsx index 7005e623b9..55ce6f75a7 100644 --- a/frontend/ui/src/pages/Channels/Providers/Airy/ChatPlugin/sections/CustomiseSection.tsx +++ b/frontend/ui/src/pages/Channels/Providers/Airy/ChatPlugin/sections/CustomiseSection.tsx @@ -2,6 +2,8 @@ import React, {createRef, useState} from 'react'; import {Button, Input, ListenOutsideClick} from 'components'; import styles from './CustomiseSection.module.scss'; import {SketchPicker} from 'react-color'; +import {AiryChatPlugin, AiryChatPluginConfiguration} from 'chat-plugin'; +import {env} from '../../../../../../env'; interface CustomiseSectionProps { channelId: string; @@ -39,7 +41,7 @@ export const CustomiseSection = ({channelId, host}: CustomiseSectionProps) => { setShowBackgroundColorPicker(!showBackgroundColorPicker); }; - const getConfig = () => { + const getTemplateConfig = () => { if ( headerText === '' && bubbleIconUrl === '' && @@ -65,6 +67,21 @@ export const CustomiseSection = ({channelId, host}: CustomiseSectionProps) => { };`; }; + const demoConfig: AiryChatPluginConfiguration = { + apiHost: env.API_HOST, + channelId, + config: { + showMode: true, + ...(headerText && {headerText}), + ...(headerTextColor && {headerTextColor}), + ...(primaryColor && {primaryColor}), + ...(accentColor && {accentColor}), + ...(backgroundColor && {backgroundColor}), + ...(bubbleIconUrl && {bubbleIcon: bubbleIconUrl}), + ...(sendMessageIconUrl && {sendMessageIcon: sendMessageIconUrl}), + }, + }; + const copyToClipboard = () => { codeAreaRef.current?.select(); document.execCommand('copy'); @@ -75,7 +92,7 @@ export const CustomiseSection = ({channelId, host}: CustomiseSectionProps) => { (function(w, d, s, n) { w[n] = w[n] || {}; w[n].channelId = "${channelId}"; - w[n].host = "${host}";${getConfig()} + w[n].host = "${host}";${getTemplateConfig()} var f = d.getElementsByTagName(s)[0], j = d.createElement(s); j.async = true; @@ -288,6 +305,9 @@ export const CustomiseSection = ({channelId, host}: CustomiseSectionProps) => { />
+
+ +
); }; diff --git a/frontend/ui/src/pages/Channels/Providers/Airy/ChatPlugin/sections/EditChatPlugin.tsx b/frontend/ui/src/pages/Channels/Providers/Airy/ChatPlugin/sections/EditChatPlugin.tsx index 3c3bef213a..b6ce8f9183 100644 --- a/frontend/ui/src/pages/Channels/Providers/Airy/ChatPlugin/sections/EditChatPlugin.tsx +++ b/frontend/ui/src/pages/Channels/Providers/Airy/ChatPlugin/sections/EditChatPlugin.tsx @@ -33,8 +33,8 @@ export const EditChatPlugin = ({channel, host, updateConnection}: EditChatPlugin }; const ConnectContent = () => { - const [displayName, setDisplayName] = useState(channel.metadata?.name || ''); - const [imageUrl, setImageUrl] = useState(channel.metadata?.imageUrl || ''); + const [displayName, setDisplayName] = useState(channel?.metadata?.name || ''); + const [imageUrl, setImageUrl] = useState(channel?.metadata?.imageUrl || ''); switch (currentPage) { case 'settings': diff --git a/frontend/ui/src/pages/Inbox/ConversationListHeader/index.tsx b/frontend/ui/src/pages/Inbox/ConversationListHeader/index.tsx index 2e188ac624..f72d313b81 100644 --- a/frontend/ui/src/pages/Inbox/ConversationListHeader/index.tsx +++ b/frontend/ui/src/pages/Inbox/ConversationListHeader/index.tsx @@ -5,6 +5,7 @@ import {SearchField} from 'components'; import {StateModel} from '../../../reducers'; import {setSearch, resetFilteredConversationAction} from '../../../actions/conversationsFilter'; +import {allConversations} from '../../../selectors/conversations'; import {ReactComponent as IconSearch} from 'assets/images/icons/search.svg'; import {ReactComponent as BackIcon} from 'assets/images/icons/arrow-left-2.svg'; @@ -17,6 +18,7 @@ const mapStateToProps = (state: StateModel) => { return { user: state.data.user, currentFilter: state.data.conversations.filtered.currentFilter || {}, + conversations: allConversations(state), }; }; @@ -59,6 +61,12 @@ const ConversationListHeader = (props: ConnectedProps) => { handleSearch(value); }; + const InboxConversationCount = () => { + const {conversations} = props; + + return
{`Inbox (${conversations.length})`}
; + }; + const renderSearchInput = isShowingSearchInput ? (
) : (
-
Inbox
+
+ ); + }; + + const ClosedStateButton = () => { + return ( +
+ +
+ ); + }; useEffect(() => { if (active && unread) { @@ -64,6 +92,7 @@ const ConversationListItem = (props: ConversationListItemProps) => {
{participant && participant.displayName}
+ {currentConversationState === 'OPEN' ? : }
diff --git a/frontend/ui/src/pages/Inbox/ConversationsFilter/index.tsx b/frontend/ui/src/pages/Inbox/ConversationsFilter/index.tsx index c57ddd08ce..65a41d23ac 100644 --- a/frontend/ui/src/pages/Inbox/ConversationsFilter/index.tsx +++ b/frontend/ui/src/pages/Inbox/ConversationsFilter/index.tsx @@ -17,7 +17,6 @@ const mapStateToProps = (state: StateModel) => { return { conversationsFilter: state.data.conversations.filtered.currentFilter, isFilterActive: isFilterActive(state), - conversationsPaginationData: state.data.conversations.all.paginationData, filteredPaginationData: state.data.conversations.filtered.paginationData, conversations: allConversations(state), }; @@ -108,8 +107,7 @@ const ConversationsFilter = (props: ConversationsFilterProps) => { }; const itemsCount = () => { - const {conversationsPaginationData, filteredPaginationData} = props; - const formatter = new Intl.NumberFormat('en-US'); + const {filteredPaginationData} = props; if ( filteredPaginationData.filteredTotal !== undefined && @@ -122,14 +120,6 @@ const ConversationsFilter = (props: ConversationsFilterProps) => { ); } - if (conversationsPaginationData.total) { - return ( -
- {`${formatter.format(filteredPaginationData.filteredTotal || props.conversations.length)} Conversations`} -
- ); - } - return
 
; }; diff --git a/frontend/ui/src/pages/Inbox/MessageInput/index.module.scss b/frontend/ui/src/pages/Inbox/MessageInput/index.module.scss index b43a2f45ac..c27c3d3c1d 100644 --- a/frontend/ui/src/pages/Inbox/MessageInput/index.module.scss +++ b/frontend/ui/src/pages/Inbox/MessageInput/index.module.scss @@ -152,8 +152,6 @@ justify-content: center; align-items: center; position: relative; - margin-left: 4px; - width: 30px; height: 30px; svg { diff --git a/frontend/ui/src/pages/Inbox/MessageInput/index.tsx b/frontend/ui/src/pages/Inbox/MessageInput/index.tsx index 01669bafa9..c763bb4eb0 100644 --- a/frontend/ui/src/pages/Inbox/MessageInput/index.tsx +++ b/frontend/ui/src/pages/Inbox/MessageInput/index.tsx @@ -331,7 +331,9 @@ const MessageInput = (props: MessageInputProps & ConnectedProps & { - showSuggestedReplies: (sugggestions: Suggestions) => void; + showSuggestedReplies: (suggestions: Suggestions) => void; }; const mapStateToProps = (state: StateModel, ownProps: ConversationRouteProps) => { @@ -172,7 +172,7 @@ const MessageList = (props: MessageListProps) => { const prevMessage = messages[index - 1]; const nextMessage = messages[index + 1]; - const lastInGroup = nextMessage ? isFromContact(message) !== isFromContact(nextMessage) : true; + const lastInGroup = nextMessage ? message.fromContact !== nextMessage.fromContact : true; const sentAt = lastInGroup ? formatTime(message.sentAt) : null; @@ -190,7 +190,7 @@ const MessageList = (props: MessageListProps) => {
)} ; export type Config = { - components: { - 'sources-chatplugin': { - enabled: boolean; - }; - 'sources-facebook': { - enabled: boolean; - }; - 'sources-google': { - enabled: boolean; - }; - 'sources-twilio': { - enabled: boolean; - }; - }; - features: {}; + components: {[key: string]: {enabled: boolean}}; }; const defaultState = { - components: { - 'sources-chatplugin': { - enabled: false, - }, - 'sources-facebook': { - enabled: false, - }, - 'sources-google': { - enabled: false, - }, - 'sources-twilio': { - enabled: false, - }, - }, - features: {}, + components: {}, }; export default function configReducer(state = defaultState, action: Action): Config { diff --git a/frontend/ui/src/reducers/data/conversations/index.ts b/frontend/ui/src/reducers/data/conversations/index.ts index 85ac7412c9..2af6c3f3e5 100644 --- a/frontend/ui/src/reducers/data/conversations/index.ts +++ b/frontend/ui/src/reducers/data/conversations/index.ts @@ -193,6 +193,22 @@ function allReducer( action: Action | MessageAction ): AllConversationsState { switch (action.type) { + case getType(actions.setStateConversationAction): + return { + ...state, + items: { + ...state.items, + [action.payload.conversationId]: { + id: action.payload.conversationId, + ...state.items[action.payload.conversationId], + metadata: { + ...state.items[action.payload.conversationId].metadata, + state: action.payload.state, + }, + }, + }, + }; + case getType(metadataActions.setMetadataAction): if (action.payload.subject !== 'conversation') { return state; @@ -208,6 +224,7 @@ function allReducer( metadata: { // Ensure that there is always a display name present ...(action.payload as MetadataEvent).metadata, + state: action.payload.metadata.state || state.items[action.payload.identifier].metadata.state, contact: { ...state.items[action.payload.identifier]?.metadata.contact, ...(action.payload as MetadataEvent).metadata.contact, diff --git a/infrastructure/controller/BUILD b/infrastructure/controller/BUILD index 0ea9c5dddf..feeb72fdaf 100644 --- a/infrastructure/controller/BUILD +++ b/infrastructure/controller/BUILD @@ -12,6 +12,7 @@ go_library( visibility = ["//visibility:private"], deps = [ "//infrastructure/controller/pkg/configmap-controller", + "//infrastructure/controller/pkg/endpoints", "@io_k8s_api//core/v1:go_default_library", "@io_k8s_client_go//kubernetes:go_default_library", "@io_k8s_client_go//tools/clientcmd:go_default_library", diff --git a/infrastructure/controller/main.go b/infrastructure/controller/main.go index 1c49b437cc..4592b729f0 100644 --- a/infrastructure/controller/main.go +++ b/infrastructure/controller/main.go @@ -11,6 +11,7 @@ package main import ( "flag" cm "github.com/airyhq/airy/infrastructure/controller/pkg/configmap-controller" + endpoints "github.com/airyhq/airy/infrastructure/controller/pkg/endpoints" v1 "k8s.io/api/core/v1" "os" @@ -58,6 +59,8 @@ func main() { defer close(stop) go configMapController.Run(1, stop) + go endpoints.Serve(clientSet) + // Wait forever select {} } diff --git a/infrastructure/controller/pkg/endpoints/BUILD b/infrastructure/controller/pkg/endpoints/BUILD new file mode 100644 index 0000000000..5a5a032318 --- /dev/null +++ b/infrastructure/controller/pkg/endpoints/BUILD @@ -0,0 +1,15 @@ +load("@com_github_airyhq_bazel_tools//lint:buildifier.bzl", "check_pkg") +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "endpoints", + srcs = ["endpoints.go"], + importpath = "github.com/airyhq/airy/infrastructure/controller/pkg/endpoints", + visibility = ["//visibility:public"], + deps = [ + "@io_k8s_apimachinery//pkg/apis/meta/v1:go_default_library", + "@io_k8s_client_go//kubernetes:go_default_library", + ], +) + +check_pkg(name = "buildifier") diff --git a/infrastructure/controller/pkg/endpoints/endpoints.go b/infrastructure/controller/pkg/endpoints/endpoints.go new file mode 100644 index 0000000000..107cf6ba8c --- /dev/null +++ b/infrastructure/controller/pkg/endpoints/endpoints.go @@ -0,0 +1,46 @@ +package endpoints + +import ( + "context" + "encoding/json" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "log" + "net/http" +) + +type Server struct { + clientSet *kubernetes.Clientset +} + +type ComponentsResponse struct { + Components []string `json:"components"` +} + +func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { + deployments, _ := s.clientSet.AppsV1().Deployments("default").List(context.TODO(), v1.ListOptions{ + LabelSelector: "core.airy.co/component", + }) + + componentsMap := make(map[string]int) + for _, deployment := range deployments.Items { + component := deployment.ObjectMeta.Labels["core.airy.co/component"] + componentsMap[component] = 1 + } + + components := make([]string, 0, len(componentsMap)) + for component := range componentsMap { + components = append(components, component) + } + + resp, _ := json.Marshal(&ComponentsResponse{Components: components}) + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + w.Write(resp) +} + +func Serve(clientSet *kubernetes.Clientset) { + s := &Server{clientSet: clientSet} + http.Handle("/components", s) + log.Fatal(http.ListenAndServe(":8080", nil)) +} diff --git a/infrastructure/helm-chart/charts/apps/Chart.yaml b/infrastructure/helm-chart/charts/apps/Chart.yaml index 4e50d70558..b170120a89 100644 --- a/infrastructure/helm-chart/charts/apps/Chart.yaml +++ b/infrastructure/helm-chart/charts/apps/Chart.yaml @@ -1,5 +1,5 @@ apiVersion: v1 appVersion: "1.0" description: A Helm chart for the Airy Core application -name: apps +name: components version: 1.0 diff --git a/infrastructure/helm-chart/charts/apps/charts/api/charts/api-admin/templates/deployment.yaml b/infrastructure/helm-chart/charts/apps/charts/api/charts/api-admin/templates/deployment.yaml index f25e864cea..919d2d7a2b 100644 --- a/infrastructure/helm-chart/charts/apps/charts/api/charts/api-admin/templates/deployment.yaml +++ b/infrastructure/helm-chart/charts/apps/charts/api/charts/api-admin/templates/deployment.yaml @@ -7,6 +7,8 @@ metadata: app: api-admin type: api core.airy.co/managed: "true" + core.airy.co/mandatory: "{{ .Values.mandatory }}" + core.airy.co/component: "{{ .Values.component }}" spec: replicas: 1 selector: @@ -28,7 +30,7 @@ spec: imagePullPolicy: Always envFrom: - configMapRef: - name: api-config + name: security env: - name: KUBERNETES_NAMESPACE valueFrom: diff --git a/infrastructure/helm-chart/charts/apps/charts/api/charts/api-admin/values.yaml b/infrastructure/helm-chart/charts/apps/charts/api/charts/api-admin/values.yaml index ad3878079d..93d50c15b2 100644 --- a/infrastructure/helm-chart/charts/apps/charts/api/charts/api-admin/values.yaml +++ b/infrastructure/helm-chart/charts/apps/charts/api/charts/api-admin/values.yaml @@ -1 +1,3 @@ -image: api/admin \ No newline at end of file +component: api-admin +mandatory: true +image: api/admin diff --git a/infrastructure/helm-chart/charts/apps/charts/api/charts/api-auth/templates/deployment.yaml b/infrastructure/helm-chart/charts/apps/charts/api/charts/api-auth/templates/deployment.yaml index 4562a744f3..946fb566aa 100644 --- a/infrastructure/helm-chart/charts/apps/charts/api/charts/api-auth/templates/deployment.yaml +++ b/infrastructure/helm-chart/charts/apps/charts/api/charts/api-auth/templates/deployment.yaml @@ -7,6 +7,8 @@ metadata: app: api-auth type: api core.airy.co/managed: "true" + core.airy.co/mandatory: "{{ .Values.mandatory }}" + core.airy.co/component: "{{ .Values.component }}" spec: replicas: 1 selector: @@ -29,6 +31,8 @@ spec: envFrom: - configMapRef: name: api-config + - configMapRef: + name: security env: - name: DB_USERNAME valueFrom: diff --git a/infrastructure/helm-chart/charts/apps/charts/api/charts/api-auth/values.yaml b/infrastructure/helm-chart/charts/apps/charts/api/charts/api-auth/values.yaml index fefc71667c..1bcdbec8f8 100644 --- a/infrastructure/helm-chart/charts/apps/charts/api/charts/api-auth/values.yaml +++ b/infrastructure/helm-chart/charts/apps/charts/api/charts/api-auth/values.yaml @@ -1 +1,3 @@ -image: api/auth \ No newline at end of file +component: api-auth +mandatory: true +image: api/auth diff --git a/infrastructure/helm-chart/charts/apps/charts/api/charts/api-communication/templates/deployment.yaml b/infrastructure/helm-chart/charts/apps/charts/api/charts/api-communication/templates/deployment.yaml index 1c7df28b3d..808b812cf4 100644 --- a/infrastructure/helm-chart/charts/apps/charts/api/charts/api-communication/templates/deployment.yaml +++ b/infrastructure/helm-chart/charts/apps/charts/api/charts/api-communication/templates/deployment.yaml @@ -7,6 +7,8 @@ metadata: app: api-communication type: api core.airy.co/managed: "true" + core.airy.co/mandatory: "{{ .Values.mandatory }}" + core.airy.co/component: "{{ .Values.component }}" spec: replicas: 1 selector: @@ -28,7 +30,7 @@ spec: imagePullPolicy: Always envFrom: - configMapRef: - name: api-config + name: security env: - name: KAFKA_BROKERS valueFrom: diff --git a/infrastructure/helm-chart/charts/apps/charts/api/charts/api-communication/values.yaml b/infrastructure/helm-chart/charts/apps/charts/api/charts/api-communication/values.yaml index a727f3ccff..f0a957737f 100644 --- a/infrastructure/helm-chart/charts/apps/charts/api/charts/api-communication/values.yaml +++ b/infrastructure/helm-chart/charts/apps/charts/api/charts/api-communication/values.yaml @@ -1 +1,3 @@ -image: api/communication \ No newline at end of file +component: api-communication +mandatory: true +image: api/communication diff --git a/infrastructure/helm-chart/charts/apps/charts/api/charts/api-websocket/templates/deployment.yaml b/infrastructure/helm-chart/charts/apps/charts/api/charts/api-websocket/templates/deployment.yaml index 73329e252a..7b315268ee 100644 --- a/infrastructure/helm-chart/charts/apps/charts/api/charts/api-websocket/templates/deployment.yaml +++ b/infrastructure/helm-chart/charts/apps/charts/api/charts/api-websocket/templates/deployment.yaml @@ -7,6 +7,8 @@ metadata: app: api-websocket type: api core.airy.co/managed: "true" + core.airy.co/mandatory: "{{ .Values.mandatory }}" + core.airy.co/component: "{{ .Values.component }}" spec: replicas: 1 selector: @@ -28,7 +30,7 @@ spec: imagePullPolicy: Always envFrom: - configMapRef: - name: api-config + name: security env: - name: KAFKA_BROKERS valueFrom: diff --git a/infrastructure/helm-chart/charts/apps/charts/api/charts/api-websocket/values.yaml b/infrastructure/helm-chart/charts/apps/charts/api/charts/api-websocket/values.yaml index 542d24bf95..de05c30573 100644 --- a/infrastructure/helm-chart/charts/apps/charts/api/charts/api-websocket/values.yaml +++ b/infrastructure/helm-chart/charts/apps/charts/api/charts/api-websocket/values.yaml @@ -1 +1,3 @@ +component: api-websocket +mandatory: true image: api/websocket diff --git a/infrastructure/helm-chart/charts/apps/charts/api/templates/api.yaml b/infrastructure/helm-chart/charts/apps/charts/api/templates/api.yaml index 4eed517c56..3ab861e422 100644 --- a/infrastructure/helm-chart/charts/apps/charts/api/templates/api.yaml +++ b/infrastructure/helm-chart/charts/apps/charts/api/templates/api.yaml @@ -9,6 +9,3 @@ data: MAIL_URL: {{ .Values.mailUrl }} MAIL_USERNAME: {{ .Values.mailUsername }} MAIL_PASSWORD: {{ .Values.mailPassword }} - JWT_SECRET: {{ .Values.jwtSecret | default (randAlphaNum 128) | quote }} - SYSTEM_TOKEN: {{ .Values.systemToken | default (randAlphaNum 24) | quote }} - ALLOWED_ORIGINS: {{ .Values.allowedOrigins | quote }} diff --git a/infrastructure/helm-chart/charts/apps/charts/api/templates/security.yaml b/infrastructure/helm-chart/charts/apps/charts/api/templates/security.yaml new file mode 100644 index 0000000000..f81ba5080f --- /dev/null +++ b/infrastructure/helm-chart/charts/apps/charts/api/templates/security.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: security + namespace: {{ .Values.global.namespace }} +data: + jwtSecret: {{ .Values.jwtSecret | default (randAlphaNum 128) | quote }} + systemToken: {{ .Values.systemToken | default (randAlphaNum 24) | quote }} + allowedOrigins: {{ .Values.allowedOrigins | quote }} diff --git a/infrastructure/helm-chart/charts/apps/charts/frontend/charts/frontend-chat-plugin/templates/deployment.yaml b/infrastructure/helm-chart/charts/apps/charts/frontend/charts/frontend-chat-plugin/templates/deployment.yaml index 08c8f2eb34..2924de34c7 100644 --- a/infrastructure/helm-chart/charts/apps/charts/frontend/charts/frontend-chat-plugin/templates/deployment.yaml +++ b/infrastructure/helm-chart/charts/apps/charts/frontend/charts/frontend-chat-plugin/templates/deployment.yaml @@ -7,6 +7,8 @@ metadata: app: frontend-chat-plugin type: frontend core.airy.co/managed: "true" + core.airy.co/mandatory: "{{ .Values.mandatory }}" + core.airy.co/component: "{{ .Values.component }}" spec: replicas: 1 selector: diff --git a/infrastructure/helm-chart/charts/apps/charts/frontend/charts/frontend-chat-plugin/values.yaml b/infrastructure/helm-chart/charts/apps/charts/frontend/charts/frontend-chat-plugin/values.yaml index 90c3960ca1..11fcdd5569 100644 --- a/infrastructure/helm-chart/charts/apps/charts/frontend/charts/frontend-chat-plugin/values.yaml +++ b/infrastructure/helm-chart/charts/apps/charts/frontend/charts/frontend-chat-plugin/values.yaml @@ -1 +1,3 @@ +component: frontend-chat-plugin +mandatory: true image: frontend/chat-plugin diff --git a/infrastructure/helm-chart/charts/apps/charts/frontend/charts/frontend-ui/templates/deployment.yaml b/infrastructure/helm-chart/charts/apps/charts/frontend/charts/frontend-ui/templates/deployment.yaml index 8957e7fb11..cefb0b9076 100644 --- a/infrastructure/helm-chart/charts/apps/charts/frontend/charts/frontend-ui/templates/deployment.yaml +++ b/infrastructure/helm-chart/charts/apps/charts/frontend/charts/frontend-ui/templates/deployment.yaml @@ -7,6 +7,10 @@ metadata: app: frontend-ui type: frontend core.airy.co/managed: "true" + core.airy.co/mandatory: "{{ .Values.mandatory }}" + core.airy.co/component: "{{ .Values.component }}" + annotations: + core.airy.co/config-items-mandatory: "API_HOST" spec: replicas: 1 selector: diff --git a/infrastructure/helm-chart/charts/apps/charts/frontend/charts/frontend-ui/values.yaml b/infrastructure/helm-chart/charts/apps/charts/frontend/charts/frontend-ui/values.yaml index cf13af5804..ac7f39953e 100644 --- a/infrastructure/helm-chart/charts/apps/charts/frontend/charts/frontend-ui/values.yaml +++ b/infrastructure/helm-chart/charts/apps/charts/frontend/charts/frontend-ui/values.yaml @@ -1 +1,3 @@ -image: frontend/ui \ No newline at end of file +component: frontend-ui +mandatory: true +image: frontend/ui diff --git a/infrastructure/helm-chart/charts/apps/charts/integration/Chart.yaml b/infrastructure/helm-chart/charts/apps/charts/integration/Chart.yaml new file mode 100644 index 0000000000..d2c5a72a0d --- /dev/null +++ b/infrastructure/helm-chart/charts/apps/charts/integration/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart for the Airy Core integration components +name: integrations +version: 1.0 diff --git a/infrastructure/helm-chart/charts/apps/charts/integration/charts/webhook/Chart.yaml b/infrastructure/helm-chart/charts/apps/charts/integration/charts/webhook/Chart.yaml new file mode 100644 index 0000000000..f4747e0caa --- /dev/null +++ b/infrastructure/helm-chart/charts/apps/charts/integration/charts/webhook/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart for the Webhook integration component +name: integration-webhook +version: 1.0 diff --git a/infrastructure/helm-chart/charts/apps/charts/integration/charts/webhook/templates/deployments.yaml b/infrastructure/helm-chart/charts/apps/charts/integration/charts/webhook/templates/deployments.yaml new file mode 100644 index 0000000000..5d9bec820d --- /dev/null +++ b/infrastructure/helm-chart/charts/apps/charts/integration/charts/webhook/templates/deployments.yaml @@ -0,0 +1,172 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: webhook-consumer + namespace: {{ .Values.global.namespace }} + labels: + app: webhook-consumer + type: webhook + core.airy.co/managed: "true" + core.airy.co/mandatory: "{{ .Values.mandatory }}" + core.airy.co/component: "{{ .Values.component }}" +spec: + replicas: 0 + selector: + matchLabels: + app: webhook-consumer + strategy: + rollingUpdate: + maxSurge: 1 + maxUnavailable: 1 + type: RollingUpdate + template: + metadata: + labels: + app: webhook-consumer + spec: + containers: + - name: app + image: "{{ .Values.global.containerRegistry}}/{{ .Values.imageConsumer }}:{{ .Values.global.appImageTag }}" + imagePullPolicy: Always + env: + - name: SERVICE_NAME + value: webhook-consumer + - name: REDIS_HOSTNAME + valueFrom: + configMapKeyRef: + name: redis-config + key: REDIS_HOSTNAME + - name: REDIS_PORT + valueFrom: + configMapKeyRef: + name: redis-config + key: REDIS_PORT + - name: WEBHOOK_NAME + valueFrom: + configMapKeyRef: + name: "{{ .Values.component }}" + key: name + livenessProbe: + httpGet: + path: /health + port: 8080 + httpHeaders: + - name: Health-Check + value: health-check + initialDelaySeconds: 60 + periodSeconds: 10 + failureThreshold: 3 + initContainers: + - name: wait + image: busybox + command: ["/bin/sh", "/opt/provisioning/wait-for-service.sh"] + env: + - name: SERVICE_NAME + valueFrom: + configMapKeyRef: + name: redis-config + key: REDIS_HOSTNAME + - name: SERVICE_PORT + valueFrom: + configMapKeyRef: + name: redis-config + key: REDIS_PORT + volumeMounts: + - name: provisioning-scripts + mountPath: /opt/provisioning + volumes: + - name: provisioning-scripts + configMap: + name: provisioning-scripts +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: webhook-publisher + namespace: {{ .Values.global.namespace }} + labels: + app: webhook-publisher + type: webhook + core.airy.co/managed: "true" + core.airy.co/mandatory: "{{ .Values.mandatory }}" + core.airy.co/component: "{{ .Values.component }}" +spec: + replicas: 0 + selector: + matchLabels: + app: webhook-publisher + strategy: + rollingUpdate: + maxSurge: 1 + maxUnavailable: 1 + type: RollingUpdate + template: + metadata: + labels: + app: webhook-publisher + spec: + containers: + - name: app + image: "{{ .Values.global.containerRegistry}}/{{ .Values.imagePublisher }}:{{ .Values.global.appImageTag }}" + imagePullPolicy: Always + env: + - name: KAFKA_BROKERS + valueFrom: + configMapKeyRef: + name: kafka-config + key: KAFKA_BROKERS + - name: KAFKA_SCHEMA_REGISTRY_URL + valueFrom: + configMapKeyRef: + name: kafka-config + key: KAFKA_SCHEMA_REGISTRY_URL + - name: KAFKA_COMMIT_INTERVAL_MS + valueFrom: + configMapKeyRef: + name: kafka-config + key: KAFKA_COMMIT_INTERVAL_MS + - name: SERVICE_NAME + value: webhook-publisher + - name: REDIS_HOSTNAME + valueFrom: + configMapKeyRef: + name: redis-config + key: REDIS_HOSTNAME + - name: REDIS_PORT + valueFrom: + configMapKeyRef: + name: redis-config + key: REDIS_PORT + - name: WEBHOOK_NAME + valueFrom: + configMapKeyRef: + name: "{{ .Values.component }}" + key: name + livenessProbe: + tcpSocket: + port: 6000 + initialDelaySeconds: 60 + periodSeconds: 10 + failureThreshold: 3 + initContainers: + - name: wait + image: busybox + command: ["/bin/sh", "/opt/provisioning/wait-for-minimum-kafkas.sh"] + env: + - name: KAFKA_BROKERS + valueFrom: + configMapKeyRef: + name: kafka-config + key: KAFKA_BROKERS + - name: REPLICAS + valueFrom: + configMapKeyRef: + name: kafka-config + key: KAFKA_MINIMUM_REPLICAS + volumeMounts: + - name: provisioning-scripts + mountPath: /opt/provisioning + volumes: + - name: provisioning-scripts + configMap: + name: provisioning-scripts diff --git a/infrastructure/helm-chart/charts/apps/charts/webhook/charts/webhook-publisher/templates/service.yaml b/infrastructure/helm-chart/charts/apps/charts/integration/charts/webhook/templates/services.yaml similarity index 55% rename from infrastructure/helm-chart/charts/apps/charts/webhook/charts/webhook-publisher/templates/service.yaml rename to infrastructure/helm-chart/charts/apps/charts/integration/charts/webhook/templates/services.yaml index 3b9694c98e..3433fcd446 100644 --- a/infrastructure/helm-chart/charts/apps/charts/webhook/charts/webhook-publisher/templates/service.yaml +++ b/infrastructure/helm-chart/charts/apps/charts/integration/charts/webhook/templates/services.yaml @@ -1,5 +1,19 @@ apiVersion: v1 kind: Service +metadata: + name: webhook-consumer + namespace: {{ .Values.global.namespace }} +spec: + ports: + - port: 80 + targetPort: 8080 + protocol: TCP + type: NodePort + selector: + app: webhook-consumer +--- +apiVersion: v1 +kind: Service metadata: name: webhook-publisher namespace: {{ .Values.global.namespace }} diff --git a/infrastructure/helm-chart/charts/apps/charts/integration/charts/webhook/values.yaml b/infrastructure/helm-chart/charts/apps/charts/integration/charts/webhook/values.yaml new file mode 100644 index 0000000000..89ba74830e --- /dev/null +++ b/infrastructure/helm-chart/charts/apps/charts/integration/charts/webhook/values.yaml @@ -0,0 +1,5 @@ +component: integration-webhook +mandatory: false +enabled: false +imageConsumer: webhook/consumer +imagePublisher: webhook/publisher \ No newline at end of file diff --git a/infrastructure/helm-chart/charts/apps/charts/media-resolver/templates/user-storage.yaml b/infrastructure/helm-chart/charts/apps/charts/media-resolver/templates/user-storage.yaml deleted file mode 100644 index 10c2174377..0000000000 --- a/infrastructure/helm-chart/charts/apps/charts/media-resolver/templates/user-storage.yaml +++ /dev/null @@ -1,12 +0,0 @@ -{{ if .Values.storage }} -apiVersion: v1 -kind: ConfigMap -metadata: - name: user-storage -data: - STORAGE_S3_KEY: {{ .Values.storage.s3.key }} - STORAGE_S3_SECRET: "{{ .Values.storage.s3.secret }}" - STORAGE_S3_BUCKET: {{ .Values.storage.s3.bucket }} - STORAGE_S3_REGION: {{ .Values.storage.s3.region }} - STORAGE_S3_PATH: {{ .Values.storage.s3.path }} -{{ end }} diff --git a/infrastructure/helm-chart/charts/apps/charts/media-resolver/values.yaml b/infrastructure/helm-chart/charts/apps/charts/media-resolver/values.yaml deleted file mode 100644 index 535200d921..0000000000 --- a/infrastructure/helm-chart/charts/apps/charts/media-resolver/values.yaml +++ /dev/null @@ -1,8 +0,0 @@ -image: media/resolver -storage: - s3: - key: "changeme" - secret: "changeme" - bucket: "changeme" - region: "changeme" - path: "path" diff --git a/infrastructure/helm-chart/charts/apps/charts/media/Chart.yaml b/infrastructure/helm-chart/charts/apps/charts/media/Chart.yaml new file mode 100644 index 0000000000..a4a914e3b8 --- /dev/null +++ b/infrastructure/helm-chart/charts/apps/charts/media/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart for the Airy Core media components +name: media +version: 1.0 diff --git a/infrastructure/helm-chart/charts/apps/charts/media-resolver/Chart.yaml b/infrastructure/helm-chart/charts/apps/charts/media/charts/resolver/Chart.yaml similarity index 100% rename from infrastructure/helm-chart/charts/apps/charts/media-resolver/Chart.yaml rename to infrastructure/helm-chart/charts/apps/charts/media/charts/resolver/Chart.yaml diff --git a/infrastructure/helm-chart/charts/apps/charts/media-resolver/templates/deployment.yaml b/infrastructure/helm-chart/charts/apps/charts/media/charts/resolver/templates/deployment.yaml similarity index 67% rename from infrastructure/helm-chart/charts/apps/charts/media-resolver/templates/deployment.yaml rename to infrastructure/helm-chart/charts/apps/charts/media/charts/resolver/templates/deployment.yaml index 54de7a956a..e705fc780c 100644 --- a/infrastructure/helm-chart/charts/apps/charts/media-resolver/templates/deployment.yaml +++ b/infrastructure/helm-chart/charts/apps/charts/media/charts/resolver/templates/deployment.yaml @@ -7,6 +7,8 @@ metadata: app: media-resolver type: media core.airy.co/managed: "true" + core.airy.co/mandatory: "{{ .Values.mandatory }}" + core.airy.co/component: "{{ .Values.component }}" spec: replicas: 0 selector: @@ -26,9 +28,6 @@ spec: - name: app image: "{{ .Values.global.containerRegistry}}/{{ .Values.image }}:{{ .Values.global.appImageTag }}" imagePullPolicy: Always - envFrom: - - configMapRef: - name: user-storage env: - name: KAFKA_BROKERS valueFrom: @@ -46,7 +45,32 @@ spec: name: kafka-config key: KAFKA_COMMIT_INTERVAL_MS - name: SERVICE_NAME - value: media-resolver + value: "{{ .Values.component }}" + - name: STORAGE_S3_KEY + valueFrom: + configMapKeyRef: + name: "{{ .Values.component }}" + key: s3Key + - name: STORAGE_S3_SECRET + valueFrom: + configMapKeyRef: + name: "{{ .Values.component }}" + key: s3Secret + - name: STORAGE_S3_BUCKET + valueFrom: + configMapKeyRef: + name: "{{ .Values.component }}" + key: s3Bucket + - name: STORAGE_S3_REGION + valueFrom: + configMapKeyRef: + name: "{{ .Values.component }}" + key: s3Region + - name: STORAGE_S3_PATH + valueFrom: + configMapKeyRef: + name: "{{ .Values.component }}" + key: s3Path livenessProbe: httpGet: path: /actuator/health diff --git a/infrastructure/helm-chart/charts/apps/charts/media-resolver/templates/service.yaml b/infrastructure/helm-chart/charts/apps/charts/media/charts/resolver/templates/service.yaml similarity index 100% rename from infrastructure/helm-chart/charts/apps/charts/media-resolver/templates/service.yaml rename to infrastructure/helm-chart/charts/apps/charts/media/charts/resolver/templates/service.yaml diff --git a/infrastructure/helm-chart/charts/apps/charts/media/charts/resolver/values.yaml b/infrastructure/helm-chart/charts/apps/charts/media/charts/resolver/values.yaml new file mode 100644 index 0000000000..7e0169f011 --- /dev/null +++ b/infrastructure/helm-chart/charts/apps/charts/media/charts/resolver/values.yaml @@ -0,0 +1,3 @@ +component: media-resolver +mandatory: false +image: media/resolver diff --git a/infrastructure/helm-chart/charts/apps/charts/sources/charts/chatplugin/templates/deployment.yaml b/infrastructure/helm-chart/charts/apps/charts/sources/charts/chatplugin/templates/deployment.yaml index e81fd2973f..9b7e5378b2 100644 --- a/infrastructure/helm-chart/charts/apps/charts/sources/charts/chatplugin/templates/deployment.yaml +++ b/infrastructure/helm-chart/charts/apps/charts/sources/charts/chatplugin/templates/deployment.yaml @@ -7,6 +7,8 @@ metadata: app: sources-chatplugin type: sources core.airy.co/managed: "true" + core.airy.co/mandatory: "{{ .Values.mandatory }}" + core.airy.co/component: "{{ .Values.component }}" spec: replicas: 1 selector: @@ -26,12 +28,15 @@ spec: - name: app image: "{{ .Values.global.containerRegistry}}/{{ .Values.image }}:{{ .Values.global.appImageTag }}" imagePullPolicy: Always + envFrom: + - configMapRef: + name: security env: - - name: ALLOWED_ORIGINS + - name: allowedOrigins valueFrom: configMapKeyRef: - name: api-config - key: ALLOWED_ORIGINS + name: security + key: allowedOrigins - name: JWT_SECRET valueFrom: configMapKeyRef: diff --git a/infrastructure/helm-chart/charts/apps/charts/sources/charts/chatplugin/values.yaml b/infrastructure/helm-chart/charts/apps/charts/sources/charts/chatplugin/values.yaml index 51b0f7e5e9..7158ddd96e 100644 --- a/infrastructure/helm-chart/charts/apps/charts/sources/charts/chatplugin/values.yaml +++ b/infrastructure/helm-chart/charts/apps/charts/sources/charts/chatplugin/values.yaml @@ -1 +1,3 @@ +component: sources-chat-plugin +mandatory: true image: sources/chat-plugin \ No newline at end of file diff --git a/infrastructure/helm-chart/charts/apps/charts/sources/charts/facebook/charts/facebook-connector/Chart.yaml b/infrastructure/helm-chart/charts/apps/charts/sources/charts/facebook/charts/facebook-connector/Chart.yaml deleted file mode 100644 index d5877e85de..0000000000 --- a/infrastructure/helm-chart/charts/apps/charts/sources/charts/facebook/charts/facebook-connector/Chart.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: v1 -appVersion: "1.0" -description: A Helm chart for the Facebook Connector app -name: sources-facebook-connector -version: 1.0 diff --git a/infrastructure/helm-chart/charts/apps/charts/sources/charts/facebook/charts/facebook-connector/templates/deployment.yaml b/infrastructure/helm-chart/charts/apps/charts/sources/charts/facebook/charts/facebook-connector/templates/deployment.yaml deleted file mode 100644 index 9cb63b85f2..0000000000 --- a/infrastructure/helm-chart/charts/apps/charts/sources/charts/facebook/charts/facebook-connector/templates/deployment.yaml +++ /dev/null @@ -1,94 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: sources-facebook-connector - namespace: {{ .Values.global.namespace }} - labels: - app: sources-facebook-connector - type: sources - core.airy.co/managed: "true" -spec: - replicas: 0 - selector: - matchLabels: - app: sources-facebook-connector - strategy: - rollingUpdate: - maxSurge: 1 - maxUnavailable: 1 - type: RollingUpdate - template: - metadata: - labels: - app: sources-facebook-connector - spec: - containers: - - name: app - image: "{{ .Values.global.containerRegistry}}/{{ .Values.image }}:{{ .Values.global.appImageTag }}" - imagePullPolicy: Always - envFrom: - - configMapRef: - name: api-config - env: - - name: KAFKA_BROKERS - valueFrom: - configMapKeyRef: - name: kafka-config - key: KAFKA_BROKERS - - name: KAFKA_SCHEMA_REGISTRY_URL - valueFrom: - configMapKeyRef: - name: kafka-config - key: KAFKA_SCHEMA_REGISTRY_URL - - name: KAFKA_COMMIT_INTERVAL_MS - valueFrom: - configMapKeyRef: - name: kafka-config - key: KAFKA_COMMIT_INTERVAL_MS - - name: SERVICE_NAME - value: facebook-connector - - name: FACEBOOK_WEBHOOK_SECRET - valueFrom: - configMapKeyRef: - name: sources-facebook - key: FACEBOOK_WEBHOOK_SECRET - - name: FACEBOOK_APP_ID - valueFrom: - configMapKeyRef: - name: sources-facebook - key: FACEBOOK_APP_ID - - name: FACEBOOK_APP_SECRET - valueFrom: - configMapKeyRef: - name: sources-facebook - key: FACEBOOK_APP_SECRET - livenessProbe: - httpGet: - path: /actuator/health - port: 8080 - httpHeaders: - - name: Health-Check - value: health-check - initialDelaySeconds: 60 - initContainers: - - name: wait - image: busybox - command: ["/bin/sh", "/opt/provisioning/wait-for-minimum-kafkas.sh"] - env: - - name: KAFKA_BROKERS - valueFrom: - configMapKeyRef: - name: kafka-config - key: KAFKA_BROKERS - - name: REPLICAS - valueFrom: - configMapKeyRef: - name: kafka-config - key: KAFKA_MINIMUM_REPLICAS - volumeMounts: - - name: provisioning-scripts - mountPath: /opt/provisioning - volumes: - - name: provisioning-scripts - configMap: - name: provisioning-scripts diff --git a/infrastructure/helm-chart/charts/apps/charts/sources/charts/facebook/charts/facebook-connector/values.yaml b/infrastructure/helm-chart/charts/apps/charts/sources/charts/facebook/charts/facebook-connector/values.yaml deleted file mode 100644 index e3a61e3837..0000000000 --- a/infrastructure/helm-chart/charts/apps/charts/sources/charts/facebook/charts/facebook-connector/values.yaml +++ /dev/null @@ -1 +0,0 @@ -image: sources/facebook-connector diff --git a/infrastructure/helm-chart/charts/apps/charts/sources/charts/facebook/charts/facebook-events-router/Chart.yaml b/infrastructure/helm-chart/charts/apps/charts/sources/charts/facebook/charts/facebook-events-router/Chart.yaml deleted file mode 100644 index dee5eb654a..0000000000 --- a/infrastructure/helm-chart/charts/apps/charts/sources/charts/facebook/charts/facebook-events-router/Chart.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: v1 -appVersion: "1.0" -description: A Helm chart for the Sources Facebook Events Router app -name: sources-facebook-events-router -version: 1.0 \ No newline at end of file diff --git a/infrastructure/helm-chart/charts/apps/charts/sources/charts/facebook/charts/facebook-events-router/templates/deployment.yaml b/infrastructure/helm-chart/charts/apps/charts/sources/charts/facebook/charts/facebook-events-router/templates/deployment.yaml deleted file mode 100644 index 5433177506..0000000000 --- a/infrastructure/helm-chart/charts/apps/charts/sources/charts/facebook/charts/facebook-events-router/templates/deployment.yaml +++ /dev/null @@ -1,79 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: sources-facebook-events-router - namespace: {{ .Values.global.namespace }} - labels: - app: sources-facebook-events-router - type: sources - core.airy.co/managed: "true" -spec: - replicas: 0 - selector: - matchLabels: - app: sources-facebook-events-router - strategy: - rollingUpdate: - maxSurge: 1 - maxUnavailable: 1 - type: RollingUpdate - template: - metadata: - labels: - app: sources-facebook-events-router - spec: - containers: - - name: app - image: "{{ .Values.global.containerRegistry}}/{{ .Values.image }}:{{ .Values.global.appImageTag }}" - imagePullPolicy: Always - env: - - name: KAFKA_BROKERS - valueFrom: - configMapKeyRef: - name: kafka-config - key: KAFKA_BROKERS - - name: KAFKA_SCHEMA_REGISTRY_URL - valueFrom: - configMapKeyRef: - name: kafka-config - key: KAFKA_SCHEMA_REGISTRY_URL - - name: KAFKA_COMMIT_INTERVAL_MS - valueFrom: - configMapKeyRef: - name: kafka-config - key: KAFKA_COMMIT_INTERVAL_MS - - name: FACEBOOK_APP_ID - valueFrom: - configMapKeyRef: - name: sources-facebook - key: FACEBOOK_APP_ID - - name: SERVICE_NAME - value: facebook-events-router - livenessProbe: - tcpSocket: - port: 6000 - initialDelaySeconds: 60 - periodSeconds: 10 - failureThreshold: 3 - initContainers: - - name: wait - image: busybox - command: ["/bin/sh", "/opt/provisioning/wait-for-minimum-kafkas.sh"] - env: - - name: KAFKA_BROKERS - valueFrom: - configMapKeyRef: - name: kafka-config - key: KAFKA_BROKERS - - name: REPLICAS - valueFrom: - configMapKeyRef: - name: kafka-config - key: KAFKA_MINIMUM_REPLICAS - volumeMounts: - - name: provisioning-scripts - mountPath: /opt/provisioning - volumes: - - name: provisioning-scripts - configMap: - name: provisioning-scripts diff --git a/infrastructure/helm-chart/charts/apps/charts/sources/charts/facebook/charts/facebook-events-router/values.yaml b/infrastructure/helm-chart/charts/apps/charts/sources/charts/facebook/charts/facebook-events-router/values.yaml deleted file mode 100644 index bd150cabec..0000000000 --- a/infrastructure/helm-chart/charts/apps/charts/sources/charts/facebook/charts/facebook-events-router/values.yaml +++ /dev/null @@ -1 +0,0 @@ -image: sources/facebook-events-router \ No newline at end of file diff --git a/infrastructure/helm-chart/charts/apps/charts/sources/charts/facebook/templates/deployments.yaml b/infrastructure/helm-chart/charts/apps/charts/sources/charts/facebook/templates/deployments.yaml new file mode 100644 index 0000000000..454e1f2b13 --- /dev/null +++ b/infrastructure/helm-chart/charts/apps/charts/sources/charts/facebook/templates/deployments.yaml @@ -0,0 +1,178 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: sources-facebook-connector + namespace: {{ .Values.global.namespace }} + labels: + app: sources-facebook-connector + type: sources + core.airy.co/managed: "true" + core.airy.co/mandatory: "{{ .Values.mandatory }}" + core.airy.co/component: "{{ .Values.component }}" +spec: + replicas: 0 + selector: + matchLabels: + app: sources-facebook-connector + strategy: + rollingUpdate: + maxSurge: 1 + maxUnavailable: 1 + type: RollingUpdate + template: + metadata: + labels: + app: sources-facebook-connector + spec: + containers: + - name: app + image: "{{ .Values.global.containerRegistry}}/{{ .Values.imageConnector }}:{{ .Values.global.appImageTag }}" + imagePullPolicy: Always + envFrom: + - configMapRef: + name: security + env: + - name: KAFKA_BROKERS + valueFrom: + configMapKeyRef: + name: kafka-config + key: KAFKA_BROKERS + - name: KAFKA_SCHEMA_REGISTRY_URL + valueFrom: + configMapKeyRef: + name: kafka-config + key: KAFKA_SCHEMA_REGISTRY_URL + - name: KAFKA_COMMIT_INTERVAL_MS + valueFrom: + configMapKeyRef: + name: kafka-config + key: KAFKA_COMMIT_INTERVAL_MS + - name: SERVICE_NAME + value: facebook-connector + - name: FACEBOOK_WEBHOOK_SECRET + valueFrom: + configMapKeyRef: + name: "{{ .Values.component }}" + key: WebhookSecret + - name: FACEBOOK_APP_ID + valueFrom: + configMapKeyRef: + name: "{{ .Values.component }}" + key: appId + - name: FACEBOOK_APP_SECRET + valueFrom: + configMapKeyRef: + name: "{{ .Values.component }}" + key: appSecret + livenessProbe: + httpGet: + path: /actuator/health + port: 8080 + httpHeaders: + - name: Health-Check + value: health-check + initialDelaySeconds: 60 + initContainers: + - name: wait + image: busybox + command: ["/bin/sh", "/opt/provisioning/wait-for-minimum-kafkas.sh"] + env: + - name: KAFKA_BROKERS + valueFrom: + configMapKeyRef: + name: kafka-config + key: KAFKA_BROKERS + - name: REPLICAS + valueFrom: + configMapKeyRef: + name: kafka-config + key: KAFKA_MINIMUM_REPLICAS + volumeMounts: + - name: provisioning-scripts + mountPath: /opt/provisioning + volumes: + - name: provisioning-scripts + configMap: + name: provisioning-scripts +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: sources-facebook-events-router + namespace: {{ .Values.global.namespace }} + labels: + app: sources-facebook-events-router + type: sources + core.airy.co/managed: "true" + core.airy.co/mandatory: "{{ .Values.mandatory }}" + core.airy.co/component: "{{ .Values.component }}" +spec: + replicas: 0 + selector: + matchLabels: + app: sources-facebook-events-router + strategy: + rollingUpdate: + maxSurge: 1 + maxUnavailable: 1 + type: RollingUpdate + template: + metadata: + labels: + app: sources-facebook-events-router + spec: + containers: + - name: app + image: "{{ .Values.global.containerRegistry}}/{{ .Values.imageEventsRouter }}:{{ .Values.global.appImageTag }}" + imagePullPolicy: Always + env: + - name: KAFKA_BROKERS + valueFrom: + configMapKeyRef: + name: kafka-config + key: KAFKA_BROKERS + - name: KAFKA_SCHEMA_REGISTRY_URL + valueFrom: + configMapKeyRef: + name: kafka-config + key: KAFKA_SCHEMA_REGISTRY_URL + - name: KAFKA_COMMIT_INTERVAL_MS + valueFrom: + configMapKeyRef: + name: kafka-config + key: KAFKA_COMMIT_INTERVAL_MS + - name: FACEBOOK_APP_ID + valueFrom: + configMapKeyRef: + name: "{{ .Values.component }}" + key: appId + - name: SERVICE_NAME + value: facebook-events-router + livenessProbe: + tcpSocket: + port: 6000 + initialDelaySeconds: 60 + periodSeconds: 10 + failureThreshold: 3 + initContainers: + - name: wait + image: busybox + command: ["/bin/sh", "/opt/provisioning/wait-for-minimum-kafkas.sh"] + env: + - name: KAFKA_BROKERS + valueFrom: + configMapKeyRef: + name: kafka-config + key: KAFKA_BROKERS + - name: REPLICAS + valueFrom: + configMapKeyRef: + name: kafka-config + key: KAFKA_MINIMUM_REPLICAS + volumeMounts: + - name: provisioning-scripts + mountPath: /opt/provisioning + volumes: + - name: provisioning-scripts + configMap: + name: provisioning-scripts diff --git a/infrastructure/helm-chart/charts/apps/charts/sources/charts/facebook/charts/facebook-connector/templates/service.yaml b/infrastructure/helm-chart/charts/apps/charts/sources/charts/facebook/templates/service.yaml similarity index 100% rename from infrastructure/helm-chart/charts/apps/charts/sources/charts/facebook/charts/facebook-connector/templates/service.yaml rename to infrastructure/helm-chart/charts/apps/charts/sources/charts/facebook/templates/service.yaml diff --git a/infrastructure/helm-chart/charts/apps/charts/sources/charts/facebook/values.yaml b/infrastructure/helm-chart/charts/apps/charts/sources/charts/facebook/values.yaml new file mode 100644 index 0000000000..22743c6e6e --- /dev/null +++ b/infrastructure/helm-chart/charts/apps/charts/sources/charts/facebook/values.yaml @@ -0,0 +1,4 @@ +component: sources-facebook +mandatory: false +imageConnector: sources/facebook-connector +imageEventsRouter: sources/facebook-events-router diff --git a/infrastructure/helm-chart/charts/apps/charts/sources/charts/google/charts/google-connector/Chart.yaml b/infrastructure/helm-chart/charts/apps/charts/sources/charts/google/charts/google-connector/Chart.yaml deleted file mode 100644 index bf7c54fab6..0000000000 --- a/infrastructure/helm-chart/charts/apps/charts/sources/charts/google/charts/google-connector/Chart.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: v1 -appVersion: '1.0' -description: A Helm chart for the Google Connector -name: sources-google-connector -version: 1.0 diff --git a/infrastructure/helm-chart/charts/apps/charts/sources/charts/google/charts/google-connector/templates/deployment.yaml b/infrastructure/helm-chart/charts/apps/charts/sources/charts/google/charts/google-connector/templates/deployment.yaml deleted file mode 100644 index f8b159fbf3..0000000000 --- a/infrastructure/helm-chart/charts/apps/charts/sources/charts/google/charts/google-connector/templates/deployment.yaml +++ /dev/null @@ -1,87 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: sources-google-connector - namespace: {{ .Values.global.namespace }} - labels: - app: sources-google-connector - type: sources - core.airy.co/managed: "true" -spec: - replicas: 0 - selector: - matchLabels: - app: sources-google-connector - strategy: - rollingUpdate: - maxSurge: 1 - maxUnavailable: 1 - type: RollingUpdate - template: - metadata: - labels: - app: sources-google-connector - spec: - containers: - - name: app - image: '{{ .Values.global.containerRegistry}}/{{ .Values.image }}:{{ .Values.global.appImageTag }}' - imagePullPolicy: Always - envFrom: - - configMapRef: - name: api-config - env: - - name: KAFKA_BROKERS - valueFrom: - configMapKeyRef: - name: kafka-config - key: KAFKA_BROKERS - - name: KAFKA_SCHEMA_REGISTRY_URL - valueFrom: - configMapKeyRef: - name: kafka-config - key: KAFKA_SCHEMA_REGISTRY_URL - - name: KAFKA_COMMIT_INTERVAL_MS - valueFrom: - configMapKeyRef: - name: kafka-config - key: KAFKA_COMMIT_INTERVAL_MS - - name: GOOGLE_SA_FILE - valueFrom: - configMapKeyRef: - name: sources-google - key: GOOGLE_SA_FILE - - name: GOOGLE_PARTNER_KEY - valueFrom: - configMapKeyRef: - name: sources-google - key: GOOGLE_PARTNER_KEY - livenessProbe: - httpGet: - path: /actuator/health - port: 8080 - httpHeaders: - - name: Health-Check - value: health-check - initialDelaySeconds: 60 - initContainers: - - name: wait - image: busybox - command: ["/bin/sh", "/opt/provisioning/wait-for-minimum-kafkas.sh"] - env: - - name: KAFKA_BROKERS - valueFrom: - configMapKeyRef: - name: kafka-config - key: KAFKA_BROKERS - - name: REPLICAS - valueFrom: - configMapKeyRef: - name: kafka-config - key: KAFKA_MINIMUM_REPLICAS - volumeMounts: - - name: provisioning-scripts - mountPath: /opt/provisioning - volumes: - - name: provisioning-scripts - configMap: - name: provisioning-scripts diff --git a/infrastructure/helm-chart/charts/apps/charts/sources/charts/google/charts/google-connector/values.yaml b/infrastructure/helm-chart/charts/apps/charts/sources/charts/google/charts/google-connector/values.yaml deleted file mode 100644 index 9bdfe99536..0000000000 --- a/infrastructure/helm-chart/charts/apps/charts/sources/charts/google/charts/google-connector/values.yaml +++ /dev/null @@ -1 +0,0 @@ -image: sources/google-connector diff --git a/infrastructure/helm-chart/charts/apps/charts/sources/charts/google/charts/google-events-router/Chart.yaml b/infrastructure/helm-chart/charts/apps/charts/sources/charts/google/charts/google-events-router/Chart.yaml deleted file mode 100644 index db60bfa5a1..0000000000 --- a/infrastructure/helm-chart/charts/apps/charts/sources/charts/google/charts/google-events-router/Chart.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: v1 -appVersion: "1.0" -description: A Helm chart for the Sources Google Events Router app -name: sources-google-events-router -version: 1.0 \ No newline at end of file diff --git a/infrastructure/helm-chart/charts/apps/charts/sources/charts/google/charts/google-events-router/templates/deployment.yaml b/infrastructure/helm-chart/charts/apps/charts/sources/charts/google/charts/google-events-router/templates/deployment.yaml deleted file mode 100644 index 3441667ccc..0000000000 --- a/infrastructure/helm-chart/charts/apps/charts/sources/charts/google/charts/google-events-router/templates/deployment.yaml +++ /dev/null @@ -1,82 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: sources-google-events-router - namespace: {{ .Values.global.namespace }} - labels: - app: sources-google-events-router - type: sources - core.airy.co/managed: "true" -spec: - replicas: 0 - selector: - matchLabels: - app: sources-google-events-router - strategy: - rollingUpdate: - maxSurge: 1 - maxUnavailable: 1 - type: RollingUpdate - template: - metadata: - labels: - app: sources-google-events-router - spec: - containers: - - name: app - image: "{{ .Values.global.containerRegistry}}/{{ .Values.image }}:{{ .Values.global.appImageTag }}" - imagePullPolicy: Always - env: - - name: KAFKA_BROKERS - valueFrom: - configMapKeyRef: - name: kafka-config - key: KAFKA_BROKERS - - name: KAFKA_SCHEMA_REGISTRY_URL - valueFrom: - configMapKeyRef: - name: kafka-config - key: KAFKA_SCHEMA_REGISTRY_URL - - name: KAFKA_COMMIT_INTERVAL_MS - valueFrom: - configMapKeyRef: - name: kafka-config - key: KAFKA_COMMIT_INTERVAL_MS - - name: GOOGLE_SA_FILE - valueFrom: - configMapKeyRef: - name: sources-google - key: GOOGLE_SA_FILE - - name: GOOGLE_PARTNER_KEY - valueFrom: - configMapKeyRef: - name: sources-google - key: GOOGLE_PARTNER_KEY - livenessProbe: - tcpSocket: - port: 6000 - initialDelaySeconds: 60 - periodSeconds: 10 - failureThreshold: 3 - initContainers: - - name: wait - image: busybox - command: ["/bin/sh", "/opt/provisioning/wait-for-minimum-kafkas.sh"] - env: - - name: KAFKA_BROKERS - valueFrom: - configMapKeyRef: - name: kafka-config - key: KAFKA_BROKERS - - name: REPLICAS - valueFrom: - configMapKeyRef: - name: kafka-config - key: KAFKA_MINIMUM_REPLICAS - volumeMounts: - - name: provisioning-scripts - mountPath: /opt/provisioning - volumes: - - name: provisioning-scripts - configMap: - name: provisioning-scripts diff --git a/infrastructure/helm-chart/charts/apps/charts/sources/charts/google/charts/google-events-router/values.yaml b/infrastructure/helm-chart/charts/apps/charts/sources/charts/google/charts/google-events-router/values.yaml deleted file mode 100644 index ffdd0256ff..0000000000 --- a/infrastructure/helm-chart/charts/apps/charts/sources/charts/google/charts/google-events-router/values.yaml +++ /dev/null @@ -1 +0,0 @@ -image: sources/google-events-router \ No newline at end of file diff --git a/infrastructure/helm-chart/charts/apps/charts/sources/charts/google/templates/deployments.yaml b/infrastructure/helm-chart/charts/apps/charts/sources/charts/google/templates/deployments.yaml new file mode 100644 index 0000000000..32a05ca40b --- /dev/null +++ b/infrastructure/helm-chart/charts/apps/charts/sources/charts/google/templates/deployments.yaml @@ -0,0 +1,174 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: sources-google-connector + namespace: {{ .Values.global.namespace }} + labels: + app: sources-google-connector + type: sources + core.airy.co/managed: "true" + core.airy.co/mandatory: "{{ .Values.mandatory }}" + core.airy.co/component: "{{ .Values.component }}" +spec: + replicas: 0 + selector: + matchLabels: + app: sources-google-connector + strategy: + rollingUpdate: + maxSurge: 1 + maxUnavailable: 1 + type: RollingUpdate + template: + metadata: + labels: + app: sources-google-connector + spec: + containers: + - name: app + image: '{{ .Values.global.containerRegistry}}/{{ .Values.imageConnector }}:{{ .Values.global.appImageTag }}' + imagePullPolicy: Always + envFrom: + - configMapRef: + name: security + env: + - name: KAFKA_BROKERS + valueFrom: + configMapKeyRef: + name: kafka-config + key: KAFKA_BROKERS + - name: KAFKA_SCHEMA_REGISTRY_URL + valueFrom: + configMapKeyRef: + name: kafka-config + key: KAFKA_SCHEMA_REGISTRY_URL + - name: KAFKA_COMMIT_INTERVAL_MS + valueFrom: + configMapKeyRef: + name: kafka-config + key: KAFKA_COMMIT_INTERVAL_MS + - name: GOOGLE_SA_FILE + valueFrom: + configMapKeyRef: + name: "{{ .Values.component }}" + key: saFile + - name: GOOGLE_PARTNER_KEY + valueFrom: + configMapKeyRef: + name: "{{ .Values.component }}" + key: partnerKey + livenessProbe: + httpGet: + path: /actuator/health + port: 8080 + httpHeaders: + - name: Health-Check + value: health-check + initialDelaySeconds: 60 + initContainers: + - name: wait + image: busybox + command: ["/bin/sh", "/opt/provisioning/wait-for-minimum-kafkas.sh"] + env: + - name: KAFKA_BROKERS + valueFrom: + configMapKeyRef: + name: kafka-config + key: KAFKA_BROKERS + - name: REPLICAS + valueFrom: + configMapKeyRef: + name: kafka-config + key: KAFKA_MINIMUM_REPLICAS + volumeMounts: + - name: provisioning-scripts + mountPath: /opt/provisioning + volumes: + - name: provisioning-scripts + configMap: + name: provisioning-scripts +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: sources-google-events-router + namespace: {{ .Values.global.namespace }} + labels: + app: sources-google-events-router + type: sources + core.airy.co/managed: "true" + core.airy.co/mandatory: "{{ .Values.mandatory }}" + core.airy.co/component: "{{ .Values.component }}" +spec: + replicas: 0 + selector: + matchLabels: + app: sources-google-events-router + strategy: + rollingUpdate: + maxSurge: 1 + maxUnavailable: 1 + type: RollingUpdate + template: + metadata: + labels: + app: sources-google-events-router + spec: + containers: + - name: app + image: "{{ .Values.global.containerRegistry}}/{{ .Values.imageEventsRouter }}:{{ .Values.global.appImageTag }}" + imagePullPolicy: Always + env: + - name: KAFKA_BROKERS + valueFrom: + configMapKeyRef: + name: kafka-config + key: KAFKA_BROKERS + - name: KAFKA_SCHEMA_REGISTRY_URL + valueFrom: + configMapKeyRef: + name: kafka-config + key: KAFKA_SCHEMA_REGISTRY_URL + - name: KAFKA_COMMIT_INTERVAL_MS + valueFrom: + configMapKeyRef: + name: kafka-config + key: KAFKA_COMMIT_INTERVAL_MS + - name: GOOGLE_SA_FILE + valueFrom: + configMapKeyRef: + name: "{{ .Values.component }}" + key: saFile + - name: GOOGLE_PARTNER_KEY + valueFrom: + configMapKeyRef: + name: "{{ .Values.component }}" + key: partnerKey + livenessProbe: + tcpSocket: + port: 6000 + initialDelaySeconds: 60 + periodSeconds: 10 + failureThreshold: 3 + initContainers: + - name: wait + image: busybox + command: ["/bin/sh", "/opt/provisioning/wait-for-minimum-kafkas.sh"] + env: + - name: KAFKA_BROKERS + valueFrom: + configMapKeyRef: + name: kafka-config + key: KAFKA_BROKERS + - name: REPLICAS + valueFrom: + configMapKeyRef: + name: kafka-config + key: KAFKA_MINIMUM_REPLICAS + volumeMounts: + - name: provisioning-scripts + mountPath: /opt/provisioning + volumes: + - name: provisioning-scripts + configMap: + name: provisioning-scripts diff --git a/infrastructure/helm-chart/charts/apps/charts/sources/charts/google/charts/google-connector/templates/service.yaml b/infrastructure/helm-chart/charts/apps/charts/sources/charts/google/templates/service.yaml similarity index 100% rename from infrastructure/helm-chart/charts/apps/charts/sources/charts/google/charts/google-connector/templates/service.yaml rename to infrastructure/helm-chart/charts/apps/charts/sources/charts/google/templates/service.yaml diff --git a/infrastructure/helm-chart/charts/apps/charts/sources/charts/google/values.yaml b/infrastructure/helm-chart/charts/apps/charts/sources/charts/google/values.yaml new file mode 100644 index 0000000000..de3f251098 --- /dev/null +++ b/infrastructure/helm-chart/charts/apps/charts/sources/charts/google/values.yaml @@ -0,0 +1,4 @@ +component: sources-google +mandatory: false +imageConnector: sources/google-connector +imageEventsRouter: sources/google-events-router diff --git a/infrastructure/helm-chart/charts/apps/charts/sources/charts/twilio/charts/twilio-connector/Chart.yaml b/infrastructure/helm-chart/charts/apps/charts/sources/charts/twilio/charts/twilio-connector/Chart.yaml deleted file mode 100644 index 18a2f56479..0000000000 --- a/infrastructure/helm-chart/charts/apps/charts/sources/charts/twilio/charts/twilio-connector/Chart.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: v1 -appVersion: "1.0" -description: A Helm chart for the Twilio Connector -name: sources-twilio-connector -version: 1.0 diff --git a/infrastructure/helm-chart/charts/apps/charts/sources/charts/twilio/charts/twilio-connector/templates/deployment.yaml b/infrastructure/helm-chart/charts/apps/charts/sources/charts/twilio/charts/twilio-connector/templates/deployment.yaml deleted file mode 100644 index 19942b1d5f..0000000000 --- a/infrastructure/helm-chart/charts/apps/charts/sources/charts/twilio/charts/twilio-connector/templates/deployment.yaml +++ /dev/null @@ -1,87 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: sources-twilio-connector - namespace: {{ .Values.global.namespace }} - labels: - app: sources-twilio-connector - type: sources - core.airy.co/managed: "true" -spec: - replicas: 0 - selector: - matchLabels: - app: sources-twilio-connector - strategy: - rollingUpdate: - maxSurge: 1 - maxUnavailable: 1 - type: RollingUpdate - template: - metadata: - labels: - app: sources-twilio-connector - spec: - containers: - - name: app - image: "{{ .Values.global.containerRegistry}}/{{ .Values.image }}:{{ .Values.global.appImageTag }}" - imagePullPolicy: Always - envFrom: - - configMapRef: - name: api-config - env: - - name: KAFKA_BROKERS - valueFrom: - configMapKeyRef: - name: kafka-config - key: KAFKA_BROKERS - - name: KAFKA_SCHEMA_REGISTRY_URL - valueFrom: - configMapKeyRef: - name: kafka-config - key: KAFKA_SCHEMA_REGISTRY_URL - - name: KAFKA_COMMIT_INTERVAL_MS - valueFrom: - configMapKeyRef: - name: kafka-config - key: KAFKA_COMMIT_INTERVAL_MS - - name: TWILIO_AUTH_TOKEN - valueFrom: - configMapKeyRef: - name: sources-twilio - key: TWILIO_AUTH_TOKEN - - name: TWILIO_ACCOUNT_SID - valueFrom: - configMapKeyRef: - name: sources-twilio - key: TWILIO_ACCOUNT_SID - livenessProbe: - httpGet: - path: /actuator/health - port: 8080 - httpHeaders: - - name: Health-Check - value: health-check - initialDelaySeconds: 60 - initContainers: - - name: wait - image: busybox - command: ["/bin/sh", "/opt/provisioning/wait-for-minimum-kafkas.sh"] - env: - - name: KAFKA_BROKERS - valueFrom: - configMapKeyRef: - name: kafka-config - key: KAFKA_BROKERS - - name: REPLICAS - valueFrom: - configMapKeyRef: - name: kafka-config - key: KAFKA_MINIMUM_REPLICAS - volumeMounts: - - name: provisioning-scripts - mountPath: /opt/provisioning - volumes: - - name: provisioning-scripts - configMap: - name: provisioning-scripts diff --git a/infrastructure/helm-chart/charts/apps/charts/sources/charts/twilio/charts/twilio-connector/values.yaml b/infrastructure/helm-chart/charts/apps/charts/sources/charts/twilio/charts/twilio-connector/values.yaml deleted file mode 100644 index 9dbbc94cb5..0000000000 --- a/infrastructure/helm-chart/charts/apps/charts/sources/charts/twilio/charts/twilio-connector/values.yaml +++ /dev/null @@ -1 +0,0 @@ -image: sources/twilio-connector diff --git a/infrastructure/helm-chart/charts/apps/charts/sources/charts/twilio/charts/twilio-events-router/Chart.yaml b/infrastructure/helm-chart/charts/apps/charts/sources/charts/twilio/charts/twilio-events-router/Chart.yaml deleted file mode 100644 index 5ebfe66da0..0000000000 --- a/infrastructure/helm-chart/charts/apps/charts/sources/charts/twilio/charts/twilio-events-router/Chart.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: v1 -appVersion: "1.0" -description: A Helm chart for the Sources Twilio Events Router app -name: sources-twilio-events-router -version: 1.0 diff --git a/infrastructure/helm-chart/charts/apps/charts/sources/charts/twilio/charts/twilio-events-router/templates/deployment.yaml b/infrastructure/helm-chart/charts/apps/charts/sources/charts/twilio/charts/twilio-events-router/templates/deployment.yaml deleted file mode 100644 index a6eee5e113..0000000000 --- a/infrastructure/helm-chart/charts/apps/charts/sources/charts/twilio/charts/twilio-events-router/templates/deployment.yaml +++ /dev/null @@ -1,82 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: sources-twilio-events-router - namespace: {{ .Values.global.namespace }} - labels: - app: sources-twilio-events-router - type: sources - core.airy.co/managed: "true" -spec: - replicas: 0 - selector: - matchLabels: - app: sources-twilio-events-router - strategy: - rollingUpdate: - maxSurge: 1 - maxUnavailable: 1 - type: RollingUpdate - template: - metadata: - labels: - app: sources-twilio-events-router - spec: - containers: - - name: app - image: "{{ .Values.global.containerRegistry}}/{{ .Values.image }}:{{ .Values.global.appImageTag }}" - imagePullPolicy: Always - env: - - name: KAFKA_BROKERS - valueFrom: - configMapKeyRef: - name: kafka-config - key: KAFKA_BROKERS - - name: KAFKA_SCHEMA_REGISTRY_URL - valueFrom: - configMapKeyRef: - name: kafka-config - key: KAFKA_SCHEMA_REGISTRY_URL - - name: KAFKA_COMMIT_INTERVAL_MS - valueFrom: - configMapKeyRef: - name: kafka-config - key: KAFKA_COMMIT_INTERVAL_MS - - name: TWILIO_AUTH_TOKEN - valueFrom: - configMapKeyRef: - name: sources-twilio - key: TWILIO_AUTH_TOKEN - - name: TWILIO_ACCOUNT_SID - valueFrom: - configMapKeyRef: - name: sources-twilio - key: TWILIO_ACCOUNT_SID - livenessProbe: - tcpSocket: - port: 6000 - initialDelaySeconds: 60 - periodSeconds: 10 - failureThreshold: 3 - initContainers: - - name: wait - image: busybox - command: ["/bin/sh", "/opt/provisioning/wait-for-minimum-kafkas.sh"] - env: - - name: KAFKA_BROKERS - valueFrom: - configMapKeyRef: - name: kafka-config - key: KAFKA_BROKERS - - name: REPLICAS - valueFrom: - configMapKeyRef: - name: kafka-config - key: KAFKA_MINIMUM_REPLICAS - volumeMounts: - - name: provisioning-scripts - mountPath: /opt/provisioning - volumes: - - name: provisioning-scripts - configMap: - name: provisioning-scripts diff --git a/infrastructure/helm-chart/charts/apps/charts/sources/charts/twilio/charts/twilio-events-router/values.yaml b/infrastructure/helm-chart/charts/apps/charts/sources/charts/twilio/charts/twilio-events-router/values.yaml deleted file mode 100644 index 6720f59d37..0000000000 --- a/infrastructure/helm-chart/charts/apps/charts/sources/charts/twilio/charts/twilio-events-router/values.yaml +++ /dev/null @@ -1 +0,0 @@ -image: sources/twilio-events-router diff --git a/infrastructure/helm-chart/charts/apps/charts/sources/charts/twilio/templates/deployments.yaml b/infrastructure/helm-chart/charts/apps/charts/sources/charts/twilio/templates/deployments.yaml new file mode 100644 index 0000000000..528c2a086d --- /dev/null +++ b/infrastructure/helm-chart/charts/apps/charts/sources/charts/twilio/templates/deployments.yaml @@ -0,0 +1,176 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: sources-twilio-connector + namespace: {{ .Values.global.namespace }} + labels: + app: sources-twilio-connector + type: sources + core.airy.co/managed: "true" + core.airy.co/mandatory: "{{ .Values.mandatory }}" + core.airy.co/component: "{{ .Values.component }}" +spec: + replicas: 0 + selector: + matchLabels: + app: sources-twilio-connector + strategy: + rollingUpdate: + maxSurge: 1 + maxUnavailable: 1 + type: RollingUpdate + template: + metadata: + labels: + app: sources-twilio-connector + spec: + containers: + - name: app + image: "{{ .Values.global.containerRegistry}}/{{ .Values.imageConnector }}:{{ .Values.global.appImageTag }}" + imagePullPolicy: Always + envFrom: + - configMapRef: + name: security + env: + - name: KAFKA_BROKERS + valueFrom: + configMapKeyRef: + name: kafka-config + key: KAFKA_BROKERS + - name: KAFKA_SCHEMA_REGISTRY_URL + valueFrom: + configMapKeyRef: + name: kafka-config + key: KAFKA_SCHEMA_REGISTRY_URL + - name: KAFKA_COMMIT_INTERVAL_MS + valueFrom: + configMapKeyRef: + name: kafka-config + key: KAFKA_COMMIT_INTERVAL_MS + - name: TWILIO_AUTH_TOKEN + valueFrom: + configMapKeyRef: + name: "{{ .Values.component }}" + key: authToken + - name: TWILIO_ACCOUNT_SID + valueFrom: + configMapKeyRef: + name: "{{ .Values.component }}" + key: accountSid + livenessProbe: + httpGet: + path: /actuator/health + port: 8080 + httpHeaders: + - name: Health-Check + value: health-check + initialDelaySeconds: 60 + initContainers: + - name: wait + image: busybox + command: ["/bin/sh", "/opt/provisioning/wait-for-minimum-kafkas.sh"] + env: + - name: KAFKA_BROKERS + valueFrom: + configMapKeyRef: + name: kafka-config + key: KAFKA_BROKERS + - name: REPLICAS + valueFrom: + configMapKeyRef: + name: kafka-config + key: KAFKA_MINIMUM_REPLICAS + volumeMounts: + - name: provisioning-scripts + mountPath: /opt/provisioning + volumes: + - name: provisioning-scripts + configMap: + name: provisioning-scripts +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: sources-twilio-events-router + namespace: {{ .Values.global.namespace }} + labels: + app: sources-twilio-events-router + type: sources + core.airy.co/managed: "true" + core.airy.co/mandatory: "{{ .Values.mandatory }}" + core.airy.co/component: "{{ .Values.component }}" + annotations: + core.airy.co/config-items-mandatory: "TWILIO_AUTH_TOKEN TWILIO_ACCOUNT_SID" +spec: + replicas: 0 + selector: + matchLabels: + app: sources-twilio-events-router + strategy: + rollingUpdate: + maxSurge: 1 + maxUnavailable: 1 + type: RollingUpdate + template: + metadata: + labels: + app: sources-twilio-events-router + spec: + containers: + - name: app + image: "{{ .Values.global.containerRegistry}}/{{ .Values.imageEventsRouter }}:{{ .Values.global.appImageTag }}" + imagePullPolicy: Always + env: + - name: KAFKA_BROKERS + valueFrom: + configMapKeyRef: + name: kafka-config + key: KAFKA_BROKERS + - name: KAFKA_SCHEMA_REGISTRY_URL + valueFrom: + configMapKeyRef: + name: kafka-config + key: KAFKA_SCHEMA_REGISTRY_URL + - name: KAFKA_COMMIT_INTERVAL_MS + valueFrom: + configMapKeyRef: + name: kafka-config + key: KAFKA_COMMIT_INTERVAL_MS + - name: TWILIO_AUTH_TOKEN + valueFrom: + configMapKeyRef: + name: "{{ .Values.component }}" + key: authToken + - name: TWILIO_ACCOUNT_SID + valueFrom: + configMapKeyRef: + name: "{{ .Values.component }}" + key: accountSid + livenessProbe: + tcpSocket: + port: 6000 + initialDelaySeconds: 60 + periodSeconds: 10 + failureThreshold: 3 + initContainers: + - name: wait + image: busybox + command: ["/bin/sh", "/opt/provisioning/wait-for-minimum-kafkas.sh"] + env: + - name: KAFKA_BROKERS + valueFrom: + configMapKeyRef: + name: kafka-config + key: KAFKA_BROKERS + - name: REPLICAS + valueFrom: + configMapKeyRef: + name: kafka-config + key: KAFKA_MINIMUM_REPLICAS + volumeMounts: + - name: provisioning-scripts + mountPath: /opt/provisioning + volumes: + - name: provisioning-scripts + configMap: + name: provisioning-scripts diff --git a/infrastructure/helm-chart/charts/apps/charts/sources/charts/twilio/charts/twilio-connector/templates/service.yaml b/infrastructure/helm-chart/charts/apps/charts/sources/charts/twilio/templates/service.yaml similarity index 100% rename from infrastructure/helm-chart/charts/apps/charts/sources/charts/twilio/charts/twilio-connector/templates/service.yaml rename to infrastructure/helm-chart/charts/apps/charts/sources/charts/twilio/templates/service.yaml diff --git a/infrastructure/helm-chart/charts/apps/charts/sources/charts/twilio/values.yaml b/infrastructure/helm-chart/charts/apps/charts/sources/charts/twilio/values.yaml new file mode 100644 index 0000000000..236fef954e --- /dev/null +++ b/infrastructure/helm-chart/charts/apps/charts/sources/charts/twilio/values.yaml @@ -0,0 +1,4 @@ +component: sources-twilio +mandatory: false +imageConnector: sources/twilio-connector +imageEventsRouter: sources/twilio-events-router diff --git a/infrastructure/helm-chart/charts/apps/charts/webhook/Chart.yaml b/infrastructure/helm-chart/charts/apps/charts/webhook/Chart.yaml deleted file mode 100644 index fa6cf0a483..0000000000 --- a/infrastructure/helm-chart/charts/apps/charts/webhook/Chart.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: v1 -appVersion: "1.0" -description: A Helm chart for the Airy Core webhook component -name: webhook -version: 1.0 diff --git a/infrastructure/helm-chart/charts/apps/charts/webhook/charts/webhook-consumer/Chart.yaml b/infrastructure/helm-chart/charts/apps/charts/webhook/charts/webhook-consumer/Chart.yaml deleted file mode 100644 index 5d824dfcc1..0000000000 --- a/infrastructure/helm-chart/charts/apps/charts/webhook/charts/webhook-consumer/Chart.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: v1 -appVersion: "1.0" -description: A Helm chart for the Webhook Consumer app -name: webhook-consumer -version: 1.0 \ No newline at end of file diff --git a/infrastructure/helm-chart/charts/apps/charts/webhook/charts/webhook-consumer/templates/deployment.yaml b/infrastructure/helm-chart/charts/apps/charts/webhook/charts/webhook-consumer/templates/deployment.yaml deleted file mode 100644 index 6cfc2d389f..0000000000 --- a/infrastructure/helm-chart/charts/apps/charts/webhook/charts/webhook-consumer/templates/deployment.yaml +++ /dev/null @@ -1,78 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: webhook-consumer - namespace: {{ .Values.global.namespace }} - labels: - app: webhook-consumer - type: webhook - core.airy.co/managed: "true" -spec: - replicas: 0 - selector: - matchLabels: - app: webhook-consumer - strategy: - rollingUpdate: - maxSurge: 1 - maxUnavailable: 1 - type: RollingUpdate - template: - metadata: - labels: - app: webhook-consumer - spec: - containers: - - name: app - image: "{{ .Values.global.containerRegistry}}/{{ .Values.image }}:{{ .Values.global.appImageTag }}" - imagePullPolicy: Always - env: - - name: SERVICE_NAME - value: webhook-consumer - - name: REDIS_HOSTNAME - valueFrom: - configMapKeyRef: - name: redis-config - key: REDIS_HOSTNAME - - name: REDIS_PORT - valueFrom: - configMapKeyRef: - name: redis-config - key: REDIS_PORT - - name: WEBHOOK_NAME - valueFrom: - configMapKeyRef: - name: webhooks-config - key: NAME - livenessProbe: - httpGet: - path: /health - port: 8080 - httpHeaders: - - name: Health-Check - value: health-check - initialDelaySeconds: 60 - periodSeconds: 10 - failureThreshold: 3 - initContainers: - - name: wait - image: busybox - command: ["/bin/sh", "/opt/provisioning/wait-for-service.sh"] - env: - - name: SERVICE_NAME - valueFrom: - configMapKeyRef: - name: redis-config - key: REDIS_HOSTNAME - - name: SERVICE_PORT - valueFrom: - configMapKeyRef: - name: redis-config - key: REDIS_PORT - volumeMounts: - - name: provisioning-scripts - mountPath: /opt/provisioning - volumes: - - name: provisioning-scripts - configMap: - name: provisioning-scripts diff --git a/infrastructure/helm-chart/charts/apps/charts/webhook/charts/webhook-consumer/values.yaml b/infrastructure/helm-chart/charts/apps/charts/webhook/charts/webhook-consumer/values.yaml deleted file mode 100644 index dc75dbee55..0000000000 --- a/infrastructure/helm-chart/charts/apps/charts/webhook/charts/webhook-consumer/values.yaml +++ /dev/null @@ -1 +0,0 @@ -image: webhook/consumer \ No newline at end of file diff --git a/infrastructure/helm-chart/charts/apps/charts/webhook/charts/webhook-publisher/Chart.yaml b/infrastructure/helm-chart/charts/apps/charts/webhook/charts/webhook-publisher/Chart.yaml deleted file mode 100644 index 71d1a3a335..0000000000 --- a/infrastructure/helm-chart/charts/apps/charts/webhook/charts/webhook-publisher/Chart.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: v1 -appVersion: "1.0" -description: A Helm chart for the Webhook Publisher app -name: webhook-publisher -version: 1.0 \ No newline at end of file diff --git a/infrastructure/helm-chart/charts/apps/charts/webhook/charts/webhook-publisher/templates/deployment.yaml b/infrastructure/helm-chart/charts/apps/charts/webhook/charts/webhook-publisher/templates/deployment.yaml deleted file mode 100644 index 669a897b82..0000000000 --- a/infrastructure/helm-chart/charts/apps/charts/webhook/charts/webhook-publisher/templates/deployment.yaml +++ /dev/null @@ -1,89 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: webhook-publisher - namespace: {{ .Values.global.namespace }} - labels: - app: webhook-publisher - type: webhook - core.airy.co/managed: "true" -spec: - replicas: 0 - selector: - matchLabels: - app: webhook-publisher - strategy: - rollingUpdate: - maxSurge: 1 - maxUnavailable: 1 - type: RollingUpdate - template: - metadata: - labels: - app: webhook-publisher - spec: - containers: - - name: app - image: "{{ .Values.global.containerRegistry}}/{{ .Values.image }}:{{ .Values.global.appImageTag }}" - imagePullPolicy: Always - env: - - name: KAFKA_BROKERS - valueFrom: - configMapKeyRef: - name: kafka-config - key: KAFKA_BROKERS - - name: KAFKA_SCHEMA_REGISTRY_URL - valueFrom: - configMapKeyRef: - name: kafka-config - key: KAFKA_SCHEMA_REGISTRY_URL - - name: KAFKA_COMMIT_INTERVAL_MS - valueFrom: - configMapKeyRef: - name: kafka-config - key: KAFKA_COMMIT_INTERVAL_MS - - name: SERVICE_NAME - value: webhook-publisher - - name: REDIS_HOSTNAME - valueFrom: - configMapKeyRef: - name: redis-config - key: REDIS_HOSTNAME - - name: REDIS_PORT - valueFrom: - configMapKeyRef: - name: redis-config - key: REDIS_PORT - - name: WEBHOOK_NAME - valueFrom: - configMapKeyRef: - name: webhooks-config - key: NAME - livenessProbe: - tcpSocket: - port: 6000 - initialDelaySeconds: 60 - periodSeconds: 10 - failureThreshold: 3 - initContainers: - - name: wait - image: busybox - command: ["/bin/sh", "/opt/provisioning/wait-for-minimum-kafkas.sh"] - env: - - name: KAFKA_BROKERS - valueFrom: - configMapKeyRef: - name: kafka-config - key: KAFKA_BROKERS - - name: REPLICAS - valueFrom: - configMapKeyRef: - name: kafka-config - key: KAFKA_MINIMUM_REPLICAS - volumeMounts: - - name: provisioning-scripts - mountPath: /opt/provisioning - volumes: - - name: provisioning-scripts - configMap: - name: provisioning-scripts diff --git a/infrastructure/helm-chart/charts/apps/charts/webhook/charts/webhook-publisher/values.yaml b/infrastructure/helm-chart/charts/apps/charts/webhook/charts/webhook-publisher/values.yaml deleted file mode 100644 index bf1aac3b89..0000000000 --- a/infrastructure/helm-chart/charts/apps/charts/webhook/charts/webhook-publisher/values.yaml +++ /dev/null @@ -1 +0,0 @@ -image: webhook/publisher \ No newline at end of file diff --git a/infrastructure/helm-chart/charts/apps/charts/webhook/charts/webhook-consumer/templates/service.yaml b/infrastructure/helm-chart/charts/controller/templates/service.yaml similarity index 68% rename from infrastructure/helm-chart/charts/apps/charts/webhook/charts/webhook-consumer/templates/service.yaml rename to infrastructure/helm-chart/charts/controller/templates/service.yaml index 4cdb451059..4c6c76c112 100644 --- a/infrastructure/helm-chart/charts/apps/charts/webhook/charts/webhook-consumer/templates/service.yaml +++ b/infrastructure/helm-chart/charts/controller/templates/service.yaml @@ -1,13 +1,14 @@ apiVersion: v1 kind: Service metadata: - name: webhook-consumer + name: airy-controller namespace: {{ .Values.global.namespace }} spec: ports: - - port: 80 + - name: web + port: 80 targetPort: 8080 protocol: TCP type: NodePort selector: - app: webhook-consumer + app: airy-controller diff --git a/infrastructure/helm-chart/templates/ingress.yaml b/infrastructure/helm-chart/templates/ingress.yaml index 5e2dbb7c99..caf6d5c241 100644 --- a/infrastructure/helm-chart/templates/ingress.yaml +++ b/infrastructure/helm-chart/templates/ingress.yaml @@ -64,6 +64,20 @@ spec: name: api-communication port: number: 80 + - path: /conversations.setState + pathType: Prefix + backend: + service: + name: api-communication + port: + number: 80 + - path: /conversations.removeState + pathType: Prefix + backend: + service: + name: api-communication + port: + number: 80 - path: /messages.list pathType: Prefix backend: diff --git a/infrastructure/helm-chart/values.yaml b/infrastructure/helm-chart/values.yaml index fefc8c0c04..d5b229d138 100644 --- a/infrastructure/helm-chart/values.yaml +++ b/infrastructure/helm-chart/values.yaml @@ -1,5 +1,5 @@ global: - appImageTag: latest + # required override: appImageTag containerRegistry: ghcr.io/airyhq namespace: default ngrokEnabled: false diff --git a/integration/chat-plugin/end_conversation.spec.ts b/integration/chat-plugin/end_conversation.spec.ts new file mode 100644 index 0000000000..62667f3fbb --- /dev/null +++ b/integration/chat-plugin/end_conversation.spec.ts @@ -0,0 +1,31 @@ +import { + cyBubble, + cyInputbarTextarea, + cyInputbarButton, + cyChatPluginMessageList, + cyChatPluginHeaderBarCloseButton, + cyChatPluginEndChatModalButton, + cyChatPluginStartNewConversation, +} from 'chat-plugin-handles'; + +describe('End ChatPlugin Conversation', () => { + it('Send message from Inbox to Chatplugin, ends the current conversation and starts a new conversation', () => { + cy.visit('/chatplugin/ui/example?channel_id=' + Cypress.env('channelId')); + cy.get(`[data-cy=${cyBubble}]`).click(); + cy.get(`[data-cy=${cyInputbarTextarea}]`).type(Cypress.env('messageChatplugin')); + cy.get(`[data-cy=${cyInputbarButton}]`).click(); + cy.get(`[data-cy=${cyChatPluginMessageList}]`).children().should('have.length', 2); + + cy.wait(500); + + cy.get(`[data-cy=${cyChatPluginHeaderBarCloseButton}]`).click(); + cy.get(`[data-cy=${cyChatPluginEndChatModalButton}]`).click(); + cy.get(`[data-cy=${cyChatPluginStartNewConversation}]`).click(); + cy.get(`[data-cy=${cyBubble}]`).click(); + cy.get(`[data-cy=${cyInputbarTextarea}]`).type(Cypress.env('newMessageChatplugin')); + cy.get(`[data-cy=${cyInputbarButton}]`).click(); + cy.wait(500); + + cy.get(`[data-cy=${cyChatPluginMessageList}]`).children().should('have.length', 2); + }); +}); diff --git a/integration/chat-plugin/websocket_test.spec.ts b/integration/chat-plugin/websocket_test.spec.ts index c34e7d5b10..5e70217bab 100644 --- a/integration/chat-plugin/websocket_test.spec.ts +++ b/integration/chat-plugin/websocket_test.spec.ts @@ -43,7 +43,6 @@ describe('Websocket test', () => { password: Cypress.env('password'), }).then(response => { const loginToken = response.body['token']; - console.log('LoginToken', loginToken); cy.request({ method: 'POST', diff --git a/integration/cypress.json b/integration/cypress.json index 011594abd8..5f905238e1 100644 --- a/integration/cypress.json +++ b/integration/cypress.json @@ -10,6 +10,7 @@ "searchQuery": "Cypress Filter", "messageInbox": "Hello from Cypress Inbox!", "messageChatplugin": "Hello from Cypress ChatPlugin!", + "newMessageChatplugin": "Hello once again from Cypress ChatPlugin!", "websocketMessage": "Websocket Test Message", "channelId": "3502a0a7-933d-5410-b5fc-51f041146d89" }, diff --git a/integration/ui/send_chatplugin_message.spec.ts b/integration/ui/send_chatplugin_message.spec.ts index 4d1fdd09c6..02c34488c5 100644 --- a/integration/ui/send_chatplugin_message.spec.ts +++ b/integration/ui/send_chatplugin_message.spec.ts @@ -36,7 +36,7 @@ describe('Send chat plugin message', () => { cy.wait(500); cy.get(`[data-cy=${cyChannelsChatPluginList}]`).filter(`:contains("${Cypress.env('chatPluginName')}")`); - cy.visit('http://airy.core/chatplugin/ui/example?channel_id=' + Cypress.env('channelId')); + cy.visit('/chatplugin/ui/example?channel_id=' + Cypress.env('channelId')); cy.get(`[data-cy=${cyBubble}]`).click(); cy.get(`[data-cy=${cyInputbarTextarea}]`).type(Cypress.env('messageChatplugin')); cy.get(`[data-cy=${cyInputbarButton}]`).click(); diff --git a/lib/java/spring/auth/src/main/java/co/airy/spring/auth/AuthConfig.java b/lib/java/spring/auth/src/main/java/co/airy/spring/auth/AuthConfig.java index e77d813c9f..7fb0542bf4 100644 --- a/lib/java/spring/auth/src/main/java/co/airy/spring/auth/AuthConfig.java +++ b/lib/java/spring/auth/src/main/java/co/airy/spring/auth/AuthConfig.java @@ -28,7 +28,7 @@ public class AuthConfig extends WebSecurityConfigurerAdapter { private final String[] ignoreAuthPatterns; private final String systemToken; - public AuthConfig(Jwt jwt, @Value("${system_token:#{null}}") String systemToken, List ignorePatternBeans) { + public AuthConfig(Jwt jwt, @Value("${systemToken:#{null}}") String systemToken, List ignorePatternBeans) { this.jwt = jwt; this.systemToken = systemToken; this.ignoreAuthPatterns = ignorePatternBeans.stream() @@ -54,7 +54,7 @@ protected void configure(final HttpSecurity http) throws Exception { @Bean CorsConfigurationSource corsConfigurationSource(final Environment environment) { - final String allowed = environment.getProperty("ALLOWED_ORIGINS", ""); + final String allowed = environment.getProperty("allowedOrigins", ""); CorsConfiguration config = new CorsConfiguration(); config.setAllowCredentials(true); config.addAllowedOrigin(allowed); diff --git a/lib/java/spring/auth/src/test/java/co/airy/spring/auth/JwtAuthenticationFilterTest.java b/lib/java/spring/auth/src/test/java/co/airy/spring/auth/JwtAuthenticationFilterTest.java index a42f7433c8..1f430f97e2 100644 --- a/lib/java/spring/auth/src/test/java/co/airy/spring/auth/JwtAuthenticationFilterTest.java +++ b/lib/java/spring/auth/src/test/java/co/airy/spring/auth/JwtAuthenticationFilterTest.java @@ -21,8 +21,8 @@ @SpringBootTest(properties = { "auth.jwt-secret=424242424242424242424242424242424242424242424242424242", - "system_token=user-generated-api-token", - "ALLOWED_ORIGINS=*" + "systemToken=user-generated-api-token", + "allowedOrigins=*" }, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = AirySpringBootApplication.class) @AutoConfigureMockMvc @ExtendWith(SpringExtension.class) diff --git a/lib/typescript/assets/images/icons/template-alt.svg b/lib/typescript/assets/images/icons/template-alt.svg index 214ba0e272..f38cdc71aa 100644 --- a/lib/typescript/assets/images/icons/template-alt.svg +++ b/lib/typescript/assets/images/icons/template-alt.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/lib/typescript/httpclient/BUILD b/lib/typescript/httpclient/BUILD index 5d12a453f0..2558c8b93e 100644 --- a/lib/typescript/httpclient/BUILD +++ b/lib/typescript/httpclient/BUILD @@ -1,19 +1,70 @@ load("//tools/lint:web.bzl", "web_lint") load("@com_github_airyhq_bazel_tools//lint:buildifier.bzl", "check_pkg") load("@com_github_airyhq_bazel_tools//web:typescript.bzl", "ts_web_library") +load("//tools/build/npm:rules.bzl", "assemble_npm", "deploy_npm") +load("@com_github_airyhq_bazel_tools//web:web_library.bzl", "web_library") package(default_visibility = ["//visibility:public"]) +module_deps = [ + "//lib/typescript/model", + "//lib/typescript/types", +] + ts_web_library( name = "httpclient", - deps = [ - "//lib/typescript/model", - "//lib/typescript/types", + deps = module_deps + [ "@npm//@types/node", "@npm//camelcase-keys", ], ) +web_library( + name = "dist", + app_lib = ":httpclient", + entry = "lib/typescript/httpclient/index.js", + externals = { + "@types/node": "@types/node", + "camelcase-keys": "camelcase-keys", + }, + module_deps = module_deps, + output = { + "library": "@airyhq/http-client", + "globalObject": "this", + "libraryTarget": "umd", + "filename": "index.js", + }, +) + +genrule( + name = "npm_library", + srcs = [ + "package.json", + "README.md", + ":dist", + ":httpclient", + ], + outs = ["httpclient_lib"], + cmd = """ + mkdir -p $(OUTS)/{dist} && cp -R $(location :dist) $(OUTS) \ + && cp $(location :package.json) $(location :README.md) $(OUTS) \ + && mv $(RULEDIR)/src $(OUTS) +""", +) + +assemble_npm( + name = "assemble-npm", + target = ":npm_library", + version_file = "//:VERSION", +) + +deploy_npm( + name = "publish-npm", + release = "https://registry.npmjs.org/", + snapshot = "https://registry.npmjs.org/", + target = ":assemble-npm", +) + check_pkg(name = "buildifier") web_lint() diff --git a/lib/typescript/httpclient/client.ts b/lib/typescript/httpclient/client.ts index 94d4353d7a..66d68877ac 100644 --- a/lib/typescript/httpclient/client.ts +++ b/lib/typescript/httpclient/client.ts @@ -16,7 +16,9 @@ import { UpdateChannelRequestPayload, ListTemplatesRequestPayload, PaginatedResponse, -} from './payload'; + MetadataUpsertRequestPayload, + SetStateConversationRequestPayload, +} from './src/payload'; import { listChannelsDef, listConversationsDef, @@ -40,7 +42,9 @@ import { sendMessagesDef, getConfigDef, listTemplatesDef, -} from './endpoints'; + metadataUpsertDef, + setStateConversationDef, +} from './src/endpoints'; function isString(object: any) { return typeof object === 'string' || object instanceof String; @@ -175,6 +179,10 @@ export class HttpClient { public listTemplates = this.getRequest(listTemplatesDef); + public metadataUpsert = this.getRequest(metadataUpsertDef); + + public setStateConversation = this.getRequest(setStateConversationDef); + private getRequest({ endpoint, mapRequest, diff --git a/lib/typescript/httpclient/index.ts b/lib/typescript/httpclient/index.ts index f00d65a2ea..1eb8bf1257 100644 --- a/lib/typescript/httpclient/index.ts +++ b/lib/typescript/httpclient/index.ts @@ -1,4 +1,2 @@ -export * from './endpoints'; +export * from './src'; export * from './client'; -export * from './payload'; -export * from './messagesForChannels'; diff --git a/lib/typescript/httpclient/package.json b/lib/typescript/httpclient/package.json new file mode 100644 index 0000000000..048753c7ad --- /dev/null +++ b/lib/typescript/httpclient/package.json @@ -0,0 +1,25 @@ +{ + "name": "@airyhq/http-client", + "version": "0.1.0", + "description": "Airy http client library", + "main": "dist/index.js", + "browser": { + "./dist/index.js": "./dist/index.browser.js", + "./dist/index.esm.js": "./dist/index.browser.esm.js" + }, + "typings": "index.d.ts", + "repository": { + "type": "git", + "url": "git+https://github.com/airyhq/airy.git" + }, + "author": "Airy, Inc.", + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/airyhq/airy/issues" + }, + "homepage": "https://github.com/airyhq/airy#readme", + "dependencies": { + "@types/node": "^14.14.37", + "camelcase-keys": "^6.2.2" + } +} diff --git a/lib/typescript/httpclient/endpoints/connectChatPluginChannel.ts b/lib/typescript/httpclient/src/endpoints/connectChatPluginChannel.ts similarity index 100% rename from lib/typescript/httpclient/endpoints/connectChatPluginChannel.ts rename to lib/typescript/httpclient/src/endpoints/connectChatPluginChannel.ts diff --git a/lib/typescript/httpclient/endpoints/connectFacebookChannel.ts b/lib/typescript/httpclient/src/endpoints/connectFacebookChannel.ts similarity index 100% rename from lib/typescript/httpclient/endpoints/connectFacebookChannel.ts rename to lib/typescript/httpclient/src/endpoints/connectFacebookChannel.ts diff --git a/lib/typescript/httpclient/endpoints/connectTwilioSmsChannel.ts b/lib/typescript/httpclient/src/endpoints/connectTwilioSmsChannel.ts similarity index 100% rename from lib/typescript/httpclient/endpoints/connectTwilioSmsChannel.ts rename to lib/typescript/httpclient/src/endpoints/connectTwilioSmsChannel.ts diff --git a/lib/typescript/httpclient/endpoints/connectTwilioWhatsappChannel.ts b/lib/typescript/httpclient/src/endpoints/connectTwilioWhatsappChannel.ts similarity index 100% rename from lib/typescript/httpclient/endpoints/connectTwilioWhatsappChannel.ts rename to lib/typescript/httpclient/src/endpoints/connectTwilioWhatsappChannel.ts diff --git a/lib/typescript/httpclient/endpoints/createTag.ts b/lib/typescript/httpclient/src/endpoints/createTag.ts similarity index 100% rename from lib/typescript/httpclient/endpoints/createTag.ts rename to lib/typescript/httpclient/src/endpoints/createTag.ts diff --git a/lib/typescript/httpclient/endpoints/deleteTag.ts b/lib/typescript/httpclient/src/endpoints/deleteTag.ts similarity index 100% rename from lib/typescript/httpclient/endpoints/deleteTag.ts rename to lib/typescript/httpclient/src/endpoints/deleteTag.ts diff --git a/lib/typescript/httpclient/endpoints/disconnectChannel.ts b/lib/typescript/httpclient/src/endpoints/disconnectChannel.ts similarity index 100% rename from lib/typescript/httpclient/endpoints/disconnectChannel.ts rename to lib/typescript/httpclient/src/endpoints/disconnectChannel.ts diff --git a/lib/typescript/httpclient/endpoints/exploreFacebookChannels.ts b/lib/typescript/httpclient/src/endpoints/exploreFacebookChannels.ts similarity index 100% rename from lib/typescript/httpclient/endpoints/exploreFacebookChannels.ts rename to lib/typescript/httpclient/src/endpoints/exploreFacebookChannels.ts diff --git a/lib/typescript/httpclient/endpoints/getConfig.ts b/lib/typescript/httpclient/src/endpoints/getConfig.ts similarity index 100% rename from lib/typescript/httpclient/endpoints/getConfig.ts rename to lib/typescript/httpclient/src/endpoints/getConfig.ts diff --git a/lib/typescript/httpclient/endpoints/getConversationInfo.ts b/lib/typescript/httpclient/src/endpoints/getConversationInfo.ts similarity index 100% rename from lib/typescript/httpclient/endpoints/getConversationInfo.ts rename to lib/typescript/httpclient/src/endpoints/getConversationInfo.ts diff --git a/lib/typescript/httpclient/endpoints/index.ts b/lib/typescript/httpclient/src/endpoints/index.ts similarity index 91% rename from lib/typescript/httpclient/endpoints/index.ts rename to lib/typescript/httpclient/src/endpoints/index.ts index 495d444b0e..5f7c07ad5f 100644 --- a/lib/typescript/httpclient/endpoints/index.ts +++ b/lib/typescript/httpclient/src/endpoints/index.ts @@ -20,3 +20,5 @@ export * from './sendMessages'; export * from './getConfig'; export * from './updateChannel'; export * from './listTemplates'; +export * from './metadataUpsert'; +export * from './setStateConversation'; diff --git a/lib/typescript/httpclient/endpoints/listChannels.ts b/lib/typescript/httpclient/src/endpoints/listChannels.ts similarity index 100% rename from lib/typescript/httpclient/endpoints/listChannels.ts rename to lib/typescript/httpclient/src/endpoints/listChannels.ts diff --git a/lib/typescript/httpclient/endpoints/listConversations.ts b/lib/typescript/httpclient/src/endpoints/listConversations.ts similarity index 100% rename from lib/typescript/httpclient/endpoints/listConversations.ts rename to lib/typescript/httpclient/src/endpoints/listConversations.ts diff --git a/lib/typescript/httpclient/endpoints/listMessages.ts b/lib/typescript/httpclient/src/endpoints/listMessages.ts similarity index 100% rename from lib/typescript/httpclient/endpoints/listMessages.ts rename to lib/typescript/httpclient/src/endpoints/listMessages.ts diff --git a/lib/typescript/httpclient/endpoints/listTags.ts b/lib/typescript/httpclient/src/endpoints/listTags.ts similarity index 100% rename from lib/typescript/httpclient/endpoints/listTags.ts rename to lib/typescript/httpclient/src/endpoints/listTags.ts diff --git a/lib/typescript/httpclient/endpoints/listTemplates.ts b/lib/typescript/httpclient/src/endpoints/listTemplates.ts similarity index 100% rename from lib/typescript/httpclient/endpoints/listTemplates.ts rename to lib/typescript/httpclient/src/endpoints/listTemplates.ts diff --git a/lib/typescript/httpclient/endpoints/loginViaEmail.ts b/lib/typescript/httpclient/src/endpoints/loginViaEmail.ts similarity index 100% rename from lib/typescript/httpclient/endpoints/loginViaEmail.ts rename to lib/typescript/httpclient/src/endpoints/loginViaEmail.ts diff --git a/lib/typescript/httpclient/src/endpoints/metadataUpsert.ts b/lib/typescript/httpclient/src/endpoints/metadataUpsert.ts new file mode 100644 index 0000000000..225303a803 --- /dev/null +++ b/lib/typescript/httpclient/src/endpoints/metadataUpsert.ts @@ -0,0 +1,10 @@ +import {MetadataUpsertRequestPayload} from '../payload/MetadataUpsertRequestPayload'; + +export const metadataUpsertDef = { + endpoint: 'metadata.upsert', + mapRequest: (request: MetadataUpsertRequestPayload) => ({ + id: request.id, + subject: request.subject, + data: request.data, + }), +}; diff --git a/lib/typescript/httpclient/endpoints/readConversations.ts b/lib/typescript/httpclient/src/endpoints/readConversations.ts similarity index 100% rename from lib/typescript/httpclient/endpoints/readConversations.ts rename to lib/typescript/httpclient/src/endpoints/readConversations.ts diff --git a/lib/typescript/httpclient/endpoints/sendMessages.ts b/lib/typescript/httpclient/src/endpoints/sendMessages.ts similarity index 100% rename from lib/typescript/httpclient/endpoints/sendMessages.ts rename to lib/typescript/httpclient/src/endpoints/sendMessages.ts diff --git a/lib/typescript/httpclient/src/endpoints/setStateConversation.ts b/lib/typescript/httpclient/src/endpoints/setStateConversation.ts new file mode 100644 index 0000000000..c603e4a78a --- /dev/null +++ b/lib/typescript/httpclient/src/endpoints/setStateConversation.ts @@ -0,0 +1,9 @@ +import {SetStateConversationRequestPayload} from '../payload/SetStateConversationRequestPayload'; + +export const setStateConversationDef = { + endpoint: 'conversations.setState', + mapRequest: (request: SetStateConversationRequestPayload) => ({ + conversation_id: request.conversationId, + state: request.state, + }), +}; diff --git a/lib/typescript/httpclient/endpoints/tagConversation.ts b/lib/typescript/httpclient/src/endpoints/tagConversation.ts similarity index 100% rename from lib/typescript/httpclient/endpoints/tagConversation.ts rename to lib/typescript/httpclient/src/endpoints/tagConversation.ts diff --git a/lib/typescript/httpclient/endpoints/untagConversation.ts b/lib/typescript/httpclient/src/endpoints/untagConversation.ts similarity index 100% rename from lib/typescript/httpclient/endpoints/untagConversation.ts rename to lib/typescript/httpclient/src/endpoints/untagConversation.ts diff --git a/lib/typescript/httpclient/endpoints/updateChannel.ts b/lib/typescript/httpclient/src/endpoints/updateChannel.ts similarity index 100% rename from lib/typescript/httpclient/endpoints/updateChannel.ts rename to lib/typescript/httpclient/src/endpoints/updateChannel.ts diff --git a/lib/typescript/httpclient/endpoints/updateTag.ts b/lib/typescript/httpclient/src/endpoints/updateTag.ts similarity index 100% rename from lib/typescript/httpclient/endpoints/updateTag.ts rename to lib/typescript/httpclient/src/endpoints/updateTag.ts diff --git a/lib/typescript/httpclient/src/index.ts b/lib/typescript/httpclient/src/index.ts new file mode 100644 index 0000000000..9dad2435c9 --- /dev/null +++ b/lib/typescript/httpclient/src/index.ts @@ -0,0 +1,3 @@ +export * from './endpoints'; +export * from './payload'; +export * from './messagesForChannels'; diff --git a/lib/typescript/httpclient/messagesForChannels/index.ts b/lib/typescript/httpclient/src/messagesForChannels/index.ts similarity index 100% rename from lib/typescript/httpclient/messagesForChannels/index.ts rename to lib/typescript/httpclient/src/messagesForChannels/index.ts diff --git a/lib/typescript/httpclient/messagesForChannels/text/index.ts b/lib/typescript/httpclient/src/messagesForChannels/text/index.ts similarity index 100% rename from lib/typescript/httpclient/messagesForChannels/text/index.ts rename to lib/typescript/httpclient/src/messagesForChannels/text/index.ts diff --git a/lib/typescript/httpclient/payload/ConnectChannelFacebookRequestPayload.ts b/lib/typescript/httpclient/src/payload/ConnectChannelFacebookRequestPayload.ts similarity index 100% rename from lib/typescript/httpclient/payload/ConnectChannelFacebookRequestPayload.ts rename to lib/typescript/httpclient/src/payload/ConnectChannelFacebookRequestPayload.ts diff --git a/lib/typescript/httpclient/payload/ConnectChannelRequestPayload.ts b/lib/typescript/httpclient/src/payload/ConnectChannelRequestPayload.ts similarity index 100% rename from lib/typescript/httpclient/payload/ConnectChannelRequestPayload.ts rename to lib/typescript/httpclient/src/payload/ConnectChannelRequestPayload.ts diff --git a/lib/typescript/httpclient/payload/ConnectChatPluginRequestPayload.ts b/lib/typescript/httpclient/src/payload/ConnectChatPluginRequestPayload.ts similarity index 100% rename from lib/typescript/httpclient/payload/ConnectChatPluginRequestPayload.ts rename to lib/typescript/httpclient/src/payload/ConnectChatPluginRequestPayload.ts diff --git a/lib/typescript/httpclient/payload/ConnectTwilioSmsRequestPayload.ts b/lib/typescript/httpclient/src/payload/ConnectTwilioSmsRequestPayload.ts similarity index 100% rename from lib/typescript/httpclient/payload/ConnectTwilioSmsRequestPayload.ts rename to lib/typescript/httpclient/src/payload/ConnectTwilioSmsRequestPayload.ts diff --git a/lib/typescript/httpclient/payload/ConnectTwilioWhatsappRequestPayload.ts b/lib/typescript/httpclient/src/payload/ConnectTwilioWhatsappRequestPayload.ts similarity index 100% rename from lib/typescript/httpclient/payload/ConnectTwilioWhatsappRequestPayload.ts rename to lib/typescript/httpclient/src/payload/ConnectTwilioWhatsappRequestPayload.ts diff --git a/lib/typescript/httpclient/payload/CreateTagRequestPayload.ts b/lib/typescript/httpclient/src/payload/CreateTagRequestPayload.ts similarity index 100% rename from lib/typescript/httpclient/payload/CreateTagRequestPayload.ts rename to lib/typescript/httpclient/src/payload/CreateTagRequestPayload.ts diff --git a/lib/typescript/httpclient/payload/DisconnectChannelRequestPayload.ts b/lib/typescript/httpclient/src/payload/DisconnectChannelRequestPayload.ts similarity index 100% rename from lib/typescript/httpclient/payload/DisconnectChannelRequestPayload.ts rename to lib/typescript/httpclient/src/payload/DisconnectChannelRequestPayload.ts diff --git a/lib/typescript/httpclient/payload/ExploreChannelRequestPayload.ts b/lib/typescript/httpclient/src/payload/ExploreChannelRequestPayload.ts similarity index 100% rename from lib/typescript/httpclient/payload/ExploreChannelRequestPayload.ts rename to lib/typescript/httpclient/src/payload/ExploreChannelRequestPayload.ts diff --git a/lib/typescript/httpclient/payload/ListConversationsRequestPayload.ts b/lib/typescript/httpclient/src/payload/ListConversationsRequestPayload.ts similarity index 100% rename from lib/typescript/httpclient/payload/ListConversationsRequestPayload.ts rename to lib/typescript/httpclient/src/payload/ListConversationsRequestPayload.ts diff --git a/lib/typescript/httpclient/payload/ListMessagesRequestPayload.ts b/lib/typescript/httpclient/src/payload/ListMessagesRequestPayload.ts similarity index 100% rename from lib/typescript/httpclient/payload/ListMessagesRequestPayload.ts rename to lib/typescript/httpclient/src/payload/ListMessagesRequestPayload.ts diff --git a/lib/typescript/httpclient/payload/ListTemplatesRequestPayload.ts b/lib/typescript/httpclient/src/payload/ListTemplatesRequestPayload.ts similarity index 100% rename from lib/typescript/httpclient/payload/ListTemplatesRequestPayload.ts rename to lib/typescript/httpclient/src/payload/ListTemplatesRequestPayload.ts diff --git a/lib/typescript/httpclient/payload/LoginViaEmailRequestPayload.ts b/lib/typescript/httpclient/src/payload/LoginViaEmailRequestPayload.ts similarity index 100% rename from lib/typescript/httpclient/payload/LoginViaEmailRequestPayload.ts rename to lib/typescript/httpclient/src/payload/LoginViaEmailRequestPayload.ts diff --git a/lib/typescript/httpclient/src/payload/MetadataUpsertRequestPayload.ts b/lib/typescript/httpclient/src/payload/MetadataUpsertRequestPayload.ts new file mode 100644 index 0000000000..d9ccd04965 --- /dev/null +++ b/lib/typescript/httpclient/src/payload/MetadataUpsertRequestPayload.ts @@ -0,0 +1,5 @@ +export interface MetadataUpsertRequestPayload { + id: string; + subject: string; + data: {}; +} diff --git a/lib/typescript/httpclient/payload/PaginatedPayload.ts b/lib/typescript/httpclient/src/payload/PaginatedPayload.ts similarity index 100% rename from lib/typescript/httpclient/payload/PaginatedPayload.ts rename to lib/typescript/httpclient/src/payload/PaginatedPayload.ts diff --git a/lib/typescript/httpclient/payload/PaginatedResponse.ts b/lib/typescript/httpclient/src/payload/PaginatedResponse.ts similarity index 100% rename from lib/typescript/httpclient/payload/PaginatedResponse.ts rename to lib/typescript/httpclient/src/payload/PaginatedResponse.ts diff --git a/lib/typescript/httpclient/payload/SendMessagesRequestPayload.ts b/lib/typescript/httpclient/src/payload/SendMessagesRequestPayload.ts similarity index 100% rename from lib/typescript/httpclient/payload/SendMessagesRequestPayload.ts rename to lib/typescript/httpclient/src/payload/SendMessagesRequestPayload.ts diff --git a/lib/typescript/httpclient/src/payload/SetStateConversationRequestPayload.ts b/lib/typescript/httpclient/src/payload/SetStateConversationRequestPayload.ts new file mode 100644 index 0000000000..fedfd2e9f2 --- /dev/null +++ b/lib/typescript/httpclient/src/payload/SetStateConversationRequestPayload.ts @@ -0,0 +1,4 @@ +export interface SetStateConversationRequestPayload { + conversationId: string; + state: string; +} diff --git a/lib/typescript/httpclient/payload/TagConversationRequestPayload.ts b/lib/typescript/httpclient/src/payload/TagConversationRequestPayload.ts similarity index 100% rename from lib/typescript/httpclient/payload/TagConversationRequestPayload.ts rename to lib/typescript/httpclient/src/payload/TagConversationRequestPayload.ts diff --git a/lib/typescript/httpclient/payload/UntagConversationRequestPayload.ts b/lib/typescript/httpclient/src/payload/UntagConversationRequestPayload.ts similarity index 100% rename from lib/typescript/httpclient/payload/UntagConversationRequestPayload.ts rename to lib/typescript/httpclient/src/payload/UntagConversationRequestPayload.ts diff --git a/lib/typescript/httpclient/payload/UpdateChannelRequestPayload.ts b/lib/typescript/httpclient/src/payload/UpdateChannelRequestPayload.ts similarity index 100% rename from lib/typescript/httpclient/payload/UpdateChannelRequestPayload.ts rename to lib/typescript/httpclient/src/payload/UpdateChannelRequestPayload.ts diff --git a/lib/typescript/httpclient/payload/index.ts b/lib/typescript/httpclient/src/payload/index.ts similarity index 88% rename from lib/typescript/httpclient/payload/index.ts rename to lib/typescript/httpclient/src/payload/index.ts index cad569f27f..93e8eb21dc 100644 --- a/lib/typescript/httpclient/payload/index.ts +++ b/lib/typescript/httpclient/src/payload/index.ts @@ -15,3 +15,5 @@ export * from './SendMessagesRequestPayload'; export * from './TagConversationRequestPayload'; export * from './UntagConversationRequestPayload'; export * from './UpdateChannelRequestPayload'; +export * from './MetadataUpsertRequestPayload'; +export * from './SetStateConversationRequestPayload'; diff --git a/lib/typescript/model/Config.ts b/lib/typescript/model/Config.ts index 9a22581132..bbf7e380b8 100644 --- a/lib/typescript/model/Config.ts +++ b/lib/typescript/model/Config.ts @@ -1,17 +1,3 @@ export interface Config { - components: { - 'sources-chatplugin': { - enabled: boolean; - }; - 'sources-facebook': { - enabled: boolean; - }; - 'sources-google': { - enabled: boolean; - }; - 'sources-twilio': { - enabled: boolean; - }; - }; - features: {}; + components: {[key: string]: {enabled: boolean}}; } diff --git a/lib/typescript/model/Content.ts b/lib/typescript/model/Content.ts index 2707d476cb..a5dced030e 100644 --- a/lib/typescript/model/Content.ts +++ b/lib/typescript/model/Content.ts @@ -1,17 +1 @@ -import {Template} from './Template'; -import {Message} from './Message'; - -export interface Content { - id: string; - content: any; -} - -export type RenderedContentUnion = Message | Template | Content; - -export function isFromContact(content: RenderedContentUnion) { - if (content && 'fromContact' in content) { - return content?.fromContact; - } else { - return false; - } -} +export type Content = any; diff --git a/lib/typescript/model/Conversation.ts b/lib/typescript/model/Conversation.ts index 86aef3ec40..8f7d1f52bf 100644 --- a/lib/typescript/model/Conversation.ts +++ b/lib/typescript/model/Conversation.ts @@ -9,6 +9,7 @@ export type ConversationMetadata = Metadata & { tags: { [tagId: string]: string; }; + state: string; }; export interface Conversation { diff --git a/lib/typescript/model/Message.ts b/lib/typescript/model/Message.ts index 9b8d74b798..f2f699bbd7 100644 --- a/lib/typescript/model/Message.ts +++ b/lib/typescript/model/Message.ts @@ -11,14 +11,16 @@ export enum MessageType { video = 'video', } -export enum MessageState { +export enum DeliveryState { pending = 'PENDING', failed = 'FAILED', delivered = 'DELIVERED', } -export interface Message extends Content { - deliveryState: MessageState; +export interface Message { + id: string; + content: Content; + deliveryState: DeliveryState; fromContact: boolean; sentAt: Date; metadata?: MessageMetadata; diff --git a/lib/typescript/model/SuggestedReply.ts b/lib/typescript/model/SuggestedReply.ts index 20de020038..36cc7da688 100644 --- a/lib/typescript/model/SuggestedReply.ts +++ b/lib/typescript/model/SuggestedReply.ts @@ -1,6 +1,7 @@ import {Content} from './Content'; - -export interface SuggestedReply extends Content {} +export interface SuggestedReply { + content: Content; +} export interface Suggestions { [suggestionId: string]: SuggestedReply; } diff --git a/lib/typescript/model/Template.ts b/lib/typescript/model/Template.ts index f850e23142..b48a3f24b3 100644 --- a/lib/typescript/model/Template.ts +++ b/lib/typescript/model/Template.ts @@ -1,7 +1,9 @@ import {Source} from './Source'; import {Content} from './Content'; -export interface Template extends Content { +export interface Template { + id: string; + content: Content; name: string; source: Source; } diff --git a/lib/typescript/render/components/MessageInfoWrapper/index.tsx b/lib/typescript/render/components/MessageInfoWrapper/index.tsx index f8656ab050..37c589d20e 100644 --- a/lib/typescript/render/components/MessageInfoWrapper/index.tsx +++ b/lib/typescript/render/components/MessageInfoWrapper/index.tsx @@ -1,17 +1,17 @@ import React, {ReactNode} from 'react'; import {Avatar} from '../Avatar'; import {Contact} from 'model'; -import {DefaultRenderingProps} from '../../components/index'; import styles from './index.module.scss'; type MessageInfoWrapperProps = { children: ReactNode; lastInGroup?: boolean; isChatPlugin: boolean; + fromContact?: boolean; contact?: Contact; sentAt?: string; decoration?: ReactNode; -} & DefaultRenderingProps; +}; export const MessageInfoWrapper = (props: MessageInfoWrapperProps) => { const {sentAt, contact, fromContact, children, lastInGroup, isChatPlugin, decoration} = props; diff --git a/lib/typescript/render/components/RichCard/Media/index.tsx b/lib/typescript/render/components/RichCard/Media/index.tsx deleted file mode 100644 index 328fa0379a..0000000000 --- a/lib/typescript/render/components/RichCard/Media/index.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import React from 'react'; -import styles from './index.module.scss'; -import {MediaHeight} from '../../../providers/chatplugin/chatPluginModel'; -import {ImageWithFallback} from 'render/components/ImageWithFallback'; - -export type MediaRenderProps = { - height: MediaHeight; - contentInfo: { - altText?: string; - fileUrl: string; - forceRefresh: boolean; - }; -}; - -export const Media = ({height, contentInfo: {altText, fileUrl}}: MediaRenderProps) => ( - -); diff --git a/lib/typescript/render/components/Text/index.module.scss b/lib/typescript/render/components/Text/index.module.scss index a8dd319e21..4976e0a905 100644 --- a/lib/typescript/render/components/Text/index.module.scss +++ b/lib/typescript/render/components/Text/index.module.scss @@ -5,6 +5,7 @@ overflow-wrap: break-word; max-width: min(500px, 100%); word-break: break-word; + white-space: pre-wrap; padding: 10px; margin-top: 5px; border-radius: 8px; diff --git a/lib/typescript/render/components/Text/index.tsx b/lib/typescript/render/components/Text/index.tsx index 81ceefcb0d..b0a92c6a63 100644 --- a/lib/typescript/render/components/Text/index.tsx +++ b/lib/typescript/render/components/Text/index.tsx @@ -1,9 +1,9 @@ import React from 'react'; import styles from './index.module.scss'; -import {DefaultRenderingProps} from '../index'; -type TextRenderProps = DefaultRenderingProps & { +type TextRenderProps = { text: string; + fromContact?: boolean; }; export const Text = ({text, fromContact}: TextRenderProps) => ( diff --git a/lib/typescript/render/components/index.ts b/lib/typescript/render/components/index.ts deleted file mode 100644 index 0d6cae2f1a..0000000000 --- a/lib/typescript/render/components/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface DefaultRenderingProps { - fromContact?: boolean; -} diff --git a/lib/typescript/render/index.tsx b/lib/typescript/render/index.tsx index 04e8171d27..41f91d72ec 100644 --- a/lib/typescript/render/index.tsx +++ b/lib/typescript/render/index.tsx @@ -2,7 +2,7 @@ import React from 'react'; import {renderProviders} from './renderProviders'; import {Text} from './components/Text'; -import {getDefaultRenderingProps, RenderPropsUnion} from './props'; +import {RenderPropsUnion} from './props'; export * from './props'; @@ -25,7 +25,7 @@ export class SourceMessage extends React.Component; + return ; } render() { diff --git a/lib/typescript/render/props.ts b/lib/typescript/render/props.ts index f47de83bb6..deb1482166 100644 --- a/lib/typescript/render/props.ts +++ b/lib/typescript/render/props.ts @@ -1,6 +1,4 @@ -import {isFromContact, RenderedContentUnion} from 'model'; -import {DefaultRenderingProps} from './components'; - +import {Content} from 'model'; export interface Command { type: string; } @@ -25,7 +23,7 @@ export type CommandUnion = SuggestedReplyCommand | QuickReplyCommand; interface RenderProps { contentType: 'message' | 'template' | 'suggestedReplies' | 'quickReplies'; - content: RenderedContentUnion; + content: Content; source: string; } @@ -51,11 +49,3 @@ export type RenderPropsUnion = | TemplateRenderProps | SuggestedRepliesRenderProps | QuickRepliesRenderProps; - -export const getDefaultRenderingProps = (props: RenderPropsUnion): DefaultRenderingProps => { - const fromContact = isFromContact(props.content); - - return { - fromContact, - }; -}; diff --git a/lib/typescript/render/providers/chatplugin/ChatPluginRender.tsx b/lib/typescript/render/providers/chatplugin/ChatPluginRender.tsx index 25d1634993..8dcfe702f1 100644 --- a/lib/typescript/render/providers/chatplugin/ChatPluginRender.tsx +++ b/lib/typescript/render/providers/chatplugin/ChatPluginRender.tsx @@ -1,12 +1,13 @@ import React from 'react'; -import {getDefaultRenderingProps, RenderPropsUnion} from '../../props'; -import {RichText} from '../../components/RichText'; -import {RichCard} from '../../components/RichCard'; -import {RichCardCarousel} from '../../components/RichCardCarousel'; -import {Text} from '../../components/Text'; + +import {RenderPropsUnion} from '../../props'; import {AttachmentUnion, ContentUnion, SimpleAttachment} from './chatPluginModel'; +import {Message} from 'model'; +import {Text} from '../../components/Text'; +import {RichText} from './components/RichText'; +import {RichCard} from './components/RichCard'; +import {RichCardCarousel} from './components/RichCardCarousel'; import {QuickReplies} from './components/QuickReplies/index'; -import {RenderedContentUnion} from 'model'; export const ChatPluginRender = (props: RenderPropsUnion) => { return render(mapContent(props.content), props); @@ -14,7 +15,7 @@ export const ChatPluginRender = (props: RenderPropsUnion) => { function render(content: ContentUnion, props: RenderPropsUnion) { const defaultProps = { - ...getDefaultRenderingProps(props), + fromContact: props.content.fromContact || false, commandCallback: 'commandCallback' in props ? props.commandCallback : null, }; const invertedProps = {...defaultProps, fromContact: !defaultProps.fromContact}; @@ -59,7 +60,7 @@ function render(content: ContentUnion, props: RenderPropsUnion) { } } -function mapContent(message: RenderedContentUnion): ContentUnion { +function mapContent(message: Message): ContentUnion { const messageContent = message.content.message ?? message.content; if (messageContent.quick_replies) { diff --git a/lib/typescript/render/providers/chatplugin/chatPluginModel.ts b/lib/typescript/render/providers/chatplugin/chatPluginModel.ts index e930391474..d3a5bf07a0 100644 --- a/lib/typescript/render/providers/chatplugin/chatPluginModel.ts +++ b/lib/typescript/render/providers/chatplugin/chatPluginModel.ts @@ -1,4 +1,3 @@ -import {Suggestion} from '../../components/RichCard'; export interface Content { type: 'text' | 'image' | 'video' | 'richText' | 'richCard' | 'richCardCarousel' | 'quickReplies'; } @@ -44,9 +43,26 @@ export interface RichCardContent extends Content { forceRefresh: boolean; }; }; - suggestions: Suggestion[]; + suggestions: RichCardSuggestion[]; } +export type RichCardSuggestion = { + reply?: { + text: string; + postbackData: string; + }; + action?: { + text: string; + postbackData: string; + openUrlAction?: { + url: string; + }; + dialAction?: { + phoneNumber: string; + }; + }; +}; + export interface RichCardCarouselContent extends Content { type: 'richCardCarousel'; cardWidth: string; diff --git a/lib/typescript/render/providers/chatplugin/components/QuickReplies/index.tsx b/lib/typescript/render/providers/chatplugin/components/QuickReplies/index.tsx index 185c2c1a12..9ea497b396 100644 --- a/lib/typescript/render/providers/chatplugin/components/QuickReplies/index.tsx +++ b/lib/typescript/render/providers/chatplugin/components/QuickReplies/index.tsx @@ -1,6 +1,5 @@ import React from 'react'; -import styles from './index.module.scss'; -import {DefaultRenderingProps} from '../../../../components/index'; + import {Text} from '../../../../components/Text'; import {Video} from '../../../../components/Video'; import {Image} from '../../../../components/Image'; @@ -8,9 +7,12 @@ import {ImageWithFallback} from 'render/components/ImageWithFallback'; import {QuickReply, AttachmentUnion} from 'render/providers/chatplugin/chatPluginModel'; import {CommandUnion} from 'render/props'; -export type QuickRepliesRenderProps = DefaultRenderingProps & { +import styles from './index.module.scss'; + +export type QuickRepliesRenderProps = { text?: string; attachment?: AttachmentUnion; + fromContact?: boolean; quickReplies: QuickReply[]; commandCallback?: (command: CommandUnion) => void; }; diff --git a/lib/typescript/render/components/RichCard/Media/index.module.scss b/lib/typescript/render/providers/chatplugin/components/RichCard/Media/index.module.scss similarity index 100% rename from lib/typescript/render/components/RichCard/Media/index.module.scss rename to lib/typescript/render/providers/chatplugin/components/RichCard/Media/index.module.scss diff --git a/lib/typescript/render/providers/chatplugin/components/RichCard/Media/index.tsx b/lib/typescript/render/providers/chatplugin/components/RichCard/Media/index.tsx new file mode 100644 index 0000000000..2e1c798a54 --- /dev/null +++ b/lib/typescript/render/providers/chatplugin/components/RichCard/Media/index.tsx @@ -0,0 +1,32 @@ +import React from 'react'; + +import {MediaHeight} from '../../../chatPluginModel'; +import {ImageWithFallback} from 'render/components/ImageWithFallback'; + +import styles from './index.module.scss'; + +export type MediaRenderProps = { + height: MediaHeight; + contentInfo: { + altText?: string; + fileUrl: string; + forceRefresh: boolean; + }; +}; + +const getHeight = (height: MediaHeight): string => { + switch (height) { + case MediaHeight.short: + return styles.short; + case MediaHeight.medium: + return styles.medium; + case MediaHeight.tall: + return styles.tall; + default: + return styles.medium; + } +}; + +export const Media = ({height, contentInfo: {altText, fileUrl}}: MediaRenderProps) => ( + +); diff --git a/lib/typescript/render/components/RichCard/index.module.scss b/lib/typescript/render/providers/chatplugin/components/RichCard/index.module.scss similarity index 100% rename from lib/typescript/render/components/RichCard/index.module.scss rename to lib/typescript/render/providers/chatplugin/components/RichCard/index.module.scss diff --git a/lib/typescript/render/components/RichCard/index.tsx b/lib/typescript/render/providers/chatplugin/components/RichCard/index.tsx similarity index 82% rename from lib/typescript/render/components/RichCard/index.tsx rename to lib/typescript/render/providers/chatplugin/components/RichCard/index.tsx index 645506b7b1..3133f98a75 100644 --- a/lib/typescript/render/components/RichCard/index.tsx +++ b/lib/typescript/render/providers/chatplugin/components/RichCard/index.tsx @@ -1,36 +1,22 @@ import React from 'react'; -import styles from './index.module.scss'; + import {Media, MediaRenderProps} from './Media'; -import {CommandUnion} from '../../props'; +import {CommandUnion} from '../../../../props'; +import {RichCardSuggestion} from '../../chatPluginModel'; -export type Suggestion = { - reply?: { - text: string; - postbackData: string; - }; - action?: { - text: string; - postbackData: string; - openUrlAction?: { - url: string; - }; - dialAction?: { - phoneNumber: string; - }; - }; -}; +import styles from './index.module.scss'; export type RichCardRenderProps = { title?: string; description?: string; - suggestions: Suggestion[]; + suggestions: RichCardSuggestion[]; media: MediaRenderProps; cardWidth?: string; commandCallback?: (command: CommandUnion) => void; }; export const RichCard = ({title, description, suggestions, media, cardWidth, commandCallback}: RichCardRenderProps) => { - const clickSuggestion = (suggestion: Suggestion) => { + const clickSuggestion = (suggestion: RichCardSuggestion) => { if (suggestion.reply) { commandCallback && commandCallback({ @@ -61,7 +47,7 @@ export const RichCard = ({title, description, suggestions, media, cardWidth, com {title &&

{title}

} {description && {description}}
- {suggestions.map((suggestion: Suggestion, idx: number) => ( + {suggestions.map((suggestion: RichCardSuggestion, idx: number) => ( + ))} +
+
+ + + ); +}; diff --git a/lib/typescript/render/providers/google/components/RichCardCarousel/index.module.scss b/lib/typescript/render/providers/google/components/RichCardCarousel/index.module.scss new file mode 100644 index 0000000000..ad9f348e95 --- /dev/null +++ b/lib/typescript/render/providers/google/components/RichCardCarousel/index.module.scss @@ -0,0 +1,6 @@ +@import 'assets/scss/fonts.scss'; +@import 'assets/scss/colors.scss'; + +.richCard { + padding-right: 5px; +} diff --git a/lib/typescript/render/components/RichCardCarousel/index.tsx b/lib/typescript/render/providers/google/components/RichCardCarousel/index.tsx similarity index 85% rename from lib/typescript/render/components/RichCardCarousel/index.tsx rename to lib/typescript/render/providers/google/components/RichCardCarousel/index.tsx index 478b9850c8..1da41099fa 100644 --- a/lib/typescript/render/components/RichCardCarousel/index.tsx +++ b/lib/typescript/render/providers/google/components/RichCardCarousel/index.tsx @@ -1,16 +1,19 @@ import React from 'react'; + import {Carousel} from 'components'; -import styles from './index.module.scss'; import {MediaRenderProps} from '../RichCard/Media'; -import {RichCard, Suggestion} from '../RichCard'; -import {CommandUnion} from '../../props'; +import {RichCard} from '../RichCard'; +import {RichCardSuggestion} from '../../googleModel'; +import {CommandUnion} from '../../../../props'; + +import styles from './index.module.scss'; type Card = { id?: string; title?: string; description?: string; media: MediaRenderProps; - suggestions: Suggestion[]; + suggestions: RichCardSuggestion[]; }; export type RichCardCarouselRenderProps = { diff --git a/lib/typescript/render/providers/google/components/RichText/index.module.scss b/lib/typescript/render/providers/google/components/RichText/index.module.scss new file mode 100644 index 0000000000..4ff77b327a --- /dev/null +++ b/lib/typescript/render/providers/google/components/RichText/index.module.scss @@ -0,0 +1,49 @@ +@import 'assets/scss/colors.scss'; + +.contactContent { + width: 100%; + overflow-wrap: break-word; + word-break: break-word; + padding: 10px; + margin-top: 5px; + background: var(--color-background-blue); + color: var(--color-text-contrast); + border-radius: 8px; +} + +.memberContent { + width: 100%; + overflow-wrap: break-word; + word-break: break-word; + padding: 10px; + margin-top: 5px; + background: var(--color-airy-blue); + color: white; + border-radius: 8px; + a { + color: white; + &:visited, + &:hover { + color: white; + } + } +} + +.richText { + p:first-child { + margin-top: 0; + } + p:last-child { + margin-bottom: 0; + } + + b, + strong { + font-weight: 700; + } + + i, + em { + font-style: italic; + } +} diff --git a/lib/typescript/render/providers/google/components/RichText/index.tsx b/lib/typescript/render/providers/google/components/RichText/index.tsx new file mode 100644 index 0000000000..496106943c --- /dev/null +++ b/lib/typescript/render/providers/google/components/RichText/index.tsx @@ -0,0 +1,25 @@ +import React from 'react'; +import ReactMarkdown from 'react-markdown'; + +import {Message} from 'model'; + +import styles from './index.module.scss'; + +type RichTextRenderProps = { + message: Message; + text: string; + fromContact?: boolean; + fallback: string; + containsRichText: boolean; +}; + +export const RichText = (props: RichTextRenderProps) => { + const {message, text, fromContact} = props; + return ( +
+ + {text} + +
+ ); +}; diff --git a/lib/typescript/render/providers/google/components/Suggestions/index.tsx b/lib/typescript/render/providers/google/components/Suggestions/index.tsx index 373a4049fa..8b71ae124a 100644 --- a/lib/typescript/render/providers/google/components/Suggestions/index.tsx +++ b/lib/typescript/render/providers/google/components/Suggestions/index.tsx @@ -1,15 +1,18 @@ import React from 'react'; -import styles from './index.module.scss'; -import {DefaultRenderingProps} from '../../../../components/index'; + import {SuggestionsUnion} from '../../googleModel'; import {Image} from '../../../../components/Image'; import {Text} from '../../../../components/Text'; + import linkIcon from 'assets/images/icons/link.svg'; import phoneIcon from 'assets/images/icons/phone.svg'; -type SuggestionsRendererProps = DefaultRenderingProps & { +import styles from './index.module.scss'; + +type SuggestionsRendererProps = { text?: string; fallback?: string; + fromContact?: boolean; image?: { fileUrl: string; altText: string; diff --git a/lib/typescript/render/providers/google/googleModel.ts b/lib/typescript/render/providers/google/googleModel.ts index 6587dfe77d..db37799f97 100644 --- a/lib/typescript/render/providers/google/googleModel.ts +++ b/lib/typescript/render/providers/google/googleModel.ts @@ -1,5 +1,3 @@ -import {Suggestion} from '../../components/RichCard'; - export enum MediaHeight { short = 'SHORT', medium = 'MEDIUM', @@ -31,9 +29,26 @@ export interface RichCardContent extends Content { forceRefresh: boolean; }; }; - suggestions: Suggestion[]; + suggestions: RichCardSuggestion[]; } +export type RichCardSuggestion = { + reply?: { + text: string; + postbackData: string; + }; + action?: { + text: string; + postbackData: string; + openUrlAction?: { + url: string; + }; + dialAction?: { + phoneNumber: string; + }; + }; +}; + export interface RichCardCarouselContent extends Content { type: 'richCardCarousel'; cardWidth: string; diff --git a/lib/typescript/render/providers/twilio/twilioSMS/TwilioSMSRender.tsx b/lib/typescript/render/providers/twilio/twilioSMS/TwilioSMSRender.tsx index c9987487b7..17addfa590 100644 --- a/lib/typescript/render/providers/twilio/twilioSMS/TwilioSMSRender.tsx +++ b/lib/typescript/render/providers/twilio/twilioSMS/TwilioSMSRender.tsx @@ -1,23 +1,23 @@ import React from 'react'; -import {isFromContact, RenderedContentUnion} from 'model'; +import {Message} from 'model'; import {Text} from '../../../components/Text'; -import {getDefaultRenderingProps, RenderPropsUnion} from '../../../props'; +import {RenderPropsUnion} from '../../../props'; import {ContentUnion} from './twilioSMSModel'; export const TwilioSMSRender = (props: RenderPropsUnion) => { - const message = props.content; - const content = isFromContact(message) ? inboundContent(message) : outboundContent(message); + const message: Message = props.content; + const content = message.fromContact ? inboundContent(message) : outboundContent(message); return render(content, props); }; function render(content: ContentUnion, props: RenderPropsUnion) { switch (content.type) { case 'text': - return ; + return ; } } -const inboundContent = (message: RenderedContentUnion): ContentUnion => { +const inboundContent = (message: Message): ContentUnion => { const messageContent = message.content; const startText = messageContent.search('&Body='); const endText = messageContent.search('&FromCountry='); @@ -32,7 +32,7 @@ const inboundContent = (message: RenderedContentUnion): ContentUnion => { }; }; -const outboundContent = (message: RenderedContentUnion): ContentUnion => { +const outboundContent = (message: Message): ContentUnion => { const messageContent = message.content.message ?? message.content; return { type: 'text', diff --git a/lib/typescript/render/providers/twilio/twilioWhatsapp/TwilioWhatsappRender.tsx b/lib/typescript/render/providers/twilio/twilioWhatsapp/TwilioWhatsappRender.tsx index 1ea20ffc00..d6ae8e1a2d 100644 --- a/lib/typescript/render/providers/twilio/twilioWhatsapp/TwilioWhatsappRender.tsx +++ b/lib/typescript/render/providers/twilio/twilioWhatsapp/TwilioWhatsappRender.tsx @@ -1,23 +1,23 @@ import React from 'react'; -import {isFromContact, RenderedContentUnion} from 'model'; +import {Message} from 'model'; import {Text} from '../../../components/Text'; -import {getDefaultRenderingProps, RenderPropsUnion} from '../../../props'; +import {RenderPropsUnion} from '../../../props'; import {ContentUnion} from './twilioWhatsappModel'; export const TwilioWhatsappRender = (props: RenderPropsUnion) => { - const message = props.content; - const content = isFromContact(message) ? inboundContent(message) : outboundContent(message); + const message: Message = props.content; + const content = message.fromContact ? inboundContent(message) : outboundContent(message); return render(content, props); }; function render(content: ContentUnion, props: RenderPropsUnion) { switch (content.type) { case 'text': - return ; + return ; } } -const inboundContent = (message: RenderedContentUnion): ContentUnion => { +const inboundContent = (message: Message): ContentUnion => { const messageContent = message.content; const startText = messageContent.search('&Body='); const endText = messageContent.search('&To=whatsapp'); @@ -32,7 +32,7 @@ const inboundContent = (message: RenderedContentUnion): ContentUnion => { }; }; -const outboundContent = (message: RenderedContentUnion): ContentUnion => { +const outboundContent = (message: Message): ContentUnion => { const messageContent = message.content.message ?? message.content; return { type: 'text', diff --git a/lib/typescript/websocketclient/payload.ts b/lib/typescript/websocketclient/payload.ts index 1a76fb027b..18c989b329 100644 --- a/lib/typescript/websocketclient/payload.ts +++ b/lib/typescript/websocketclient/payload.ts @@ -1,4 +1,4 @@ -import {MessageState, Metadata, MetadataEvent} from 'model'; +import {DeliveryState, Metadata, MetadataEvent} from 'model'; interface Event { type: 'message' | 'channel' | 'metadata'; @@ -7,7 +7,7 @@ interface Event { interface MessagePayload { id: string; content: string; - delivery_state: MessageState; + delivery_state: DeliveryState; from_contact: boolean; sent_at: Date; metadata: any; diff --git a/package.json b/package.json index f50c14e24d..426a4c32f0 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "@crello/react-lottie": "^0.0.11", "@reduxjs/toolkit": "^1.5.1", "@stomp/stompjs": "^6.1.0", - "@types/node": "14.14.37", + "@types/node": "14.14.41", "@types/react": "16.9.34", "@types/react-dom": "16.9.2", "@types/react-redux": "7.1.16", @@ -22,8 +22,8 @@ "react-color": "^2.19.3", "react-dom": "16.14.0", "react-facebook-login": "^4.1.1", - "react-markdown": "^5.0.3", - "react-modal": "^3.12.1", + "react-markdown": "^6.0.0", + "react-modal": "^3.13.1", "react-redux": "7.2.3", "react-router-dom": "5.2.0", "react-window": "1.8.6", @@ -42,17 +42,17 @@ "@babel/preset-react": "^7.13.13", "@babel/preset-typescript": "^7.13.0", "@bazel/bazelisk": "^1.7.5", - "@bazel/typescript": "^3.3.0", + "@bazel/typescript": "^3.4.0", "@svgr/webpack": "^5.5.0", "@types/lodash-es": "^4.17.4", "@types/react-window-infinite-loader": "^1.0.3", "@types/resize-observer-browser": "^0.1.5", - "@typescript-eslint/eslint-plugin": "^4.21.0", - "@typescript-eslint/parser": "^4.21.0", + "@typescript-eslint/eslint-plugin": "^4.22.0", + "@typescript-eslint/parser": "^4.22.0", "babel-loader": "^8.0.6", "copy-webpack-plugin": "^8.1.1", - "css-loader": "^5.2.1", - "cypress": "^7.0.1", + "css-loader": "^5.2.4", + "cypress": "^7.1.0", "eslint": "^7.24.0", "eslint-plugin-react": "^7.23.2", "file-loader": "^6.2.0", @@ -60,14 +60,14 @@ "minimist": "^1.2.5", "prettier": "^2.2.1", "react-hot-loader": "^4.13.0", - "sass": "^1.32.8", + "sass": "^1.32.11", "sass-loader": "^11", "style-loader": "^2.0.0", "terser-webpack-plugin": "^5.1.1", "typescript": "3.7.4", "url-loader": "^4.1.1", - "webpack": "^5.31.2", - "webpack-bundle-analyzer": "^4.4.0", + "webpack": "^5.34.0", + "webpack-bundle-analyzer": "^4.4.1", "webpack-cli": "^4.6.0", "webpack-dev-server": "^3.11.2" } diff --git a/tools/build/npm/BUILD b/tools/build/npm/BUILD new file mode 100644 index 0000000000..93bcbca1ba --- /dev/null +++ b/tools/build/npm/BUILD @@ -0,0 +1,34 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +load("@bazel_skylib//:bzl_library.bzl", "bzl_library") + +bzl_library( + name = "lib", + srcs = [ + "rules.bzl", + ], + visibility = ["//visibility:public"], +) + +py_binary( + name = "assemble", + srcs = ["assemble.py"], + visibility = ["//visibility:public"], +) diff --git a/tools/build/npm/README.md b/tools/build/npm/README.md new file mode 100644 index 0000000000..bec9efa25e --- /dev/null +++ b/tools/build/npm/README.md @@ -0,0 +1,3 @@ +Npm deployment tools + +These Bazel rules were copied and adapted under the Apache 2.0 license from the excellent bazel distribution ruleset by https://github.com/graknlabs. \ No newline at end of file diff --git a/tools/build/npm/assemble.py b/tools/build/npm/assemble.py new file mode 100644 index 0000000000..ad138d23b7 --- /dev/null +++ b/tools/build/npm/assemble.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + + +import argparse +import glob +import json +import shutil +import subprocess +import os +import tempfile + +parser = argparse.ArgumentParser() +parser.add_argument('--package', help="NPM package to pack") +parser.add_argument('--version_file', help="Version file") +parser.add_argument('--output', help="Output archive") + +args = parser.parse_args() + +with open(args.version_file) as version_file: + version = version_file.read().strip() + +new_package_root = tempfile.mktemp() + +shutil.copytree(args.package, new_package_root, + ignore=lambda _, names: list( + filter(lambda x: 'external' in x, names))) +package_json_fn = os.path.join(new_package_root, 'package.json') + +with open(package_json_fn) as f: + package_json = json.load(f) + +package_json['version'] = version + +os.chmod(package_json_fn, 0o755) + +with open(package_json_fn, 'w') as f: + json.dump(package_json, f) + + +os.chmod(new_package_root, 0o755) +for root, dirs, files in os.walk(new_package_root): + for d in dirs: + os.chmod(os.path.join(root, d), 0o755) + for f in files: + os.chmod(os.path.join(root, f), 0o755) + +subprocess.check_call([ + 'npm', + 'pack' +], env={ + 'PATH': ':'.join([ + '/usr/bin/', + '/bin/', + os.path.realpath('external/nodejs/bin/nodejs/bin/'), + os.path.realpath('external/nodejs_darwin_amd64/bin/'), + os.path.realpath('external/nodejs_linux_amd64/bin/'), + os.path.realpath('external/nodejs_windows_amd64/bin/'), + ]) +}, cwd=new_package_root) + +archives = glob.glob(os.path.join(new_package_root, '*.tgz')) +if len(archives) != 1: + raise Exception('expected one archive instead of {}'.format(archives)) + +shutil.copy(archives[0], args.output) +shutil.rmtree(new_package_root) diff --git a/tools/build/npm/rules.bzl b/tools/build/npm/rules.bzl new file mode 100644 index 0000000000..652cfb688e --- /dev/null +++ b/tools/build/npm/rules.bzl @@ -0,0 +1,136 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +def _assemble_npm_impl(ctx): + if len(ctx.files.target) != 1: + fail("target contains more files than expected") + + if not ctx.attr.version_file: + version_file = ctx.actions.declare_file(ctx.attr.name + "__do_not_reference.version") + version = ctx.var.get("version", "0.0.0") + + if len(version) == 40: + # this is a commit SHA, most likely + version = "0.0.0-{}".format(version) + + ctx.actions.run_shell( + inputs = [], + outputs = [version_file], + command = "echo {} > {}".format(version, version_file.path), + ) + else: + version_file = ctx.file.version_file + + args = ctx.actions.args() + args.add("--package", ctx.files.target[0].path) + args.add("--output", ctx.outputs.npm_package.path) + args.add("--version_file", version_file.path) + + ctx.actions.run( + inputs = ctx.files.target + ctx.files._npm + [version_file], + outputs = [ctx.outputs.npm_package], + arguments = [args], + executable = ctx.executable._assemble_script, + # note: do not run in RBE + ) + +assemble_npm = rule( + implementation = _assemble_npm_impl, + attrs = { + "target": attr.label( + mandatory = True, + doc = "`npm_library` label to be included in the package", + ), + "version_file": attr.label( + allow_single_file = True, + doc = """ + File containing version string. + Alternatively, pass --define version=VERSION to Bazel invocation. + Not specifying version at all defaults to '0.0.0' + """, + ), + "_assemble_script": attr.label( + default = "//tools/build/npm:assemble", + executable = True, + cfg = "host", + ), + "_npm": attr.label( + default = Label("@nodejs//:npm"), + allow_files = True, + ), + }, + outputs = { + "npm_package": "%{name}.tar.gz", + }, + doc = "Assemble `npm_package` target for further deployment. Currently does not support remote execution (RBE).", +) + +def _deploy_npm(ctx): + ctx.actions.expand_template( + template = ctx.file._deployment_script_template, + output = ctx.outputs.executable, + substitutions = { + "{snapshot}": ctx.attr.snapshot, + "{release}": ctx.attr.release, + }, + is_executable = True, + ) + + files = [ + ctx.file.target, + ] + files.extend(ctx.files._npm) + + return DefaultInfo( + executable = ctx.outputs.executable, + runfiles = ctx.runfiles( + files = files, + symlinks = { + "deploy_npm.tgz": ctx.file.target, + }, + ), + ) + +deploy_npm = rule( + implementation = _deploy_npm, + executable = True, + attrs = { + "target": attr.label( + mandatory = True, + allow_single_file = True, + doc = "`assemble_npm` label to be included in the package", + ), + "snapshot": attr.string( + mandatory = True, + doc = "Snapshot repository to deploy npm artifact to", + ), + "release": attr.string( + mandatory = True, + doc = "Release repository to deploy npm artifact to", + ), + "_deployment_script_template": attr.label( + allow_single_file = True, + default = "//tools/build/npm/templates:deploy.py", + ), + "_npm": attr.label( + default = Label("@nodejs//:npm"), + allow_files = True, + ), + }, +) diff --git a/tools/build/npm/templates/BUILD b/tools/build/npm/templates/BUILD new file mode 100644 index 0000000000..6c1b1ff1db --- /dev/null +++ b/tools/build/npm/templates/BUILD @@ -0,0 +1,20 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +exports_files(["deploy.py"]) diff --git a/tools/build/npm/templates/deploy.py b/tools/build/npm/templates/deploy.py new file mode 100644 index 0000000000..10a8fe02c4 --- /dev/null +++ b/tools/build/npm/templates/deploy.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + + +import argparse +import os +import subprocess +import shutil +import re + +# usual importing is not possible because +# this script and module with common functions +# are at different directory levels in sandbox +import tempfile + +parser = argparse.ArgumentParser() +parser.add_argument('repo_type') +args = parser.parse_args() + +repo_type = args.repo_type +npm_repositories = { + "snapshot": "{snapshot}", + "release": "{release}", +} +npm_registry = npm_repositories[repo_type] + +npm_username, npm_password, npm_email = ( + os.getenv('DEPLOY_NPM_USERNAME'), + os.getenv('DEPLOY_NPM_PASSWORD'), + os.getenv('DEPLOY_NPM_EMAIL'), +) + +if not npm_username: + raise Exception( + 'username should be passed via ' + '$DEPLOY_NPM_USERNAME env variable' + ) + +if not npm_password: + raise Exception( + 'password should be passed via ' + '$DEPLOY_NPM_PASSWORD env variable' + ) + +if not npm_email: + raise Exception( + 'email should be passed via ' + '$DEPLOY_NPM_EMAIL env variable' + ) + +expect_input_tmpl = '''spawn npm adduser --registry={registry} +expect {{ + "Username:" {{send "{username}\r"; exp_continue}} + "Password:" {{send "$env(PASSWORD)\r"; exp_continue}} + "Email: (this IS public)" {{send "{email}\r"; exp_continue}} +}}''' + +with tempfile.NamedTemporaryFile('wt', delete=False) as expect_input_file: + expect_input_file.write(expect_input_tmpl.format( + registry=npm_registry, + username=npm_username, + email=npm_email, + )) + +node_path = ':'.join([ + '/usr/bin/', + '/bin/', + os.path.realpath('external/nodejs/bin/nodejs/bin/'), + os.path.realpath('external/nodejs_darwin_amd64/bin/'), + os.path.realpath('external/nodejs_linux_amd64/bin/'), + os.path.realpath('external/nodejs_windows_amd64/bin/'), +]) + +with open(expect_input_file.name) as expect_input: + subprocess.check_call([ + '/usr/bin/expect', + ], stdin=expect_input, env={ + 'PATH': node_path, + 'PASSWORD': npm_password + }) + +subprocess.check_call([ + 'npm', + 'publish', + '--registry={}'.format(npm_registry), + 'deploy_npm.tgz' +], env={ + 'PATH': node_path +}) diff --git a/yarn.lock b/yarn.lock index 892a652612..7a4d4a8787 100644 --- a/yarn.lock +++ b/yarn.lock @@ -980,10 +980,10 @@ resolved "https://registry.yarnpkg.com/@bazel/bazelisk/-/bazelisk-1.7.5.tgz#dd1a52e3d23464f72de55aa3dc4777847fa85373" integrity sha512-JHwP9JhfZUSoj4sku471Bjw4uE773U2Agujnx0CdPkeRk25khy1l3VyjaPaHB+z1fmMnM6ED3M7tetQUsovUQg== -"@bazel/typescript@^3.3.0": - version "3.3.0" - resolved "https://registry.yarnpkg.com/@bazel/typescript/-/typescript-3.3.0.tgz#6781f3aec3f974b864519ae9f47eaf09b134910b" - integrity sha512-neprCOVshAjlDAHgD5PAA4uqhkBzWve20Rl07Ip6BGnCNhU3XLkv+b86TtyWcU8tYMILoCSey9gzXs07XhEz6g== +"@bazel/typescript@^3.4.0": + version "3.4.0" + resolved "https://registry.yarnpkg.com/@bazel/typescript/-/typescript-3.4.0.tgz#031d989682ff8605ed8745f31448c2f76a1b653a" + integrity sha512-XlWrlQnsdQHTwsliUAf4mySHOgqRY2S57LKG2rKRjm+a015Lzlmxo6jRQaxjr68UmuhmlklRw0WfCFxdR81AvQ== dependencies: protobufjs "6.8.8" semver "5.6.0" @@ -1286,10 +1286,10 @@ "@types/estree" "*" "@types/json-schema" "*" -"@types/estree@*", "@types/estree@^0.0.46": - version "0.0.46" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.46.tgz#0fb6bfbbeabd7a30880504993369c4bf1deab1fe" - integrity sha512-laIjwTQaD+5DukBZaygQ79K1Z0jb1bPEMRrkXSLjtCcZm+abyp5YbrqpSLzD42FwWW6gK/aS4NYpJ804nG2brg== +"@types/estree@*", "@types/estree@^0.0.47": + version "0.0.47" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.47.tgz#d7a51db20f0650efec24cd04994f523d93172ed4" + integrity sha512-c5ciR06jK8u9BstrmJyO97m+klJrrhCf9u3rLu3DEAJBirxRqSCvDQoYKmxuYwQI5SZChAWu+tq9oVlGRuzPAg== "@types/glob@^7.1.1": version "7.1.3" @@ -1299,6 +1299,13 @@ "@types/minimatch" "*" "@types/node" "*" +"@types/hast@^2.0.0": + version "2.3.1" + resolved "https://registry.yarnpkg.com/@types/hast/-/hast-2.3.1.tgz#b16872f2a6144c7025f296fb9636a667ebb79cd9" + integrity sha512-viwwrB+6xGzw+G1eWpF9geV3fnsDgXqHG+cqgiHrvQfDUW5hzhCyV7Sy3UJxhfRFBsgky2SSW33qi/YrIkjX5Q== + dependencies: + "@types/unist" "*" + "@types/history@*": version "4.7.8" resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.8.tgz#49348387983075705fe8f4e02fb67f7daaec4934" @@ -1339,7 +1346,7 @@ resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.1.tgz#459c65fa1867dafe6a8f322c4c51695663cc55e9" integrity sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w== -"@types/mdast@^3.0.0", "@types/mdast@^3.0.3": +"@types/mdast@^3.0.0": version "3.0.3" resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-3.0.3.tgz#2d7d671b1cd1ea3deb306ea75036c2a0407d2deb" integrity sha512-SXPBMnFVQg1s00dlMCc/jCdvPqdE4mXaMMCeRlxLDmTAEoegHT53xKtkDnzDTOcmMHUfcjyf36/YYZ6SxRdnsw== @@ -1351,10 +1358,10 @@ resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.4.tgz#f0ec25dbf2f0e4b18647313ac031134ca5b24b21" integrity sha512-1z8k4wzFnNjVK/tlxvrWuK5WMt6mydWWP7+zvH5eFep4oj+UkrfiJTRtjCeBXNpwaA/FYqqtb4/QS4ianFpIRA== -"@types/node@*", "@types/node@14.14.37", "@types/node@^14.14.31": - version "14.14.37" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.37.tgz#a3dd8da4eb84a996c36e331df98d82abd76b516e" - integrity sha512-XYmBiy+ohOR4Lh5jE379fV2IU+6Jn4g5qASinhitfyO71b/sCo6MKsMLF5tc7Zf2CE8hViVQyYSobJNke8OvUw== +"@types/node@*", "@types/node@14.14.41", "@types/node@^14.14.31": + version "14.14.41" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.41.tgz#d0b939d94c1d7bd53d04824af45f1139b8c45615" + integrity sha512-dueRKfaJL4RTtSa7bWeTK1M+VH+Gns73oCgzvYfHZywRCoPSd8EkXBL0mZ9unPTveBn+D9phZBaxuzpwjWkW0g== "@types/node@^10.1.0": version "10.17.55" @@ -1467,13 +1474,13 @@ resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.3.tgz#9c088679876f374eb5983f150d4787aa6fb32d7e" integrity sha512-FvUupuM3rlRsRtCN+fDudtmytGO6iHJuuRKS1Ss0pG5z8oX0diNEw94UEL7hgDbpN94rgaK5R7sWm6RrSkZuAQ== -"@typescript-eslint/eslint-plugin@^4.21.0": - version "4.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.21.0.tgz#3fce2bfa76d95c00ac4f33dff369cb593aab8878" - integrity sha512-FPUyCPKZbVGexmbCFI3EQHzCZdy2/5f+jv6k2EDljGdXSRc0cKvbndd2nHZkSLqCNOPk0jB6lGzwIkglXcYVsQ== +"@typescript-eslint/eslint-plugin@^4.22.0": + version "4.22.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.22.0.tgz#3d5f29bb59e61a9dba1513d491b059e536e16dbc" + integrity sha512-U8SP9VOs275iDXaL08Ln1Fa/wLXfj5aTr/1c0t0j6CdbOnxh+TruXu1p4I0NAvdPBQgoPjHsgKn28mOi0FzfoA== dependencies: - "@typescript-eslint/experimental-utils" "4.21.0" - "@typescript-eslint/scope-manager" "4.21.0" + "@typescript-eslint/experimental-utils" "4.22.0" + "@typescript-eslint/scope-manager" "4.22.0" debug "^4.1.1" functional-red-black-tree "^1.0.1" lodash "^4.17.15" @@ -1481,60 +1488,60 @@ semver "^7.3.2" tsutils "^3.17.1" -"@typescript-eslint/experimental-utils@4.21.0": - version "4.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.21.0.tgz#0b0bb7c15d379140a660c003bdbafa71ae9134b6" - integrity sha512-cEbgosW/tUFvKmkg3cU7LBoZhvUs+ZPVM9alb25XvR0dal4qHL3SiUqHNrzoWSxaXA9gsifrYrS1xdDV6w/gIA== +"@typescript-eslint/experimental-utils@4.22.0": + version "4.22.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.22.0.tgz#68765167cca531178e7b650a53456e6e0bef3b1f" + integrity sha512-xJXHHl6TuAxB5AWiVrGhvbGL8/hbiCQ8FiWwObO3r0fnvBdrbWEDy1hlvGQOAWc6qsCWuWMKdVWlLAEMpxnddg== dependencies: "@types/json-schema" "^7.0.3" - "@typescript-eslint/scope-manager" "4.21.0" - "@typescript-eslint/types" "4.21.0" - "@typescript-eslint/typescript-estree" "4.21.0" + "@typescript-eslint/scope-manager" "4.22.0" + "@typescript-eslint/types" "4.22.0" + "@typescript-eslint/typescript-estree" "4.22.0" eslint-scope "^5.0.0" eslint-utils "^2.0.0" -"@typescript-eslint/parser@^4.21.0": - version "4.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.21.0.tgz#a227fc2af4001668c3e3f7415d4feee5093894c1" - integrity sha512-eyNf7QmE5O/l1smaQgN0Lj2M/1jOuNg2NrBm1dqqQN0sVngTLyw8tdCbih96ixlhbF1oINoN8fDCyEH9SjLeIA== +"@typescript-eslint/parser@^4.22.0": + version "4.22.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.22.0.tgz#e1637327fcf796c641fe55f73530e90b16ac8fe8" + integrity sha512-z/bGdBJJZJN76nvAY9DkJANYgK3nlRstRRi74WHm3jjgf2I8AglrSY+6l7ogxOmn55YJ6oKZCLLy+6PW70z15Q== dependencies: - "@typescript-eslint/scope-manager" "4.21.0" - "@typescript-eslint/types" "4.21.0" - "@typescript-eslint/typescript-estree" "4.21.0" + "@typescript-eslint/scope-manager" "4.22.0" + "@typescript-eslint/types" "4.22.0" + "@typescript-eslint/typescript-estree" "4.22.0" debug "^4.1.1" -"@typescript-eslint/scope-manager@4.21.0": - version "4.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.21.0.tgz#c81b661c4b8af1ec0c010d847a8f9ab76ab95b4d" - integrity sha512-kfOjF0w1Ix7+a5T1knOw00f7uAP9Gx44+OEsNQi0PvvTPLYeXJlsCJ4tYnDj5PQEYfpcgOH5yBlw7K+UEI9Agw== +"@typescript-eslint/scope-manager@4.22.0": + version "4.22.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.22.0.tgz#ed411545e61161a8d702e703a4b7d96ec065b09a" + integrity sha512-OcCO7LTdk6ukawUM40wo61WdeoA7NM/zaoq1/2cs13M7GyiF+T4rxuA4xM+6LeHWjWbss7hkGXjFDRcKD4O04Q== dependencies: - "@typescript-eslint/types" "4.21.0" - "@typescript-eslint/visitor-keys" "4.21.0" + "@typescript-eslint/types" "4.22.0" + "@typescript-eslint/visitor-keys" "4.22.0" -"@typescript-eslint/types@4.21.0": - version "4.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.21.0.tgz#abdc3463bda5d31156984fa5bc316789c960edef" - integrity sha512-+OQaupjGVVc8iXbt6M1oZMwyKQNehAfLYJJ3SdvnofK2qcjfor9pEM62rVjBknhowTkh+2HF+/KdRAc/wGBN2w== +"@typescript-eslint/types@4.22.0": + version "4.22.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.22.0.tgz#0ca6fde5b68daf6dba133f30959cc0688c8dd0b6" + integrity sha512-sW/BiXmmyMqDPO2kpOhSy2Py5w6KvRRsKZnV0c4+0nr4GIcedJwXAq+RHNK4lLVEZAJYFltnnk1tJSlbeS9lYA== -"@typescript-eslint/typescript-estree@4.21.0": - version "4.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.21.0.tgz#3817bd91857beeaeff90f69f1f112ea58d350b0a" - integrity sha512-ZD3M7yLaVGVYLw4nkkoGKumb7Rog7QID9YOWobFDMQKNl+vPxqVIW/uDk+MDeGc+OHcoG2nJ2HphwiPNajKw3w== +"@typescript-eslint/typescript-estree@4.22.0": + version "4.22.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.22.0.tgz#b5d95d6d366ff3b72f5168c75775a3e46250d05c" + integrity sha512-TkIFeu5JEeSs5ze/4NID+PIcVjgoU3cUQUIZnH3Sb1cEn1lBo7StSV5bwPuJQuoxKXlzAObjYTilOEKRuhR5yg== dependencies: - "@typescript-eslint/types" "4.21.0" - "@typescript-eslint/visitor-keys" "4.21.0" + "@typescript-eslint/types" "4.22.0" + "@typescript-eslint/visitor-keys" "4.22.0" debug "^4.1.1" globby "^11.0.1" is-glob "^4.0.1" semver "^7.3.2" tsutils "^3.17.1" -"@typescript-eslint/visitor-keys@4.21.0": - version "4.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.21.0.tgz#990a9acdc124331f5863c2cf21c88ba65233cd8d" - integrity sha512-dH22dROWGi5Z6p+Igc8bLVLmwy7vEe8r+8c+raPQU0LxgogPUrRAtRGtvBWmlr9waTu3n+QLt/qrS/hWzk1x5w== +"@typescript-eslint/visitor-keys@4.22.0": + version "4.22.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.22.0.tgz#169dae26d3c122935da7528c839f42a8a42f6e47" + integrity sha512-nnMu4F+s4o0sll6cBSsTeVsT4cwxB7zECK3dFxzEjPBii9xLpq4yqqsy/FU5zMfan6G60DKZSCXAa3sHJZrcYw== dependencies: - "@typescript-eslint/types" "4.21.0" + "@typescript-eslint/types" "4.22.0" eslint-visitor-keys "^2.0.0" "@webassemblyjs/ast@1.11.0": @@ -2303,7 +2310,7 @@ check-more-types@^2.24.0: resolved "https://registry.yarnpkg.com/check-more-types/-/check-more-types-2.24.0.tgz#1420ffb10fd444dcfc79b43891bbfffd32a84600" integrity sha1-FCD/sQ/URNz8ebQ4kbv//TKoRgA= -"chokidar@>=2.0.0 <4.0.0": +"chokidar@>=3.0.0 <4.0.0": version "3.5.1" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.1.tgz#ee9ce7bbebd2b79f49f304799d5468e31e14e68a" integrity sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw== @@ -2479,6 +2486,11 @@ combined-stream@^1.0.6, combined-stream@~1.0.6: dependencies: delayed-stream "~1.0.0" +comma-separated-tokens@^1.0.0: + version "1.0.8" + resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz#632b80b6117867a158f1080ad498b2fbe7e3f5ea" + integrity sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw== + commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" @@ -2620,9 +2632,9 @@ core-js-compat@^3.9.0, core-js-compat@^3.9.1: semver "7.0.0" core-js@3: - version "3.10.1" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.10.1.tgz#e683963978b6806dcc6c0a4a8bd4ab0bdaf3f21a" - integrity sha512-pwCxEXnj27XG47mu7SXAwhLP3L5CrlvCB91ANUkIz40P27kUcvNfSdvyZJ9CLHiVoKSp+TTChMQMSKQEH/IQxA== + version "3.10.2" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.10.2.tgz#17cb038ce084522a717d873b63f2b3ee532e2cd5" + integrity sha512-W+2oVYeNghuBr3yTzZFQ5rfmjZtYB/Ubg87R5YOmlGrIb+Uw9f7qjUbhsj+/EkXhcV7eOD3jiM4+sgraX3FZUw== core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" @@ -2660,23 +2672,22 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" -css-loader@^5.2.1: - version "5.2.1" - resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-5.2.1.tgz#15fbd5b6ac4c1b170a098f804c5abd0722f2aa73" - integrity sha512-YCyRzlt/jgG1xanXZDG/DHqAueOtXFHeusP9TS478oP1J++JSKOyEgGW1GHVoCj/rkS+GWOlBwqQJBr9yajQ9w== +css-loader@^5.2.4: + version "5.2.4" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-5.2.4.tgz#e985dcbce339812cb6104ef3670f08f9893a1536" + integrity sha512-OFYGyINCKkdQsTrSYxzGSFnGS4gNjcXkKkQgWxK138jgnPt+lepxdjSZNc8sHAl5vP3DhsJUxufWIjOwI8PMMw== dependencies: camelcase "^6.2.0" - cssesc "^3.0.0" icss-utils "^5.1.0" loader-utils "^2.0.0" - postcss "^8.2.8" + postcss "^8.2.10" postcss-modules-extract-imports "^3.0.0" postcss-modules-local-by-default "^4.0.0" postcss-modules-scope "^3.0.0" postcss-modules-values "^4.0.0" postcss-value-parser "^4.1.0" schema-utils "^3.0.0" - semver "^7.3.4" + semver "^7.3.5" css-select-base-adapter@^0.1.1: version "0.1.1" @@ -2736,10 +2747,10 @@ csstype@^3.0.2: resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.7.tgz#2a5fb75e1015e84dd15692f71e89a1450290950b" integrity sha512-KxnUB0ZMlnUWCsx2Z8MUsr6qV6ja1w9ArPErJaJaF8a5SOWoHLIszeCTKGRGRgtLgYrs1E8CHkNSP1VZTTPc9g== -cypress@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/cypress/-/cypress-7.0.1.tgz#8603f84d828fd4c5462a856f55cef5642e4ce573" - integrity sha512-dMZmZDo+x3jslEQiXRGQlMmMVMhe4JpMHQ6g1unMGXTUsapU1EXlnubevmKmqZ1IQpntAlDKmx8dupOTd3oW+g== +cypress@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/cypress/-/cypress-7.1.0.tgz#6cb5dc22c6271a9d7a79a2477251a95afc77e531" + integrity sha512-AptQP9fVtN/FfOv8rJ9hTGJE2XQFc8saLHT38r/EeyWhzp0q/+P/DYRTDtjGZHeLTCNznAUrT4lal8jm+ouS7Q== dependencies: "@cypress/listr-verbose-renderer" "^0.4.1" "@cypress/request" "^2.88.5" @@ -2977,15 +2988,6 @@ dom-serializer@0: domelementtype "^2.0.1" entities "^2.0.0" -dom-serializer@^1.0.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.2.0.tgz#3433d9136aeb3c627981daa385fc7f32d27c48f1" - integrity sha512-n6kZFH/KlCrqs/1GHMOd5i2fd/beQHuehKdWvNNffbGHTr/almdhuVvTVFb3V7fglz+nC50fFusu3lY33h12pA== - dependencies: - domelementtype "^2.0.1" - domhandler "^4.0.0" - entities "^2.0.0" - dom-walk@^0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.2.tgz#0c548bef048f4d1f2a97249002236060daa3fd84" @@ -3001,11 +3003,6 @@ domelementtype@^2.0.1: resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.2.0.tgz#9a0b6c2782ed6a1c7323d42267183df9bd8b1d57" integrity sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A== -domelementtype@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.1.0.tgz#a851c080a6d1c3d94344aed151d99f669edf585e" - integrity sha512-LsTgx/L5VpD+Q8lmsXSHW2WpA+eBlZ9HPf3erD1IoPF00/3JKHZ3BknUVA2QGDNu69ZNmyFmCWBSO45XjYKC5w== - domhandler@^2.3.0: version "2.4.2" resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.2.tgz#8805097e933d65e85546f726d60f5eb88b44f803" @@ -3013,20 +3010,6 @@ domhandler@^2.3.0: dependencies: domelementtype "1" -domhandler@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-3.3.0.tgz#6db7ea46e4617eb15cf875df68b2b8524ce0037a" - integrity sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA== - dependencies: - domelementtype "^2.0.1" - -domhandler@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.0.0.tgz#01ea7821de996d85f69029e81fa873c21833098e" - integrity sha512-KPTbnGQ1JeEMQyO1iYXoagsI6so/C96HZiFyByU3T6iAzpXn8EGEvct6unm1ZGoed8ByO2oirxgwxBmqKF9haA== - dependencies: - domelementtype "^2.1.0" - domutils@^1.5.1, domutils@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" @@ -3035,15 +3018,6 @@ domutils@^1.5.1, domutils@^1.7.0: dom-serializer "0" domelementtype "1" -domutils@^2.4.2: - version "2.5.0" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.5.0.tgz#42f49cffdabb92ad243278b331fd761c1c2d3039" - integrity sha512-Ho16rzNMOFk2fPwChGh3D2D9OEHAfG19HgmRR2l+WLSsIstNsAYBzePH412bL0y5T44ejABIVfTHQ8nqi/tBCg== - dependencies: - dom-serializer "^1.0.1" - domelementtype "^2.0.1" - domhandler "^4.0.0" - dot-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751" @@ -3115,10 +3089,10 @@ end-of-stream@^1.1.0: dependencies: once "^1.4.0" -enhanced-resolve@^5.7.0: - version "5.7.0" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.7.0.tgz#525c5d856680fbd5052de453ac83e32049958b5c" - integrity sha512-6njwt/NsZFUKhM6j9U8hzVyD4E4r0x7NQzhTCbcWOJ0IQjNSAoalWmb0AE51Wn+fwan5qVESWi7t2ToBxs9vrw== +enhanced-resolve@^5.8.0: + version "5.8.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.8.0.tgz#d9deae58f9d3773b6a111a5a46831da5be5c9ac0" + integrity sha512-Sl3KRpJA8OpprrtaIswVki3cWPiPKxXuFxJXBp+zNb6s6VwNWwFRUdtmzd2ReUut8n+sCPx7QCtQ7w5wfJhSgQ== dependencies: graceful-fs "^4.2.4" tapable "^2.2.0" @@ -4056,16 +4030,6 @@ html-minifier-terser@^5.0.1: relateurl "^0.2.7" terser "^4.6.3" -html-to-react@^1.3.4: - version "1.4.5" - resolved "https://registry.yarnpkg.com/html-to-react/-/html-to-react-1.4.5.tgz#59091c11021d1ef315ef738460abb6a4a41fe1ce" - integrity sha512-KONZUDFPg5OodWaQu2ymfkDmU0JA7zB1iPfvyHehTmMUZnk0DS7/TyCMTzsLH6b4BvxX15g88qZCXFhJWktsmA== - dependencies: - domhandler "^3.3.0" - htmlparser2 "^5.0" - lodash.camelcase "^4.3.0" - ramda "^0.27.1" - html-webpack-plugin@^5.3.1: version "5.3.1" resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-5.3.1.tgz#8797327548e3de438e3494e0c6d06f181a7f20d1" @@ -4089,16 +4053,6 @@ htmlparser2@^3.10.1: inherits "^2.0.1" readable-stream "^3.1.1" -htmlparser2@^5.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-5.0.1.tgz#7daa6fc3e35d6107ac95a4fc08781f091664f6e7" - integrity sha512-vKZZra6CSe9qsJzh0BjBGXo8dvzNsq/oGvsjfRdOrrryfeD9UOBEEQdeoqCRmKZchF5h2zOBMQ6YuQ0uRUmdbQ== - dependencies: - domelementtype "^2.0.1" - domhandler "^3.3.0" - domutils "^2.4.2" - entities "^2.0.0" - http-deceiver@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" @@ -4268,6 +4222,11 @@ ini@2.0.0: resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5" integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA== +inline-style-parser@0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/inline-style-parser/-/inline-style-parser-0.1.1.tgz#ec8a3b429274e9c0a1f1c4ffa9453a7fef72cea1" + integrity sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q== + internal-ip@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-4.3.0.tgz#845452baad9d2ca3b69c635a137acb9a0dad0907" @@ -4928,11 +4887,6 @@ lodash-es@^4.17.15, lodash-es@^4.17.21: resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee" integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw== -lodash.camelcase@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" - integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY= - lodash.debounce@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" @@ -5037,12 +4991,12 @@ material-colors@^1.2.1: resolved "https://registry.yarnpkg.com/material-colors/-/material-colors-1.2.6.tgz#6d1958871126992ceecc72f4bcc4d8f010865f46" integrity sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg== -mdast-add-list-metadata@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/mdast-add-list-metadata/-/mdast-add-list-metadata-1.0.1.tgz#95e73640ce2fc1fa2dcb7ec443d09e2bfe7db4cf" - integrity sha512-fB/VP4MJ0LaRsog7hGPxgOrSL3gE/2uEdZyDuSEnKCv/8IkYHiDkIQSbChiJoHyxZZXZ9bzckyRk+vNxFzh8rA== +mdast-util-definitions@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-definitions/-/mdast-util-definitions-4.0.0.tgz#c5c1a84db799173b4dcf7643cda999e440c24db2" + integrity sha512-k8AJ6aNnUkB7IE+5azR9h81O5EQ/cTDXtWdMq9Kk5KcEW/8ritU5CeLg/9HhOC++nALHBlaogJ5jz0Ybk3kPMQ== dependencies: - unist-util-visit-parents "1.1.2" + unist-util-visit "^2.0.0" mdast-util-from-markdown@^0.8.0: version "0.8.5" @@ -5055,6 +5009,20 @@ mdast-util-from-markdown@^0.8.0: parse-entities "^2.0.0" unist-util-stringify-position "^2.0.0" +mdast-util-to-hast@^10.2.0: + version "10.2.0" + resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-10.2.0.tgz#61875526a017d8857b71abc9333942700b2d3604" + integrity sha512-JoPBfJ3gBnHZ18icCwHR50orC9kNH81tiR1gs01D8Q5YpV6adHNO9nKNuFBCJQ941/32PT1a63UF/DitmS3amQ== + dependencies: + "@types/mdast" "^3.0.0" + "@types/unist" "^2.0.0" + mdast-util-definitions "^4.0.0" + mdurl "^1.0.0" + unist-builder "^2.0.0" + unist-util-generated "^1.0.0" + unist-util-position "^3.0.0" + unist-util-visit "^2.0.0" + mdast-util-to-string@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz#b8cfe6a713e1091cb5b728fc48885a4767f8b97b" @@ -5070,6 +5038,11 @@ mdn-data@2.0.4: resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b" integrity sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA== +mdurl@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" + integrity sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4= + media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" @@ -5260,7 +5233,7 @@ nan@^2.12.1: resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19" integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ== -nanoid@^3.1.20: +nanoid@^3.1.22: version "3.1.22" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.22.tgz#b35f8fb7d151990a8aebd5aa5015c03cf726f844" integrity sha512-/2ZUaJX2ANuLtTvqTlgqBQNJoQO398KyJgZloL0PZkC0dpysjncRUPsFe3DUPzz/y3h+u7C46np8RMuvF3jsSQ== @@ -5807,13 +5780,13 @@ postcss-value-parser@^4.1.0: resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb" integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ== -postcss@^8.2.8: - version "8.2.8" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.2.8.tgz#0b90f9382efda424c4f0f69a2ead6f6830d08ece" - integrity sha512-1F0Xb2T21xET7oQV9eKuctbM9S7BC0fetoHCc4H13z0PT6haiRLP4T0ZY4XWh7iLP0usgqykT6p9B2RtOf4FPw== +postcss@^8.2.10: + version "8.2.10" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.2.10.tgz#ca7a042aa8aff494b334d0ff3e9e77079f6f702b" + integrity sha512-b/h7CPV7QEdrqIxtAf2j31U5ef05uBDuvoXv6L51Q4rcS1jdlXAVKJv+atCFdUXYl9dyTHGyoMzIepwowRJjFw== dependencies: colorette "^1.2.2" - nanoid "^3.1.20" + nanoid "^3.1.22" source-map "^0.6.1" preact@^10.5.12: @@ -5868,6 +5841,13 @@ prop-types@^15.5.10, prop-types@^15.5.6, prop-types@^15.6.0, prop-types@^15.6.1, object-assign "^4.1.1" react-is "^16.8.1" +property-information@^5.0.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/property-information/-/property-information-5.6.0.tgz#61675545fb23002f245c6540ec46077d4da3ed69" + integrity sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA== + dependencies: + xtend "^4.0.0" + protobufjs@6.8.8: version "6.8.8" resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.8.8.tgz#c8b4f1282fd7a90e6f5b109ed11c84af82908e7c" @@ -5958,7 +5938,7 @@ quick-lru@^4.0.1: resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== -ramda@^0.27.1, ramda@~0.27.1: +ramda@~0.27.1: version "0.27.1" resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.27.1.tgz#66fc2df3ef873874ffc2da6aa8984658abacf5c9" integrity sha512-PgIdVpn5y5Yns8vqb8FzBUEYn98V3xcPgawAkkgj0YJ0qDsnHCiNmZYfOGMgOvoB0eWFLpYbhxUR3mxfDIMvpw== @@ -6036,36 +6016,43 @@ react-hot-loader@^4.13.0: shallowequal "^1.1.0" source-map "^0.7.3" -react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.6: +react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== +react-is@^17.0.0: + version "17.0.2" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" + integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== + react-lifecycles-compat@^3.0.0, react-lifecycles-compat@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== -react-markdown@^5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/react-markdown/-/react-markdown-5.0.3.tgz#41040ea7a9324b564b328fb81dd6c04f2a5373ac" - integrity sha512-jDWOc1AvWn0WahpjW6NK64mtx6cwjM4iSsLHJPNBqoAgGOVoIdJMqaKX4++plhOtdd4JksdqzlDibgPx6B/M2w== +react-markdown@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/react-markdown/-/react-markdown-6.0.0.tgz#e63cd32d095e864384d524986c44c34c919de517" + integrity sha512-MC+zljUJeoLb4RbDm/wRbfoQFEZGz4TDOt/wb4dEehdaJWxLMn/T2IgwhQy0VYhuPEd2fhd7iOayE8lmENU0FA== dependencies: - "@types/mdast" "^3.0.3" + "@types/hast" "^2.0.0" "@types/unist" "^2.0.3" - html-to-react "^1.3.4" - mdast-add-list-metadata "1.0.1" + comma-separated-tokens "^1.0.0" prop-types "^15.7.2" - react-is "^16.8.6" + property-information "^5.0.0" + react-is "^17.0.0" remark-parse "^9.0.0" + remark-rehype "^8.0.0" + space-separated-tokens "^1.1.0" + style-to-object "^0.3.0" unified "^9.0.0" unist-util-visit "^2.0.0" - xtend "^4.0.1" -react-modal@^3.12.1: - version "3.12.1" - resolved "https://registry.yarnpkg.com/react-modal/-/react-modal-3.12.1.tgz#38c33f70d81c33d02ff1ed115530443a3dc2afd3" - integrity sha512-WGuXn7Fq31PbFJwtWmOk+jFtGC7E9tJVbFX0lts8ZoS5EPi9+WWylUJWLKKVm3H4GlQ7ZxY7R6tLlbSIBQ5oZA== +react-modal@^3.13.1: + version "3.13.1" + resolved "https://registry.yarnpkg.com/react-modal/-/react-modal-3.13.1.tgz#a02dce63bbfee7582936f1e9835d518ef8f56453" + integrity sha512-m6yXK7I4YKssQnsjHK7xITSXy2O81BSOHOsg0/uWAsdKtuT9HF2tdoYhRuxNNQg2V+LgepsoHUPJKS8m6no+eg== dependencies: exenv "^1.2.0" prop-types "^15.5.10" @@ -6281,6 +6268,13 @@ remark-parse@^9.0.0: dependencies: mdast-util-from-markdown "^0.8.0" +remark-rehype@^8.0.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/remark-rehype/-/remark-rehype-8.1.0.tgz#610509a043484c1e697437fa5eb3fd992617c945" + integrity sha512-EbCu9kHgAxKmW1yEYjx3QafMyGY3q8noUbNUI5xyKbaFP89wbhDrKxyIQNukNYthzjNHZu6J7hwFg7hRm1svYA== + dependencies: + mdast-util-to-hast "^10.2.0" + remove-trailing-separator@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" @@ -6483,12 +6477,12 @@ sass-loader@^11: klona "^2.0.4" neo-async "^2.6.2" -sass@^1.32.8: - version "1.32.8" - resolved "https://registry.yarnpkg.com/sass/-/sass-1.32.8.tgz#f16a9abd8dc530add8834e506878a2808c037bdc" - integrity sha512-Sl6mIeGpzjIUZqvKnKETfMf0iDAswD9TNlv13A7aAF3XZlRPMq4VvJWBC2N2DXbp94MQVdNSFG6LfF/iOXrPHQ== +sass@^1.32.11: + version "1.32.11" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.32.11.tgz#b236b3ea55c76602c2ef2bd0445f0db581baa218" + integrity sha512-O9tRcob/fegUVSIV1ihLLZcftIOh0AF1VpKgusUfLqnb2jQ0GLDwI5ivv1FYWivGv8eZ/AwntTyTzjcHu0c/qw== dependencies: - chokidar ">=2.0.0 <4.0.0" + chokidar ">=3.0.0 <4.0.0" sax@~1.2.4: version "1.2.4" @@ -6562,10 +6556,10 @@ semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@^7.2.1, semver@^7.3.2, semver@^7.3.4: - version "7.3.4" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97" - integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw== +semver@^7.2.1, semver@^7.3.2, semver@^7.3.5: + version "7.3.5" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" + integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== dependencies: lru-cache "^6.0.0" @@ -6824,6 +6818,11 @@ source-map@^0.7.3, source-map@~0.7.2: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== +space-separated-tokens@^1.1.0: + version "1.1.5" + resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz#85f32c3d10d9682007e917414ddc5c26d1aa6899" + integrity sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA== + spdy-transport@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-3.0.0.tgz#00d4863a6400ad75df93361a1608605e5dcdcf31" @@ -7021,6 +7020,13 @@ style-loader@^2.0.0: loader-utils "^2.0.0" schema-utils "^3.0.0" +style-to-object@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/style-to-object/-/style-to-object-0.3.0.tgz#b1b790d205991cc783801967214979ee19a76e46" + integrity sha512-CzFnRRXhzWIdItT3OmF8SQfWyahHhjq3HwcMNCNLn+N7klOOqPjMeG/4JSu77D7ypZdGvSzvkrbyeTMizz2VrA== + dependencies: + inline-style-parser "0.1.1" + supports-color@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" @@ -7361,11 +7367,26 @@ uniq@^1.0.1: resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" integrity sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8= +unist-builder@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/unist-builder/-/unist-builder-2.0.3.tgz#77648711b5d86af0942f334397a33c5e91516436" + integrity sha512-f98yt5pnlMWlzP539tPc4grGMsFaQQlP/vM396b00jngsiINumNmsY8rkXjfoi1c6QaM8nQ3vaGDuoKWbe/1Uw== + +unist-util-generated@^1.0.0: + version "1.1.6" + resolved "https://registry.yarnpkg.com/unist-util-generated/-/unist-util-generated-1.1.6.tgz#5ab51f689e2992a472beb1b35f2ce7ff2f324d4b" + integrity sha512-cln2Mm1/CZzN5ttGK7vkoGw+RZ8VcUH6BtGbq98DDtRGquAAOXig1mrBQYelOwMXYS8rK+vZDyyojSjp7JX+Lg== + unist-util-is@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-4.1.0.tgz#976e5f462a7a5de73d94b706bac1b90671b57797" integrity sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg== +unist-util-position@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/unist-util-position/-/unist-util-position-3.1.0.tgz#1c42ee6301f8d52f47d14f62bbdb796571fa2d47" + integrity sha512-w+PkwCbYSFw8vpgWD0v7zRCl1FpY3fjDSQ3/N/wNd9Ffa4gPi8+4keqt99N3XW6F99t/mUzp2xAhNmfKWp95QA== + unist-util-stringify-position@^2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz#cce3bfa1cdf85ba7375d1d5b17bdc4cada9bd9da" @@ -7373,11 +7394,6 @@ unist-util-stringify-position@^2.0.0: dependencies: "@types/unist" "^2.0.2" -unist-util-visit-parents@1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-1.1.2.tgz#f6e3afee8bdbf961c0e6f028ea3c0480028c3d06" - integrity sha512-yvo+MMLjEwdc3RhhPYSximset7rwjMrdt9E41Smmvg25UQIenzrN83cRnF1JMzoMi9zZOQeYXHSDf7p+IQkW3Q== - unist-util-visit-parents@^3.0.0: version "3.1.1" resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz#65a6ce698f78a6b0f56aa0e88f13801886cdaef6" @@ -7564,10 +7580,10 @@ wbuf@^1.1.0, wbuf@^1.7.3: dependencies: minimalistic-assert "^1.0.0" -webpack-bundle-analyzer@^4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.4.0.tgz#74013106e7e2b07cbd64f3a5ae847f7e814802c7" - integrity sha512-9DhNa+aXpqdHk8LkLPTBU/dMfl84Y+WE2+KnfI6rSpNRNVKa0VGLjPd2pjFubDeqnWmulFggxmWBxhfJXZnR0g== +webpack-bundle-analyzer@^4.4.1: + version "4.4.1" + resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.4.1.tgz#c71fb2eaffc10a4754d7303b224adb2342069da1" + integrity sha512-j5m7WgytCkiVBoOGavzNokBOqxe6Mma13X1asfVYtKWM3wxBiRRu1u1iG0Iol5+qp9WgyhkMmBAcvjEfJ2bdDw== dependencies: acorn "^8.0.4" acorn-walk "^8.0.0" @@ -7673,20 +7689,20 @@ webpack-sources@^2.1.1: source-list-map "^2.0.1" source-map "^0.6.1" -webpack@^5.31.2: - version "5.31.2" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.31.2.tgz#40d9b9d15b7d76af73d3f1cae895b82613a544d6" - integrity sha512-0bCQe4ybo7T5Z0SC5axnIAH+1WuIdV4FwLYkaAlLtvfBhIx8bPS48WHTfiRZS1VM+pSiYt7e/rgLs3gLrH82lQ== +webpack@^5.34.0: + version "5.34.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.34.0.tgz#8f12bfd3474e7543232345b89294cfe8a5191c10" + integrity sha512-+WiFMgaZqhu7zKN64LQ7z0Ml4WWI+9RwG6zmS0wJDQXiCeg3hpN8fYFNJ+6WlosDT55yVxTfK7XHUAOVR4rLyA== dependencies: "@types/eslint-scope" "^3.7.0" - "@types/estree" "^0.0.46" + "@types/estree" "^0.0.47" "@webassemblyjs/ast" "1.11.0" "@webassemblyjs/wasm-edit" "1.11.0" "@webassemblyjs/wasm-parser" "1.11.0" acorn "^8.0.4" browserslist "^4.14.5" chrome-trace-event "^1.0.2" - enhanced-resolve "^5.7.0" + enhanced-resolve "^5.8.0" es-module-lexer "^0.4.0" eslint-scope "^5.1.1" events "^3.2.0" @@ -7790,7 +7806,7 @@ ws@^7.3.1: resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.4.tgz#383bc9742cb202292c9077ceab6f6047b17f2d59" integrity sha512-Qm8k8ojNQIMx7S+Zp8u/uHOx7Qazv3Yv4q68MiWWWOJhiwG5W3x7iqmRtJo8xxrciZUY4vRxUTJCKuRnF28ZZw== -xtend@^4.0.1: +xtend@^4.0.0: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==