diff --git a/.bazelrc b/.bazelrc index a697abf6a2..3a5f19bd9a 100644 --- a/.bazelrc +++ b/.bazelrc @@ -27,6 +27,5 @@ build:ci --test_env=ROOT_LOG_LEVEL=ERROR build:ci --noshow_progress build:ci --verbose_failures -build:ci --remote_cache=https://storage.googleapis.com/airy-ci-cache test:ci --flaky_test_attempts=2 diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index 71267c6f1f..70627d022d 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -1,6 +1,6 @@ name: PR Labeler on: - pull_request: + pull_request_target: types: [opened] jobs: diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 7a175e0394..3863188de0 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -24,9 +24,16 @@ jobs: - name: Enable CI settings run: | - echo "$GCS_SA_KEY" > key.json cat <>.bazelrc common --config=ci + EOF + + - name: Add bazel cache secret + if: ${{ github.actor != 'dependabot[bot]' }} + run: | + echo "$GCS_SA_KEY" > key.json + cat <>.bazelrc + build:ci --remote_cache=https://storage.googleapis.com/airy-ci-cache build:ci --google_credentials=key.json EOF env: @@ -63,7 +70,7 @@ jobs: 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') }} + if: ${{ startsWith(github.ref, 'refs/heads/main') }} run: | sudo apt-get install -y expect bazel run //lib/typescript/httpclient:publish-npm release @@ -71,4 +78,13 @@ jobs: DEPLOY_NPM_USERNAME: ${{ secrets.DEPLOY_NPM_USERNAME }} DEPLOY_NPM_PASSWORD: ${{ secrets.DEPLOY_NPM_PASSWORD }} DEPLOY_NPM_EMAIL: ${{ secrets.DEPLOY_NPM_EMAIL }} + - name: Publish chat-plugin library to npm + if: ${{ startsWith(github.ref, 'refs/heads/main') }} + run: | + sudo apt-get install -y expect + bazel run //frontend/chat-plugin: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/VERSION b/VERSION index 2157409059..ca222b7cf3 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.22.0 +0.23.0 diff --git a/WORKSPACE b/WORKSPACE index 19fc5a30b0..5d8796edbd 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 = "bbfbc0844c30b52e146690412030cfe9c6b475e3", + commit = "5943abd41d625ea3bca952bb0087cc01e95b932f", remote = "https://github.com/airyhq/bazel-tools.git", - shallow_since = "1620236403 +0200", + shallow_since = "1622467587 +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/config/ClientConfigController.java b/backend/api/admin/src/main/java/co/airy/core/api/config/ClientConfigController.java index c74a7c1af6..ca652afbda 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 @@ -1,5 +1,6 @@ package co.airy.core.api.config; +import co.airy.core.api.config.payload.ClientConfigResponsePayload; import co.airy.spring.auth.PrincipalAccess; import org.springframework.http.ResponseEntity; import org.springframework.security.core.Authentication; @@ -17,7 +18,7 @@ public ClientConfigController(ServiceDiscovery serviceDiscovery) { @PostMapping("/client.config") public ResponseEntity getConfig(Authentication auth) { return ResponseEntity.ok(ClientConfigResponsePayload.builder() - .components(serviceDiscovery.getComponents()) + .services(serviceDiscovery.getServices()) .userProfile(PrincipalAccess.getUserProfile(auth)) .build()); } 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 deleted file mode 100644 index 19a56f14c0..0000000000 --- a/backend/api/admin/src/main/java/co/airy/core/api/config/ComponentResponsePayload.java +++ /dev/null @@ -1,17 +0,0 @@ -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 deleted file mode 100644 index 34e69af59d..0000000000 --- a/backend/api/admin/src/main/java/co/airy/core/api/config/ComponentsResponsePayload.java +++ /dev/null @@ -1,17 +0,0 @@ -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/HealthApi.java b/backend/api/admin/src/main/java/co/airy/core/api/config/HealthApi.java new file mode 100644 index 0000000000..ecf6506cce --- /dev/null +++ b/backend/api/admin/src/main/java/co/airy/core/api/config/HealthApi.java @@ -0,0 +1,40 @@ +package co.airy.core.api.config; + +import co.airy.log.AiryLoggerFactory; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.slf4j.Logger; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.ResponseEntity; +import org.springframework.scheduling.annotation.Async; +import org.springframework.scheduling.annotation.AsyncResult; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestTemplate; + +import java.util.concurrent.Future; + +@Component +public class HealthApi { + private static final Logger log = AiryLoggerFactory.getLogger(HealthApi.class); + private final ObjectMapper objectMapper = new ObjectMapper(); + private final String namespace; + private final RestTemplate restTemplate; + + public HealthApi(@Value("${kubernetes.namespace}") String namespace, RestTemplate restTemplate) { + this.namespace = namespace; + this.restTemplate = restTemplate; + } + + @Async + public Future isHealthy(String service) { + try { + final ResponseEntity response = restTemplate.getForEntity(String.format("http://%s.%s/actuator/health", service, namespace), String.class); + log.info("response body {}", response.getBody()); + final JsonNode jsonNode = objectMapper.readTree(response.getBody()); + return new AsyncResult<>("UP".equalsIgnoreCase(jsonNode.get("status").textValue())); + } catch (Exception e) { + return new AsyncResult<>(false); + } + } +} + 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 c369dd7fff..00d71addd2 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,41 +1,66 @@ package co.airy.core.api.config; +import co.airy.core.api.config.dto.ServiceInfo; +import co.airy.core.api.config.payload.ServicesResponsePayload; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.ResponseEntity; +import org.springframework.scheduling.annotation.Async; +import org.springframework.scheduling.annotation.AsyncResult; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import org.springframework.web.client.RestTemplate; -import java.util.HashSet; import java.util.Map; -import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CopyOnWriteArraySet; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +import static java.util.stream.Collectors.toMap; @Component public class ServiceDiscovery { private final String namespace; private final RestTemplate restTemplate; + private final HealthApi healthApi; - private Map> components = new ConcurrentHashMap<>(); + private final Map services = new ConcurrentHashMap<>(); - public ServiceDiscovery(@Value("${kubernetes.namespace}") String namespace, RestTemplate restTemplate) { + public ServiceDiscovery(@Value("${kubernetes.namespace}") String namespace, RestTemplate restTemplate, HealthApi healthApi) { this.namespace = namespace; this.restTemplate = restTemplate; + this.healthApi = healthApi; } - public Map> getComponents() { - return components; + public Map getServices() { + return services; } @Scheduled(fixedRate = 1_000) - private void updateComponentsStatus() { - 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); + public void updateComponentsStatus() { + final ResponseEntity response = restTemplate.getForEntity(String.format("http://airy-controller.%s/services", namespace), + ServicesResponsePayload.class); + + final Map newServices = response.getBody().getServices(); + // Start all requests in parallel + final Map> healthRequests = newServices + .keySet().stream() + .collect(toMap(serviceName -> serviceName, healthApi::isHealthy)); + + healthRequests.forEach((serviceName, value) -> { + Boolean healthResponse; + try { + healthResponse = value.get(30, TimeUnit.SECONDS); + } catch (Exception e) { + e.printStackTrace(); + healthResponse = false; + } + + newServices.get(serviceName).setHealthy(healthResponse); + }); + + this.services.clear(); + this.services.putAll(newServices); } } diff --git a/backend/api/admin/src/main/java/co/airy/core/api/config/dto/ServiceInfo.java b/backend/api/admin/src/main/java/co/airy/core/api/config/dto/ServiceInfo.java new file mode 100644 index 0000000000..2585bcc0b3 --- /dev/null +++ b/backend/api/admin/src/main/java/co/airy/core/api/config/dto/ServiceInfo.java @@ -0,0 +1,14 @@ +package co.airy.core.api.config.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class ServiceInfo { + private boolean enabled; + private boolean healthy; + private String component; +} 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/payload/ClientConfigResponsePayload.java similarity index 70% rename from backend/api/admin/src/main/java/co/airy/core/api/config/ClientConfigResponsePayload.java rename to backend/api/admin/src/main/java/co/airy/core/api/config/payload/ClientConfigResponsePayload.java index 5ec8e9fd7f..8ac50e2f10 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/payload/ClientConfigResponsePayload.java @@ -1,5 +1,6 @@ -package co.airy.core.api.config; +package co.airy.core.api.config.payload; +import co.airy.core.api.config.dto.ServiceInfo; import co.airy.spring.auth.session.UserProfile; import lombok.AllArgsConstructor; import lombok.Builder; @@ -13,6 +14,6 @@ @NoArgsConstructor @AllArgsConstructor public class ClientConfigResponsePayload { - private Map> components; + private Map services; private UserProfile userProfile; } diff --git a/backend/api/admin/src/main/java/co/airy/core/api/config/payload/ServicesResponsePayload.java b/backend/api/admin/src/main/java/co/airy/core/api/config/payload/ServicesResponsePayload.java new file mode 100644 index 0000000000..717214e0bd --- /dev/null +++ b/backend/api/admin/src/main/java/co/airy/core/api/config/payload/ServicesResponsePayload.java @@ -0,0 +1,18 @@ +package co.airy.core.api.config.payload; + +import co.airy.core.api.config.dto.ServiceInfo; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.Map; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class ServicesResponsePayload implements Serializable { + private Map services; +} 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 fd2b371da8..36f9769273 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 @@ -86,24 +86,32 @@ static void afterAll() throws Exception { @BeforeEach void beforeEach() throws Exception { webTestHelper.waitUntilHealthy(); - mockServer = MockRestServiceServer.createServer(restTemplate); } @Test public void canReturnConfig() throws Exception { - mockServer.expect(once(), requestTo(new URI("http://airy-controller.default/components"))) + mockServer.expect(once(), requestTo(new URI("http://airy-controller.default/services"))) + .andExpect(method(HttpMethod.GET)) + .andRespond( + withSuccess("{\"services\": {\"api-communication\":{\"enabled\":true,\"component\":\"api-communication\"}}}", MediaType.APPLICATION_JSON) + ); + + mockServer.expect(once(), requestTo(new URI("http://api-communication.default/actuator/health"))) .andExpect(method(HttpMethod.GET)) .andRespond( - withSuccess("{\"components\": [\"api-communication\"]}", MediaType.APPLICATION_JSON) + withSuccess("{\"status\": \"DOWN\"}", MediaType.APPLICATION_JSON) ); retryOnException(() -> webTestHelper.post("/client.config", "{}") .andExpect(status().isOk()) - .andExpect(jsonPath("$.components.*", hasSize(1))) - .andExpect(jsonPath("$.components", hasKey("api-communication"))) - .andExpect(jsonPath("$.components.*.enabled", everyItem(is(true)))), + .andExpect(jsonPath("$.services.*", hasSize(1))) + .andExpect(jsonPath("$.services", hasKey("api-communication"))) + .andExpect(jsonPath("$.services.*.enabled", everyItem(is(true)))) + .andExpect(jsonPath("$.services.*.healthy", everyItem(is(false)))), "client.config call failed"); + + mockServer.verify(); } } 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 543aee6cb4..c7fd2f7586 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 @@ -3,7 +3,6 @@ import co.airy.avro.communication.Metadata; import co.airy.avro.communication.ReadReceipt; import co.airy.core.api.communication.dto.Conversation; -import co.airy.core.api.communication.dto.ConversationIndex; import co.airy.core.api.communication.dto.LuceneQueryResult; import co.airy.core.api.communication.lucene.AiryAnalyzer; import co.airy.core.api.communication.lucene.ExtendedQueryParser; @@ -18,12 +17,11 @@ import co.airy.model.metadata.MetadataKeys; import co.airy.model.metadata.Subject; import co.airy.model.metadata.dto.MetadataMap; -import co.airy.pagination.Page; -import co.airy.pagination.Paginator; import co.airy.spring.web.payload.RequestErrorResponsePayload; import org.apache.kafka.streams.state.KeyValueIterator; import org.apache.kafka.streams.state.ReadOnlyKeyValueStore; import org.apache.lucene.queryparser.classic.ParseException; +import org.apache.lucene.search.MatchAllDocsQuery; import org.apache.lucene.search.Query; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -42,7 +40,6 @@ 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; @RestController @@ -63,35 +60,40 @@ public class ConversationsController { ResponseEntity conversationList(@RequestBody(required = false) @Valid ConversationListRequestPayload request) { request = Optional.ofNullable(request).orElse(new ConversationListRequestPayload()); final String queryFilter = request.getFilters(); + final int pageSize = request.getPageSize(); + + int cursor = 0; + // To keep pagination endpoints uniform we also use the string type for the cursor here + if (request.getCursor() != null) { + try { + cursor = Integer.parseInt(request.getCursor()); + } catch (NumberFormatException e) { + return ResponseEntity.badRequest().build(); + } + } + + Query query; if (queryFilter == null) { - return listConversations(request); + query = new MatchAllDocsQuery(); + } else { + try { + query = queryParser.parse(queryFilter); + } catch (ParseException e) { + return ResponseEntity.status(HttpStatus.BAD_REQUEST) + .body(new RequestErrorResponsePayload("Failed to parse Lucene query: " + e.getMessage())); + } } - return queryConversations(request); + return queryConversations(query, cursor, pageSize); } - private ResponseEntity queryConversations(ConversationListRequestPayload requestPayload) { + private ResponseEntity queryConversations(Query query, Integer cursor, int pageSize) { final ReadOnlyLuceneStore conversationLuceneStore = stores.getConversationLuceneStore(); final ReadOnlyKeyValueStore conversationsStore = stores.getConversationsStore(); - final Query query; - try { - query = queryParser.parse(requestPayload.getFilters()); - } catch (ParseException e) { - return ResponseEntity.status(HttpStatus.BAD_REQUEST) - .body(new RequestErrorResponsePayload("Failed to parse Lucene query: " + e.getMessage())); - } - - final LuceneQueryResult queryResult = conversationLuceneStore.query(query); - - final List conversationIndices = queryResult.getConversations(); + final LuceneQueryResult queryResult = conversationLuceneStore.query(query, cursor, pageSize); - final Paginator paginator = new Paginator<>(conversationIndices, ConversationIndex::getId) - .from(requestPayload.getCursor()).perPage(requestPayload.getPageSize()); - - final Page page = paginator.page(); - - final List conversations = paginator.page().getData() + final List conversations = queryResult.getConversations() .stream() .map((conversationIndex -> conversationsStore.get(conversationIndex.getId()))) .collect(toList()); @@ -99,41 +101,19 @@ private ResponseEntity queryConversations(ConversationListRequestPayload requ final List enrichedConversations = stores.addChannelMetadata(conversations); int totalSize = queryResult.getTotal(); + String nextCursor = null; + if (cursor + pageSize < queryResult.getFilteredTotal()) { + nextCursor = String.valueOf(cursor + pageSize); + } return ResponseEntity.ok( ConversationListResponsePayload.builder() .data(enrichedConversations.stream().map(ConversationResponsePayload::fromConversation).collect(Collectors.toList())) .paginationData( PaginationData.builder() - .filteredTotal(conversationIndices.size()) - .nextCursor(page.getNextCursor()) - .previousCursor(page.getPreviousCursor()) - .total(totalSize) - .build() - ).build()); - } - - private ResponseEntity listConversations(ConversationListRequestPayload requestPayload) { - final List allConversations = fetchAllConversations(); - int totalSize = allConversations.size(); - allConversations.sort(comparing(conversation -> ((Conversation) conversation).getLastMessageContainer().getMessage().getSentAt()).reversed()); - - final Paginator paginator = new Paginator<>(allConversations, Conversation::getId) - .from(requestPayload.getCursor()).perPage(requestPayload.getPageSize()); - - final Page page = paginator.page(); - - final List conversationsPage = page.getData(); - final List conversations = stores.addChannelMetadata(conversationsPage); - - return ResponseEntity.ok( - ConversationListResponsePayload.builder() - .data(conversations.stream().map(ConversationResponsePayload::fromConversation).collect(Collectors.toList())) - .paginationData( - PaginationData.builder() - .filteredTotal(allConversations.size()) - .nextCursor(page.getNextCursor()) - .previousCursor(page.getPreviousCursor()) + .filteredTotal(queryResult.getFilteredTotal()) + .nextCursor(nextCursor) + .previousCursor(String.valueOf(cursor)) .total(totalSize) .build() ).build()); diff --git a/backend/api/communication/src/main/java/co/airy/core/api/communication/dto/ConversationIndex.java b/backend/api/communication/src/main/java/co/airy/core/api/communication/dto/ConversationIndex.java index b64f9e292f..3edf21c752 100644 --- a/backend/api/communication/src/main/java/co/airy/core/api/communication/dto/ConversationIndex.java +++ b/backend/api/communication/src/main/java/co/airy/core/api/communication/dto/ConversationIndex.java @@ -23,6 +23,7 @@ public class ConversationIndex implements Serializable { private Long createdAt; private Integer unreadMessageCount; private List tagIds; + private Long lastMessageAt; @Builder.Default private List metadata = new ArrayList<>(); @@ -41,6 +42,7 @@ public static ConversationIndex fromConversation(Conversation conversation) { .createdAt(conversation.getCreatedAt()) .tagIds(conversation.getTagIds()) .unreadMessageCount(conversation.getUnreadMessageCount()) + .lastMessageAt(conversation.getLastMessageContainer().getMessage().getSentAt()) .build(); } } diff --git a/backend/api/communication/src/main/java/co/airy/core/api/communication/dto/LuceneQueryResult.java b/backend/api/communication/src/main/java/co/airy/core/api/communication/dto/LuceneQueryResult.java index 8ad5b06d27..13d34424ff 100644 --- a/backend/api/communication/src/main/java/co/airy/core/api/communication/dto/LuceneQueryResult.java +++ b/backend/api/communication/src/main/java/co/airy/core/api/communication/dto/LuceneQueryResult.java @@ -9,5 +9,6 @@ @Builder public class LuceneQueryResult { private List conversations; + private long filteredTotal; private int total; } diff --git a/backend/api/communication/src/main/java/co/airy/core/api/communication/lucene/DocumentMapper.java b/backend/api/communication/src/main/java/co/airy/core/api/communication/lucene/DocumentMapper.java index ea0248332d..96bb705564 100644 --- a/backend/api/communication/src/main/java/co/airy/core/api/communication/lucene/DocumentMapper.java +++ b/backend/api/communication/src/main/java/co/airy/core/api/communication/lucene/DocumentMapper.java @@ -6,6 +6,7 @@ import org.apache.lucene.document.Field; import org.apache.lucene.document.IntPoint; import org.apache.lucene.document.LongPoint; +import org.apache.lucene.document.NumericDocValuesField; import org.apache.lucene.document.StoredField; import org.apache.lucene.document.StringField; import org.apache.lucene.document.TextField; @@ -24,12 +25,15 @@ public Document fromConversationIndex(ConversationIndex conversation) { if (conversation.getDisplayName() != null) { document.add(new TextField("display_name", conversation.getDisplayName(), Field.Store.YES)); } + document.add(new StringField("source", conversation.getSource(), Field.Store.YES)); document.add(new LongPoint("created_at", conversation.getCreatedAt())); document.add(new StoredField("created_at", conversation.getCreatedAt())); document.add(new IntPoint("unread_count", conversation.getUnreadMessageCount())); document.add(new StoredField("unread_count", conversation.getUnreadMessageCount())); + // sort enabled field + document.add(new NumericDocValuesField("last_message_at", conversation.getLastMessageAt())); for (String tagId : conversation.getTagIds()) { document.add(new TextField("tag_ids", tagId, Field.Store.YES)); } diff --git a/backend/api/communication/src/main/java/co/airy/core/api/communication/lucene/LuceneDiskStore.java b/backend/api/communication/src/main/java/co/airy/core/api/communication/lucene/LuceneDiskStore.java index c05522c093..75deb46aa7 100644 --- a/backend/api/communication/src/main/java/co/airy/core/api/communication/lucene/LuceneDiskStore.java +++ b/backend/api/communication/src/main/java/co/airy/core/api/communication/lucene/LuceneDiskStore.java @@ -107,8 +107,8 @@ public void delete(String id) throws IOException { } @Override - public LuceneQueryResult query(Query query) { - return lucene.query(query); + public LuceneQueryResult query(Query query, int cursor, int pageSize) { + return lucene.query(query, cursor, pageSize); } public static class Builder implements StoreBuilder { diff --git a/backend/api/communication/src/main/java/co/airy/core/api/communication/lucene/LuceneProvider.java b/backend/api/communication/src/main/java/co/airy/core/api/communication/lucene/LuceneProvider.java index a177fc2612..3953802af1 100644 --- a/backend/api/communication/src/main/java/co/airy/core/api/communication/lucene/LuceneProvider.java +++ b/backend/api/communication/src/main/java/co/airy/core/api/communication/lucene/LuceneProvider.java @@ -11,7 +11,11 @@ import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; import org.apache.lucene.search.ScoreDoc; +import org.apache.lucene.search.Sort; +import org.apache.lucene.search.SortField; +import org.apache.lucene.search.SortedNumericSortField; import org.apache.lucene.search.TopDocs; +import org.apache.lucene.search.TopFieldCollector; import org.apache.lucene.store.FSDirectory; import org.slf4j.Logger; import org.springframework.stereotype.Component; @@ -51,25 +55,31 @@ public void delete(String id) throws IOException { } @Override - public LuceneQueryResult query(Query query) { + public LuceneQueryResult query(Query query, int cursor, int pageSize) { try { refreshReader(); - final IndexSearcher indexSearcher = new IndexSearcher(reader); - final TopDocs topDocs = indexSearcher.search(query, Integer.MAX_VALUE); + final IndexSearcher searcher = new IndexSearcher(reader); + SortField lastMessageSort = new SortedNumericSortField("last_message_at", SortField.Type.LONG, true); + Sort sort = new Sort(lastMessageSort); + final TopFieldCollector collector = TopFieldCollector.create(sort, 2000, Integer.MAX_VALUE); - List conversations = new ArrayList<>(topDocs.scoreDocs.length); - for (ScoreDoc scoreDoc : topDocs.scoreDocs) { - final Document doc = indexSearcher.doc(scoreDoc.doc); + searcher.search(query, collector); + final TopDocs hits = collector.topDocs(cursor, pageSize); + + List conversations = new ArrayList<>(hits.scoreDocs.length); + for (ScoreDoc scoreDoc : hits.scoreDocs) { + final Document doc = searcher.doc(scoreDoc.doc); conversations.add(documentMapper.fromDocument(doc)); } return LuceneQueryResult.builder() .conversations(conversations) - .total(reader.maxDoc()).build(); + .filteredTotal(hits.totalHits.value) + .total(reader.numDocs()).build(); } catch (Exception e) { log.error("Failed to query Lucene store with query {}", query, e); return LuceneQueryResult.builder().conversations(List.of()) - .total(reader.maxDoc()).build(); + .total(reader.numDocs()).filteredTotal(0).build(); } } diff --git a/backend/api/communication/src/main/java/co/airy/core/api/communication/lucene/ReadOnlyLuceneStore.java b/backend/api/communication/src/main/java/co/airy/core/api/communication/lucene/ReadOnlyLuceneStore.java index 45a1c3c8fc..4599b79d39 100644 --- a/backend/api/communication/src/main/java/co/airy/core/api/communication/lucene/ReadOnlyLuceneStore.java +++ b/backend/api/communication/src/main/java/co/airy/core/api/communication/lucene/ReadOnlyLuceneStore.java @@ -4,5 +4,5 @@ import org.apache.lucene.search.Query; public interface ReadOnlyLuceneStore { - LuceneQueryResult query(Query query); + LuceneQueryResult query(Query query, int cursor, int pageSize); } diff --git a/backend/api/communication/src/test/java/co/airy/core/api/communication/ConversationsListTest.java b/backend/api/communication/src/test/java/co/airy/core/api/communication/ConversationsListTest.java index 5a004f2450..995503b443 100644 --- a/backend/api/communication/src/test/java/co/airy/core/api/communication/ConversationsListTest.java +++ b/backend/api/communication/src/test/java/co/airy/core/api/communication/ConversationsListTest.java @@ -35,8 +35,11 @@ import static java.util.Comparator.reverseOrder; import static java.util.stream.Collectors.toList; import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.nullValue; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -58,7 +61,7 @@ class ConversationsListTest { private static final Channel defaultChannel = Channel.newBuilder() .setConnectionState(ChannelConnectionState.CONNECTED) .setId(UUID.randomUUID().toString()) - .setSource("facebook") + .setSource("airy") .setSourceChannelId("ps-id") .build(); @@ -108,7 +111,7 @@ void beforeEach() throws Exception { @Test void canFetchAllConversations() throws Exception { retryOnException( - () -> webTestHelper.post("/conversations.list", "{} ") + () -> webTestHelper.post("/conversations.list") .andExpect(status().isOk()) .andExpect(jsonPath("$.data", hasSize(conversations.size()))) .andExpect(jsonPath("pagination_data.total", is(conversations.size()))) @@ -120,6 +123,54 @@ void canFetchAllConversations() throws Exception { String.format("Expected %s conversations in order", conversations.size())); } + @Test + void canFetchPaginated() throws Exception { + final int cursor = conversations.size() + 1; + retryOnException( + () -> webTestHelper.post("/conversations.list", "{\"cursor\": \"" + cursor + "\"}") + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data", hasSize(0))) + .andExpect(jsonPath("$.pagination_data.total", is(conversations.size()))), + "Expected 0 conversations"); + + webTestHelper.post("/conversations.list", "{\"page_size\": 2, \"cursor\": 0}") + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data", hasSize(2))) + .andExpect(jsonPath("$.pagination_data.filtered_total", is(conversations.size()))) + .andExpect(jsonPath("$.pagination_data.total", is(conversations.size()))) + .andExpect(jsonPath("$.pagination_data.previous_cursor", is(not(nullValue())))) + .andExpect(jsonPath("$.pagination_data.next_cursor", equalTo("2"))); + + webTestHelper.post("/conversations.list", "{\"page_size\": 2, \"cursor\": 1}") + .andExpect(status().isOk()) + .andExpect(jsonPath("$.pagination_data.filtered_total", is(conversations.size()))) + .andExpect(jsonPath("$.pagination_data.total", is(5))) + .andExpect(jsonPath("$.data", hasSize(2))); + + webTestHelper.post("/conversations.list", "{\"page_size\": 2, \"cursor\": 3}") + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data", hasSize(2))) + .andExpect(jsonPath("$.pagination_data.next_cursor", is(nullValue()))); + + webTestHelper.post("/conversations.list", "{\"page_size\": 1, \"cursor\": 0}") + .andExpect(status().isOk()) + .andExpect(jsonPath("$.pagination_data.filtered_total", is(conversations.size()))) + .andExpect(jsonPath("$.pagination_data.total", is(conversations.size()))) + .andExpect(jsonPath("$.data", hasSize(1))); + + webTestHelper.post("/conversations.list", "{\"page_size\": 1, \"cursor\": 0, \"filters\": \"display_name:" + firstNameToFind.toLowerCase() + "\"}") + .andExpect(status().isOk()) + .andExpect(jsonPath("$.pagination_data.filtered_total", is(1))) + .andExpect(jsonPath("$.pagination_data.total", is(conversations.size()))) + .andExpect(jsonPath("$.data", hasSize(1))); + + webTestHelper.post("/conversations.list", "{\"page_size\": 10000, \"cursor\": 0, \"filters\": \"display_name:" + firstNameToFind.toLowerCase() + "\"}") + .andExpect(status().isOk()) + .andExpect(jsonPath("$.pagination_data.filtered_total", is(1))) + .andExpect(jsonPath("$.pagination_data.total", is(conversations.size()))) + .andExpect(jsonPath("$.data", hasSize(1))); + } + @Test void canFilterByConversationId() throws Exception { final JsonNodeFactory jsonNodeFactory = JsonNodeFactory.instance; @@ -159,6 +210,11 @@ void canFilterByUnreadMessageCount() throws Exception { checkConversationsFound("{\"filters\": \"unread_count:2\"}", 1); } + @Test + void canFindBySource() throws Exception { + checkConversationsFound("{\"filters\": \"source:" + channelToFind.getSource() + "\"}", 2); + } + @Test void canFilterByCombinedQueries() throws Exception { final JsonNodeFactory jsonNodeFactory = JsonNodeFactory.instance; diff --git a/cli/go.mod b/cli/go.mod index 5cd5f38470..a7a837e4ec 100644 --- a/cli/go.mod +++ b/cli/go.mod @@ -3,6 +3,9 @@ module cli go 1.16 require ( + github.com/Masterminds/goutils v1.1.1 // indirect + github.com/Masterminds/semver v1.5.0 // indirect + github.com/Masterminds/sprig v2.22.0+incompatible // indirect github.com/TwinProduction/go-color v1.0.0 github.com/airyhq/airy/lib/go/httpclient v0.0.0 github.com/aws/aws-sdk-go v1.37.29 @@ -10,8 +13,10 @@ require ( github.com/aws/aws-sdk-go-v2/service/ec2 v1.1.1 github.com/aws/aws-sdk-go-v2/service/eks v1.1.1 github.com/aws/aws-sdk-go-v2/service/iam v1.1.1 + github.com/huandu/xstrings v1.3.2 // indirect github.com/imdario/mergo v0.3.11 // indirect github.com/kr/pretty v0.2.1 + github.com/mitchellh/copystructure v1.1.2 // indirect github.com/mitchellh/go-homedir v1.1.0 github.com/spf13/cast v1.3.1 // indirect github.com/spf13/cobra v1.1.1 diff --git a/cli/go.sum b/cli/go.sum index 11779bd507..36789213e1 100644 --- a/cli/go.sum +++ b/cli/go.sum @@ -25,6 +25,12 @@ github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6L github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= +github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= +github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= +github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= +github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60= +github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= @@ -151,6 +157,7 @@ github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OI github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= @@ -184,6 +191,8 @@ github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0m github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw= +github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA= @@ -220,6 +229,8 @@ github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNx github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/copystructure v1.1.2 h1:Th2TIvG1+6ma3e/0/bopBKohOTY7s4dA8V2q4EUcBJ0= +github.com/mitchellh/copystructure v1.1.2/go.mod h1:EBArHfARyrSWO/+Wyr9zwEkc6XMFB9XyNgFNmRkZZU4= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= @@ -229,6 +240,8 @@ github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0Qu github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/reflectwalk v1.0.1 h1:FVzMWA5RllMAKIdUSC8mdWo3XtwoecrH79BY70sEEpE= +github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= diff --git a/cli/integration/cli_test.go b/cli/integration/cli_test.go index ce6331a1ef..e40c222af3 100644 --- a/cli/integration/cli_test.go +++ b/cli/integration/cli_test.go @@ -14,7 +14,7 @@ func TestCli(t *testing.T) { }() cwdPath, err := filepath.Abs(".") - dir, err := ioutil.TempDir(cwdPath, "config-dir") + dir, err := ioutil.TempDir(cwdPath, "workspace") if err != nil { t.Fatal(err) } diff --git a/cli/integration/golden/cli.no-args.golden b/cli/integration/golden/cli.no-args.golden index fabad7314f..337993f036 100644 --- a/cli/integration/golden/cli.no-args.golden +++ b/cli/integration/golden/cli.no-args.golden @@ -13,8 +13,8 @@ Available Commands: version Prints version information Flags: - --apihost string Airy Core HTTP API endpoint - --config-dir string config directory of an airy core instance (default is the cwd) - -h, --help help for airy + --apihost string Airy Core HTTP API endpoint + -h, --help help for airy + --workspace string workspace directory of an Airy core instance (default is the cwd) Use "airy [command] --help" for more information about a command. diff --git a/cli/pkg/cmd/config/BUILD b/cli/pkg/cmd/config/BUILD index 082b603f97..6f911b65aa 100644 --- a/cli/pkg/cmd/config/BUILD +++ b/cli/pkg/cmd/config/BUILD @@ -3,10 +3,7 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library") go_library( name = "config", - srcs = [ - "config.go", - "parser.go", - ], + srcs = ["config.go"], importpath = "cli/pkg/cmd/config", visibility = ["//visibility:public"], deps = [ @@ -14,7 +11,6 @@ go_library( "//cli/pkg/kube", "//cli/pkg/workspace", "@com_github_spf13_cobra//:cobra", - "@in_gopkg_yaml_v2//:yaml_v2", "@io_k8s_apimachinery//pkg/apis/meta/v1:go_default_library", ], ) diff --git a/cli/pkg/cmd/config/config.go b/cli/pkg/cmd/config/config.go index 6b45bb65c5..b6c85593d8 100644 --- a/cli/pkg/cmd/config/config.go +++ b/cli/pkg/cmd/config/config.go @@ -21,13 +21,13 @@ var ConfigCmd = &cobra.Command{ } func applyConfig(cmd *cobra.Command, args []string) { - cfgDir, err := cmd.Flags().GetString("config-dir") + cfgDir, err := cmd.Flags().GetString("workspace") if err != nil { console.Exit(err) } dir := workspace.Init(cfgDir) - conf, err := parseConf(dir.GetAiryYaml()) + conf, err := dir.LoadAiryYaml() if err != nil { console.Exit("error parsing configuration file: ", err) } @@ -37,7 +37,7 @@ func applyConfig(cmd *cobra.Command, args []string) { console.Exit("could not find an installation of Airy Core. Get started here https://airy.co/docs/core/getting-started/installation/introduction") } - secData := conf.Security.getData() + secData := getSecurityData(conf.Security) if len(secData) != 0 { applyErr := kube.ApplyConfigMap("security", conf.Kubernetes.Namespace, secData, map[string]string{}, clientset) if applyErr != nil { @@ -78,6 +78,29 @@ func applyConfig(cmd *cobra.Command, args []string) { } } + +func getSecurityData(s workspace.SecurityConf) map[string]string { + m := make(map[string]string, len(s.Oidc)) + + if s.SystemToken != "" { + m["systemToken"] = s.SystemToken + } + if s.AllowedOrigins != "" { + m["allowedOrigins"] = s.AllowedOrigins + } + if s.JwtSecret != "" { + m["jwtSecret"] = s.JwtSecret + } + + for key, value := range s.Oidc { + if value != "" { + m["oidc." + key] = value + } + } + + return m +} + var applyConfigCmd = &cobra.Command{ Use: "apply", TraverseChildren: true, diff --git a/cli/pkg/cmd/config/parser.go b/cli/pkg/cmd/config/parser.go deleted file mode 100644 index bd80f92450..0000000000 --- a/cli/pkg/cmd/config/parser.go +++ /dev/null @@ -1,61 +0,0 @@ -package config - -import ( - "io/ioutil" - - "gopkg.in/yaml.v2" -) - -type kubernetesConf struct { - AppImageTag string `yaml:"appImageTag"` - ContainerRegistry string `yaml:"containerRegistry"` - Namespace string `yaml:"namespace"` - NgrokEnabled string `yaml:"ngrokEnabled"` -} - -type componentsConf map[string]map[string]string - -type airyConf struct { - Kubernetes kubernetesConf - Security securityConf - Components map[string]componentsConf -} - -type securityConf struct { - SystemToken string `yaml:"systemToken"` - AllowedOrigins string `yaml:"allowedOrigins"` - JwtSecret string `yaml:"jwtSecret"` - Oidc map[string]string `yaml:"oidc"` -} - -func (s securityConf) getData() map[string]string { - m := make(map[string]string, len(s.Oidc)) - - if s.SystemToken != "" { - m["systemToken"] = s.SystemToken - } - if s.AllowedOrigins != "" { - m["allowedOrigins"] = s.AllowedOrigins - } - if s.JwtSecret != "" { - m["jwtSecret"] = s.JwtSecret - } - - for key, value := range s.Oidc { - if value != "" { - m["oidc." + key] = value - } - } - - return m -} - -func parseConf(configFile string) (airyConf, error) { - data, err := ioutil.ReadFile(configFile) - if err != nil { - return airyConf{}, err - } - conf := airyConf{} - err = yaml.Unmarshal(data, &conf) - return conf, err -} diff --git a/cli/pkg/cmd/create/create.go b/cli/pkg/cmd/create/create.go index e6d2980bd4..1262f4a36b 100644 --- a/cli/pkg/cmd/create/create.go +++ b/cli/pkg/cmd/create/create.go @@ -20,9 +20,9 @@ var ( version string initOnly bool CreateCmd = &cobra.Command{ - Use: "create [config directory]", + Use: "create [workspace directory]", Short: "Creates an instance of Airy Core", - Long: `Creates a config directory (default .) with default configuration and starts an Airy Core instance using the given provider`, + Long: `Creates a workspace directory (default .) with default configuration and starts an Airy Core instance using the given provider`, Args: cobra.MaximumNArgs(1), Run: create, } @@ -32,9 +32,8 @@ func init() { CreateCmd.Flags().StringVar(&providerName, "provider", "minikube", "One of the supported providers (aws|minikube).") CreateCmd.Flags().StringToStringVar(&providerConfig, "provider-config", nil, "Additional configuration for the providers.") CreateCmd.Flags().StringVar(&namespace, "namespace", "default", "(optional) Kubernetes namespace that Airy should be installed to.") - CreateCmd.Flags().BoolVar(&initOnly, "init-only", false, "Only create the airy config directory and exit") + CreateCmd.Flags().BoolVar(&initOnly, "init-only", false, "Only create the airy workspace directory and exit") CreateCmd.MarkFlagRequired("provider") - } func create(cmd *cobra.Command, args []string) { @@ -43,22 +42,23 @@ func create(cmd *cobra.Command, args []string) { cfgDir = args[0] } - dir, err := workspace.Create(cfgDir) + w := console.GetMiddleware(func(input string) string { + return color.Colorize(color.Cyan, "#\t"+input) + }) + provider := providers.MustGet(providers.ProviderName(providerName), w) + overrides := provider.GetOverrides() + overrides.Version = version + overrides.Namespace = namespace + dir, err := workspace.Create(cfgDir, overrides) if err != nil { - console.Exit("could not initialize Airy config directory", err) + console.Exit("could not initialize Airy workspace directory", err) } - fmt.Println("📁 Initialized Airy config directory at", dir.GetPath(".")) + fmt.Println("📁 Initialized Airy workspace directory at", dir.GetPath(".")) if initOnly == true { os.Exit(0) } fmt.Println("⚙️ Creating core with provider", providerName) - - w := console.GetMiddleware(func(input string) string { - return color.Colorize(color.Cyan, "#\t"+input) - }) - provider := providers.MustGet(providers.ProviderName(providerName), w) - fmt.Fprintln(w) fmt.Fprintln(w, providerName, "provider output:") fmt.Fprintln(w) @@ -86,11 +86,11 @@ func create(cmd *cobra.Command, args []string) { fmt.Println("🚀 Starting core with default components") - if err := helm.InstallCharts(provider.GetHelmOverrides()); err != nil { + if err := helm.InstallCharts(); err != nil { console.Exit("installing Helm charts failed with err: ", err) } - if err = provider.PostInstallation(namespace); err != nil { + if err = provider.PostInstallation(dir); err != nil { console.Exit("failed to run post installation hook: ", err) } diff --git a/cli/pkg/cmd/create/helm.go b/cli/pkg/cmd/create/helm.go index f3b8ff911f..99ddddcb6c 100644 --- a/cli/pkg/cmd/create/helm.go +++ b/cli/pkg/cmd/create/helm.go @@ -1,9 +1,11 @@ package create import ( + "bufio" "context" "fmt" "io/ioutil" + "strings" batchv1 "k8s.io/api/batch/v1" corev1 "k8s.io/api/core/v1" @@ -75,13 +77,11 @@ func (h *Helm) Setup() error { return nil } -func (h *Helm) InstallCharts(overrides []string) error { +func (h *Helm) InstallCharts() error { 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...)) + "core", "/apps/helm-chart/"})) } func (h *Helm) runHelm(args []string) error { @@ -176,7 +176,7 @@ func (h *Helm) upsertAiryConfigMap() error { } cmData := map[string]string{ - "airy-config-map.yaml": string(file), + "airy-config-map.yaml": airyYamlToHelmValues(string(file)), } if cm.GetName() != "" { @@ -196,6 +196,18 @@ func (h *Helm) upsertAiryConfigMap() error { return err } +// Transform Airy yaml to make it usable as values +// by moving all data to a "global:" root node +func airyYamlToHelmValues(content string) string { + scanner := bufio.NewScanner(strings.NewReader(content)) + var builder strings.Builder + builder.WriteString("global:\n") + for scanner.Scan() { + builder.WriteString(" " + scanner.Text() + "\n") + } + return builder.String() +} + func (h *Helm) cleanupJob() error { jobsClient := h.clientset.BatchV1().Jobs(h.namespace) diff --git a/cli/pkg/cmd/root.go b/cli/pkg/cmd/root.go index 568faa9c64..c4271c3bb4 100644 --- a/cli/pkg/cmd/root.go +++ b/cli/pkg/cmd/root.go @@ -55,7 +55,7 @@ func init() { viper.BindPFlag("apihost", RootCmd.PersistentFlags().Lookup("apihost")) viper.SetDefault("apihost", "http://airy.core") - RootCmd.PersistentFlags().StringVar(&cliConfigDir, "config-dir", "", "config directory of an airy core instance (default is the cwd)") + RootCmd.PersistentFlags().StringVar(&cliConfigDir, "workspace", "", "workspace directory of an Airy core instance (default is the cwd)") RootCmd.AddCommand(api.APICmd) RootCmd.AddCommand(config.ConfigCmd) RootCmd.AddCommand(status.StatusCmd) diff --git a/cli/pkg/providers/BUILD b/cli/pkg/providers/BUILD index d7811db6ee..12fdc8eabf 100644 --- a/cli/pkg/providers/BUILD +++ b/cli/pkg/providers/BUILD @@ -10,5 +10,6 @@ go_library( "//cli/pkg/providers/aws", "//cli/pkg/providers/minikube", "//cli/pkg/workspace", + "//cli/pkg/workspace/template", ], ) diff --git a/cli/pkg/providers/aws/BUILD b/cli/pkg/providers/aws/BUILD index 10d9d3cd15..0f2e9a043e 100644 --- a/cli/pkg/providers/aws/BUILD +++ b/cli/pkg/providers/aws/BUILD @@ -16,6 +16,7 @@ go_library( "//cli/pkg/console", "//cli/pkg/kube", "//cli/pkg/workspace", + "//cli/pkg/workspace/template", "@com_github_aws_aws_sdk_go//aws", "@com_github_aws_aws_sdk_go_v2_config//:config", "@com_github_aws_aws_sdk_go_v2_service_ec2//:ec2", diff --git a/cli/pkg/providers/aws/aws.go b/cli/pkg/providers/aws/aws.go index 3ea7fd9e54..7e54c39e03 100644 --- a/cli/pkg/providers/aws/aws.go +++ b/cli/pkg/providers/aws/aws.go @@ -4,6 +4,7 @@ import ( "cli/pkg/console" "cli/pkg/kube" "cli/pkg/workspace" + tmpl "cli/pkg/workspace/template" "context" "fmt" "io" @@ -42,11 +43,18 @@ func New(w io.Writer) *provider { } } -func (p *provider) GetHelmOverrides() []string { - return []string{"--set", "global.ngrokEnabled=false", "--set", "global.loadbalancer.annotations={service.beta.kubernetes.io/aws-load-balancer-type: nlb}"} +func (p *provider) GetOverrides() tmpl.Variables { + return tmpl.Variables{ + LoadbalancerAnnotations: map[string]string{"service.beta.kubernetes.io/aws-load-balancer-type": "nlb"}, + } } -func (p *provider) PostInstallation(namespace string) error { +func (p *provider) PostInstallation(dir workspace.ConfigDir) error { + conf, err := dir.LoadAiryYaml() + if err != nil { + return err + } + clientset, err := p.context.GetClientSet() if err != nil { return err @@ -59,18 +67,21 @@ func (p *provider) PostInstallation(namespace string) error { loadBalancerUrl := ingressService.Status.LoadBalancer.Ingress[0].Hostname - if err = p.updateIngress("airy-core", loadBalancerUrl, namespace); err != nil { + if err = p.updateIngress("airy-core", loadBalancerUrl, conf.Kubernetes.Namespace); err != nil { return err } - if err = p.updateIngress("airy-core-ui", loadBalancerUrl, namespace); err != nil { + if err = p.updateIngress("airy-core-ui", loadBalancerUrl, conf.Kubernetes.Namespace); err != nil { return err } - if err = p.updateHostsConfigMap(loadBalancerUrl, namespace); err != nil { + if err = p.updateHostsConfigMap(loadBalancerUrl, conf.Kubernetes.Namespace); err != nil { return err } - return nil + return dir.UpdateAiryYaml(func(conf workspace.AiryConf) workspace.AiryConf { + conf.Kubernetes.Host = loadBalancerUrl + return conf + }) } type KubeConfig struct { @@ -85,7 +96,6 @@ func (p *provider) Provision(providerConfig map[string]string, dir workspace.Con console.Exit(err) } - specifiedVpcId, _ := providerConfig["vpcId"] id := RandString(8) name := "Airy-" + id fmt.Fprintf(p.w, "Creating Airy Core instance with id: %s. This might take a while.\n", name) @@ -105,29 +115,33 @@ func (p *provider) Provision(providerConfig map[string]string, dir workspace.Con p.ec2Client = ec2.NewFromConfig(cfg) - var VpcId *string var subnetIds []string + instanceType := providerConfig["instanceType"] + if instanceType == "" { + instanceType = "c5.xlarge" + } - if specifiedVpcId == "" { + vpcId := providerConfig["vpcId"] + if vpcId == "" { vpc, err := p.createVpc("192.168.0.0/16", name) if err != nil { console.Exit("Error creating vpc: ", err) } - VpcId = vpc.VpcId - fmt.Fprintf(p.w, "VPC created with id: %s.\n", *VpcId) + vpcId = *vpc.VpcId + fmt.Fprintf(p.w, "VPC created with id: %s.\n", vpcId) fmt.Fprintf(p.w, "Enabling DNS on VPC...\n") - if err = p.enableDNSOnVpc(VpcId); err != nil { + if err = p.enableDNSOnVpc(&vpcId); err != nil { console.Exit("Error enabling DNS on VPC.", err) } fmt.Fprintf(p.w, "Creating Internet Gateway...\n") - internetGateway, err := p.createInternetGateway(VpcId) + internetGateway, err := p.createInternetGateway(&vpcId) if err != nil { console.Exit("Could not create internet gateway: ", err) } fmt.Fprintf(p.w, "Creating route table...\n") - routeTable, err := p.createRoute(VpcId, name, internetGateway) + routeTable, err := p.createRoute(&vpcId, name, internetGateway) if err != nil { console.Exit("Error creating route table: ", err) } @@ -137,13 +151,13 @@ func (p *provider) Provision(providerConfig map[string]string, dir workspace.Con console.Exit("Unable to get availability zones. Make sure you have set the ENV variable AWS_REGION") } fmt.Fprintf(p.w, "Creating first subnet...\n") - firstSubnet, err := p.createSubnet(VpcId, name, "192.168.64.0/18", *availabilityZones.AvailabilityZones[0].ZoneName) + firstSubnet, err := p.createSubnet(&vpcId, name, "192.168.64.0/18", *availabilityZones.AvailabilityZones[0].ZoneName) if err != nil { console.Exit("Error creating subnet: ", err) } fmt.Fprintf(p.w, "Creating second subnet\n") - secondSubnet, err := p.createSubnet(VpcId, name, "192.168.128.0/18", *availabilityZones.AvailabilityZones[1].ZoneName) + secondSubnet, err := p.createSubnet(&vpcId, name, "192.168.128.0/18", *availabilityZones.AvailabilityZones[1].ZoneName) if err != nil { console.Exit("Error creating subnet: ", err) } @@ -171,9 +185,8 @@ func (p *provider) Provision(providerConfig map[string]string, dir workspace.Con subnetIds = append(subnetIds, *firstSubnet.SubnetId) subnetIds = append(subnetIds, *secondSubnet.SubnetId) } else { - VpcId = &specifiedVpcId - fmt.Fprintf(p.w, "Using existing VPC: %s.\n", *VpcId) - subnets, subnetErr := p.getSubnets(*VpcId) + fmt.Fprintf(p.w, "Using existing VPC: %s.\n", vpcId) + subnets, subnetErr := p.getSubnets(vpcId) if subnetErr != nil { console.Exit("Unable to get subnets from VPC", subnetErr) } @@ -204,7 +217,7 @@ func (p *provider) Provision(providerConfig map[string]string, dir workspace.Con return describeClusterResult.Cluster.Status == "ACTIVE" }) - nodeGroup, err := p.createNodeGroup(name, role.Arn, subnetIds) + nodeGroup, err := p.createNodeGroup(name, role.Arn, subnetIds, instanceType) if err != nil { console.Exit("Error creating node group: ", err) } @@ -444,12 +457,12 @@ func (p *provider) createCluster(name string, roleArn *string, subnetIds []strin } -func (p *provider) createNodeGroup(name string, roleArn *string, subnetIds []string) (*eksTypes.Nodegroup, error) { +func (p *provider) createNodeGroup(name string, roleArn *string, subnetIds []string, instanceType string) (*eksTypes.Nodegroup, error) { tagKey := "kubernetes.io/cluster/" + name createdNodeGroup, err := p.eksClient.CreateNodegroup(context.TODO(), &eks.CreateNodegroupInput{ AmiType: "AL2_x86_64", ClusterName: aws.String(name), - InstanceTypes: []string{"c5.xlarge"}, + InstanceTypes: []string{instanceType}, NodeRole: roleArn, NodegroupName: aws.String(name), Subnets: subnetIds, diff --git a/cli/pkg/providers/minikube/BUILD b/cli/pkg/providers/minikube/BUILD index 1d4d11f6b5..d01045276f 100644 --- a/cli/pkg/providers/minikube/BUILD +++ b/cli/pkg/providers/minikube/BUILD @@ -11,6 +11,7 @@ go_library( deps = [ "//cli/pkg/kube", "//cli/pkg/workspace", + "//cli/pkg/workspace/template", "@com_github_txn2_txeh//:txeh", "@io_k8s_apimachinery//pkg/apis/meta/v1:go_default_library", "@io_k8s_client_go//util/homedir:go_default_library", diff --git a/cli/pkg/providers/minikube/minikube.go b/cli/pkg/providers/minikube/minikube.go index 24b670aa18..7dd817c40e 100644 --- a/cli/pkg/providers/minikube/minikube.go +++ b/cli/pkg/providers/minikube/minikube.go @@ -3,6 +3,7 @@ package minikube import ( "cli/pkg/kube" "cli/pkg/workspace" + "cli/pkg/workspace/template" "context" "fmt" "io" @@ -31,8 +32,11 @@ func New(w io.Writer) *provider { } } -func (p *provider) GetHelmOverrides() []string { - return []string{"--set", "global.ngrokEnabled=true", "--set", "global.nodePort=80"} +func (p *provider) GetOverrides() template.Variables { + return template.Variables{ + NgrokEnabled: true, + Host: "http://airy.core", + } } func (p *provider) Provision(providerConfig map[string]string, dir workspace.ConfigDir) (kube.KubeCtx, error) { @@ -87,13 +91,18 @@ func getCmd(args ...string) *exec.Cmd { return exec.Command(minikube, append(defaultArgs, args...)...) } -func (p *provider) PostInstallation(namespace string) error { +func (p *provider) PostInstallation(dir workspace.ConfigDir) error { + conf, err := dir.LoadAiryYaml() + if err != nil { + return err + } + clientset, err := p.context.GetClientSet() if err != nil { return err } - configMaps := clientset.CoreV1().ConfigMaps(namespace) + configMaps := clientset.CoreV1().ConfigMaps(conf.Kubernetes.Namespace) configMap, err := configMaps.Get(context.TODO(), "hostnames", metav1.GetOptions{}) if err != nil { return err diff --git a/cli/pkg/providers/provider.go b/cli/pkg/providers/provider.go index 183974c8ba..2a49805d12 100644 --- a/cli/pkg/providers/provider.go +++ b/cli/pkg/providers/provider.go @@ -5,6 +5,7 @@ import ( "cli/pkg/providers/aws" "cli/pkg/providers/minikube" "cli/pkg/workspace" + "cli/pkg/workspace/template" "fmt" "io" ) @@ -18,8 +19,8 @@ const ( type Provider interface { Provision(providerConfig map[string]string, dir workspace.ConfigDir) (kube.KubeCtx, error) - GetHelmOverrides() []string - PostInstallation(namespace string) error + GetOverrides() template.Variables + PostInstallation(dir workspace.ConfigDir) error } func MustGet(providerName ProviderName, w io.Writer) Provider { diff --git a/cli/pkg/workspace/BUILD b/cli/pkg/workspace/BUILD index be08e1f7db..bf16ea73f2 100644 --- a/cli/pkg/workspace/BUILD +++ b/cli/pkg/workspace/BUILD @@ -3,11 +3,15 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library") go_library( name = "workspace", srcs = [ + "airy_yaml.go", "files.go", "init.go", ], - embedsrcs = ["template/airy.yaml"], importpath = "cli/pkg/workspace", visibility = ["//visibility:public"], - deps = ["@com_github_spf13_viper//:viper"], + deps = [ + "//cli/pkg/workspace/template", + "@com_github_spf13_viper//:viper", + "@in_gopkg_yaml_v2//:yaml_v2", + ], ) diff --git a/cli/pkg/workspace/airy_yaml.go b/cli/pkg/workspace/airy_yaml.go new file mode 100644 index 0000000000..13e2451fea --- /dev/null +++ b/cli/pkg/workspace/airy_yaml.go @@ -0,0 +1,25 @@ +package workspace + +type KubernetesConf struct { + AppImageTag string `yaml:"appImageTag"` + ContainerRegistry string `yaml:"containerRegistry"` + Namespace string `yaml:"namespace"` + NgrokEnabled string `yaml:"ngrokEnabled"` + Host string `yaml:"host"` + LoadbalancerAnnotations map[string]string `yaml:"loadbalancerAnnotations,omitempty"` +} + +type componentsConf map[string]map[string]string + +type AiryConf struct { + Kubernetes KubernetesConf `yaml:"kubernetes"` + Security SecurityConf `yaml:"security"` + Components map[string]componentsConf `yaml:"components,omitempty"` +} + +type SecurityConf struct { + SystemToken string `yaml:"systemToken,omitempty"` + AllowedOrigins string `yaml:"allowedOrigins"` + JwtSecret string `yaml:"jwtSecret"` + Oidc map[string]string `yaml:"oidc,omitempty"` +} diff --git a/cli/pkg/workspace/files.go b/cli/pkg/workspace/files.go index 5842879744..ab12675482 100644 --- a/cli/pkg/workspace/files.go +++ b/cli/pkg/workspace/files.go @@ -1,15 +1,13 @@ package workspace import ( - "embed" + "gopkg.in/yaml.v2" + "io/ioutil" "path/filepath" ) const cliConfigFileName = "cli.yaml" -//go:embed template -var templateDir embed.FS - type ConfigDir struct { Path string } @@ -18,6 +16,30 @@ func (f ConfigDir) GetAiryYaml() string { return filepath.Join(f.Path, "airy.yaml") } +func (f ConfigDir) LoadAiryYaml() (AiryConf, error) { + data, err := ioutil.ReadFile(f.GetAiryYaml()) + if err != nil { + return AiryConf{}, err + } + conf := AiryConf{} + err = yaml.Unmarshal(data, &conf) + return conf, err +} + +func (f ConfigDir) UpdateAiryYaml(apply func(AiryConf) AiryConf) error { + airyYaml, err := f.LoadAiryYaml() + if err != nil { + return err + } + airyYaml = apply(airyYaml) + out, err := yaml.Marshal(airyYaml) + if err != nil { + return err + } + + return ioutil.WriteFile(f.GetAiryYaml(), out, 0644) +} + func (f ConfigDir) GetPath(fileName string) string { return filepath.Join(f.Path, fileName) } diff --git a/cli/pkg/workspace/init.go b/cli/pkg/workspace/init.go index efbe46357e..f22ff1a8dd 100644 --- a/cli/pkg/workspace/init.go +++ b/cli/pkg/workspace/init.go @@ -1,10 +1,9 @@ package workspace import ( + "cli/pkg/workspace/template" "fmt" "github.com/spf13/viper" - "io/fs" - "io/ioutil" "os" "path/filepath" ) @@ -17,7 +16,7 @@ func Init(path string) ConfigDir { if err := viper.ReadInConfig(); err != nil { if _, ok := err.(viper.ConfigFileNotFoundError); ok { fmt.Println(err) - fmt.Println("the current directory is not an airy config directory") + fmt.Println("the current directory is not an airy workspace directory") } else { fmt.Println("invalid configuration: ", err) } @@ -28,7 +27,7 @@ func Init(path string) ConfigDir { dir := ConfigDir{Path: path} if _, err := os.Stat(dir.GetAiryYaml()); os.IsNotExist(err) { - fmt.Println("the current directory is not an airy config directory") + fmt.Println("the current directory is not an airy workspace directory") os.Exit(1) } return dir @@ -49,7 +48,7 @@ func getConfigPath(path string) string { return path } -func Create(path string) (ConfigDir, error) { +func Create(path string, data template.Variables) (ConfigDir, error) { path = getConfigPath(path) if _, err := os.Stat(path); os.IsNotExist(err) { err = os.MkdirAll(path, 0755) @@ -58,15 +57,9 @@ func Create(path string) (ConfigDir, error) { } } - entries, err := templateDir.ReadDir("template") - if err != nil { - return ConfigDir{}, err - } - for _, entry := range entries { - if err := recCopy(path, "template", entry); err != nil { - return ConfigDir{}, err - } + if err := template.CopyToDir(path, data); err != nil { + return ConfigDir{}, err } viper.AddConfigPath(getConfigPath(path)) @@ -74,37 +67,6 @@ func Create(path string) (ConfigDir, error) { viper.SetConfigName(cliConfigFileName) // Init viper config - err = viper.WriteConfigAs(filepath.Join(path, cliConfigFileName)) + err := viper.WriteConfigAs(filepath.Join(path, cliConfigFileName)) return ConfigDir{Path: path}, err } - -func recCopy(writePath string, templatePath string, entry fs.DirEntry) error { - dstPath := filepath.Join(writePath, entry.Name()) - templatePath = filepath.Join(templatePath, entry.Name()) - - if !entry.IsDir() { - content, err := templateDir.ReadFile(templatePath) - if err != nil { - return err - } - - return ioutil.WriteFile(dstPath, content, 0700) - } - - if err := os.MkdirAll(dstPath, 0700); err != nil { - return err - } - - entries, err := templateDir.ReadDir(templatePath) - if err != nil { - return err - } - - for _, entry := range entries { - if err := recCopy(dstPath, templatePath, entry); err != nil { - return err - } - } - - return nil -} diff --git a/cli/pkg/workspace/template/BUILD b/cli/pkg/workspace/template/BUILD new file mode 100644 index 0000000000..dd5c410678 --- /dev/null +++ b/cli/pkg/workspace/template/BUILD @@ -0,0 +1,10 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "template", + srcs = ["copy.go"], + embedsrcs = ["src/airy.yaml"], + importpath = "cli/pkg/workspace/template", + visibility = ["//visibility:public"], + deps = ["@com_github_masterminds_sprig//:sprig"], +) diff --git a/cli/pkg/workspace/template/airy.yaml b/cli/pkg/workspace/template/airy.yaml deleted file mode 100644 index 67a9a0c061..0000000000 --- a/cli/pkg/workspace/template/airy.yaml +++ /dev/null @@ -1,6 +0,0 @@ -kubernetes: - containerRegistry: ghcr.io/airyhq - namespace: default - ngrokEnabled: false -security: - allowedOrigins: "*" diff --git a/cli/pkg/workspace/template/copy.go b/cli/pkg/workspace/template/copy.go new file mode 100644 index 0000000000..350e98d47c --- /dev/null +++ b/cli/pkg/workspace/template/copy.go @@ -0,0 +1,80 @@ +package template + +import ( + "embed" + "github.com/Masterminds/sprig" + "html/template" + "io/fs" + "os" + "path/filepath" +) + +type Variables struct { + NgrokEnabled bool + Version string + Namespace string + Host string + LoadbalancerAnnotations map[string]string +} + +//go:embed src +var templateDir embed.FS + +func CopyToDir(path string, data Variables) error { + entries, err := templateDir.ReadDir("src") + if err != nil { + return err + } + + for _, entry := range entries { + if err := recCopy(path, "src", entry, data); err != nil { + return err + } + } + return nil +} + +// Recursively copies and templates entries to the workspace +func recCopy(writePath string, templatePath string, entry fs.DirEntry, data Variables) error { + dstPath := filepath.Join(writePath, entry.Name()) + templatePath = filepath.Join(templatePath, entry.Name()) + + if !entry.IsDir() { + content, err := templateDir.ReadFile(templatePath) + if err != nil { + return err + } + if _, err := os.Stat(dstPath); err == nil { + // User already has provided this file so we don't overwrite it + return nil + } + + tmpl, err := template.New(entry.Name()).Funcs(sprig.FuncMap()).Parse(string(content)) + if err != nil { + return err + } + + f, err := os.Create(dstPath) + if err != nil { + return err + } + + return tmpl.Execute(f, data) + } + + if err := os.MkdirAll(dstPath, 0700); err != nil { + return err + } + entries, err := templateDir.ReadDir(templatePath) + if err != nil { + return err + } + + for _, entry := range entries { + if err := recCopy(dstPath, templatePath, entry, data); err != nil { + return err + } + } + + return nil +} diff --git a/cli/pkg/workspace/template/src/airy.yaml b/cli/pkg/workspace/template/src/airy.yaml new file mode 100644 index 0000000000..471f484f1e --- /dev/null +++ b/cli/pkg/workspace/template/src/airy.yaml @@ -0,0 +1,17 @@ +kubernetes: + appImageTag: {{ .Version }} + containerRegistry: ghcr.io/airyhq + namespace: {{ .Namespace }} + ngrokEnabled: {{ default "false" .NgrokEnabled }} +{{- if .Host }} + host: {{ default "http://airy.core" .Host }} +{{- end }} +{{- if .LoadbalancerAnnotations }} + loadbalancerAnnotations: + {{- range $k, $v := .LoadbalancerAnnotations }} + {{ $k }}: {{ $v }} + {{- end }} +{{- end }} +security: + allowedOrigins: "*" + jwtSecret: {{ randAlphaNum 128 }} diff --git a/docs/docs/api/authentication.md b/docs/docs/api/authentication.md index 4e3ef13245..fb9f60299e 100644 --- a/docs/docs/api/authentication.md +++ b/docs/docs/api/authentication.md @@ -54,6 +54,7 @@ security: provider: "my-provider" clientId: "client-id" clientSecret: "client-secret" + logoutSuccessUrl: "http://example.org/login" # Optional url to redirect to when logged out scope: "openid,email" # comma separated list of scopes. Must include "openid" clientAuthenticationMethod: "basic" # One of [basic,post,none] authorizationGrantType: "authorization_code" # One of [authorization_code,implicit,refresh_token,client_credentials,password] diff --git a/docs/docs/api/endpoints/conversations.md b/docs/docs/api/endpoints/conversations.md index eebfc6d56c..ed66fe79b0 100644 --- a/docs/docs/api/endpoints/conversations.md +++ b/docs/docs/api/endpoints/conversations.md @@ -197,12 +197,14 @@ tag](/api/endpoints/tags.md#create). Returns status code `200` if successful. ```json5 { "conversation_id": "CONVERSATION_ID", - "state": "open" + "state": "OPEN" } ``` **Empty response (204)** +Supported states of a conversation are `OPEN` and `CLOSED`. + ## Remove the state of a conversation `POST /conversations.removeState` diff --git a/docs/docs/changelog.md b/docs/docs/changelog.md index 64ece34d5b..179f4f4576 100644 --- a/docs/docs/changelog.md +++ b/docs/docs/changelog.md @@ -3,6 +3,80 @@ title: Changelog sidebar_label: 📝 Changelog --- +## 0.23.0 + +#### 🚀 Features + +- [[#1815](https://github.com/airyhq/airy/issues/1815)] Added emptyState for filtered items [[#1874](https://github.com/airyhq/airy/pull/1874)] +- [[#1773](https://github.com/airyhq/airy/issues/1773)] Filter conversations by source [[#1864](https://github.com/airyhq/airy/pull/1864)] +- [[#1850](https://github.com/airyhq/airy/issues/1850)] Added npm chatplugin library [[#1857](https://github.com/airyhq/airy/pull/1857)] +- [[#1823](https://github.com/airyhq/airy/issues/1823)] Improve login/logout behavior [[#1840](https://github.com/airyhq/airy/pull/1840)] +- [[#1616](https://github.com/airyhq/airy/issues/1616)] Customize instance type for AWS [[#1833](https://github.com/airyhq/airy/pull/1833)] +- [[#1107](https://github.com/airyhq/airy/issues/1107)] Add demo gif docs [[#1851](https://github.com/airyhq/airy/pull/1851)] +- [[#1650](https://github.com/airyhq/airy/issues/1650)] Message pagination [[#1651](https://github.com/airyhq/airy/pull/1651)] +- [[#1693](https://github.com/airyhq/airy/issues/1693)] Move defaults to airy yaml [[#1700](https://github.com/airyhq/airy/pull/1700)] + +#### 🐛 Bug Fixes + +- [[#1732](https://github.com/airyhq/airy/issues/1732)] Fix display name search typo [[#1873](https://github.com/airyhq/airy/pull/1873)] +- [[#1863](https://github.com/airyhq/airy/issues/1863)] Added SourceMessagePreview [[#1867](https://github.com/airyhq/airy/pull/1867)] +- [[#1733](https://github.com/airyhq/airy/issues/1733)] Fix cypress tests [[#1876](https://github.com/airyhq/airy/pull/1876)] +- [[#1865](https://github.com/airyhq/airy/issues/1865)] Fix chat plugin website installation url [[#1866](https://github.com/airyhq/airy/pull/1866)] +- [[#1808](https://github.com/airyhq/airy/issues/1808)] Improved lastMessageSent [[#1821](https://github.com/airyhq/airy/pull/1821)] +- [[#1837](https://github.com/airyhq/airy/issues/1837)] Improve facebook render library follow-up [[#1839](https://github.com/airyhq/airy/pull/1839)] +- [[#1820](https://github.com/airyhq/airy/issues/1820)] Sending messages UX improvement: focus on input and send on Enter [[#1855](https://github.com/airyhq/airy/pull/1855)] +- [[#1838](https://github.com/airyhq/airy/issues/1838)] Add call to pagination when filtering conversations [[#1852](https://github.com/airyhq/airy/pull/1852)] +- [[#1843](https://github.com/airyhq/airy/issues/1843)] Fix Airy core reachability followup [[#1836](https://github.com/airyhq/airy/pull/1836)] +- [[#1809](https://github.com/airyhq/airy/issues/1809)] Channels page breaks when there are too many channels [[#1822](https://github.com/airyhq/airy/pull/1822)] +- [[#1798](https://github.com/airyhq/airy/issues/1798)] Fix not rendered content messages in facebook render library [[#1818](https://github.com/airyhq/airy/pull/1818)] +- [[#1611](https://github.com/airyhq/airy/issues/1611)] Aligned svg files [[#1810](https://github.com/airyhq/airy/pull/1810)] +- [[#1843](https://github.com/airyhq/airy/issues/1843)] Fix Airy core reachability [[#1835](https://github.com/airyhq/airy/pull/1835)] +- [[#1342](https://github.com/airyhq/airy/issues/1342)] Fix dependabot failing PRs [[#1831](https://github.com/airyhq/airy/pull/1831)] +- [[#1599](https://github.com/airyhq/airy/issues/1599)] Add health status to components endpoint [[#1799](https://github.com/airyhq/airy/pull/1799)] + +#### 📚 Documentation + +- [[#1853](https://github.com/airyhq/airy/issues/1853)] minor grammar edits sources + UI [[#1858](https://github.com/airyhq/airy/pull/1858)] +- [[#1848](https://github.com/airyhq/airy/issues/1848)] Fix getting started-introduction [[#1849](https://github.com/airyhq/airy/pull/1849)] +- [[#1813](https://github.com/airyhq/airy/issues/1813)] Update docs on conersations states [[#1814](https://github.com/airyhq/airy/pull/1814)] + +#### 🧰 Maintenance + +- Bump @typescript-eslint/eslint-plugin from 4.25.0 to 4.26.0 [[#1881](https://github.com/airyhq/airy/pull/1881)] +- Bump @typescript-eslint/parser from 4.25.0 to 4.26.0 [[#1882](https://github.com/airyhq/airy/pull/1882)] +- Bump @babel/preset-env from 7.14.2 to 7.14.4 [[#1869](https://github.com/airyhq/airy/pull/1869)] +- Bump core-js from 3.13.0 to 3.13.1 [[#1872](https://github.com/airyhq/airy/pull/1872)] +- Bump eslint-plugin-react from 7.23.2 to 7.24.0 [[#1870](https://github.com/airyhq/airy/pull/1870)] +- Bump webpack from 5.37.1 to 5.38.1 [[#1862](https://github.com/airyhq/airy/pull/1862)] +- Bump dns-packet from 1.3.1 to 1.3.4 [[#1860](https://github.com/airyhq/airy/pull/1860)] +- Bump @bazel/typescript from 3.5.0 to 3.5.1 [[#1846](https://github.com/airyhq/airy/pull/1846)] +- Bump dns-packet from 1.3.1 to 1.3.4 in /docs [[#1861](https://github.com/airyhq/airy/pull/1861)] +- Bump @typescript-eslint/eslint-plugin from 4.24.0 to 4.25.0 [[#1841](https://github.com/airyhq/airy/pull/1841)] +- Bump css-loader from 5.2.5 to 5.2.6 [[#1842](https://github.com/airyhq/airy/pull/1842)] +- Bump @typescript-eslint/parser from 4.24.0 to 4.25.0 [[#1843](https://github.com/airyhq/airy/pull/1843)] +- Bump core-js from 3.12.1 to 3.13.0 [[#1847](https://github.com/airyhq/airy/pull/1847)] +- Bump cypress from 7.3.0 to 7.4.0 [[#1844](https://github.com/airyhq/airy/pull/1844)] +- Bump @types/react from 17.0.6 to 17.0.8 [[#1845](https://github.com/airyhq/airy/pull/1845)] +- Bump @bazel/bazelisk from 1.8.1 to 1.9.0 [[#1824](https://github.com/airyhq/airy/pull/1824)] +- Bump sass from 1.33.0 to 1.34.0 [[#1825](https://github.com/airyhq/airy/pull/1825)] +- Bump eslint from 7.26.0 to 7.27.0 [[#1826](https://github.com/airyhq/airy/pull/1826)] +- Bump browserslist from 4.16.3 to 4.16.6 [[#1830](https://github.com/airyhq/airy/pull/1830)] +- Bump @types/node from 15.3.1 to 15.6.1 [[#1829](https://github.com/airyhq/airy/pull/1829)] +- Bump webpack from 5.37.0 to 5.37.1 [[#1812](https://github.com/airyhq/airy/pull/1812)] +- Bump sass from 1.32.13 to 1.33.0 [[#1817](https://github.com/airyhq/airy/pull/1817)] +- Bump css-loader from 5.2.4 to 5.2.5 [[#1816](https://github.com/airyhq/airy/pull/1816)] +- Bump @types/react from 17.0.5 to 17.0.6 [[#1807](https://github.com/airyhq/airy/pull/1807)] +- Bump copy-webpack-plugin from 8.1.1 to 9.0.0 [[#1827](https://github.com/airyhq/airy/pull/1827)] +- Bump @types/node from 15.3.0 to 15.3.1 [[#1811](https://github.com/airyhq/airy/pull/1811)] + +#### 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.23.0/darwin/amd64/airy) +[Linux](https://airy-core-binaries.s3.amazonaws.com/0.23.0/linux/amd64/airy) +[Windows](https://airy-core-binaries.s3.amazonaws.com/0.23.0/windows/amd64/airy.exe) + ## 0.22.0 #### 🚀 Features diff --git a/docs/docs/cli/usage.md b/docs/docs/cli/usage.md index d835e4d73b..1e77ee245f 100644 --- a/docs/docs/cli/usage.md +++ b/docs/docs/cli/usage.md @@ -24,8 +24,8 @@ airy api endpoint [flags] #### Options inherited from parent commands ``` - --apihost string Airy Core HTTP API endpoint - --config-dir string config directory of an airy core instance (default is the cwd) + --apihost string Airy Core HTTP API endpoint + --workspace string workspace directory of an Airy core instance (default is the cwd) ``` @@ -52,8 +52,8 @@ airy config apply [flags] #### Options inherited from parent commands ``` - --apihost string Airy Core HTTP API endpoint - --config-dir string config directory of an airy core instance (default is the cwd) + --apihost string Airy Core HTTP API endpoint + --workspace string workspace directory of an Airy core instance (default is the cwd) ``` @@ -65,17 +65,17 @@ Creates an instance of Airy Core #### Synopsis -Creates a config directory (default .) with default configuration and starts an Airy Core instance using the given provider +Creates a workspace directory (default .) with default configuration and starts an Airy Core instance using the given provider ``` -airy create [config directory] [flags] +airy create [workspace directory] [flags] ``` #### Options ``` -h, --help help for create - --init-only Only create the airy config directory and exit + --init-only Only create the airy workspace directory and exit --namespace string (optional) Kubernetes namespace that Airy should be installed to. (default "default") --provider string One of the supported providers (aws|minikube). (default "minikube") --provider-config stringToString Additional configuration for the providers. (default []) @@ -84,8 +84,8 @@ airy create [config directory] [flags] #### Options inherited from parent commands ``` - --apihost string Airy Core HTTP API endpoint - --config-dir string config directory of an airy core instance (default is the cwd) + --apihost string Airy Core HTTP API endpoint + --workspace string workspace directory of an Airy core instance (default is the cwd) ``` @@ -108,8 +108,8 @@ airy status [flags] #### Options inherited from parent commands ``` - --apihost string Airy Core HTTP API endpoint - --config-dir string config directory of an airy core instance (default is the cwd) + --apihost string Airy Core HTTP API endpoint + --workspace string workspace directory of an Airy core instance (default is the cwd) ``` @@ -132,8 +132,8 @@ airy ui [flags] #### Options inherited from parent commands ``` - --apihost string Airy Core HTTP API endpoint - --config-dir string config directory of an airy core instance (default is the cwd) + --apihost string Airy Core HTTP API endpoint + --workspace string workspace directory of an Airy core instance (default is the cwd) ``` @@ -156,8 +156,8 @@ airy version [flags] #### Options inherited from parent commands ``` - --apihost string Airy Core HTTP API endpoint - --config-dir string config directory of an airy core instance (default is the cwd) + --apihost string Airy Core HTTP API endpoint + --workspace string workspace directory of an Airy core instance (default is the cwd) ``` diff --git a/docs/docs/getting-started/components.md b/docs/docs/getting-started/components.md index 1cd60eeacc..357eed9e60 100644 --- a/docs/docs/getting-started/components.md +++ b/docs/docs/getting-started/components.md @@ -16,7 +16,7 @@ import TLDR from "@site/src/components/TLDR"; -Airy Core comes with all the components you need for a fully-featured conversational platform +Airy Core comes with all the components you need for a fully-featured conversational platform. diff --git a/docs/docs/getting-started/installation/aws.md b/docs/docs/getting-started/installation/aws.md index 6a487327a5..a9ac32ead7 100644 --- a/docs/docs/getting-started/installation/aws.md +++ b/docs/docs/getting-started/installation/aws.md @@ -85,11 +85,18 @@ You can also use an existing VPC, without creating additional VPC resources: airy create --provider aws --provider-config vpcId=myExistingVpcId ``` +By default the command creates an AWS NodeGroup with two `c5.xlarge` instances. +For customizing the instance type run: + +```bash +airy create --provider aws --provider-config instanceType=c5.large +``` + This will execute the following actions: 1. Create the `my-airy` directory and populate it with the configuration that the CLI will need. All subsequent commands need to either be run from this - directory or use the `--config-dir` flag. + directory or use the `--workspace` flag. 2. Start an Airy Core cluster in your AWS account. 3. Print URLs for accessing the UIs and APIs (see recording). diff --git a/docs/docs/getting-started/installation/configuration.md b/docs/docs/getting-started/installation/configuration.md index 5de7bf1972..6f28c718bb 100644 --- a/docs/docs/getting-started/installation/configuration.md +++ b/docs/docs/getting-started/installation/configuration.md @@ -15,7 +15,7 @@ The configuration workflow is as simple as: ```sh $EDITOR /path/to/config/directory/airy.yaml # edit your airy.yaml file -airy config apply --config-dir /path/to/config/directory/ # apply your config +airy config apply --workspace /path/to/config/directory/ # apply your config ``` Your Airy Core instance will start and stop components accordingly to your @@ -101,5 +101,5 @@ use the [airy config apply](/cli/usage.md#config-apply) by running the following [Airy CLI](/cli/introduction.md) command. ```bash -airy config apply --config-dir /path/to/config/directory/ +airy config apply --workspace /path/to/config/directory/ ``` diff --git a/docs/docs/getting-started/installation/minikube.md b/docs/docs/getting-started/installation/minikube.md index b33a2ebad9..efcd796778 100644 --- a/docs/docs/getting-started/installation/minikube.md +++ b/docs/docs/getting-started/installation/minikube.md @@ -25,7 +25,7 @@ airy create --provider=minikube my-airy This will execute the following actions: -1. Create the `my-airy` directory and populate it with the configuration that the CLI will need. All subsequent commands need to either be run from this directory or use the `--config-dir` flag. +1. Create the `my-airy` directory and populate it with the configuration that the CLI will need. All subsequent commands need to either be run from this directory or use the `--workspace` flag. 2. Start a Minikube cluster on your system and install Airy Core on it. 3. Print URLs for accessing the UIs and APIs (see recording). diff --git a/docs/docs/getting-started/introduction.md b/docs/docs/getting-started/introduction.md index 6e5f94671d..d1df38e422 100644 --- a/docs/docs/getting-started/introduction.md +++ b/docs/docs/getting-started/introduction.md @@ -19,7 +19,7 @@ import AiryBubbleSVG from "@site/static/icons/airy-bubble.svg"; -Airy Core is an **open source**, **fully-featured**, **production-ready** +Airy Core is an **open-source**, **fully-featured**, **production-ready** conversational platform. diff --git a/docs/docs/sources/chatplugin/demo.md b/docs/docs/sources/chatplugin/demo.md index 92e4770e18..30f1662fa3 100644 --- a/docs/docs/sources/chatplugin/demo.md +++ b/docs/docs/sources/chatplugin/demo.md @@ -3,4 +3,6 @@ title: Demo sidebar_label: Demo --- -TODO +import useBaseUrl from '@docusaurus/useBaseUrl'; + +Rich Card Example diff --git a/docs/docs/sources/chatplugin/installation.md b/docs/docs/sources/chatplugin/installation.md index a69d1fffd6..7bb80ef766 100644 --- a/docs/docs/sources/chatplugin/installation.md +++ b/docs/docs/sources/chatplugin/installation.md @@ -21,7 +21,7 @@ the `` section: ``` -You must replace `CHANNEL_ID` with the channel id obtained when +You must replace `CHANNEL_ID` with the channel ID obtained when [connecting](#connecting-a-channel) the source and `SCRIPT_HOST` with the host of your Chat Plugin server. When using the local minikube environment `SCRIPT_HOST` must be set to `airy.core`. @@ -33,5 +33,5 @@ of your Chat Plugin server. When using the local minikube environment ::: To test the setup, replace the `CHANNEL_ID` in the URL -`http://airy.core/chatplugin/example?channel_id=CHANNEL_ID` and open it in your +`http://airy.core/chatplugin/ui/example?channel_id=CHANNEL_ID` and open it in your browser. diff --git a/docs/docs/sources/chatplugin/overview.md b/docs/docs/sources/chatplugin/overview.md index e943790264..402ba6a492 100644 --- a/docs/docs/sources/chatplugin/overview.md +++ b/docs/docs/sources/chatplugin/overview.md @@ -12,7 +12,7 @@ import useBaseUrl from '@docusaurus/useBaseUrl'; Airy's Live Chat Plugin is an open-source chat widget that is fully customizable -and included in Airy Core +and included in Airy Core. @@ -22,25 +22,25 @@ Having a Live Chat plugin on your website has become essential. Connect with your website visitors, communicate with them in real time, or use a bot to automate FAQs. -Airy’s Live Chat Plugin comes out of the box fully working, but thanks to its -open-source nature and React Render Props you can customize everything about it. +Airy’s Live Chat Plugin comes out of the box fully functioning, and thanks to its +open-source nature and React Render Props, you can customize everything about it. Out of the box Airy’s Live Chat Plugin supports: - Full customization of look, feel and features - All message types, including emojis -- Rich Messaging: Templates, cards & carousels +- Rich Messaging with templates, cards and carousels -## How it's build +## How it's built -The Airy Live Chat Plugin is JavaScript library built with -[preact](https://preactjs.com/) and +The Airy Live Chat Plugin is a JavaScript library built with +[Preact](https://preactjs.com/) and [TypeScript](https://www.typescriptlang.org/). The library makes heavy use of [render props](https://reactjs.org/docs/render-props.html) so that you can customize its -behavior in every aspect. The library handles all the -[communication](/api/endpoints/chatplugin.md) with Airy Core transparently for +behavior in every aspect. The library makes all of the +[communication](/api/endpoints/chatplugin.md) with Airy Core transparent for you. ## Customization @@ -58,10 +58,10 @@ Completely customize your Live Chat and make it match your brand: ## Supported message types -Airy’s Live Chat Plugin supports the following messages types: +Airy’s Live Chat Plugin supports the following message types: -- Text messages -- Emoji's +- Text Messages +- Emojis - Rich Cards - Rich Card Carousels - Suggested Replies diff --git a/docs/docs/sources/chatplugin/quickstart.md b/docs/docs/sources/chatplugin/quickstart.md index e2b929ecde..892afbf7e8 100644 --- a/docs/docs/sources/chatplugin/quickstart.md +++ b/docs/docs/sources/chatplugin/quickstart.md @@ -50,7 +50,7 @@ the next steps, so note it down. Alternatively, you can connect an Airy's Live Chat Plugin channel via the [UI](/ui/introduction). -On your instance's Airy Core UI, click on the 'Channels' icon on the left sidebar menu. Then, click on the button displaying a cross icon next to the Airy Live Chat channel. +On your instance's Airy Core UI, click on the 'Channels' icon on the left sidebar menu. Then, click on the button displaying a + icon next to the Airy Live Chat channel. chat plugin channels UI @@ -72,7 +72,7 @@ This will bring you to a page where you can edit or disconnect each channel. Cli chat plugin channels UI -Click on the 'Install app' tab. Here you will find the `channel_id`, which is located in the sample code (underlined in the screenshot below). It is required for the next steps, so note it down. +Click on the 'Install app' tab. Here you will find the `channel_id`, which is located in the sample code (highlighted in the screenshot above). It is required for the next steps, so note it down. ## Step 2: Send messages via the Chat Plugin diff --git a/docs/docs/sources/debugging-twilio.mdx b/docs/docs/sources/debugging-twilio.mdx index 17cc759812..f9d7c229ca 100644 --- a/docs/docs/sources/debugging-twilio.mdx +++ b/docs/docs/sources/debugging-twilio.mdx @@ -1,16 +1,16 @@ import useBaseUrl from '@docusaurus/useBaseUrl'; -If the channel has been successfully connected, but the message you sent does not appear in a conversation and a conversation is not created, please see below for debugging advices. +If the channel has been successfully connected, but the message you sent does not appear in a conversation and a conversation is not created, please see below for debugging advice. :::note -If you encounter errors, please follow these debugging advices: +If you encounter errors, please follow this debugging advice: - make sure that the authToken and accountSid tokens you have added to the airy.yaml file (refer back to step 1) have been applied to your Airy Core instance. An Airy Core instance should be created after editing the airy.yaml file. - verify your webhook integration (refer back to step 2). Make sure that your Twilio Webhook URL has been correctly added to your phone number in your Twilio dashboard (check for typos). - The Webhook URL should be added in the 'Messaging', 'incoming message' section. + The Webhook URL should be added in the 'Messaging', 'Incoming Message' section. - check the messaging logs in your Twilio dashboard (see screenshot below): select your phone number from the phone numbers list, click on the Messaging Logs tab, and select Incoming from the dropdown. The [error codes](https://www.twilio.com/docs/api/errors) from the logs diff --git a/docs/docs/sources/facebook.md b/docs/docs/sources/facebook.md index edeb9a3c43..5d55c95bce 100644 --- a/docs/docs/sources/facebook.md +++ b/docs/docs/sources/facebook.md @@ -12,8 +12,8 @@ import SuccessBox from "@site/src/components/SuccessBox"; -**Connect your Facebook Pages**, send and receive messages from Facebook’s 1,3 -Billion users. +**Connect your Facebook Pages**, send and receive messages from Facebook’s 1.3 +billion users. @@ -47,11 +47,11 @@ Let's proceed step by step. To connect a page, you must have an approved Facebook app. If you don't have one, you must register and create a Business app on [Facebook for Developers](https://developers.facebook.com/). -All your registered apps are listed on [developers.facebook.com/apps](https://developers.facebook.com/apps/). +All of your registered apps are listed on [developers.facebook.com/apps](https://developers.facebook.com/apps/). Facebook apps page -The dashboard of each registered apps can be found on: +The dashboard of each registered app can be found on: ``` https://developers.facebook.com/apps/INSERT_YOUR_APP_ID_HERE/dashboard/ @@ -83,7 +83,7 @@ You are now ready to configure the webhook integration. Click on the + icon next https://developers.facebook.com/apps/INSERT_YOUR_APP_ID_HERE/dashboard/#addProduct ``` -Click on the button "Set Up" on the Webhooks product card. +Click on the button 'Set Up' on the Webhooks product card. Facebook webhook add product @@ -95,7 +95,7 @@ https://developers.facebook.com/apps/INSERT_YOUR_APP_ID_HERE/webhooks/ Facebook webhook -Select "Page" from the dropdown (the default is "User") and click on the button "Subscribe to this object". +Select 'Page' from the dropdown (the default is 'User') and click on the button 'Subscribe to this object'. This will open a modal box: add your Callback URL (your instance's Facebook Webhook URL) and Verify Token (the webhookSecret you added in your `airy.yaml` file in the previous step). @@ -128,7 +128,7 @@ start sending events to your Airy Core instance. Go to the Products page (click on the + icon next to Products on the left sidebar). -Click on the button "Set Up" on the Messenger product card. +Click the 'Set Up' button on the Messenger product card. ``` https://developers.facebook.com/apps/INSERT_YOUR_APP_ID_HERE/dashboard/#addProduct @@ -142,17 +142,17 @@ Notice that at the bottom of the page, the Webhooks product has been added with Facebook messenger product -Click on the blue button "Add or Remove Pages" and select your page. +Click on the blue button 'Add or Remove Pages' and select your page. -Once your page has been added, scroll down and click on the button "Add Subscriptions". +Once your page has been added, scroll down and click on the button 'Add Subscriptions'. Facebook page subscriptions -This opens a modal box: tick "messages" and "messaging_postbacks" from the Subscription Fields list. +This opens a modal box: tick 'messages' and 'messaging_postbacks' from the Subscription Fields list. Facebook page subscriptions -Next, scroll up, and click on the button "Generate Token". +Next, scroll up, and click on the button 'Generate Token'. Facebook page token @@ -197,11 +197,11 @@ import ConnectFacebook from '../api/endpoints/connect-facebook.mdx' :::note -If you encounter errors, please follow these debugging advices: +If you encounter errors, please follow this debugging advice: - make sure that the tokens you have added to the airy.yaml file (refer back to step 1) have been applied to your Airy Core instance. An Airy Core instance should be created after editing the airy.yaml file. -- verify your webhook integration (refer back to step 2). Make sure that your Facebook Webhook URL has been correctly added on your app's dashboard. You should edit the Page subscriptions for the Webhooks and Messenger product each time you create a new instance. Make sure that you have selected 'Page' subscription and not 'User' (which is the default). +- verify your webhook integration (refer back to step 2). Make sure that your Facebook Webhook URL has been correctly added on your app's dashboard. You should edit the 'Page' subscriptions for the Webhooks and Messenger product each time you create a new instance. Make sure that you have selected 'Page' subscription and not 'User' (which is the default). ::: diff --git a/docs/docs/sources/google.md b/docs/docs/sources/google.md index 8c8750db71..86a049f7f4 100644 --- a/docs/docs/sources/google.md +++ b/docs/docs/sources/google.md @@ -42,7 +42,7 @@ Let's proceed step by step. ### Step 1: Registration You first need to be registered with Google's Business Messages before -configuring this source. Refer to [Google's Business Messages' +configuring this source. Refer to [Google's Business Messages guides](https://developers.google.com/business-communications/business-messages/guides) to find more information about this source and the registration process. @@ -53,7 +53,7 @@ Once you are registered, head over to your Google Service Account and create a k Facebook token page Copy the Google Service Account key file provided by Google to -your `airy.yaml` file as a one line string next to `saFile:` below `components/sources/google`: +your `airy.yaml` file as a one line string next to `saFile:` (below `components/sources/google`). ### Step 3: Verification by Google @@ -73,7 +73,7 @@ Once the verification process has been completed, Google's Business Messages wil ## Connecting Google's Business Messages to your Airy Core instance -After the configuration, you can connect Google's Business Messages source to your instance by sending a request to the [Channels endpoint](/api/endpoints/channels#google). +After the configuration, you can connect a Google's Business Messages source to your instance by sending a request to the [Channels endpoint](/api/endpoints/channels#google). } diff --git a/docs/docs/sources/introduction.md b/docs/docs/sources/introduction.md index 95ddccdda6..37d4a9c909 100644 --- a/docs/docs/sources/introduction.md +++ b/docs/docs/sources/introduction.md @@ -16,21 +16,21 @@ import ChannelsUI from "@site/static/icons/channelsUi-icon.svg"; Airy Core allows you to connect **many different sources**: our Live Chat -Plugin, Facebook Messenger, WhatsApp, your own custom sources. +Plugin, Facebook Messenger, WhatsApp, or your own custom sources. One of the crucial features Airy Core provides is the ability to process -conversational data from a variety of sources such as Facebook Messenger,Google +conversational data from a variety of sources such as Facebook Messenger, Google Business Messages, Twilio.WhatsApp or Twilio.SMS. -You can connect sources through API requests or using our [Channels UI](/ui/channels). Our sources guides covers both options, step-by-step. +You can connect sources through API requests or using our [Channels UI](/ui/channels). Our Sources guides cover both options, step-by-step. It's important to understand the difference between a [source](/getting-started/glossary/#source) and a [channel](/getting-started/glossary/#channel). A [channel](/getting-started/glossary/#channel) represents a connection between a [source](/getting-started/glossary/#source) and your Airy Core instance: multiple [channels](/getting-started/glossary/#channel) can thus use the same [source](/getting-started/glossary/#source) for different [conversations](/getting-started/glossary/#conversation). -Connecting a [channel](/getting-started/glossary/#channel) gives the possibility of starting a [conversation](/getting-started/glossary/#conversation) between a [source](/getting-started/glossary/#source) and your Airy Core instance. Once a [channel](/getting-started/glossary/#channel) has been connected, your Airy core instance will start ingesting [messages](/getting-started/glossary/#message) and create new [conversations](/getting-started/glossary/#conversation) accordingly. +Connecting a [channel](/getting-started/glossary/#channel) allows the possibility of starting a [conversation](/getting-started/glossary/#conversation) between a [source](/getting-started/glossary/#source) and your Airy Core instance. Once a [channel](/getting-started/glossary/#channel) has been connected, your Airy Core instance will start ingesting [messages](/getting-started/glossary/#message) and create new [conversations](/getting-started/glossary/#conversation) accordingly. -You can connect as many [channels](/getting-started/glossary/#channel) as you want for each [source](/getting-started/glossary/#source).The [Inbox UI](/ui/inbox) displays all your [conversations](/getting-started/glossary/#conversation), across all [sources](/getting-started/glossary/#source). +You can connect as many [channels](/getting-started/glossary/#channel) as you want for each [source](/getting-started/glossary/#source). The [Inbox UI](/ui/inbox) displays all of your [conversations](/getting-started/glossary/#conversation) from all of your [sources](/getting-started/glossary/#source). } @@ -94,13 +94,13 @@ integrations yourself. While sources are all different, their architecture follows a few key principles: -- The webhook integration ingests payload data as raw as you get it in a source +- The webhook integration ingests payload data as raw because you get it in a source specific topic. -- We only extracts metadata from the source data as we translate events into - conversations and messages, the content is not parsed at ingestion time, we let +- We only extract metadata from the source data as we translate events into + conversations and messages. The content is not parsed at ingestion time, we let it travel untouched through the system. These principles allow you to reprocess data from a conversation platform at any -given point time. If the data pipeline has a bug, say the messages are counted -incorrectly, you can reprocess the data and fix a bug for past data as well. +given point in time. If the data pipeline has a bug (eg: the messages are counted +incorrectly), you can reprocess the data and fix a bug for past data as well. diff --git a/docs/docs/sources/sms-twilio.md b/docs/docs/sources/sms-twilio.md index 5e0558a7eb..c7c3a59433 100644 --- a/docs/docs/sources/sms-twilio.md +++ b/docs/docs/sources/sms-twilio.md @@ -14,7 +14,7 @@ import SuccessBox from "@site/src/components/SuccessBox"; -This document provides a step by step guide to integrate a Twilio SMS source with your Airy +This document provides a step-by-step guide to integrate a Twilio SMS source with your Airy Core Platform instance. The Twilio SMS source provides a channel for sending and receiving SMS using the diff --git a/docs/docs/sources/twilio-config.mdx b/docs/docs/sources/twilio-config.mdx index 53c4a2268d..cc4f9e7965 100644 --- a/docs/docs/sources/twilio-config.mdx +++ b/docs/docs/sources/twilio-config.mdx @@ -9,4 +9,4 @@ The Twilio provider requires the following configuration: Let's proceed step by step. -First, to connect a Twilio provider to your Airy Core instance, you must have a Twilio account and a phone number. If this is not your case, head over to [Twilio's website](https://www.twilio.com/): create an account and buy a phone number. +First, to connect a Twilio provider to your Airy Core instance, you must have a Twilio account and a phone number. If this is not your case: head over to [Twilio's website](https://www.twilio.com/), create an account and buy a phone number. diff --git a/docs/docs/sources/twilio-sources.mdx b/docs/docs/sources/twilio-sources.mdx index 4ae4e8e43d..5041b4c40d 100644 --- a/docs/docs/sources/twilio-sources.mdx +++ b/docs/docs/sources/twilio-sources.mdx @@ -3,7 +3,7 @@ import SuccessBox from '@site/src/components/SuccessBox'; ### Step 1: Find the authToken and accountSid -Once you have a Twilio account, log in, and find your [Twilio auth +Once you have a Twilio account, log in and find your [Twilio auth token account SID](https://support.twilio.com/hc/en-us/articles/223136027-Auth-Tokens-and-How-to-Change-Them) on your Twilio dashboard (underlined in red in the screenshot below). @@ -15,8 +15,8 @@ https://www.twilio.com/console Copy and paste your `Auth token` and `Account Sid` as strings next to `authToken:` and `accountSid:`, below `components/sources/twilio` in your `airy.yaml` file. -Make sure the `Auth token` and `Account Sid` are contained within a pair of either single quotation marks '' or double quotation marks ""; -paste the tokens as they are and do not add or remove additional spaces. +Make sure the `Auth token` and `Account Sid` are contained within a pair of either single quotation marks '' or double quotation marks "" +and paste the tokens as they are (do not add or remove additional spaces). import ApplyVariablesNote from './applyVariables-note.mdx'; diff --git a/docs/docs/sources/whatsapp-twilio.md b/docs/docs/sources/whatsapp-twilio.md index 176f934944..d29a97588f 100644 --- a/docs/docs/sources/whatsapp-twilio.md +++ b/docs/docs/sources/whatsapp-twilio.md @@ -9,7 +9,7 @@ import ButtonBox from "@site/src/components/ButtonBox"; -Connect with **2 Billion WhatsApp users** via messages and templates. +Connect with **2 billion WhatsApp users** via messages and templates. @@ -29,7 +29,7 @@ import TwilioConfig from './twilio-config.mdx' -Then, you need to request access to enable your Twilio number for WhatsApp: complete the request form on [Twilio's webiste](https://www.twilio.com/whatsapp/request-access). To request access, you +Then, you need to request access to enable your Twilio number for WhatsApp by completing the request form on [Twilio's website](https://www.twilio.com/whatsapp/request-access). To request access, you need to have a Facebook Business Manager ID: read [Twilio's documentation](https://www.twilio.com/docs/whatsapp/api) for more information on the request process. import TwilioSources from './twilio-sources.mdx' diff --git a/docs/docs/ui/channels.md b/docs/docs/ui/channels.md index 93bf658c2e..5a065e0ef5 100644 --- a/docs/docs/ui/channels.md +++ b/docs/docs/ui/channels.md @@ -12,7 +12,7 @@ import ChannelsUI from "@site/static/icons/channelsUi-icon.svg"; -Use the Channels UI to connect your sources simply and directly with a UI +Use the Channels UI to connect your sources simply and directly with a UI. diff --git a/docs/docs/ui/inbox.md b/docs/docs/ui/inbox.md index f0d277ac5b..8e5e68d407 100644 --- a/docs/docs/ui/inbox.md +++ b/docs/docs/ui/inbox.md @@ -10,9 +10,9 @@ import useBaseUrl from '@docusaurus/useBaseUrl'; ## Introduction -Airy’s Inbox gives you an UI for all your conversations. +Airy’s Inbox gives you a UI for all of your conversations. -See all conversations from the sources you connected, no matter if they come via the [Live Chat Plugin](sources/chatplugin/overview.md), [Facebook Messenger](sources/facebook.md), [Google’s Business Messages](sources/google.md), [SMS](sources/sms-twilio.md), [WhatsApp](sources/whatsapp-twilio.md) or a custom source. +See all conversations from the sources you connected, regardless of whether they come via the [Live Chat Plugin](sources/chatplugin/overview.md), [Facebook Messenger](sources/facebook.md), [Google’s Business Messages](sources/google.md), [SMS](sources/sms-twilio.md), [WhatsApp](sources/whatsapp-twilio.md) or a custom source. The inbox supports not only text messages but a variety of different message types. @@ -115,7 +115,7 @@ Airy’s Inbox supports all [Google’s Rich Cards variants from Rich Cards to C **Render Templates for Chat Plugin** Airy’s Live Chat Plugin supports templates too. The template payload is the same as for Google Rich Cards. -This enables you and your teams to interact with your website visitors in a richer way, and also enables chat bots in the templates. +This enables you and your team to interact with your website visitors in a richer way, and also enables chat bots in the templates. } diff --git a/docs/docs/ui/introduction.md b/docs/docs/ui/introduction.md index 233915dd34..4c13971432 100644 --- a/docs/docs/ui/introduction.md +++ b/docs/docs/ui/introduction.md @@ -11,11 +11,11 @@ import LabelSVG from "@site/static/icons/label.svg"; import CommentBubbleSVG from "@site/static/icons/comment-bubble.svg"; import useBaseUrl from '@docusaurus/useBaseUrl'; -Not every message can be handled by code, this is why Airy comes with different UIs ready for you and your teams to use. +Not every message can be handled by code, this is why Airy comes with different UIs ready for you and your team to use. -While the [Chat Plugin](sources/chatplugin/overview.md) is the open-source chat UI for your website and app visitors, Airy UI has different all the UI interfaces you need internally for a messaging platform. +While the [Chat Plugin](sources/chatplugin/overview.md) is the open-source chat UI for your website and app visitors, Airy UI offers all of the UI interfaces you need internally for a messaging platform. -Airy UI comes with an open-source, customizable [inbox](inbox), filled with the conversations of all your [sources](sources/introduction.md). Organize your conversations with features such as [Filters, Search](inbox), [Tags](tags) and add [suggested replies](suggestedReplies) to messages to improve response time. +Airy UI comes with an open-source, customizable [inbox](inbox), filled with the conversations from all of your [sources](sources/introduction.md). You can organize your conversations with features such as [Filters, Search](inbox) and [Tags](tags), in addition to adding [suggested replies](suggestedReplies) to messages to improve response time. Tags are words, or combinations of words, you can use to add more context to conversations and contacts. +Tags are words, or combinations of words, that you can use to add more context to conversations and contacts. Tags provide you with an unlimited amount of flexibility to manage and customize your conversational workflow. @@ -18,14 +18,14 @@ Here are the ways you can create and use tags: ## Create -When you create a tag you can choose a color to visually identify it better in the inbox. +When you create a tag, you can choose a color to visually identify it better in the inbox. This can also be done via the [Create Tags API](api/endpoints/tags.md#create). Create Tags ## Edit -When editing tags you can change the name and the color of each tag. +When editing tags, you can change the name and the color of each tag. This can also be done via the [Edit Tags API](api/endpoints/tags.md#update). Edit Tags diff --git a/docs/static/img/sources/chatplugin/chatplugin-customize-demo.gif b/docs/static/img/sources/chatplugin/chatplugin-customize-demo.gif new file mode 100644 index 0000000000..5185808c7e Binary files /dev/null and b/docs/static/img/sources/chatplugin/chatplugin-customize-demo.gif differ diff --git a/docs/yarn.lock b/docs/yarn.lock index abe6338cd8..916d766483 100644 --- a/docs/yarn.lock +++ b/docs/yarn.lock @@ -3773,9 +3773,9 @@ dns-equal@^1.0.0: integrity sha1-s55/HabrCnW6nBcySzR1PEfgZU0= dns-packet@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-1.3.1.tgz#12aa426981075be500b910eedcd0b47dd7deda5a" - integrity sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg== + version "1.3.4" + resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-1.3.4.tgz#e3455065824a2507ba886c55a89963bb107dec6f" + integrity sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA== dependencies: ip "^1.1.0" safe-buffer "^5.0.1" diff --git a/frontend/chat-plugin/BUILD b/frontend/chat-plugin/BUILD index f52ba9f0cc..790f5f7884 100644 --- a/frontend/chat-plugin/BUILD +++ b/frontend/chat-plugin/BUILD @@ -7,6 +7,7 @@ load("@com_github_airyhq_bazel_tools//web:web_library.bzl", "web_library") 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") +load("//tools/build/npm:rules.bzl", "assemble_npm", "deploy_npm") package(default_visibility = ["//visibility:public"]) @@ -107,6 +108,35 @@ container_release( repository = "chat-plugin", ) +genrule( + name = "npm_library", + srcs = [ + "package.json", + "README.md", + ":library", + ":chat-plugin", + ], + outs = ["chat-plugin_lib"], + cmd = """ + mkdir -p $(OUTS)/{dist} && cp -R $(location :library) $(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/frontend/chat-plugin/nginx.conf b/frontend/chat-plugin/nginx.conf index e5859392ac..b612360a77 100644 --- a/frontend/chat-plugin/nginx.conf +++ b/frontend/chat-plugin/nginx.conf @@ -48,5 +48,11 @@ http { access_log off; return 200 "healthy\n"; } + + location /actuator/health { + access_log off; + default_type application/json; + return 200 '{"status":"UP"}'; + } } } diff --git a/frontend/chat-plugin/package.json b/frontend/chat-plugin/package.json new file mode 100644 index 0000000000..1ccd7874d5 --- /dev/null +++ b/frontend/chat-plugin/package.json @@ -0,0 +1,30 @@ +{ + "name": "@airyhq/chat-plugin", + "version": "0.1.0", + "description": "Airy Live Chatplugin library", + "main": "dist/index.js", + "browser": { + "./dist/index.js": "./dist/index.browser.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": { + "@stomp/stompjs": "^6.1.0", + "@types/node": "15.6.1", + "@types/react": "17.0.8", + "@types/react-dom": "17.0.5", + "linkifyjs": "^2.1.9", + "react": "^17.0.2", + "react-dom": "16.14.0", + "camelcase-keys": "^6.2.2" + } +} diff --git a/frontend/chat-plugin/src/airyRenderProps/AiryHeaderBar/index.module.scss b/frontend/chat-plugin/src/airyRenderProps/AiryHeaderBar/index.module.scss index e9548ba1c2..c831a8960d 100644 --- a/frontend/chat-plugin/src/airyRenderProps/AiryHeaderBar/index.module.scss +++ b/frontend/chat-plugin/src/airyRenderProps/AiryHeaderBar/index.module.scss @@ -33,7 +33,6 @@ .closeButton { outline: none; - padding: 2px 0 0 2px; border: none; height: 32px; width: 32px; @@ -41,6 +40,11 @@ border-radius: 100%; cursor: pointer; + svg { + width: 10px; + height: 10px; + } + path { fill: #fff; } @@ -58,6 +62,11 @@ border-radius: 100%; cursor: pointer; + svg { + height: 24px; + width: 24px; + } + path { fill: #fff; } diff --git a/frontend/ui/nginx.conf b/frontend/ui/nginx.conf index c6f960864f..f0f364f114 100644 --- a/frontend/ui/nginx.conf +++ b/frontend/ui/nginx.conf @@ -58,5 +58,11 @@ http { access_log off; return 200 "healthy\n"; } + + location /actuator/health { + access_log off; + default_type application/json; + return 200 '{"status":"UP"}'; + } } } diff --git a/frontend/ui/src/actions/conversations/index.ts b/frontend/ui/src/actions/conversations/index.ts index 53cb2854d1..14b9cdf781 100644 --- a/frontend/ui/src/actions/conversations/index.ts +++ b/frontend/ui/src/actions/conversations/index.ts @@ -57,7 +57,7 @@ export const setStateConversationAction = createAction( (conversationId: string, state: string) => ({conversationId, state}) )<{conversationId: string; state: string}>(); -export const listConversations = () => async (dispatch: Dispatch) => { +export const fetchConversations = () => async (dispatch: Dispatch) => { dispatch(loadingConversationsAction(true)); return HttpClientInstance.listConversations({page_size: 50}).then((response: PaginatedResponse) => { dispatch(mergeConversationsAction(response.data, response.paginationData)); @@ -66,7 +66,7 @@ export const listConversations = () => async (dispatch: Dispatch) => { }); }; -export const listNextConversations = () => async (dispatch: Dispatch, state: () => StateModel) => { +export const fetchNextConversationPage = () => async (dispatch: Dispatch, state: () => StateModel) => { const cursor = state().data.conversations.all.paginationData.nextCursor; dispatch(loadingConversationsAction(true)); diff --git a/frontend/ui/src/actions/conversationsFilter/index.ts b/frontend/ui/src/actions/conversationsFilter/index.ts index 80da4a8adf..4e010f99d5 100644 --- a/frontend/ui/src/actions/conversationsFilter/index.ts +++ b/frontend/ui/src/actions/conversationsFilter/index.ts @@ -1,10 +1,10 @@ import {Dispatch} from 'redux'; import _typesafe, {createAction} from 'typesafe-actions'; -import {Conversation, ConversationFilter, Pagination} from 'model'; +import {Conversation, Pagination} from 'model'; import {PaginatedResponse} from 'httpclient'; import {HttpClientInstance} from '../../InitializeAiryApi'; -import {StateModel} from '../../reducers'; +import {ConversationFilter, StateModel} from '../../reducers'; import {loadingConversationsAction} from '../conversations'; import {delay, isEqual, omit} from 'lodash-es'; @@ -50,10 +50,15 @@ export const setFilter = (filter: ConversationFilter) => { const executeFilter = (filter: ConversationFilter, dispatch: Dispatch, state: () => StateModel) => { dispatch(updateFilteredConversationsAction(filter)); - refetchConversations(dispatch, state); + fetchFilteredConversations(dispatch, state); }; -const refetchConversations = (dispatch: Dispatch, state: () => StateModel, cursor?: string) => { +export const fetchNextFilteredPage = () => (dispatch: Dispatch, state: () => StateModel) => { + const cursor = state().data.conversations.filtered.paginationData.nextCursor; + return fetchFilteredConversations(dispatch, state, cursor); +}; + +const fetchFilteredConversations = (dispatch: Dispatch, state: () => StateModel, cursor?: string) => { dispatch(loadingConversationsAction(true)); const filter = state().data.conversations.filtered.currentFilter; if (Object.keys(filter).length > 0) { @@ -90,7 +95,7 @@ const filterToLuceneSyntax = (filter: ConversationFilter): string | null => { filterQuery.push('unread_count:0'); } if (filter.displayName) { - filterQuery.push('display_name=*' + filter.displayName + '*'); + filterQuery.push('display_name:*' + filter.displayName + '*'); } if (filter.byTags && filter.byTags.length > 0) { filterQuery.push('tag_ids:(' + filter.byTags.join(' AND ') + ')'); diff --git a/frontend/ui/src/components/IconChannelFilter/index.module.scss b/frontend/ui/src/components/IconChannelFilter/index.module.scss deleted file mode 100644 index acd762a1aa..0000000000 --- a/frontend/ui/src/components/IconChannelFilter/index.module.scss +++ /dev/null @@ -1,15 +0,0 @@ -.defaultChannelIcon { - border-radius: 50%; - background: var(--color-airy-blue); - color: white; - font-size: 12px; - text-align: center; - width: 24px; - height: 24px; -} - -.channelLogo { - border-radius: 50%; - width: 24px; - height: 24px; -} diff --git a/frontend/ui/src/components/SearchField.tsx b/frontend/ui/src/components/SearchField.tsx deleted file mode 100644 index 31d2c52fd1..0000000000 --- a/frontend/ui/src/components/SearchField.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import React, {createRef, useCallback} from 'react'; - -import closeIcon from 'assets/images/icons/close.svg'; -import searchIcon from 'assets/images/icons/search.svg'; -import styles from './index.module.scss'; - -type SearchFieldProps = { - id?: string; - placeholder?: string; - value: string; - setValue: (value: string) => void; - resetClicked?: () => void; - autoFocus?: boolean; - dataCy?: string; -}; - -export const SearchField: React.FC = ({ - id, - placeholder, - value, - setValue, - resetClicked, - autoFocus, - dataCy, -}: SearchFieldProps): JSX.Element => { - const inputRef = createRef(); - const resetButton = useCallback(() => { - setValue(''); - if (resetClicked) { - resetClicked(); - } - }, [value, setValue]); - - return ( -
-
- -
- setValue(event.target.value)} - type="search" - autoFocus={autoFocus} - /> - {value !== '' && ( - - )} -
- ); -}; diff --git a/frontend/ui/src/components/IconChannelFilter/index.tsx b/frontend/ui/src/components/SourceIcon/index.tsx similarity index 77% rename from frontend/ui/src/components/IconChannelFilter/index.tsx rename to frontend/ui/src/components/SourceIcon/index.tsx index 16ea7fa1c3..03b7f1d43a 100644 --- a/frontend/ui/src/components/IconChannelFilter/index.tsx +++ b/frontend/ui/src/components/SourceIcon/index.tsx @@ -1,7 +1,5 @@ import React from 'react'; -import {Channel} from 'model'; - import {ReactComponent as GoogleIcon} from 'assets/images/icons/google_avatar.svg'; import {ReactComponent as WhatsappIcon} from 'assets/images/icons/whatsapp_avatar.svg'; import {ReactComponent as SmsIcon} from 'assets/images/icons/sms_avatar.svg'; @@ -16,7 +14,7 @@ const sourceIconsMap = { chatplugin: AiryAvatar, }; -export const IconChannelFilter = ({channel}: {channel: Channel}) => { - const SourceIcon = sourceIconsMap[channel.source]; - return ; +export const SourceIcon = ({source, ...props}) => { + const SourceIcon = sourceIconsMap[source]; + return ; }; diff --git a/frontend/ui/src/components/Tag/index.module.scss b/frontend/ui/src/components/Tag/index.module.scss index 375fa4ee9a..c301335bc2 100644 --- a/frontend/ui/src/components/Tag/index.module.scss +++ b/frontend/ui/src/components/Tag/index.module.scss @@ -36,6 +36,8 @@ .closeButton { margin-left: 4px; + height: 10px; + width: 10px; path { fill: #fff; } diff --git a/frontend/ui/src/components/index.module.scss b/frontend/ui/src/components/index.module.scss deleted file mode 100644 index f870274c67..0000000000 --- a/frontend/ui/src/components/index.module.scss +++ /dev/null @@ -1,65 +0,0 @@ -@import 'assets/scss/fonts.scss'; - -.component { - display: flex; - flex-direction: row; - border: 1px solid var(--color-light-gray); - border-radius: 8px; - padding: 4px 8px; - - input { - @include font-base; - border: none; - width: 100%; - border-radius: 0; - -webkit-appearance: none; - - &::-webkit-search-decoration, - &::-webkit-search-cancel-button { - -webkit-appearance: none; - } - - &:focus { - border: none; - outline: none; - } - } - - &:focus-within { - border-color: var(--color-airy-blue); - } -} - -.searchIcon { - svg { - width: 22px; - height: 22px; - padding-top: 2px; - margin-right: 4px; - position: relative; - top: 2px; - - path { - fill: var(--color-text-gray); - } - } -} - -.resetButton { - background-color: rgba(0, 0, 0, 0); - border: none; - cursor: pointer; - padding: 0px 4px; -} - -.closeIcon { - svg { - margin: 0; - width: 10px; - height: 10px; - - path { - fill: var(--color-text-gray); - } - } -} diff --git a/frontend/ui/src/pages/Channels/ConnectedChannelsList/ChannelListItem/index.module.scss b/frontend/ui/src/pages/Channels/ConnectedChannelsList/ChannelListItem/index.module.scss index b02d98a377..3ed9b92b8a 100644 --- a/frontend/ui/src/pages/Channels/ConnectedChannelsList/ChannelListItem/index.module.scss +++ b/frontend/ui/src/pages/Channels/ConnectedChannelsList/ChannelListItem/index.module.scss @@ -78,7 +78,7 @@ svg { position: relative; top: -1px; - left: 2px; + left: 3px; height: 8px; width: auto; margin-top: 5px; diff --git a/frontend/ui/src/pages/Channels/ConnectedChannelsList/index.module.scss b/frontend/ui/src/pages/Channels/ConnectedChannelsList/index.module.scss index 0762fd0f2a..a12751cc3d 100644 --- a/frontend/ui/src/pages/Channels/ConnectedChannelsList/index.module.scss +++ b/frontend/ui/src/pages/Channels/ConnectedChannelsList/index.module.scss @@ -8,7 +8,6 @@ padding-left: 96px; padding-top: 88px; width: 100%; - overflow-y: auto; padding: 32px; margin: 88px 1.5em 0 100px; min-height: calc(100vh - 170px); @@ -32,6 +31,8 @@ } .backIcon { + height: 13px; + width: 17px; path { fill: var(--color-airy-blue); } @@ -160,3 +161,18 @@ color: var(--color-text-contrast); margin-bottom: 4px; } + +.closeIcon { + height: 10px; + width: 10px; +} + +.plusIcon { + height: 13px; + width: 13px; +} + +.searchIcon { + height: 18px; + width: 18px; +} diff --git a/frontend/ui/src/pages/Channels/ConnectedChannelsList/index.tsx b/frontend/ui/src/pages/Channels/ConnectedChannelsList/index.tsx index 8bb11598d0..e568621d71 100644 --- a/frontend/ui/src/pages/Channels/ConnectedChannelsList/index.tsx +++ b/frontend/ui/src/pages/Channels/ConnectedChannelsList/index.tsx @@ -11,7 +11,7 @@ import ChannelListItem from './ChannelListItem'; import {SearchField} from 'components'; import {ReactComponent as ArrowLeftIcon} from 'assets/images/icons/arrow-left-2.svg'; import {ReactComponent as SearchIcon} from 'assets/images/icons/search.svg'; -import {ReactComponent as PLusIcon} from 'assets/images/icons/plus.svg'; +import {ReactComponent as PlusIcon} from 'assets/images/icons/plus.svg'; import {ReactComponent as CloseIcon} from 'assets/images/icons/close.svg'; import styles from './index.module.scss'; @@ -99,9 +99,15 @@ const ConnectedChannelsList = (props: ConnectedChannelsListProps) => { )}
- +
diff --git a/frontend/ui/src/pages/Channels/Providers/Airy/ChatPlugin/ChatPluginConnect.module.scss b/frontend/ui/src/pages/Channels/Providers/Airy/ChatPlugin/ChatPluginConnect.module.scss index 82ff082905..3f47008ccb 100644 --- a/frontend/ui/src/pages/Channels/Providers/Airy/ChatPlugin/ChatPluginConnect.module.scss +++ b/frontend/ui/src/pages/Channels/Providers/Airy/ChatPlugin/ChatPluginConnect.module.scss @@ -8,7 +8,6 @@ padding-left: 96px; padding-top: 88px; width: 100%; - overflow-y: auto; padding: 32px; margin: 88px 1.5em 0 100px; min-height: calc(100vh - 170px); @@ -33,6 +32,8 @@ } .backIcon { + height: 13px; + width: 17px; path { fill: var(--color-airy-blue); } diff --git a/frontend/ui/src/pages/Channels/Providers/Airy/ChatPlugin/ChatPluginConnect.tsx b/frontend/ui/src/pages/Channels/Providers/Airy/ChatPlugin/ChatPluginConnect.tsx index 88712a3838..b61b9fc30e 100644 --- a/frontend/ui/src/pages/Channels/Providers/Airy/ChatPlugin/ChatPluginConnect.tsx +++ b/frontend/ui/src/pages/Channels/Providers/Airy/ChatPlugin/ChatPluginConnect.tsx @@ -7,7 +7,7 @@ import {StateModel} from '../../../../../reducers'; import {allChannels} from '../../../../../selectors/channels'; import {connectChatPlugin, updateChannel, disconnectChannel} from '../../../../../actions/channel'; -import {Button, LinkButton} from 'components'; +import {Button, LinkButton, InfoButton} from 'components'; import {Channel} from 'model'; import {ConnectNewChatPlugin} from './sections/ConnectNewChatPlugin'; @@ -128,10 +128,16 @@ const ChatPluginConnect = (props: ChatPluginProps) => { )} - - - Back - +
+ + + + Back + +
); diff --git a/frontend/ui/src/pages/Channels/Providers/Facebook/Messenger/FacebookConnect.module.scss b/frontend/ui/src/pages/Channels/Providers/Facebook/Messenger/FacebookConnect.module.scss index 72e23ed6aa..a6595b810b 100644 --- a/frontend/ui/src/pages/Channels/Providers/Facebook/Messenger/FacebookConnect.module.scss +++ b/frontend/ui/src/pages/Channels/Providers/Facebook/Messenger/FacebookConnect.module.scss @@ -8,7 +8,6 @@ padding-left: 96px; padding-top: 88px; width: 100%; - overflow-y: auto; padding: 32px; margin: 88px 1.5em 0 100px; min-height: calc(100vh - 170px); @@ -31,6 +30,8 @@ } .backIcon { + height: 13px; + width: 17px; path { fill: var(--color-airy-blue); } diff --git a/frontend/ui/src/pages/Channels/Providers/Facebook/Messenger/FacebookConnect.tsx b/frontend/ui/src/pages/Channels/Providers/Facebook/Messenger/FacebookConnect.tsx index c09319e0d3..a661e9f8bf 100644 --- a/frontend/ui/src/pages/Channels/Providers/Facebook/Messenger/FacebookConnect.tsx +++ b/frontend/ui/src/pages/Channels/Providers/Facebook/Messenger/FacebookConnect.tsx @@ -5,7 +5,7 @@ import _, {connect, ConnectedProps} from 'react-redux'; import {connectFacebookChannel} from '../../../../../actions/channel'; import {StateModel} from '../../../../../reducers'; -import {Button, Input, LinkButton} from 'components'; +import {Button, Input, LinkButton, InfoButton} from 'components'; import {ConnectChannelFacebookRequestPayload} from 'httpclient'; import {ReactComponent as ArrowLeftIcon} from 'assets/images/icons/arrow-left-2.svg'; @@ -73,10 +73,17 @@ const FacebookConnect = (props: FacebookProps) => { return (

Facebook Messenger

- - - Back - +
+ + + + + Back + +

Connect Messenger

diff --git a/frontend/ui/src/pages/Channels/Providers/Google/GoogleBusinessMessagesRequirementsDialog/index.module.scss b/frontend/ui/src/pages/Channels/Providers/Google/GoogleBusinessMessagesRequirementsDialog/index.module.scss index 7651cfdb69..ed7f7f9b98 100644 --- a/frontend/ui/src/pages/Channels/Providers/Google/GoogleBusinessMessagesRequirementsDialog/index.module.scss +++ b/frontend/ui/src/pages/Channels/Providers/Google/GoogleBusinessMessagesRequirementsDialog/index.module.scss @@ -60,4 +60,9 @@ background: transparent; cursor: pointer; outline: none; + padding: 0px; + svg { + height: 10px; + width: 10px; + } } diff --git a/frontend/ui/src/pages/Channels/Providers/Google/GoogleBusinessMessagesRequirementsDialog/index.tsx b/frontend/ui/src/pages/Channels/Providers/Google/GoogleBusinessMessagesRequirementsDialog/index.tsx index 3801288fb3..84a61d3e07 100644 --- a/frontend/ui/src/pages/Channels/Providers/Google/GoogleBusinessMessagesRequirementsDialog/index.tsx +++ b/frontend/ui/src/pages/Channels/Providers/Google/GoogleBusinessMessagesRequirementsDialog/index.tsx @@ -1,7 +1,7 @@ import React from 'react'; import {SettingsModal} from 'components'; -import close from 'assets/images/icons/close.svg'; +import {ReactComponent as CloseIcon} from 'assets/images/icons/close.svg'; import styles from './index.module.scss'; @@ -15,7 +15,7 @@ export const GoogleBusinessMessagesRequirementsDialog = (props: GoogleBusinessMe

Connect Google's Business Messages

diff --git a/frontend/ui/src/pages/Channels/Providers/Twilio/SMS/TwilioSmsConnect.module.scss b/frontend/ui/src/pages/Channels/Providers/Twilio/SMS/TwilioSmsConnect.module.scss index 93be8994b6..9c9128bdb7 100644 --- a/frontend/ui/src/pages/Channels/Providers/Twilio/SMS/TwilioSmsConnect.module.scss +++ b/frontend/ui/src/pages/Channels/Providers/Twilio/SMS/TwilioSmsConnect.module.scss @@ -8,7 +8,6 @@ padding-left: 96px; padding-top: 88px; width: 100%; - overflow-y: auto; padding: 32px; margin: 88px 1.5em 0 100px; min-height: calc(100vh - 170px); diff --git a/frontend/ui/src/pages/Channels/Providers/Twilio/SMS/TwilioSmsConnect.tsx b/frontend/ui/src/pages/Channels/Providers/Twilio/SMS/TwilioSmsConnect.tsx index 404ccd752d..cea5ccd720 100644 --- a/frontend/ui/src/pages/Channels/Providers/Twilio/SMS/TwilioSmsConnect.tsx +++ b/frontend/ui/src/pages/Channels/Providers/Twilio/SMS/TwilioSmsConnect.tsx @@ -42,7 +42,15 @@ const TwilioSmsConnect = (props: TwilioSmsProps) => { } }, [channels, channelId]); - return ; + return ( + + ); }; export default connector(withRouter(TwilioSmsConnect)); diff --git a/frontend/ui/src/pages/Channels/Providers/Twilio/TwilioConnect.module.scss b/frontend/ui/src/pages/Channels/Providers/Twilio/TwilioConnect.module.scss index 82332fc0fa..4896115a01 100644 --- a/frontend/ui/src/pages/Channels/Providers/Twilio/TwilioConnect.module.scss +++ b/frontend/ui/src/pages/Channels/Providers/Twilio/TwilioConnect.module.scss @@ -88,7 +88,6 @@ padding-left: 96px; padding-top: 88px; width: 100%; - overflow-y: auto; padding: 32px; margin: 88px 1.5em 0 100px; min-height: calc(100vh - 170px); @@ -110,6 +109,8 @@ } .backIcon { + height: 13px; + width: 17px; path { fill: var(--color-airy-blue); } diff --git a/frontend/ui/src/pages/Channels/Providers/Twilio/TwilioConnect.tsx b/frontend/ui/src/pages/Channels/Providers/Twilio/TwilioConnect.tsx index 36ecaa5116..30d7710efb 100644 --- a/frontend/ui/src/pages/Channels/Providers/Twilio/TwilioConnect.tsx +++ b/frontend/ui/src/pages/Channels/Providers/Twilio/TwilioConnect.tsx @@ -4,7 +4,7 @@ import {connect, ConnectedProps} from 'react-redux'; import {connectTwilioSms, connectTwilioWhatsapp} from '../../../../actions/channel'; -import {Button, Input, LinkButton, UrlInputField} from 'components'; +import {Button, Input, LinkButton, UrlInputField, InfoButton} from 'components'; import {Channel, Source} from 'model'; import {ReactComponent as ArrowLeft} from 'assets/images/icons/arrow-left-2.svg'; @@ -17,6 +17,7 @@ type TwilioConnectProps = { source: string; pageTitle: string; buttonText: string; + infoLink: string; } & ConnectedProps & RouteComponentProps; @@ -25,7 +26,7 @@ const mapDispatchToProps = {connectTwilioWhatsapp, connectTwilioSms}; const connector = connect(null, mapDispatchToProps); const TwilioConnect = (props: TwilioConnectProps) => { - const {channel, source, pageTitle, buttonText, history, connectTwilioWhatsapp, connectTwilioSms} = props; + const {channel, source, pageTitle, buttonText, infoLink, history, connectTwilioWhatsapp, connectTwilioSms} = props; const [numberInput, setNumberInput] = useState(channel?.sourceChannelId || ''); const [nameInput, setNameInput] = useState(channel?.metadata?.name || ''); @@ -70,10 +71,13 @@ const TwilioConnect = (props: TwilioConnectProps) => { return (

{pageTitle}

- - - Back - +
+ + + + Back + +
diff --git a/frontend/ui/src/pages/Channels/Providers/Twilio/TwilioRequirementsDialog/index.module.scss b/frontend/ui/src/pages/Channels/Providers/Twilio/TwilioRequirementsDialog/index.module.scss index 57021541d0..12c1b9cb01 100644 --- a/frontend/ui/src/pages/Channels/Providers/Twilio/TwilioRequirementsDialog/index.module.scss +++ b/frontend/ui/src/pages/Channels/Providers/Twilio/TwilioRequirementsDialog/index.module.scss @@ -40,6 +40,11 @@ background: transparent; cursor: pointer; outline: none; + padding: 0px; + svg { + width: 10px; + height: 10px; + } } .inviteWrapper { width: 500px; diff --git a/frontend/ui/src/pages/Channels/Providers/Twilio/TwilioRequirementsDialog/index.tsx b/frontend/ui/src/pages/Channels/Providers/Twilio/TwilioRequirementsDialog/index.tsx index a7570194b4..6eee0b4dad 100644 --- a/frontend/ui/src/pages/Channels/Providers/Twilio/TwilioRequirementsDialog/index.tsx +++ b/frontend/ui/src/pages/Channels/Providers/Twilio/TwilioRequirementsDialog/index.tsx @@ -1,7 +1,7 @@ import React from 'react'; import {SettingsModal} from 'components'; -import close from 'assets/images/icons/close.svg'; +import {ReactComponent as CloseIcon} from 'assets/images/icons/close.svg'; import styles from './index.module.scss'; @@ -14,7 +14,7 @@ export const TwilioRequirementsDialog = (props: TwilioRequirementsDialogProps) =

Connect with Twilio First

diff --git a/frontend/ui/src/pages/Channels/Providers/Twilio/WhatsApp/TwilioWhatsappConnect.module.scss b/frontend/ui/src/pages/Channels/Providers/Twilio/WhatsApp/TwilioWhatsappConnect.module.scss index 93be8994b6..9c9128bdb7 100644 --- a/frontend/ui/src/pages/Channels/Providers/Twilio/WhatsApp/TwilioWhatsappConnect.module.scss +++ b/frontend/ui/src/pages/Channels/Providers/Twilio/WhatsApp/TwilioWhatsappConnect.module.scss @@ -8,7 +8,6 @@ padding-left: 96px; padding-top: 88px; width: 100%; - overflow-y: auto; padding: 32px; margin: 88px 1.5em 0 100px; min-height: calc(100vh - 170px); diff --git a/frontend/ui/src/pages/Channels/Providers/Twilio/WhatsApp/TwilioWhatsappConnect.tsx b/frontend/ui/src/pages/Channels/Providers/Twilio/WhatsApp/TwilioWhatsappConnect.tsx index 517f46233a..6749201c6a 100644 --- a/frontend/ui/src/pages/Channels/Providers/Twilio/WhatsApp/TwilioWhatsappConnect.tsx +++ b/frontend/ui/src/pages/Channels/Providers/Twilio/WhatsApp/TwilioWhatsappConnect.tsx @@ -43,7 +43,13 @@ const TwilioWhatsappConnect = (props: TwilioWhatsappProps) => { }, [channels, channelId]); return ( - + ); }; diff --git a/frontend/ui/src/pages/Inbox/ConversationList/index.tsx b/frontend/ui/src/pages/Inbox/ConversationList/index.tsx index 77d07b188c..512008b043 100644 --- a/frontend/ui/src/pages/Inbox/ConversationList/index.tsx +++ b/frontend/ui/src/pages/Inbox/ConversationList/index.tsx @@ -6,7 +6,8 @@ import InfiniteLoader from 'react-window-infinite-loader'; import ResizableWindowList from '../../../components/ResizableWindowList'; import {newestConversationFirst, newestFilteredConversationFirst} from '../../../selectors/conversations'; -import {listNextConversations} from '../../../actions/conversations'; +import {fetchNextConversationPage} from '../../../actions/conversations'; +import {fetchNextFilteredPage} from '../../../actions/conversationsFilter'; import ConversationListHeader from '../ConversationListHeader'; import QuickFilter from '../QuickFilter'; @@ -21,7 +22,8 @@ import {ConversationRouteProps} from '../index'; type ConversationListProps = ConnectedProps; const mapDispatchToProps = { - listNextConversations, + fetchNext: fetchNextConversationPage, + fetchNextFiltered: fetchNextFilteredPage, }; const mapStateToProps = (state: StateModel, ownProps: ConversationRouteProps) => ({ @@ -31,7 +33,6 @@ const mapStateToProps = (state: StateModel, ownProps: ConversationRouteProps) => conversationsPaginationData: state.data.conversations.all.paginationData, filteredPaginationData: state.data.conversations.filtered.paginationData, currentFilter: state.data.conversations.filtered.currentFilter, - loading: state.data.conversations.all.paginationData.loading, user: state.data.user, }); @@ -62,8 +63,8 @@ const ConversationList = (props: ConversationListProps) => { conversationsPaginationData, filteredPaginationData, currentFilter, - loading, - listNextConversations, + fetchNext, + fetchNextFiltered, } = props; const hasFilter = Object.keys(currentFilter || {}).length > 0; @@ -71,12 +72,13 @@ const ConversationList = (props: ConversationListProps) => { const paginationData = hasFilter ? filteredPaginationData : conversationsPaginationData; const hasMoreData = paginationData.nextCursor && paginationData.nextCursor.length > 0; + const loading = paginationData.loading; const isItemLoaded = (index: number) => index < items.length; const itemCount = hasMoreData ? items.length + 1 : items.length; const loadMoreItems = () => { - if (!paginationData.loading) { - hasFilter ? listNextConversations() : listNextConversations(); + if (!loading) { + hasFilter ? fetchNextFiltered() : fetchNext(); } return Promise.resolve(true); }; @@ -86,7 +88,7 @@ const ConversationList = (props: ConversationListProps) => { {({onItemsRendered, ref}) => (
{!items.length && !loading ? ( - + ) : ( { ); }; - const resizeList = () => { - //TODO - }; - return (
- resizeList()} /> +
diff --git a/frontend/ui/src/pages/Inbox/ConversationListHeader/index.module.scss b/frontend/ui/src/pages/Inbox/ConversationListHeader/index.module.scss index 21564f503a..e16525c99d 100644 --- a/frontend/ui/src/pages/Inbox/ConversationListHeader/index.module.scss +++ b/frontend/ui/src/pages/Inbox/ConversationListHeader/index.module.scss @@ -60,6 +60,8 @@ .searchBox { color: black; background: white; + display: flex; + align-items: center; } .searchButton { @@ -67,6 +69,8 @@ background-color: white; cursor: pointer; outline: none; + height: 24px; + width: 24px; &:hover { svg { path { @@ -83,10 +87,9 @@ } .searchIcon { - width: 22px; - height: 22px; - padding-top: 1px; - margin-right: 4px; + width: 18px; + height: 18px; + margin-top: 2px; position: relative; path { @@ -100,6 +103,9 @@ border: none; outline: none; padding: 0px; + height: 24px; + width: 24px; + margin-left: 12px; &:hover { svg { path { diff --git a/frontend/ui/src/pages/Inbox/ConversationListHeader/index.tsx b/frontend/ui/src/pages/Inbox/ConversationListHeader/index.tsx index e83e6e9fc0..15bf53d417 100644 --- a/frontend/ui/src/pages/Inbox/ConversationListHeader/index.tsx +++ b/frontend/ui/src/pages/Inbox/ConversationListHeader/index.tsx @@ -29,13 +29,10 @@ const mapStateToProps = (state: StateModel) => ({ const connector = connect(mapStateToProps, mapDispatchToProps); -type ConversationListHeaderProps = { - onFilterVisibilityChanged: () => void; -} & ConnectedProps & - RouteComponentProps; +type ConversationListHeaderProps = ConnectedProps & RouteComponentProps; const ConversationListHeader = (props: ConversationListHeaderProps) => { - const {setSearch, resetFilteredConversationAction, currentFilter, onFilterVisibilityChanged} = props; + const {setSearch, resetFilteredConversationAction, currentFilter} = props; const [isShowingSearchInput, setIsShowingSearchInput] = useState(false); const [searchText, setSearchText] = useState(''); @@ -77,7 +74,6 @@ const ConversationListHeader = (props: ConversationListHeaderProps) => { const toggleFilter = () => { setIsFilterOpen(!isFilterOpen); - onFilterVisibilityChanged(); }; const isFilterActive = (): boolean => Object.values(currentFilter).length > 0; @@ -101,8 +97,8 @@ const ConversationListHeader = (props: ConversationListHeaderProps) => { ) : (
-
-
diff --git a/frontend/ui/src/pages/Inbox/Messenger/ConversationHeader/index.tsx b/frontend/ui/src/pages/Inbox/Messenger/ConversationHeader/index.tsx index a7cc34321e..1786e0baa0 100644 --- a/frontend/ui/src/pages/Inbox/Messenger/ConversationHeader/index.tsx +++ b/frontend/ui/src/pages/Inbox/Messenger/ConversationHeader/index.tsx @@ -18,7 +18,6 @@ const mapStateToProps = (state, ownProps) => { const ConversationHeader = ({conversation}) => { const participant = conversation.metadata.contact; const channel = conversation.channel; - const connectorName = channel && channel.metadata.name; if (!conversation) { return null; @@ -28,9 +27,7 @@ const ConversationHeader = ({conversation}) => {
{participant && participant.displayName} - {connectorName && ( -
{channel && }
- )} +
{channel && }
) : null; diff --git a/frontend/ui/src/pages/Inbox/Messenger/MessageList/index.module.scss b/frontend/ui/src/pages/Inbox/Messenger/MessageList/index.module.scss index 448b13d3cb..5fb94b6576 100644 --- a/frontend/ui/src/pages/Inbox/Messenger/MessageList/index.module.scss +++ b/frontend/ui/src/pages/Inbox/Messenger/MessageList/index.module.scss @@ -39,6 +39,8 @@ } .suggestionIcon { + height: 16px; + width: 16px; path { fill: var(--color-text-gray); } diff --git a/frontend/ui/src/pages/Inbox/NoConversations/index.tsx b/frontend/ui/src/pages/Inbox/NoConversations/index.tsx index b912983c63..ce14da7d1d 100644 --- a/frontend/ui/src/pages/Inbox/NoConversations/index.tsx +++ b/frontend/ui/src/pages/Inbox/NoConversations/index.tsx @@ -1,8 +1,12 @@ import React from 'react'; import styles from './index.module.scss'; -const NoConversations: React.FC = (): JSX.Element => { - return Object.keys({}).length === 0 ? ( +interface NoConversationsProps { + filterSet?: boolean; +} + +const NoConversations = (props: NoConversationsProps) => { + return Object.keys({}).length && props.filterSet === false ? (
Your new messages will appear here

diff --git a/frontend/ui/src/pages/Inbox/QuickFilter/Popup.module.scss b/frontend/ui/src/pages/Inbox/QuickFilter/Popup.module.scss index 45bec7968d..a4e48d5a92 100644 --- a/frontend/ui/src/pages/Inbox/QuickFilter/Popup.module.scss +++ b/frontend/ui/src/pages/Inbox/QuickFilter/Popup.module.scss @@ -184,6 +184,8 @@ } .checkmarkIcon { + display: flex; + justify-content: center; flex: none; border-radius: 50%; width: 24px; @@ -196,18 +198,26 @@ } svg { - width: 24px; - height: 24px; - margin-bottom: 2px; - border-radius: 50%; - padding-top: 2px; - padding-left: 2px; + width: 12px; + height: auto; + align-self: center; } } -.pageName { +.itemName { width: 12em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } + +.checkmarkCircleIcon { + height: 24px; + width: 24px; + margin-right: 4px; + margin-bottom: 1px; + svg { + height: 100%; + width: 100%; + } +} diff --git a/frontend/ui/src/pages/Inbox/QuickFilter/Popup.tsx b/frontend/ui/src/pages/Inbox/QuickFilter/Popup.tsx index 3d53df1d46..a00050cd60 100644 --- a/frontend/ui/src/pages/Inbox/QuickFilter/Popup.tsx +++ b/frontend/ui/src/pages/Inbox/QuickFilter/Popup.tsx @@ -2,10 +2,10 @@ import React, {useEffect, useState} from 'react'; import _, {connect, ConnectedProps} from 'react-redux'; import {omit, sortBy} from 'lodash-es'; import {SearchField, LinkButton, Button} from 'components'; -import {Tag as TagModel, Channel, ConversationFilter} from 'model'; +import {Tag as TagModel, Channel} from 'model'; import {listTags} from '../../../actions/tags'; import {setFilter} from '../../../actions/conversationsFilter'; -import {StateModel} from '../../../reducers'; +import {ConversationFilter, StateModel} from '../../../reducers'; import DialogCustomizable from '../../../components/DialogCustomizable'; import Tag from '../../../components/Tag'; import {ReactComponent as CheckmarkIcon} from 'assets/images/icons/checkmark.svg'; @@ -13,13 +13,20 @@ import {ReactComponent as CheckmarkCircleIcon} from 'assets/images/icons/checkma import styles from './Popup.module.scss'; import {allChannels} from '../../../selectors/channels'; import ChannelAvatar from '../../../components/ChannelAvatar'; +import {prettifySource} from '../../../../../../lib/typescript/model'; +import {SourceIcon} from '../../../components/SourceIcon'; function mapStateToProps(state: StateModel) { + const channels: Channel[] = Object.values(allChannels(state)); return { user: state.data.user, filter: state.data.conversations.filtered.currentFilter, tags: state.data.tags.all, - channels: Object.values(allChannels(state)), + channels, + sources: channels.reduce>((acc, {source}) => { + acc.add(source); + return acc; + }, new Set()), }; } @@ -35,16 +42,15 @@ type PopUpFilterProps = { } & ConnectedProps; const PopUpFilter = (props: PopUpFilterProps) => { - const {filter, channels, tags, listTags, closeCallback, setFilter} = props; - - const [pageSearch, setPageSearch] = useState(''); + const {filter, channels, tags, listTags, closeCallback, setFilter, sources} = props; + const [channelSearch, setChannelSearch] = useState(''); const [tagSearch, setTagSearch] = useState(''); useEffect(() => { listTags(); }, [listTags]); - const resetPressed = (e: React.MouseEvent) => { + const onReset = (e: React.MouseEvent) => { e.stopPropagation(); setFilter({}); closeCallback(); @@ -52,17 +58,21 @@ const PopUpFilter = (props: PopUpFilterProps) => { const toggleReadOnly = (e: React.MouseEvent) => { e.stopPropagation(); - const newFilter: ConversationFilter = {...filter}; - newFilter.readOnly = !filter.readOnly; - newFilter.unreadOnly = filter.readOnly; + const newFilter: ConversationFilter = { + ...filter, + readOnly: !filter.readOnly, + unreadOnly: filter.readOnly, + }; setFilter(newFilter); }; const toggleUnreadOnly = (e: React.MouseEvent) => { e.stopPropagation(); - const newFilter: ConversationFilter = {...filter}; - newFilter.unreadOnly = !filter.unreadOnly; - newFilter.readOnly = filter.unreadOnly; + const newFilter: ConversationFilter = { + ...filter, + unreadOnly: !filter.unreadOnly, + readOnly: filter.unreadOnly, + }; setFilter(newFilter); }; @@ -77,24 +87,24 @@ const PopUpFilter = (props: PopUpFilterProps) => { return (channelsList || []).includes(channel.id); }; - const toggleChannel = (e: React.MouseEvent, channel: Channel) => { - e.stopPropagation(); - const channels = filter.byChannels ? [...filter.byChannels] : []; - isChannelSelected(channels, channel) ? channels.splice(channels.indexOf(channel.id), 1) : channels.push(channel.id); + const isSourceSelected = (sourcesList: Array, source: string) => { + return (sourcesList || []).includes(source); + }; - if (channels.length > 0) { + const toggleSelection = filterKey => (id: string) => { + let items = filter[filterKey] || []; + items = items.includes(id) ? items.filter(item => item !== id) : items.concat([id]); + if (items.length > 0) { setFilter({ ...filter, - byChannels: channels, + [filterKey]: items, }); } else { - setFilter(omit(filter, 'byChannels')); + setFilter(omit(filter, filterKey)); } }; - const isTagSelected = (tagList: string[], tag: TagModel) => { - return (tagList || []).includes(tag.id); - }; + const isTagSelected = (tagList: string[], tag: TagModel) => (tagList || []).includes(tag.id); const toggleTag = (tag: TagModel) => { const tags = filter.byTags ? [...filter.byTags] : []; @@ -109,9 +119,8 @@ const PopUpFilter = (props: PopUpFilterProps) => { } }; - const OpenIcon = () => { - return

; - }; + const toggleChannel = toggleSelection('byChannels'); + const toggleSource = toggleSelection('bySources'); return ( { : styles.filterButtonSelected } onClick={(event: React.MouseEvent) => setState(event, true)}> - +
Open
-
-

By Tags

-
- setTagSearch(value)} - /> -
-
- {sortBy(tags, tag => tag.name) - .filter((tag: TagModel) => tag.name.toLowerCase().includes(tagSearch.toLowerCase())) - .map((tag: TagModel) => ( - toggleTag(tag)} - /> - ))} + {Object.keys(tags).length > 0 && ( +
+

By Tags

+
+ setTagSearch(value)} + /> +
+
+ {sortBy(tags, tag => tag.name) + .filter((tag: TagModel) => tag.name.toLowerCase().includes(tagSearch.toLowerCase())) + .map((tag: TagModel) => ( + toggleTag(tag)} + /> + ))} +
-
+ )} - {channels.length > 1 ? ( + {channels.length > 1 && (

By Channel

setPageSearch(value)} + value={channelSearch} + setValue={(value: string) => setChannelSearch(value)} />
{sortBy(channels, channel => channel.metadata?.name) - .filter((channel: Channel) => channel.metadata?.name.toLowerCase().includes(pageSearch.toLowerCase())) + .filter((channel: Channel) => + channel.metadata?.name.toLowerCase().includes(channelSearch.toLowerCase()) + ) .map((channel, key) => (
toggleChannel(event, channel)}> + onClick={() => toggleChannel(channel.id)}> {isChannelSelected(filter.byChannels, channel) ? (
@@ -215,16 +230,43 @@ const PopUpFilter = (props: PopUpFilterProps) => { ) : ( )} -
{channel.metadata?.name || channel.sourceChannelId}
+
{channel.metadata?.name || channel.sourceChannelId}
+
+ ))} +
+
+ )} + + {sources.size > 1 && ( +
+

By Source

+
+ {Array.from(sources) + .sort() + .map(source => ( +
toggleSource(source)}> + {isSourceSelected(filter.bySources, source) ? ( +
+ +
+ ) : ( + + )} +
{prettifySource(source)}
))}
- ) : null} + )}
- Clear All + Clear All diff --git a/frontend/ui/src/pages/Inbox/QuickFilter/index.tsx b/frontend/ui/src/pages/Inbox/QuickFilter/index.tsx index 1bad8fb00f..a56d525835 100644 --- a/frontend/ui/src/pages/Inbox/QuickFilter/index.tsx +++ b/frontend/ui/src/pages/Inbox/QuickFilter/index.tsx @@ -1,8 +1,7 @@ import React, {useState, useEffect} from 'react'; import _, {connect, ConnectedProps} from 'react-redux'; -import {ConversationFilter} from 'model'; -import {StateModel} from '../../../reducers'; +import {ConversationFilter, StateModel} from '../../../reducers'; import {setFilter} from '../../../actions/conversationsFilter'; diff --git a/frontend/ui/src/pages/Inbox/index.tsx b/frontend/ui/src/pages/Inbox/index.tsx index e10692ccfb..ee058b2d8f 100644 --- a/frontend/ui/src/pages/Inbox/index.tsx +++ b/frontend/ui/src/pages/Inbox/index.tsx @@ -3,7 +3,7 @@ import _, {connect, ConnectedProps} from 'react-redux'; import {RouteComponentProps} from 'react-router-dom'; import {User} from 'model'; -import {listConversations} from '../../actions/conversations'; +import {fetchConversations} from '../../actions/conversations'; import {listChannels} from '../../actions/channel'; import {StateModel} from '../../reducers'; @@ -24,7 +24,7 @@ const mapStateToProps = (state: StateModel) => { }; const mapDispatchToProps = { - listConversations, + listConversations: fetchConversations, listChannels, }; diff --git a/frontend/ui/src/pages/Tags/EmptyStateTags.module.scss b/frontend/ui/src/pages/Tags/EmptyStateTags.module.scss index 1d1cd98aea..b48382a4d8 100644 --- a/frontend/ui/src/pages/Tags/EmptyStateTags.module.scss +++ b/frontend/ui/src/pages/Tags/EmptyStateTags.module.scss @@ -41,3 +41,9 @@ width: 370px; margin: auto; } + +.emptyImage { + height: 74px; + width: 251px; + margin-bottom: 16px; +} diff --git a/frontend/ui/src/pages/Tags/EmptyStateTags.tsx b/frontend/ui/src/pages/Tags/EmptyStateTags.tsx index fb460e7226..56c78f0798 100644 --- a/frontend/ui/src/pages/Tags/EmptyStateTags.tsx +++ b/frontend/ui/src/pages/Tags/EmptyStateTags.tsx @@ -13,7 +13,7 @@ const EmptyStateTags = (props: EmptyStateTagsProps) => {

You don't have tags yet.

Tags provide a useful way to group related conversations together and to quickly filter and search them.

- +
diff --git a/frontend/ui/src/pages/Tags/TableRow.module.scss b/frontend/ui/src/pages/Tags/TableRow.module.scss index 27e035c7df..c8aaaa511e 100644 --- a/frontend/ui/src/pages/Tags/TableRow.module.scss +++ b/frontend/ui/src/pages/Tags/TableRow.module.scss @@ -58,7 +58,7 @@ } .actionSVG { - width: 20px; + width: 16px; height: 18px; path { fill: var(--color-dark-elements-gray); @@ -71,8 +71,16 @@ } .actionSVGEdit { - @extend .actionSVG; + width: 20px; height: 24px; + path { + fill: var(--color-dark-elements-gray); + } + &:hover { + path { + fill: var(--color-airy-blue); + } + } } .actionButton { diff --git a/frontend/ui/src/pages/Tags/index.module.scss b/frontend/ui/src/pages/Tags/index.module.scss index 78253a1cad..6fba7f6302 100644 --- a/frontend/ui/src/pages/Tags/index.module.scss +++ b/frontend/ui/src/pages/Tags/index.module.scss @@ -109,6 +109,8 @@ } .plusButton { + height: 13px; + width: 13px; margin-left: 4px; } } diff --git a/frontend/ui/src/reducers/data/config/index.ts b/frontend/ui/src/reducers/data/config/index.ts index 4dde971168..6a042ec632 100644 --- a/frontend/ui/src/reducers/data/config/index.ts +++ b/frontend/ui/src/reducers/data/config/index.ts @@ -1,10 +1,11 @@ import {ActionType, getType} from 'typesafe-actions'; import * as actions from '../../../actions/config'; +import {getComponents} from 'model'; type Action = ActionType; export type Config = { - components: {[key: string]: {enabled: boolean}}; + components: {[key: string]: {enabled: boolean; healthy: boolean}}; }; const defaultState = { @@ -15,7 +16,9 @@ export default function configReducer(state = defaultState, action: Action): Con switch (action.type) { case getType(actions.saveClientConfig): return { - ...action.payload, + ...state, + // Aggregate services on their component name + components: getComponents(action.payload), }; default: return state; diff --git a/frontend/ui/src/reducers/data/conversations/index.ts b/frontend/ui/src/reducers/data/conversations/index.ts index dd50993899..2f45dc8fe0 100644 --- a/frontend/ui/src/reducers/data/conversations/index.ts +++ b/frontend/ui/src/reducers/data/conversations/index.ts @@ -2,7 +2,7 @@ import {ActionType, getType} from 'typesafe-actions'; import {combineReducers} from 'redux'; import {cloneDeep, sortBy, merge, pickBy} from 'lodash-es'; -import {Conversation, ConversationFilter, Message} from 'model'; +import {Conversation, Message} from 'model'; import * as metadataActions from '../../../actions/metadata'; import * as actions from '../../../actions/conversations'; @@ -13,6 +13,17 @@ type Action = ActionType | ActionType; type FilterAction = ActionType; type MessageAction = ActionType; +export interface ConversationFilter { + readOnly?: boolean; + unreadOnly?: boolean; + displayName?: string; + createdAt?: string; + byTags?: string[]; + byChannels?: string[]; + bySources?: string[]; + isStateOpen?: boolean; +} + export type MergedConversation = Conversation & { paginationData?: { previousCursor: string; diff --git a/frontend/ui/src/selectors/conversations.ts b/frontend/ui/src/selectors/conversations.ts index 6af5843271..13741d0b5a 100644 --- a/frontend/ui/src/selectors/conversations.ts +++ b/frontend/ui/src/selectors/conversations.ts @@ -1,7 +1,7 @@ import _, {createSelector} from 'reselect'; import {filter, pickBy, reverse, sortBy, values} from 'lodash-es'; -import {Conversation, ConversationFilter} from 'model'; -import {MergedConversation, StateModel} from '../reducers'; +import {Conversation} from 'model'; +import {ConversationFilter, MergedConversation, StateModel} from '../reducers'; import {ConversationMap} from '../reducers/data/conversations'; import {ConversationRouteProps} from '../pages/Inbox'; @@ -16,8 +16,7 @@ export const getConversation = createSelector( getCurrentFilteredConversation, (conversation, filteredConversation) => { if (!conversation && !filteredConversation) return undefined; - const mergedConversation = {...conversation, ...filteredConversation}; - return mergedConversation; + return {...conversation, ...filteredConversation}; } ); diff --git a/go.mod b/go.mod index f2be033ef2..cbddb00a76 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,9 @@ go 1.16 // Automatically generated by running //tools/update-deps require ( + github.com/Masterminds/goutils v1.1.1 // indirect + github.com/Masterminds/semver v1.5.0 // indirect + github.com/Masterminds/sprig v2.22.0+incompatible // indirect github.com/Shopify/sarama v1.28.0 github.com/TwinProduction/go-color v1.0.0 github.com/aws/aws-sdk-go v1.37.29 @@ -14,9 +17,11 @@ require ( github.com/aws/aws-sdk-go-v2/service/eks v1.1.1 github.com/aws/aws-sdk-go-v2/service/iam v1.1.1 github.com/beanstalkd/go-beanstalk v0.1.0 + github.com/huandu/xstrings v1.3.2 // indirect github.com/imdario/mergo v0.3.11 // indirect github.com/jpillora/backoff v1.0.0 github.com/kr/pretty v0.2.1 + github.com/mitchellh/copystructure v1.1.2 // indirect github.com/mitchellh/go-homedir v1.1.0 github.com/riferrei/srclient v0.2.1 github.com/spf13/cast v1.3.1 // indirect diff --git a/go.sum b/go.sum index 737bbe1c2b..ed4874dc98 100644 --- a/go.sum +++ b/go.sum @@ -40,6 +40,12 @@ github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= +github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= +github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= +github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= +github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60= +github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46 h1:lsxEuwrXEAokXB9qhlbKWPpo3KMLZQ5WB5WLQRW1uq0= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= @@ -306,6 +312,8 @@ github.com/hashicorp/serf v0.8.2 h1:YZ7UKsJv+hKjqGVUUbtE3HNj79Eln2oQ75tniF6iPt0= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw= +github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6 h1:UDMh68UUwekSh5iP2OMhRRZJiiBccgV7axzUG8vi56c= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= @@ -380,6 +388,8 @@ github.com/miekg/dns v1.0.14 h1:9jZdLNd/P4+SfEJ0TNyxYpsK8N4GtfylBLqtbYN1sbA= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/mitchellh/cli v1.0.0 h1:iGBIsUe3+HZ/AD/Vd7DErOt5sU9fa8Uj7A2s1aggv1Y= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/copystructure v1.1.2 h1:Th2TIvG1+6ma3e/0/bopBKohOTY7s4dA8V2q4EUcBJ0= +github.com/mitchellh/copystructure v1.1.2/go.mod h1:EBArHfARyrSWO/+Wyr9zwEkc6XMFB9XyNgFNmRkZZU4= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= @@ -392,6 +402,8 @@ github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0Qu github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/reflectwalk v1.0.1 h1:FVzMWA5RllMAKIdUSC8mdWo3XtwoecrH79BY70sEEpE= +github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= diff --git a/go_repositories.bzl b/go_repositories.bzl index e7bf2ec90d..68045bf9f4 100644 --- a/go_repositories.bzl +++ b/go_repositories.bzl @@ -729,6 +729,12 @@ def go_repositories(): sum = "h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=", version = "v1.0.0", ) + go_repository( + name = "com_github_huandu_xstrings", + importpath = "github.com/huandu/xstrings", + sum = "h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw=", + version = "v1.3.2", + ) go_repository( name = "com_github_ianlancetaylor_demangle", @@ -905,6 +911,24 @@ def go_repositories(): sum = "h1:TpvdAwDAt1K4ANVOfcihouRdvP+MgAfDWwBuct4l6ZY=", version = "v0.0.0-20160728113105-d5b7844b561a", ) + go_repository( + name = "com_github_masterminds_goutils", + importpath = "github.com/Masterminds/goutils", + sum = "h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=", + version = "v1.1.1", + ) + go_repository( + name = "com_github_masterminds_semver", + importpath = "github.com/Masterminds/semver", + sum = "h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=", + version = "v1.5.0", + ) + go_repository( + name = "com_github_masterminds_sprig", + importpath = "github.com/Masterminds/sprig", + sum = "h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60=", + version = "v2.22.0+incompatible", + ) go_repository( name = "com_github_mattn_go_colorable", @@ -937,6 +961,13 @@ def go_repositories(): sum = "h1:iGBIsUe3+HZ/AD/Vd7DErOt5sU9fa8Uj7A2s1aggv1Y=", version = "v1.0.0", ) + go_repository( + name = "com_github_mitchellh_copystructure", + importpath = "github.com/mitchellh/copystructure", + sum = "h1:Th2TIvG1+6ma3e/0/bopBKohOTY7s4dA8V2q4EUcBJ0=", + version = "v1.1.2", + ) + go_repository( name = "com_github_mitchellh_go_homedir", importpath = "github.com/mitchellh/go-homedir", @@ -968,6 +999,13 @@ def go_repositories(): sum = "h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=", version = "v1.1.2", ) + go_repository( + name = "com_github_mitchellh_reflectwalk", + importpath = "github.com/mitchellh/reflectwalk", + sum = "h1:FVzMWA5RllMAKIdUSC8mdWo3XtwoecrH79BY70sEEpE=", + version = "v1.0.1", + ) + go_repository( name = "com_github_modern_go_concurrent", importpath = "github.com/modern-go/concurrent", diff --git a/infrastructure/controller/pkg/endpoints/endpoints.go b/infrastructure/controller/pkg/endpoints/endpoints.go index 107cf6ba8c..fd946c315a 100644 --- a/infrastructure/controller/pkg/endpoints/endpoints.go +++ b/infrastructure/controller/pkg/endpoints/endpoints.go @@ -13,27 +13,31 @@ type Server struct { clientSet *kubernetes.Clientset } -type ComponentsResponse struct { - Components []string `json:"components"` +type ServicesResponse struct { + Services map[string]Service `json:"services"` +} + +type Service struct { + Enabled bool `json:"enabled"` + Component string `json:"component"` } func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { + // Only return apps that are part of a component deployments, _ := s.clientSet.AppsV1().Deployments("default").List(context.TODO(), v1.ListOptions{ LabelSelector: "core.airy.co/component", }) - componentsMap := make(map[string]int) + componentsMap := make(map[string]Service) 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) + app := deployment.ObjectMeta.Name + componentsMap[app] = Service{ + Enabled: *deployment.Spec.Replicas > 0, + Component: deployment.ObjectMeta.Labels["core.airy.co/component"], + } } - resp, _ := json.Marshal(&ComponentsResponse{Components: components}) + resp, _ := json.Marshal(&ServicesResponse{Services: componentsMap}) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) w.Write(resp) @@ -41,6 +45,6 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { func Serve(clientSet *kubernetes.Clientset) { s := &Server{clientSet: clientSet} - http.Handle("/components", s) + http.Handle("/services", s) log.Fatal(http.ListenAndServe(":8080", nil)) } 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 7e62ba1eee..624d198b79 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 @@ -2,7 +2,7 @@ apiVersion: apps/v1 kind: Deployment metadata: name: api-admin - namespace: {{ .Values.global.namespace }} + namespace: {{ .Values.global.kubernetes.namespace }} labels: app: api-admin type: api @@ -26,7 +26,7 @@ spec: spec: containers: - name: app - image: "{{ .Values.global.containerRegistry}}/{{ .Values.image }}:{{ .Values.global.appImageTag }}" + image: "{{ .Values.global.kubernetes.containerRegistry}}/{{ .Values.image }}:{{ .Values.global.kubernetes.appImageTag }}" imagePullPolicy: Always envFrom: - configMapRef: @@ -51,8 +51,6 @@ spec: configMapKeyRef: name: kafka-config key: KAFKA_COMMIT_INTERVAL_MS - - name: SERVICE_NAME - value: api-admin - name: BEANSTALK_HOSTNAME valueFrom: configMapKeyRef: diff --git a/infrastructure/helm-chart/charts/apps/charts/api/charts/api-admin/templates/service.yaml b/infrastructure/helm-chart/charts/apps/charts/api/charts/api-admin/templates/service.yaml index be68bf3c9a..8d23ffb6f8 100644 --- a/infrastructure/helm-chart/charts/apps/charts/api/charts/api-admin/templates/service.yaml +++ b/infrastructure/helm-chart/charts/apps/charts/api/charts/api-admin/templates/service.yaml @@ -2,7 +2,7 @@ apiVersion: v1 kind: Service metadata: name: api-admin - namespace: {{ .Values.global.namespace }} + namespace: {{ .Values.global.kubernetes.namespace }} labels: core.airy.co/prometheus: spring spec: 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 808b812cf4..c512b35024 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 @@ -2,7 +2,7 @@ apiVersion: apps/v1 kind: Deployment metadata: name: api-communication - namespace: {{ .Values.global.namespace }} + namespace: {{ .Values.global.kubernetes.namespace }} labels: app: api-communication type: api @@ -26,7 +26,7 @@ spec: spec: containers: - name: app - image: "{{ .Values.global.containerRegistry}}/{{ .Values.image }}:{{ .Values.global.appImageTag }}" + image: "{{ .Values.global.kubernetes.containerRegistry}}/{{ .Values.image }}:{{ .Values.global.kubernetes.appImageTag }}" imagePullPolicy: Always envFrom: - configMapRef: @@ -47,8 +47,6 @@ spec: configMapKeyRef: name: kafka-config key: KAFKA_COMMIT_INTERVAL_MS - - name: SERVICE_NAME - value: api-communication livenessProbe: httpGet: path: /actuator/health diff --git a/infrastructure/helm-chart/charts/apps/charts/api/charts/api-communication/templates/service.yaml b/infrastructure/helm-chart/charts/apps/charts/api/charts/api-communication/templates/service.yaml index 1698d1f61c..0d407cea80 100644 --- a/infrastructure/helm-chart/charts/apps/charts/api/charts/api-communication/templates/service.yaml +++ b/infrastructure/helm-chart/charts/apps/charts/api/charts/api-communication/templates/service.yaml @@ -2,7 +2,7 @@ apiVersion: v1 kind: Service metadata: name: api-communication - namespace: {{ .Values.global.namespace }} + namespace: {{ .Values.global.kubernetes.namespace }} labels: core.airy.co/prometheus: spring spec: 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 7b315268ee..3ac6e553cf 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 @@ -2,7 +2,7 @@ apiVersion: apps/v1 kind: Deployment metadata: name: api-websocket - namespace: {{ .Values.global.namespace }} + namespace: {{ .Values.global.kubernetes.namespace }} labels: app: api-websocket type: api @@ -26,7 +26,7 @@ spec: spec: containers: - name: app - image: "{{ .Values.global.containerRegistry}}/{{ .Values.image }}:{{ .Values.global.appImageTag }}" + image: "{{ .Values.global.kubernetes.containerRegistry}}/{{ .Values.image }}:{{ .Values.global.kubernetes.appImageTag }}" imagePullPolicy: Always envFrom: - configMapRef: @@ -47,8 +47,6 @@ spec: configMapKeyRef: name: kafka-config key: KAFKA_COMMIT_INTERVAL_MS - - name: SERVICE_NAME - value: api-websocket livenessProbe: httpGet: path: /actuator/health diff --git a/infrastructure/helm-chart/charts/apps/charts/api/charts/api-websocket/templates/service.yaml b/infrastructure/helm-chart/charts/apps/charts/api/charts/api-websocket/templates/service.yaml index 5122f45f17..b150d1344c 100644 --- a/infrastructure/helm-chart/charts/apps/charts/api/charts/api-websocket/templates/service.yaml +++ b/infrastructure/helm-chart/charts/apps/charts/api/charts/api-websocket/templates/service.yaml @@ -2,7 +2,7 @@ apiVersion: v1 kind: Service metadata: name: api-websocket - namespace: {{ .Values.global.namespace }} + namespace: {{ .Values.global.kubernetes.namespace }} labels: core.airy.co/prometheus: spring spec: diff --git a/infrastructure/helm-chart/charts/apps/charts/api/templates/security.yaml b/infrastructure/helm-chart/charts/apps/charts/api/templates/security.yaml deleted file mode 100644 index 59a13b746f..0000000000 --- a/infrastructure/helm-chart/charts/apps/charts/api/templates/security.yaml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: security - namespace: {{ .Values.global.namespace }} -data: -{{- if .Values.systemToken }} - systemToken: {{ .Values.systemToken | quote }} -{{- end }} - jwtSecret: {{ .Values.jwtSecret | default (randAlphaNum 128) | quote }} - allowedOrigins: {{ .Values.allowedOrigins | quote }} diff --git a/infrastructure/helm-chart/charts/apps/charts/api/values.yaml b/infrastructure/helm-chart/charts/apps/charts/api/values.yaml deleted file mode 100644 index 377fdd66dd..0000000000 --- a/infrastructure/helm-chart/charts/apps/charts/api/values.yaml +++ /dev/null @@ -1 +0,0 @@ -allowedOrigins: "*" 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 e776aa1a9c..e11eb38f21 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 @@ -2,7 +2,7 @@ apiVersion: apps/v1 kind: Deployment metadata: name: frontend-chat-plugin - namespace: {{ .Values.global.namespace }} + namespace: {{ .Values.global.kubernetes.namespace }} labels: app: frontend-chat-plugin type: frontend @@ -26,7 +26,7 @@ spec: spec: containers: - name: app - image: "{{ .Values.global.containerRegistry}}/{{ .Values.image }}:{{ .Values.global.appImageTag }}" + image: "{{ .Values.global.kubernetes.containerRegistry}}/{{ .Values.image }}:{{ .Values.global.kubernetes.appImageTag }}" imagePullPolicy: Always env: - name: API_HOST diff --git a/infrastructure/helm-chart/charts/apps/charts/frontend/charts/frontend-chat-plugin/templates/service.yaml b/infrastructure/helm-chart/charts/apps/charts/frontend/charts/frontend-chat-plugin/templates/service.yaml index 98c9b4bd18..eb12b9477a 100644 --- a/infrastructure/helm-chart/charts/apps/charts/frontend/charts/frontend-chat-plugin/templates/service.yaml +++ b/infrastructure/helm-chart/charts/apps/charts/frontend/charts/frontend-chat-plugin/templates/service.yaml @@ -2,7 +2,7 @@ apiVersion: v1 kind: Service metadata: name: frontend-chat-plugin - namespace: {{ .Values.global.namespace }} + namespace: {{ .Values.global.kubernetes.namespace }} spec: ports: - port: 80 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 c3ff86cde6..d90294f059 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 @@ -2,7 +2,7 @@ apiVersion: apps/v1 kind: Deployment metadata: name: frontend-ui - namespace: {{.Values.global.namespace}} + namespace: {{.Values.global.kubernetes.namespace}} labels: app: frontend-ui type: frontend @@ -28,7 +28,7 @@ spec: spec: containers: - name: app - image: "{{ .Values.global.containerRegistry}}/{{ .Values.image }}:{{ .Values.global.appImageTag }}" + image: "{{ .Values.global.kubernetes.containerRegistry}}/{{ .Values.image }}:{{ .Values.global.kubernetes.appImageTag }}" imagePullPolicy: Always env: - name: API_HOST diff --git a/infrastructure/helm-chart/charts/apps/charts/frontend/charts/frontend-ui/templates/service.yaml b/infrastructure/helm-chart/charts/apps/charts/frontend/charts/frontend-ui/templates/service.yaml index 524667be61..a2a11cb69c 100644 --- a/infrastructure/helm-chart/charts/apps/charts/frontend/charts/frontend-ui/templates/service.yaml +++ b/infrastructure/helm-chart/charts/apps/charts/frontend/charts/frontend-ui/templates/service.yaml @@ -2,7 +2,7 @@ apiVersion: v1 kind: Service metadata: name: frontend-ui - namespace: {{ .Values.global.namespace }} + namespace: {{ .Values.global.kubernetes.namespace }} spec: ports: - port: 80 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 index d13abfd0ad..112973944f 100644 --- 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 @@ -2,7 +2,7 @@ apiVersion: apps/v1 kind: Deployment metadata: name: webhook-consumer - namespace: {{ .Values.global.namespace }} + namespace: {{ .Values.global.kubernetes.namespace }} labels: app: webhook-consumer type: webhook @@ -26,11 +26,9 @@ spec: spec: containers: - name: app - image: "{{ .Values.global.containerRegistry}}/{{ .Values.imageConsumer }}:{{ .Values.global.appImageTag }}" + image: "{{ .Values.global.kubernetes.containerRegistry}}/{{ .Values.imageConsumer }}:{{ .Values.global.kubernetes.appImageTag }}" imagePullPolicy: Always env: - - name: SERVICE_NAME - value: webhook-consumer - name: BEANSTALK_HOSTNAME valueFrom: configMapKeyRef: @@ -98,7 +96,7 @@ apiVersion: apps/v1 kind: Deployment metadata: name: webhook-publisher - namespace: {{ .Values.global.namespace }} + namespace: {{ .Values.global.kubernetes.namespace }} labels: app: webhook-publisher type: webhook @@ -122,7 +120,7 @@ spec: spec: containers: - name: app - image: "{{ .Values.global.containerRegistry}}/{{ .Values.imagePublisher }}:{{ .Values.global.appImageTag }}" + image: "{{ .Values.global.kubernetes.containerRegistry}}/{{ .Values.imagePublisher }}:{{ .Values.global.kubernetes.appImageTag }}" imagePullPolicy: Always env: - name: KAFKA_BROKERS diff --git a/infrastructure/helm-chart/charts/apps/charts/integration/charts/webhook/templates/services.yaml b/infrastructure/helm-chart/charts/apps/charts/integration/charts/webhook/templates/services.yaml index 3433fcd446..c34a3a0b16 100644 --- a/infrastructure/helm-chart/charts/apps/charts/integration/charts/webhook/templates/services.yaml +++ b/infrastructure/helm-chart/charts/apps/charts/integration/charts/webhook/templates/services.yaml @@ -2,7 +2,7 @@ apiVersion: v1 kind: Service metadata: name: webhook-consumer - namespace: {{ .Values.global.namespace }} + namespace: {{ .Values.global.kubernetes.namespace }} spec: ports: - port: 80 @@ -16,7 +16,7 @@ apiVersion: v1 kind: Service metadata: name: webhook-publisher - namespace: {{ .Values.global.namespace }} + namespace: {{ .Values.global.kubernetes.namespace }} labels: core.airy.co/prometheus: spring spec: diff --git a/infrastructure/helm-chart/charts/apps/charts/media/charts/resolver/templates/deployment.yaml b/infrastructure/helm-chart/charts/apps/charts/media/charts/resolver/templates/deployment.yaml index e705fc780c..c12e2d10bc 100644 --- a/infrastructure/helm-chart/charts/apps/charts/media/charts/resolver/templates/deployment.yaml +++ b/infrastructure/helm-chart/charts/apps/charts/media/charts/resolver/templates/deployment.yaml @@ -2,7 +2,7 @@ apiVersion: apps/v1 kind: Deployment metadata: name: media-resolver - namespace: default + namespace: {{ .Values.global.kubernetes.namespace }} labels: app: media-resolver type: media @@ -26,51 +26,49 @@ spec: spec: containers: - name: app - image: "{{ .Values.global.containerRegistry}}/{{ .Values.image }}:{{ .Values.global.appImageTag }}" + image: "{{ .Values.global.kubernetes.containerRegistry}}/{{ .Values.image }}:{{ .Values.global.kubernetes.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: "{{ .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 + - 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: 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/charts/resolver/templates/service.yaml b/infrastructure/helm-chart/charts/apps/charts/media/charts/resolver/templates/service.yaml index 6cc4288b28..512b22c8e1 100644 --- a/infrastructure/helm-chart/charts/apps/charts/media/charts/resolver/templates/service.yaml +++ b/infrastructure/helm-chart/charts/apps/charts/media/charts/resolver/templates/service.yaml @@ -2,7 +2,7 @@ apiVersion: v1 kind: Service metadata: name: media-resolver - namespace: {{ .Values.global.namespace }} + namespace: {{ .Values.global.kubernetes.namespace }} labels: core.airy.co/prometheus: spring spec: 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 831bfd4af4..c61775d5eb 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 @@ -2,7 +2,7 @@ apiVersion: apps/v1 kind: Deployment metadata: name: sources-chatplugin - namespace: {{ .Values.global.namespace }} + namespace: {{ .Values.global.kubernetes.namespace }} labels: app: sources-chatplugin type: sources @@ -26,7 +26,7 @@ spec: spec: containers: - name: app - image: "{{ .Values.global.containerRegistry}}/{{ .Values.image }}:{{ .Values.global.appImageTag }}" + image: "{{ .Values.global.kubernetes.containerRegistry}}/{{ .Values.image }}:{{ .Values.global.kubernetes.appImageTag }}" imagePullPolicy: Always envFrom: - configMapRef: diff --git a/infrastructure/helm-chart/charts/apps/charts/sources/charts/chatplugin/templates/service.yaml b/infrastructure/helm-chart/charts/apps/charts/sources/charts/chatplugin/templates/service.yaml index 63511a6c8f..ba7f28ab9b 100644 --- a/infrastructure/helm-chart/charts/apps/charts/sources/charts/chatplugin/templates/service.yaml +++ b/infrastructure/helm-chart/charts/apps/charts/sources/charts/chatplugin/templates/service.yaml @@ -2,7 +2,7 @@ apiVersion: v1 kind: Service metadata: name: sources-chatplugin - namespace: {{ .Values.global.namespace }} + namespace: {{ .Values.global.kubernetes.namespace }} labels: core.airy.co/prometheus: spring spec: @@ -13,4 +13,4 @@ spec: protocol: TCP type: NodePort selector: - app: sources-chatplugin \ No newline at end of file + app: sources-chatplugin 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 index 6d1500a11f..f0b97d7f32 100644 --- 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 @@ -2,7 +2,7 @@ apiVersion: apps/v1 kind: Deployment metadata: name: sources-facebook-connector - namespace: {{ .Values.global.namespace }} + namespace: {{ .Values.global.kubernetes.namespace }} labels: app: sources-facebook-connector type: sources @@ -26,7 +26,7 @@ spec: spec: containers: - name: app - image: "{{ .Values.global.containerRegistry}}/{{ .Values.imageConnector }}:{{ .Values.global.appImageTag }}" + image: "{{ .Values.global.kubernetes.containerRegistry}}/{{ .Values.imageConnector }}:{{ .Values.global.kubernetes.appImageTag }}" imagePullPolicy: Always envFrom: - configMapRef: @@ -47,8 +47,6 @@ spec: configMapKeyRef: name: kafka-config key: KAFKA_COMMIT_INTERVAL_MS - - name: SERVICE_NAME - value: facebook-connector - name: FACEBOOK_WEBHOOK_SECRET valueFrom: configMapKeyRef: @@ -99,7 +97,7 @@ apiVersion: apps/v1 kind: Deployment metadata: name: sources-facebook-events-router - namespace: {{ .Values.global.namespace }} + namespace: {{ .Values.global.kubernetes.namespace }} labels: app: sources-facebook-events-router type: sources @@ -123,7 +121,7 @@ spec: spec: containers: - name: app - image: "{{ .Values.global.containerRegistry}}/{{ .Values.imageEventsRouter }}:{{ .Values.global.appImageTag }}" + image: "{{ .Values.global.kubernetes.containerRegistry}}/{{ .Values.imageEventsRouter }}:{{ .Values.global.kubernetes.appImageTag }}" imagePullPolicy: Always env: - name: KAFKA_BROKERS diff --git a/infrastructure/helm-chart/charts/apps/charts/sources/charts/facebook/templates/service.yaml b/infrastructure/helm-chart/charts/apps/charts/sources/charts/facebook/templates/service.yaml index e3a7c63559..fc0837af6f 100644 --- a/infrastructure/helm-chart/charts/apps/charts/sources/charts/facebook/templates/service.yaml +++ b/infrastructure/helm-chart/charts/apps/charts/sources/charts/facebook/templates/service.yaml @@ -2,7 +2,7 @@ apiVersion: v1 kind: Service metadata: name: sources-facebook-connector - namespace: {{ .Values.global.namespace }} + namespace: {{ .Values.global.kubernetes.namespace }} labels: core.airy.co/prometheus: spring spec: 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 index 32a05ca40b..d0897cbe72 100644 --- 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 @@ -2,7 +2,7 @@ apiVersion: apps/v1 kind: Deployment metadata: name: sources-google-connector - namespace: {{ .Values.global.namespace }} + namespace: {{ .Values.global.kubernetes.namespace }} labels: app: sources-google-connector type: sources @@ -26,37 +26,37 @@ spec: spec: containers: - name: app - image: '{{ .Values.global.containerRegistry}}/{{ .Values.imageConnector }}:{{ .Values.global.appImageTag }}' + image: '{{ .Values.global.kubernetes.containerRegistry}}/{{ .Values.imageConnector }}:{{ .Values.global.kubernetes.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 + - 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 @@ -92,7 +92,7 @@ apiVersion: apps/v1 kind: Deployment metadata: name: sources-google-events-router - namespace: {{ .Values.global.namespace }} + namespace: {{ .Values.global.kubernetes.namespace }} labels: app: sources-google-events-router type: sources @@ -116,7 +116,7 @@ spec: spec: containers: - name: app - image: "{{ .Values.global.containerRegistry}}/{{ .Values.imageEventsRouter }}:{{ .Values.global.appImageTag }}" + image: "{{ .Values.global.kubernetes.containerRegistry}}/{{ .Values.imageEventsRouter }}:{{ .Values.global.kubernetes.appImageTag }}" imagePullPolicy: Always env: - name: KAFKA_BROKERS diff --git a/infrastructure/helm-chart/charts/apps/charts/sources/charts/google/templates/service.yaml b/infrastructure/helm-chart/charts/apps/charts/sources/charts/google/templates/service.yaml index f298231acc..ceb90cd608 100644 --- a/infrastructure/helm-chart/charts/apps/charts/sources/charts/google/templates/service.yaml +++ b/infrastructure/helm-chart/charts/apps/charts/sources/charts/google/templates/service.yaml @@ -2,7 +2,7 @@ apiVersion: v1 kind: Service metadata: name: sources-google-connector - namespace: {{ .Values.global.namespace }} + namespace: {{ .Values.global.kubernetes.namespace }} labels: core.airy.co/prometheus: spring spec: 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 index 528c2a086d..172d841ffb 100644 --- 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 @@ -2,7 +2,7 @@ apiVersion: apps/v1 kind: Deployment metadata: name: sources-twilio-connector - namespace: {{ .Values.global.namespace }} + namespace: {{ .Values.global.kubernetes.namespace }} labels: app: sources-twilio-connector type: sources @@ -26,7 +26,7 @@ spec: spec: containers: - name: app - image: "{{ .Values.global.containerRegistry}}/{{ .Values.imageConnector }}:{{ .Values.global.appImageTag }}" + image: "{{ .Values.global.kubernetes.containerRegistry}}/{{ .Values.imageConnector }}:{{ .Values.global.kubernetes.appImageTag }}" imagePullPolicy: Always envFrom: - configMapRef: @@ -92,7 +92,7 @@ apiVersion: apps/v1 kind: Deployment metadata: name: sources-twilio-events-router - namespace: {{ .Values.global.namespace }} + namespace: {{ .Values.global.kubernetes.namespace }} labels: app: sources-twilio-events-router type: sources @@ -118,7 +118,7 @@ spec: spec: containers: - name: app - image: "{{ .Values.global.containerRegistry}}/{{ .Values.imageEventsRouter }}:{{ .Values.global.appImageTag }}" + image: "{{ .Values.global.kubernetes.containerRegistry}}/{{ .Values.imageEventsRouter }}:{{ .Values.global.kubernetes.appImageTag }}" imagePullPolicy: Always env: - name: KAFKA_BROKERS diff --git a/infrastructure/helm-chart/charts/apps/charts/sources/charts/twilio/templates/service.yaml b/infrastructure/helm-chart/charts/apps/charts/sources/charts/twilio/templates/service.yaml index dccdc304c3..3f2ae12fdd 100644 --- a/infrastructure/helm-chart/charts/apps/charts/sources/charts/twilio/templates/service.yaml +++ b/infrastructure/helm-chart/charts/apps/charts/sources/charts/twilio/templates/service.yaml @@ -2,7 +2,7 @@ apiVersion: v1 kind: Service metadata: name: sources-twilio-connector - namespace: {{ .Values.global.namespace }} + namespace: {{ .Values.global.kubernetes.namespace }} labels: core.airy.co/prometheus: spring spec: diff --git a/infrastructure/helm-chart/charts/beanstalkd/templates/service.yaml b/infrastructure/helm-chart/charts/beanstalkd/templates/service.yaml index 5e98901988..3e76b1a783 100644 --- a/infrastructure/helm-chart/charts/beanstalkd/templates/service.yaml +++ b/infrastructure/helm-chart/charts/beanstalkd/templates/service.yaml @@ -2,7 +2,7 @@ apiVersion: v1 kind: Service metadata: name: beanstalk - namespace: {{ .Values.global.namespace }} + namespace: {{ .Values.global.kubernetes.namespace }} spec: type: ClusterIP ports: diff --git a/infrastructure/helm-chart/charts/beanstalkd/templates/statefulset.yaml b/infrastructure/helm-chart/charts/beanstalkd/templates/statefulset.yaml index c7563570af..d1c604bc11 100644 --- a/infrastructure/helm-chart/charts/beanstalkd/templates/statefulset.yaml +++ b/infrastructure/helm-chart/charts/beanstalkd/templates/statefulset.yaml @@ -2,7 +2,7 @@ apiVersion: apps/v1 kind: StatefulSet metadata: name: beanstalkd - namespace: {{ .Values.global.namespace}} + namespace: {{ .Values.global.kubernetes.namespace}} spec: serviceName: beanstalkd replicas: 1 diff --git a/infrastructure/helm-chart/charts/controller/templates/deployment.yaml b/infrastructure/helm-chart/charts/controller/templates/deployment.yaml index 10344f4fa8..205b05c359 100644 --- a/infrastructure/helm-chart/charts/controller/templates/deployment.yaml +++ b/infrastructure/helm-chart/charts/controller/templates/deployment.yaml @@ -2,7 +2,7 @@ apiVersion: apps/v1 kind: Deployment metadata: name: airy-controller - namespace: {{ .Values.global.namespace }} + namespace: {{ .Values.global.kubernetes.namespace }} labels: app: airy-controller spec: @@ -24,7 +24,7 @@ spec: automountServiceAccountToken: true containers: - name: app - image: "{{ .Values.global.containerRegistry}}/{{ .Values.image }}:{{ .Values.global.appImageTag }}" + image: "{{ .Values.global.kubernetes.containerRegistry}}/{{ .Values.image }}:{{ .Values.global.kubernetes.appImageTag }}" imagePullPolicy: Always env: - name: LABEL_SELECTOR diff --git a/infrastructure/helm-chart/charts/controller/templates/service.yaml b/infrastructure/helm-chart/charts/controller/templates/service.yaml index 4c6c76c112..515d208102 100644 --- a/infrastructure/helm-chart/charts/controller/templates/service.yaml +++ b/infrastructure/helm-chart/charts/controller/templates/service.yaml @@ -2,7 +2,7 @@ apiVersion: v1 kind: Service metadata: name: airy-controller - namespace: {{ .Values.global.namespace }} + namespace: {{ .Values.global.kubernetes.namespace }} spec: ports: - name: web diff --git a/infrastructure/helm-chart/charts/controller/templates/serviceAccount.yaml b/infrastructure/helm-chart/charts/controller/templates/serviceAccount.yaml index 64aa249cb5..589bfd3911 100644 --- a/infrastructure/helm-chart/charts/controller/templates/serviceAccount.yaml +++ b/infrastructure/helm-chart/charts/controller/templates/serviceAccount.yaml @@ -2,14 +2,14 @@ apiVersion: v1 kind: ServiceAccount metadata: name: airy-controller - namespace: {{ .Values.global.namespace }} + namespace: {{ .Values.global.kubernetes.namespace }} automountServiceAccountToken: true --- kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: name: airy-controller - namespace: {{ .Values.global.namespace }} + namespace: {{ .Values.global.kubernetes.namespace }} rules: - apiGroups: [""] resources: ["pods", "configmaps", "services"] @@ -22,11 +22,11 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: airy-controller - namespace: {{ .Values.global.namespace }} + namespace: {{ .Values.global.kubernetes.namespace }} subjects: - kind: ServiceAccount name: airy-controller - namespace: {{ .Values.global.namespace }} + namespace: {{ .Values.global.kubernetes.namespace }} roleRef: kind: ClusterRole name: airy-controller diff --git a/infrastructure/helm-chart/charts/kafka/charts/kafka/templates/headless-service.yaml b/infrastructure/helm-chart/charts/kafka/charts/kafka/templates/headless-service.yaml index c0ed8c0a8f..cc9f83021f 100644 --- a/infrastructure/helm-chart/charts/kafka/charts/kafka/templates/headless-service.yaml +++ b/infrastructure/helm-chart/charts/kafka/charts/kafka/templates/headless-service.yaml @@ -2,7 +2,7 @@ apiVersion: v1 kind: Service metadata: name: {{ template "kafka.fullname" . }}-headless - namespace: {{ .Values.global.namespace }} + namespace: {{ .Values.global.kubernetes.namespace }} labels: app: {{ template "kafka.name" . }} chart: {{ template "kafka.chart" . }} @@ -15,4 +15,4 @@ spec: clusterIP: None selector: app: {{ template "kafka.name" . }} - release: {{ .Release.Name }} \ No newline at end of file + release: {{ .Release.Name }} diff --git a/infrastructure/helm-chart/charts/kafka/charts/kafka/templates/nodeport-service.yaml b/infrastructure/helm-chart/charts/kafka/charts/kafka/templates/nodeport-service.yaml index 1395e58323..29226f1362 100644 --- a/infrastructure/helm-chart/charts/kafka/charts/kafka/templates/nodeport-service.yaml +++ b/infrastructure/helm-chart/charts/kafka/charts/kafka/templates/nodeport-service.yaml @@ -11,7 +11,7 @@ apiVersion: v1 kind: Service metadata: name: "{{ template "kafka.fullname" $root }}-{{ $i }}-nodeport" - namespace: {{ $root.Values.global.namespace }} + namespace: {{ $root.Values.global.kubernetes.namespace }} labels: app: {{ include "kafka.name" $root }} chart: {{ template "kafka.chart" $root }} diff --git a/infrastructure/helm-chart/charts/kafka/charts/kafka/templates/prometheus.yaml b/infrastructure/helm-chart/charts/kafka/charts/kafka/templates/prometheus.yaml index 28d5623ea1..fe8cdc3400 100644 --- a/infrastructure/helm-chart/charts/kafka/charts/kafka/templates/prometheus.yaml +++ b/infrastructure/helm-chart/charts/kafka/charts/kafka/templates/prometheus.yaml @@ -7,7 +7,7 @@ apiVersion: apps/v1beta2 kind: Deployment metadata: name: {{ template "kafka.prometheus.name" . }} - namespace: {{ .Values.global.namespace }} + namespace: {{ .Values.global.kubernetes.namespace }} labels: app: {{ template "kafka.prometheus.name" . }} chart: {{ template "kafka.chart" . }} diff --git a/infrastructure/helm-chart/charts/kafka/charts/kafka/templates/service.yaml b/infrastructure/helm-chart/charts/kafka/charts/kafka/templates/service.yaml index ebd6edba7e..332395d659 100644 --- a/infrastructure/helm-chart/charts/kafka/charts/kafka/templates/service.yaml +++ b/infrastructure/helm-chart/charts/kafka/charts/kafka/templates/service.yaml @@ -2,7 +2,7 @@ apiVersion: v1 kind: Service metadata: name: {{ template "kafka.fullname" . }} - namespace: {{ .Values.global.namespace }} + namespace: {{ .Values.global.kubernetes.namespace }} labels: app: {{ template "kafka.name" . }} chart: {{ template "kafka.chart" . }} @@ -21,7 +21,7 @@ apiVersion: v1 kind: Service metadata: name: {{ template "kafka.prometheus.name" . }} - namespace: {{ .Values.global.namespace }} + namespace: {{ .Values.global.kubernetes.namespace }} labels: app: {{ template "kafka.prometheus.name" . }} chart: {{ template "kafka.chart" . }} diff --git a/infrastructure/helm-chart/charts/kafka/charts/kafka/templates/statefulset.yaml b/infrastructure/helm-chart/charts/kafka/charts/kafka/templates/statefulset.yaml index 11ad7b06be..0f34598ff7 100644 --- a/infrastructure/helm-chart/charts/kafka/charts/kafka/templates/statefulset.yaml +++ b/infrastructure/helm-chart/charts/kafka/charts/kafka/templates/statefulset.yaml @@ -6,7 +6,7 @@ apiVersion: apps/v1beta1 kind: StatefulSet metadata: name: {{ template "kafka.fullname" . }} - namespace: {{ .Values.global.namespace }} + namespace: {{ .Values.global.kubernetes.namespace }} labels: app: {{ template "kafka.name" . }} chart: {{ template "kafka.chart" . }} diff --git a/infrastructure/helm-chart/charts/kafka/charts/schema-registry/templates/deployment.yaml b/infrastructure/helm-chart/charts/kafka/charts/schema-registry/templates/deployment.yaml index d82cd3e106..218e4b66ea 100644 --- a/infrastructure/helm-chart/charts/kafka/charts/schema-registry/templates/deployment.yaml +++ b/infrastructure/helm-chart/charts/kafka/charts/schema-registry/templates/deployment.yaml @@ -6,7 +6,7 @@ apiVersion: apps/v1beta2 kind: Deployment metadata: name: schema-registry - namespace: {{ .Values.global.namespace }} + namespace: {{ .Values.global.kubernetes.namespace }} labels: app: schema-registry release: {{ .Release.Name }} @@ -24,7 +24,7 @@ spec: spec: containers: - name: schema-registry-server - image: "{{ .Values.global.containerRegistry}}/{{ .Values.image }}:{{ .Values.imageTag }}" + image: "{{ .Values.global.kubernetes.containerRegistry}}/{{ .Values.image }}:{{ .Values.imageTag }}" imagePullPolicy: "{{ .Values.imagePullPolicy }}" ports: - name: schema-registry diff --git a/infrastructure/helm-chart/charts/kafka/charts/zookeeper/templates/headless-service.yaml b/infrastructure/helm-chart/charts/kafka/charts/zookeeper/templates/headless-service.yaml index bf68b9d2fb..59e1c1d68e 100644 --- a/infrastructure/helm-chart/charts/kafka/charts/zookeeper/templates/headless-service.yaml +++ b/infrastructure/helm-chart/charts/kafka/charts/zookeeper/templates/headless-service.yaml @@ -2,7 +2,7 @@ apiVersion: v1 kind: Service metadata: name: {{ template "cp-zookeeper.fullname" . }}-headless - namespace: {{ .Values.global.namespace }} + namespace: {{ .Values.global.kubernetes.namespace }} labels: app: {{ template "cp-zookeeper.name" . }} chart: {{ template "cp-zookeeper.chart" . }} @@ -17,4 +17,4 @@ spec: clusterIP: None selector: app: {{ template "cp-zookeeper.name" . }} - release: {{ .Release.Name }} \ No newline at end of file + release: {{ .Release.Name }} diff --git a/infrastructure/helm-chart/charts/kafka/charts/zookeeper/templates/poddisruptionbudget.yaml b/infrastructure/helm-chart/charts/kafka/charts/zookeeper/templates/poddisruptionbudget.yaml index 95e7873b43..c51f5a5676 100644 --- a/infrastructure/helm-chart/charts/kafka/charts/zookeeper/templates/poddisruptionbudget.yaml +++ b/infrastructure/helm-chart/charts/kafka/charts/zookeeper/templates/poddisruptionbudget.yaml @@ -2,7 +2,7 @@ apiVersion: policy/v1beta1 kind: PodDisruptionBudget metadata: name: {{ template "cp-zookeeper.fullname" . }}-pdb - namespace: {{ .Values.global.namespace }} + namespace: {{ .Values.global.kubernetes.namespace }} labels: app: {{ template "cp-zookeeper.name" . }} chart: {{ template "cp-zookeeper.chart" . }} diff --git a/infrastructure/helm-chart/charts/kafka/charts/zookeeper/templates/service.yaml b/infrastructure/helm-chart/charts/kafka/charts/zookeeper/templates/service.yaml index 46a5f3e01d..943c20466c 100644 --- a/infrastructure/helm-chart/charts/kafka/charts/zookeeper/templates/service.yaml +++ b/infrastructure/helm-chart/charts/kafka/charts/zookeeper/templates/service.yaml @@ -2,7 +2,7 @@ apiVersion: v1 kind: Service metadata: name: {{ template "cp-zookeeper.fullname" . }} - namespace: {{ .Values.global.namespace }} + namespace: {{ .Values.global.kubernetes.namespace }} labels: app: {{ template "cp-zookeeper.name" . }} chart: {{ template "cp-zookeeper.chart" . }} diff --git a/infrastructure/helm-chart/charts/kafka/charts/zookeeper/templates/statefulset.yaml b/infrastructure/helm-chart/charts/kafka/charts/zookeeper/templates/statefulset.yaml index e8f3732810..80e2c6225c 100644 --- a/infrastructure/helm-chart/charts/kafka/charts/zookeeper/templates/statefulset.yaml +++ b/infrastructure/helm-chart/charts/kafka/charts/zookeeper/templates/statefulset.yaml @@ -6,7 +6,7 @@ apiVersion: apps/v1beta1 kind: StatefulSet metadata: name: {{ template "cp-zookeeper.fullname" . }} - namespace: {{ .Values.global.namespace }} + namespace: {{ .Values.global.kubernetes.namespace }} labels: app: {{ template "cp-zookeeper.name" . }} chart: {{ template "cp-zookeeper.chart" . }} diff --git a/infrastructure/helm-chart/charts/prerequisites/templates/beanstalk.yaml b/infrastructure/helm-chart/charts/prerequisites/templates/beanstalk.yaml index e16f2f2879..e490959472 100644 --- a/infrastructure/helm-chart/charts/prerequisites/templates/beanstalk.yaml +++ b/infrastructure/helm-chart/charts/prerequisites/templates/beanstalk.yaml @@ -2,7 +2,7 @@ apiVersion: v1 kind: ConfigMap metadata: name: beanstalk-config - namespace: {{ .Values.global.namespace }} + namespace: {{ .Values.global.kubernetes.namespace }} data: BEANSTALK_HOSTNAME: {{ .Values.beanstalk.hostname }} BEANSTALK_PORT: "{{ .Values.beanstalk.port }}" diff --git a/infrastructure/helm-chart/charts/prerequisites/templates/kafka.yaml b/infrastructure/helm-chart/charts/prerequisites/templates/kafka.yaml index b9a09b6948..1377f2e146 100644 --- a/infrastructure/helm-chart/charts/prerequisites/templates/kafka.yaml +++ b/infrastructure/helm-chart/charts/prerequisites/templates/kafka.yaml @@ -2,7 +2,7 @@ apiVersion: v1 kind: ConfigMap metadata: name: kafka-config - namespace: {{ .Values.global.namespace }} + namespace: {{ .Values.global.kubernetes.namespace }} data: KAFKA_BROKERS: {{ .Values.kafka.brokers }} KAFKA_MINIMUM_REPLICAS: "1" diff --git a/infrastructure/helm-chart/charts/provisioning/templates/job-wait.yaml b/infrastructure/helm-chart/charts/provisioning/templates/job-wait.yaml index bb906d9308..de96d6d759 100644 --- a/infrastructure/helm-chart/charts/provisioning/templates/job-wait.yaml +++ b/infrastructure/helm-chart/charts/provisioning/templates/job-wait.yaml @@ -13,7 +13,7 @@ spec: command: ["/bin/sh", "/opt/provisioning/wait-for-service-url.sh"] env: - name: SERVICE_URL - value: api-communication.{{ .Values.global.namespace }}:80 + value: api-communication.{{ .Values.global.kubernetes.namespace }}:80 volumeMounts: - name: provisioning-scripts mountPath: /opt/provisioning @@ -39,7 +39,7 @@ spec: command: ["/bin/sh", "/opt/provisioning/wait-for-service-url.sh"] env: - name: SERVICE_URL - value: frontend-ui.{{ .Values.global.namespace }}:80 + value: frontend-ui.{{ .Values.global.kubernetes.namespace }}:80 volumeMounts: - name: provisioning-scripts mountPath: /opt/provisioning @@ -65,7 +65,7 @@ spec: command: ["/bin/sh", "/opt/provisioning/wait-for-service-url.sh"] env: - name: SERVICE_URL - value: frontend-chat-plugin.{{ .Values.global.namespace }}:80 + value: frontend-chat-plugin.{{ .Values.global.kubernetes.namespace }}:80 volumeMounts: - name: provisioning-scripts mountPath: /opt/provisioning diff --git a/infrastructure/helm-chart/charts/provisioning/templates/kafka-create-topics.yaml b/infrastructure/helm-chart/charts/provisioning/templates/kafka-create-topics.yaml index 7988e5d864..6f4f46bf78 100644 --- a/infrastructure/helm-chart/charts/provisioning/templates/kafka-create-topics.yaml +++ b/infrastructure/helm-chart/charts/provisioning/templates/kafka-create-topics.yaml @@ -41,8 +41,6 @@ data: kafka-topics.sh --create --if-not-exists --zookeeper "${ZOOKEEPER}" --replication-factor "${REPLICAS}" --partitions "${PARTITIONS}" --topic "${AIRY_CORE_NAMESPACE}application.communication.webhooks" - kafka-topics.sh --create --if-not-exists --zookeeper "${ZOOKEEPER}" --replication-factor "${REPLICAS}" --partitions "${PARTITIONS}" --topic "${AIRY_CORE_NAMESPACE}ops.application.health" --config retention.ms=3600000 - kafka-topics.sh --create --if-not-exists --zookeeper "${ZOOKEEPER}" --replication-factor "${REPLICAS}" --partitions "${PARTITIONS}" --topic "${AIRY_CORE_NAMESPACE}ops.application.logs" kafka-topics.sh --create --if-not-exists --zookeeper "${ZOOKEEPER}" --replication-factor "${REPLICAS}" --partitions "${PARTITIONS}" --topic "${AIRY_CORE_NAMESPACE}source.facebook.events" diff --git a/infrastructure/helm-chart/charts/tools/charts/ahkq/templates/deployment.yaml b/infrastructure/helm-chart/charts/tools/charts/ahkq/templates/deployment.yaml index 54cba71b22..02fbf6e711 100644 --- a/infrastructure/helm-chart/charts/tools/charts/ahkq/templates/deployment.yaml +++ b/infrastructure/helm-chart/charts/tools/charts/ahkq/templates/deployment.yaml @@ -24,7 +24,7 @@ spec: spec: containers: - name: app - image: tchiotludo/akhq:0.16.0 + image: tchiotludo/akhq:0.16.0 imagePullPolicy: Always volumeMounts: - name: akhq-config diff --git a/infrastructure/helm-chart/charts/tools/charts/ahkq/templates/ingress.yaml b/infrastructure/helm-chart/charts/tools/charts/ahkq/templates/ingress.yaml index 26a4a27956..ea34bcf65c 100644 --- a/infrastructure/helm-chart/charts/tools/charts/ahkq/templates/ingress.yaml +++ b/infrastructure/helm-chart/charts/tools/charts/ahkq/templates/ingress.yaml @@ -3,12 +3,12 @@ kind: Ingress apiVersion: networking.k8s.io/v1 metadata: name: airy-core-tools - namespace: {{ .Values.global.namespace }} + namespace: {{ .Values.global.kubernetes.namespace }} annotations: kubernetes.io/ingress.class: traefik spec: rules: - - host: {{ get (urlParse .Values.global.host) "host" }} + - host: {{ get (urlParse .Values.global.kubernetes.host) "host" }} http: paths: - path: /tools/akhq diff --git a/infrastructure/helm-chart/templates/configmap.yaml b/infrastructure/helm-chart/templates/configmap.yaml index 9201578330..0d4d1febc6 100644 --- a/infrastructure/helm-chart/templates/configmap.yaml +++ b/infrastructure/helm-chart/templates/configmap.yaml @@ -2,6 +2,18 @@ apiVersion: v1 kind: ConfigMap metadata: name: hostnames - namespace: {{ .Values.global.namespace }} + namespace: {{ .Values.global.kubernetes.namespace }} data: - HOST: {{ .Values.global.host }} + HOST: {{ .Values.global.kubernetes.host }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: security + namespace: {{ .Values.global.kubernetes.namespace }} +data: + {{- if .Values.global.security.systemToken }} + systemToken: {{ .Values.global.security.systemToken | quote }} + {{- end }} + jwtSecret: {{ .Values.global.security.jwtSecret | quote }} + allowedOrigins: {{ .Values.global.security.allowedOrigins | quote }} diff --git a/infrastructure/helm-chart/templates/core.yaml b/infrastructure/helm-chart/templates/core.yaml index e8bb29acb6..4e176a7600 100644 --- a/infrastructure/helm-chart/templates/core.yaml +++ b/infrastructure/helm-chart/templates/core.yaml @@ -2,8 +2,8 @@ apiVersion: v1 kind: ConfigMap metadata: name: core-config - namespace: {{ .Values.global.namespace }} + namespace: {{ .Values.global.kubernetes.namespace }} data: - APP_IMAGE_TAG: {{ .Values.global.appImageTag }} + APP_IMAGE_TAG: {{ .Values.global.kubernetes.appImageTag }} CORE_ID: {{ randAlphaNum 10 | lower }} CHATPLUGIN_JWT_SECRET: {{ randAlphaNum 128 | quote }} diff --git a/infrastructure/helm-chart/templates/ingress-controller.yaml b/infrastructure/helm-chart/templates/ingress-controller.yaml index c2d93cc032..287451122f 100644 --- a/infrastructure/helm-chart/templates/ingress-controller.yaml +++ b/infrastructure/helm-chart/templates/ingress-controller.yaml @@ -86,8 +86,8 @@ metadata: name: traefik namespace: kube-system annotations: - {{- range .Values.global.loadbalancer.annotations }} - {{ . }} + {{- range $k, $v := .Values.global.kubernetes.loadbalancerAnnotations }} + {{ $k }}: {{ $v }} {{- end }} spec: selector: @@ -98,21 +98,3 @@ spec: targetPort: 80 name: web type: LoadBalancer -{{ if .Values.global.nodePort }} ---- -kind: Service -apiVersion: v1 -metadata: - name: traefik-node-port - namespace: kube-system -spec: - selector: - k8s-app: traefik-ingress-lb - ports: - - protocol: TCP - port: 80 - nodePort: {{ .Values.global.nodePort }} - targetPort: 80 - name: web - type: NodePort -{{end}} diff --git a/infrastructure/helm-chart/templates/ingress-ui.yaml b/infrastructure/helm-chart/templates/ingress-ui.yaml index a80424b39b..0094317e89 100644 --- a/infrastructure/helm-chart/templates/ingress-ui.yaml +++ b/infrastructure/helm-chart/templates/ingress-ui.yaml @@ -2,13 +2,13 @@ kind: Ingress apiVersion: networking.k8s.io/v1 metadata: name: 'airy-core-ui' - namespace: {{ .Values.global.namespace }} + namespace: {{ .Values.global.kubernetes.namespace }} annotations: kubernetes.io/ingress.class: "traefik" traefik.frontend.rule.type: PathPrefixStrip spec: rules: - - host: {{ get (urlParse .Values.global.host) "host" }} + - host: {{ get (urlParse .Values.global.kubernetes.host) "host" }} http: paths: - path: /ui diff --git a/infrastructure/helm-chart/templates/ingress.yaml b/infrastructure/helm-chart/templates/ingress.yaml index 43fdc92023..09ec25c1bb 100644 --- a/infrastructure/helm-chart/templates/ingress.yaml +++ b/infrastructure/helm-chart/templates/ingress.yaml @@ -2,10 +2,10 @@ kind: Ingress apiVersion: networking.k8s.io/v1 metadata: name: 'airy-core' - namespace: {{ .Values.global.namespace }} + namespace: {{ .Values.global.kubernetes.namespace }} spec: rules: - - host: {{ get (urlParse .Values.global.host) "host" }} + - host: {{ get (urlParse .Values.global.kubernetes.host) "host" }} http: paths: - path: /ws.communication diff --git a/infrastructure/helm-chart/templates/ngrok.yaml b/infrastructure/helm-chart/templates/ngrok.yaml index 0d532c6540..c604607921 100644 --- a/infrastructure/helm-chart/templates/ngrok.yaml +++ b/infrastructure/helm-chart/templates/ngrok.yaml @@ -1,9 +1,25 @@ -{{ if .Values.global.ngrokEnabled }} +{{ if .Values.global.kubernetes.ngrokEnabled }} +kind: Service +apiVersion: v1 +metadata: + name: traefik-node-port + namespace: kube-system +spec: + selector: + k8s-app: traefik-ingress-lb + ports: + - protocol: TCP + port: 80 + nodePort: 80 + targetPort: 80 + name: web + type: NodePort +--- kind: Ingress apiVersion: networking.k8s.io/v1 metadata: name: 'airy-core-ngrok' - namespace: {{ .Values.global.namespace }} + namespace: {{ .Values.global.kubernetes.namespace }} spec: rules: - host: '*.tunnel.airy.co' @@ -35,7 +51,7 @@ apiVersion: apps/v1 kind: Deployment metadata: name: ngrok-proxy - namespace: {{ .Values.global.namespace }} + namespace: {{ .Values.global.kubernetes.namespace }} labels: app: ngrok-proxy spec: @@ -78,7 +94,7 @@ apiVersion: v1 kind: ConfigMap metadata: name: ngrok-client-config - namespace: {{ .Values.global.namespace }} + namespace: {{ .Values.global.kubernetes.namespace }} data: config.yml: | server_addr: proxy.tunnel.airy.co:4443 diff --git a/infrastructure/helm-chart/values.yaml b/infrastructure/helm-chart/values.yaml index 3e0e8cfff5..bcbfaf7f2c 100644 --- a/infrastructure/helm-chart/values.yaml +++ b/infrastructure/helm-chart/values.yaml @@ -1,8 +1,4 @@ global: - # required override: appImageTag - containerRegistry: ghcr.io/airyhq - namespace: default - ngrokEnabled: false - host: http://airy.core - loadbalancer: - annotations: {} + kubernetes: + host: "http://airy.core" + loadbalancerAnnotations: {} diff --git a/integration/chat-plugin/end_conversation.spec.ts b/integration/chat-plugin/end_conversation.spec.ts index 62667f3fbb..a6335577a7 100644 --- a/integration/chat-plugin/end_conversation.spec.ts +++ b/integration/chat-plugin/end_conversation.spec.ts @@ -1,5 +1,4 @@ import { - cyBubble, cyInputbarTextarea, cyInputbarButton, cyChatPluginMessageList, @@ -11,7 +10,6 @@ import { 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); @@ -21,7 +19,6 @@ describe('End ChatPlugin Conversation', () => { 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); diff --git a/integration/chat-plugin/websocket_test.spec.ts b/integration/chat-plugin/websocket_test.spec.ts index 2995c44a11..21050acdf4 100644 --- a/integration/chat-plugin/websocket_test.spec.ts +++ b/integration/chat-plugin/websocket_test.spec.ts @@ -1,8 +1,7 @@ -import {cyBubble, cyInputbarTextarea, cyInputbarButton, cyChatPluginMessageList} from 'chat-plugin-handles'; +import {cyInputbarTextarea, cyInputbarButton, cyChatPluginMessageList} from 'chat-plugin-handles'; import { cyChannelsChatPluginAddButton, - cyChannelsChatPluginConnectButton, cyChannelsChatPluginFormNameInput, cyChannelsChatPluginFormSubmitButton, } from 'handles'; @@ -13,12 +12,10 @@ describe('Websocket test', () => { cy.wait(500); cy.url().should('include', '/channels'); cy.get(`[data-cy=${cyChannelsChatPluginAddButton}]`).click(); - cy.get(`[data-cy=${cyChannelsChatPluginConnectButton}]`).click(); cy.get(`[data-cy=${cyChannelsChatPluginFormNameInput}]`).type(Cypress.env('chatPluginName')); cy.get(`[data-cy=${cyChannelsChatPluginFormSubmitButton}]`).click(); 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/integration/ui/connect_chatplugin.spec.ts b/integration/ui/connect_chatplugin.spec.ts index 4c7bdaa605..f273a7b412 100644 --- a/integration/ui/connect_chatplugin.spec.ts +++ b/integration/ui/connect_chatplugin.spec.ts @@ -1,10 +1,9 @@ import { cyChannelsChatPluginAddButton, - cyChannelsChatPluginConnectButton, cyChannelsChatPluginFormNameInput, cyChannelsChatPluginFormSubmitButton, - cyChannelsChatPluginList, cyChannelsFormBackButton, + cyChannelsChatPluginList, } from 'handles'; describe('Connect chatplugin channel', () => { @@ -13,7 +12,6 @@ describe('Connect chatplugin channel', () => { cy.wait(500); cy.url().should('include', '/ui/channels'); cy.get(`[data-cy=${cyChannelsChatPluginAddButton}]`).click(); - cy.get(`[data-cy=${cyChannelsChatPluginConnectButton}]`).click(); cy.get(`[data-cy=${cyChannelsChatPluginFormNameInput}]`).type(Cypress.env('chatPluginName')); cy.get(`[data-cy=${cyChannelsChatPluginFormSubmitButton}]`).click(); cy.url().should('include', '/ui/channels/connected'); diff --git a/integration/ui/create_tag.spec.ts b/integration/ui/create_tag.spec.ts index 29a9eba978..e634d597fa 100644 --- a/integration/ui/create_tag.spec.ts +++ b/integration/ui/create_tag.spec.ts @@ -6,7 +6,6 @@ import { cyTagsSearchField, cyTagsTable, cyChannelsChatPluginAddButton, - cyChannelsChatPluginConnectButton, cyChannelsChatPluginFormNameInput, cyChannelsChatPluginFormSubmitButton, cyTagsTableRowDisplayDeleteModal, @@ -15,21 +14,19 @@ import { cyChannelsFormBackButton, } from 'handles'; -import {cyBubble, cyInputbarButton, cyInputbarTextarea} from 'chat-plugin-handles'; +import {cyInputbarButton, cyInputbarTextarea} from 'chat-plugin-handles'; describe('Creates and Deletes a Tag', () => { it('Creates and Deletes a Tag', () => { cy.visit('/ui/channels'); cy.wait(500); cy.get(`[data-cy=${cyChannelsChatPluginAddButton}]`).click(); - cy.get(`[data-cy=${cyChannelsChatPluginConnectButton}]`).click(); cy.get(`[data-cy=${cyChannelsChatPluginFormNameInput}]`).type(Cypress.env('chatPluginName')); cy.get(`[data-cy=${cyChannelsChatPluginFormSubmitButton}]`).click(); cy.get(`[data-cy=${cyChannelsFormBackButton}]`).click(); 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/integration/ui/filter_conversation.spec.ts b/integration/ui/filter_conversation.spec.ts index b9724fd1cf..d5a019535a 100644 --- a/integration/ui/filter_conversation.spec.ts +++ b/integration/ui/filter_conversation.spec.ts @@ -3,13 +3,12 @@ import { cySearchField, cyConversationList, cyChannelsChatPluginAddButton, - cyChannelsChatPluginConnectButton, cyChannelsChatPluginFormNameInput, cyChannelsChatPluginFormSubmitButton, cyChannelsFormBackButton, } from 'handles'; -import {cyBubble, cyInputbarButton, cyInputbarTextarea} from 'chat-plugin-handles'; +import {cyInputbarButton, cyInputbarTextarea} from 'chat-plugin-handles'; describe('Filter conversation', () => { it('Filter conversation', () => { @@ -17,14 +16,12 @@ describe('Filter conversation', () => { cy.wait(500); cy.get(`[data-cy=${cyChannelsChatPluginAddButton}]`).click(); - cy.get(`[data-cy=${cyChannelsChatPluginConnectButton}]`).click(); cy.get(`[data-cy=${cyChannelsChatPluginFormNameInput}]`).type(Cypress.env('chatPluginName')); cy.get(`[data-cy=${cyChannelsChatPluginFormSubmitButton}]`).click(); cy.get(`[data-cy=${cyChannelsFormBackButton}]`).click(); 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/integration/ui/send_chatplugin_message.spec.ts b/integration/ui/send_chatplugin_message.spec.ts index f324d8b28c..3cd7091200 100644 --- a/integration/ui/send_chatplugin_message.spec.ts +++ b/integration/ui/send_chatplugin_message.spec.ts @@ -3,21 +3,19 @@ import { cyMessageSendButton, cyMessageList, cyChannelsChatPluginAddButton, - cyChannelsChatPluginConnectButton, cyChannelsChatPluginFormNameInput, cyChannelsChatPluginFormSubmitButton, cyChannelsChatPluginList, cyChannelsFormBackButton, } from 'handles'; -import {cyBubble, cyInputbarButton, cyInputbarTextarea} from 'chat-plugin-handles'; +import {cyInputbarButton, cyInputbarTextarea} from 'chat-plugin-handles'; describe('Send chat plugin message', () => { it('Send chat plugin message', () => { cy.visit('/ui/channels'); cy.wait(500); cy.get(`[data-cy=${cyChannelsChatPluginAddButton}]`).click(); - cy.get(`[data-cy=${cyChannelsChatPluginConnectButton}]`).click(); cy.get(`[data-cy=${cyChannelsChatPluginFormNameInput}]`).type(Cypress.env('chatPluginName')); cy.get(`[data-cy=${cyChannelsChatPluginFormSubmitButton}]`).click(); @@ -26,7 +24,6 @@ describe('Send chat plugin message', () => { cy.get(`[data-cy=${cyChannelsChatPluginList}]`).filter(`:contains("${Cypress.env('chatPluginName')}")`); 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/kafka/schema/src/main/java/co/airy/kafka/schema/ops/OpsApplicationHealth.java b/lib/java/kafka/schema/src/main/java/co/airy/kafka/schema/ops/OpsApplicationHealth.java deleted file mode 100644 index 4b8ac1dd59..0000000000 --- a/lib/java/kafka/schema/src/main/java/co/airy/kafka/schema/ops/OpsApplicationHealth.java +++ /dev/null @@ -1,16 +0,0 @@ -package co.airy.kafka.schema.ops; - -import co.airy.kafka.schema.OpsApplication; - -import java.util.Map; - -public class OpsApplicationHealth extends OpsApplication { - - @Override - public String dataset() { - return "health"; - } - - @Override - public Map config() { return Map.of("retention.ms", "3600000");} // 1 hour -} 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 e794db20b1..b3bffaf3b9 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 @@ -19,7 +19,9 @@ import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; +import org.springframework.security.oauth2.client.oidc.web.logout.OidcClientInitiatedLogoutSuccessHandler; import org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter; import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.security.web.access.AccessDeniedHandler; @@ -27,6 +29,7 @@ import org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint; import org.springframework.security.web.authentication.Http403ForbiddenEntryPoint; import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint; +import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; import org.springframework.security.web.util.matcher.AndRequestMatcher; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.security.web.util.matcher.NegatedRequestMatcher; @@ -43,6 +46,7 @@ import java.util.Arrays; import java.util.LinkedHashMap; import java.util.List; +import java.util.Optional; @Configuration @EnableWebSecurity @@ -56,14 +60,14 @@ public class AuthConfig extends WebSecurityConfigurerAdapter { private final String[] ignoreAuthPatterns; private final String systemToken; private final String jwtSecret; - private final UserService userService; private final ConfigProvider configProvider; + private final String logoutSuccessUrl; public AuthConfig(@Value("${systemToken:#{null}}") String systemToken, @Value("${jwtSecret:#{null}}") String jwtSecret, List ignorePatternBeans, ConfigProvider configProvider, - UserService userService + @Value("${oidc.logoutSuccessUrl:/ui/}") String logoutSuccessUrl ) { this.systemToken = systemToken; this.jwtSecret = jwtSecret; @@ -71,7 +75,7 @@ public AuthConfig(@Value("${systemToken:#{null}}") String systemToken, .flatMap((ignoreAuthPatternBean -> ignoreAuthPatternBean.getIgnorePattern().stream())) .toArray(String[]::new); this.configProvider = configProvider; - this.userService = userService; + this.logoutSuccessUrl = logoutSuccessUrl; } @Override @@ -94,20 +98,26 @@ protected void configure(final HttpSecurity http) throws Exception { } if (configProvider.isPresent()) { - log.info("Oidc auth enabled with provider: {}", configProvider.getRegistration().getRegistrationId()); + final String registrationId = configProvider.getRegistration().getRegistrationId(); + log.info("Oidc auth enabled with provider: {}", registrationId); + + // By default oauth2Login creates an authentication entrypoint that redirects clients to the + // login form. For API clients we instead want to return a 403. + http.exceptionHandling() + .defaultAuthenticationEntryPointFor(new Http403ForbiddenEntryPoint(), + new NegatedRequestMatcher(new OrRequestMatcher(new AntPathRequestMatcher("/login/**"), + new AntPathRequestMatcher("/logout/**"), new AntPathRequestMatcher("/oauth/**")))); http .securityContext().securityContextRepository(new CookieSecurityContextRepository(new Jwt(jwtSecret))) .and().logout().permitAll().deleteCookies(AuthCookie.NAME) + .logoutSuccessUrl(logoutSuccessUrl) .and() - .oauth2Login(oauth2 -> oauth2.defaultSuccessUrl("/ui/")) + .oauth2Login(oauth2 -> oauth2 + // Replace the default login page with co.airy.spring.auth.oidc.LoginRedirect + .loginPage("/login") + .defaultSuccessUrl("/ui/")) .addFilterAfter(new EmailFilter(configProvider), OAuth2LoginAuthenticationFilter.class); - - // By default oauth2Login creates an authentication entrypoint that redirects clients to the - // login form. For API clients we instead want to return a 403. - http.exceptionHandling().defaultAuthenticationEntryPointFor(new Http403ForbiddenEntryPoint(), - new NegatedRequestMatcher(new OrRequestMatcher(new AntPathRequestMatcher("/login/**"), - new AntPathRequestMatcher("/logout/**"), new AntPathRequestMatcher("/oauth/**")))); } } } diff --git a/lib/java/spring/auth/src/main/java/co/airy/spring/auth/oidc/ClientConfig.java b/lib/java/spring/auth/src/main/java/co/airy/spring/auth/oidc/ClientConfig.java index a46c2b4ec8..b9b6bff511 100644 --- a/lib/java/spring/auth/src/main/java/co/airy/spring/auth/oidc/ClientConfig.java +++ b/lib/java/spring/auth/src/main/java/co/airy/spring/auth/oidc/ClientConfig.java @@ -7,10 +7,12 @@ import org.springframework.context.annotation.Configuration; import org.springframework.security.oauth2.client.InMemoryOAuth2AuthorizedClientService; import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService; +import org.springframework.security.oauth2.client.oidc.web.logout.OidcClientInitiatedLogoutSuccessHandler; import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository; import org.springframework.security.oauth2.client.web.AuthenticatedPrincipalOAuth2AuthorizedClientRepository; import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository; +import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; @Configuration public class ClientConfig { diff --git a/lib/java/spring/auth/src/main/java/co/airy/spring/auth/oidc/LoginRedirect.java b/lib/java/spring/auth/src/main/java/co/airy/spring/auth/oidc/LoginRedirect.java new file mode 100644 index 0000000000..b744efecd4 --- /dev/null +++ b/lib/java/spring/auth/src/main/java/co/airy/spring/auth/oidc/LoginRedirect.java @@ -0,0 +1,24 @@ +package co.airy.spring.auth.oidc; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +@RestController +@ConditionalOnBean(ClientRegistrationRepository.class) +public class LoginRedirect { + private final String registrationId; + public LoginRedirect(ConfigProvider config) { + this.registrationId = config.getRegistration().getRegistrationId(); + } + + @RequestMapping(value = "/login", method = RequestMethod.GET) + public void loginRedirect(HttpServletResponse response) throws IOException { + response.sendRedirect(String.format("/oauth2/authorization/%s", registrationId)); + } +} diff --git a/lib/java/spring/auth/src/test/java/co/airy/spring/auth/OidcTest.java b/lib/java/spring/auth/src/test/java/co/airy/spring/auth/OidcTest.java index a2f649a97c..383f55510c 100644 --- a/lib/java/spring/auth/src/test/java/co/airy/spring/auth/OidcTest.java +++ b/lib/java/spring/auth/src/test/java/co/airy/spring/auth/OidcTest.java @@ -11,9 +11,11 @@ import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.web.servlet.MockMvc; +import static org.hamcrest.core.StringContains.containsString; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -40,9 +42,10 @@ void redirectsToAuth() throws Exception { } @Test - void servesLoginPage() throws Exception { + void loginRedirectsToProviderLogin() throws Exception { mvc.perform(get("/login")) - .andExpect(status().isOk()); + .andExpect(status().is3xxRedirection()) + .andExpect(header().string("Location", containsString("oauth2/authorization/github"))); } @Test diff --git a/lib/java/spring/core/src/main/java/co/airy/spring/core/AirySpringBootApplication.java b/lib/java/spring/core/src/main/java/co/airy/spring/core/AirySpringBootApplication.java index 5037829f26..58fe0071f9 100644 --- a/lib/java/spring/core/src/main/java/co/airy/spring/core/AirySpringBootApplication.java +++ b/lib/java/spring/core/src/main/java/co/airy/spring/core/AirySpringBootApplication.java @@ -4,8 +4,10 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.PropertySource; import org.springframework.context.annotation.PropertySources; +import org.springframework.scheduling.annotation.EnableAsync; +@EnableAsync @SpringBootApplication(scanBasePackages = "co.airy") @PropertySources({ @PropertySource("classpath:default.properties"), diff --git a/lib/java/spring/kafka/healthcheck/BUILD b/lib/java/spring/kafka/healthcheck/BUILD deleted file mode 100644 index 1250763271..0000000000 --- a/lib/java/spring/kafka/healthcheck/BUILD +++ /dev/null @@ -1,22 +0,0 @@ -load("@com_github_airyhq_bazel_tools//lint:buildifier.bzl", "check_pkg") -load("//tools/build:java_library.bzl", "custom_java_library") -load("//tools/build:avro.bzl", "avro_java_library") - -avro_java_library( - name = "healthcheck_schema", - srcs = ["healtcheck.avsc"], -) - -custom_java_library( - name = "healthcheck", - srcs = glob(["**/*.java"]), - visibility = ["//visibility:public"], - deps = [ - ":healthcheck_schema", - "//lib/java/kafka/core:kafka-core", - "//lib/java/kafka/schema:ops-application-health", - "@maven//:org_springframework_spring_context", - ], -) - -check_pkg(name = "buildifier") diff --git a/lib/java/spring/kafka/healthcheck/healtcheck.avsc b/lib/java/spring/kafka/healthcheck/healtcheck.avsc deleted file mode 100644 index 7df6a8e3d3..0000000000 --- a/lib/java/spring/kafka/healthcheck/healtcheck.avsc +++ /dev/null @@ -1,9 +0,0 @@ -{ - "namespace": "co.airy.avro.ops", - "name": "HealthCheck", - "type": "record", - "fields": [ - {"name": "app", "type": "string"}, - {"name": "time", "type": "long", "logicalType": "timestamp-millis"} - ] -} diff --git a/lib/java/spring/kafka/healthcheck/src/main/java/co/airy/spring/kafka/healthcheck/ProducerHealthCheck.java b/lib/java/spring/kafka/healthcheck/src/main/java/co/airy/spring/kafka/healthcheck/ProducerHealthCheck.java deleted file mode 100644 index d22cb2219a..0000000000 --- a/lib/java/spring/kafka/healthcheck/src/main/java/co/airy/spring/kafka/healthcheck/ProducerHealthCheck.java +++ /dev/null @@ -1,37 +0,0 @@ -package co.airy.spring.kafka.healthcheck; - -import co.airy.avro.ops.HealthCheck; -import co.airy.kafka.schema.ops.OpsApplicationHealth; -import org.apache.kafka.clients.producer.KafkaProducer; -import org.apache.kafka.clients.producer.ProducerRecord; -import org.springframework.stereotype.Service; - -import java.time.Instant; - -@Service -public class ProducerHealthCheck { - private final KafkaProducer producer; - - ProducerHealthCheck(KafkaProducer producer) { - this.producer = producer; - } - - private final String opsApplicationHealth = new OpsApplicationHealth().name(); - - public void sendHealthCheck() throws Exception { - final String serviceName = System.getenv("SERVICE_NAME"); - if (serviceName == null) { - throw new IllegalStateException("SERVICE_NAME not set"); - } - - sendHealthCheck(serviceName); - } - - private void sendHealthCheck(String app) throws Exception { - producer.send(new ProducerRecord<>(opsApplicationHealth, null, - HealthCheck.newBuilder() - .setApp(app) - .setTime(Instant.now().toEpochMilli()) - .build())).get(); - } -} diff --git a/lib/java/spring/test/src/main/java/co/airy/spring/test/WebTestHelper.java b/lib/java/spring/test/src/main/java/co/airy/spring/test/WebTestHelper.java index aa46aee7dd..37a650eb7c 100644 --- a/lib/java/spring/test/src/main/java/co/airy/spring/test/WebTestHelper.java +++ b/lib/java/spring/test/src/main/java/co/airy/spring/test/WebTestHelper.java @@ -33,6 +33,10 @@ public ResultActions post(String url, String body) throws Exception { .content(body)); } + public ResultActions post(String url) throws Exception { + return this.mvc.perform(MockMvcRequestBuilders.post(url)); + } + public ResultActions get(String url) throws Exception { return this.mvc.perform(MockMvcRequestBuilders.get(url)); } diff --git a/lib/typescript/assets/images/empty-state/inbox-empty-state.svg b/lib/typescript/assets/images/empty-state/inbox-empty-state.svg index 4298911b48..1194fbcd06 100644 --- a/lib/typescript/assets/images/empty-state/inbox-empty-state.svg +++ b/lib/typescript/assets/images/empty-state/inbox-empty-state.svg @@ -1 +1,20 @@ - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/lib/typescript/assets/images/empty-state/tags-empty-state.svg b/lib/typescript/assets/images/empty-state/tags-empty-state.svg index 1723b17f57..8dec9c67ba 100644 --- a/lib/typescript/assets/images/empty-state/tags-empty-state.svg +++ b/lib/typescript/assets/images/empty-state/tags-empty-state.svg @@ -1 +1,9 @@ - \ No newline at end of file + + + + + + + + + \ No newline at end of file diff --git a/lib/typescript/assets/images/icons/airy-icon.svg b/lib/typescript/assets/images/icons/airy-icon.svg index 3679bd7d1c..333f365c89 100644 --- a/lib/typescript/assets/images/icons/airy-icon.svg +++ b/lib/typescript/assets/images/icons/airy-icon.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/lib/typescript/assets/images/icons/airy_avatar.svg b/lib/typescript/assets/images/icons/airy_avatar.svg index 46138a4e05..bc61724728 100644 --- a/lib/typescript/assets/images/icons/airy_avatar.svg +++ b/lib/typescript/assets/images/icons/airy_avatar.svg @@ -1 +1,6 @@ - \ No newline at end of file + + + + + + \ No newline at end of file diff --git a/lib/typescript/assets/images/icons/airy_primary_rgb.svg b/lib/typescript/assets/images/icons/airy_primary_rgb.svg index 2ac7dc1a20..c8da7d4cf5 100644 --- a/lib/typescript/assets/images/icons/airy_primary_rgb.svg +++ b/lib/typescript/assets/images/icons/airy_primary_rgb.svg @@ -1 +1,4 @@ - \ No newline at end of file + + + + \ No newline at end of file diff --git a/lib/typescript/assets/images/icons/arrow-left-2.svg b/lib/typescript/assets/images/icons/arrow-left-2.svg index 54ed3ef7db..ff4c2a0264 100644 --- a/lib/typescript/assets/images/icons/arrow-left-2.svg +++ b/lib/typescript/assets/images/icons/arrow-left-2.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/lib/typescript/assets/images/icons/attachmentImage.svg b/lib/typescript/assets/images/icons/attachmentImage.svg index be9e3748c7..213c6db356 100644 --- a/lib/typescript/assets/images/icons/attachmentImage.svg +++ b/lib/typescript/assets/images/icons/attachmentImage.svg @@ -1,8 +1,5 @@ - - - icon/media/image - Created with Sketch. + diff --git a/lib/typescript/assets/images/icons/attachmentTemplate.svg b/lib/typescript/assets/images/icons/attachmentTemplate.svg index 36217bc906..4c59a17282 100644 --- a/lib/typescript/assets/images/icons/attachmentTemplate.svg +++ b/lib/typescript/assets/images/icons/attachmentTemplate.svg @@ -1,8 +1,5 @@ - - - icon/airyfont/interface/template - Created with Sketch. + diff --git a/lib/typescript/assets/images/icons/attachmentVideo.svg b/lib/typescript/assets/images/icons/attachmentVideo.svg index 62196dd968..dec1c95e3c 100644 --- a/lib/typescript/assets/images/icons/attachmentVideo.svg +++ b/lib/typescript/assets/images/icons/attachmentVideo.svg @@ -1,4 +1,4 @@ - + diff --git a/lib/typescript/assets/images/icons/checkmark-circle.svg b/lib/typescript/assets/images/icons/checkmark-circle.svg index a4cc89e313..f3a928a218 100644 --- a/lib/typescript/assets/images/icons/checkmark-circle.svg +++ b/lib/typescript/assets/images/icons/checkmark-circle.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/lib/typescript/assets/images/icons/checkmark.svg b/lib/typescript/assets/images/icons/checkmark.svg index b00e8ec30e..1445ff999c 100644 --- a/lib/typescript/assets/images/icons/checkmark.svg +++ b/lib/typescript/assets/images/icons/checkmark.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/lib/typescript/assets/images/icons/chevron-down.svg b/lib/typescript/assets/images/icons/chevron-down.svg index 7ebaf6e373..a5253c7d24 100644 --- a/lib/typescript/assets/images/icons/chevron-down.svg +++ b/lib/typescript/assets/images/icons/chevron-down.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/lib/typescript/assets/images/icons/chevronLeft.svg b/lib/typescript/assets/images/icons/chevronLeft.svg deleted file mode 100644 index 417f696a28..0000000000 --- a/lib/typescript/assets/images/icons/chevronLeft.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/lib/typescript/assets/images/icons/chevron_left.svg b/lib/typescript/assets/images/icons/chevron_left.svg deleted file mode 100644 index 417f696a28..0000000000 --- a/lib/typescript/assets/images/icons/chevron_left.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/lib/typescript/assets/images/icons/close.svg b/lib/typescript/assets/images/icons/close.svg index 42172751e5..0adabf932b 100644 --- a/lib/typescript/assets/images/icons/close.svg +++ b/lib/typescript/assets/images/icons/close.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/lib/typescript/assets/images/icons/cog.svg b/lib/typescript/assets/images/icons/cog.svg deleted file mode 100644 index cb71fe029e..0000000000 --- a/lib/typescript/assets/images/icons/cog.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/lib/typescript/assets/images/icons/edit.svg b/lib/typescript/assets/images/icons/edit.svg deleted file mode 100644 index f5dcde5b1b..0000000000 --- a/lib/typescript/assets/images/icons/edit.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/lib/typescript/assets/images/icons/exclamation-triangle.svg b/lib/typescript/assets/images/icons/exclamation-triangle.svg index af8a279c9e..4f61395b9b 100644 --- a/lib/typescript/assets/images/icons/exclamation-triangle.svg +++ b/lib/typescript/assets/images/icons/exclamation-triangle.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/lib/typescript/assets/images/icons/exclamation.svg b/lib/typescript/assets/images/icons/exclamation.svg index 458474130b..a0432f9676 100644 --- a/lib/typescript/assets/images/icons/exclamation.svg +++ b/lib/typescript/assets/images/icons/exclamation.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/lib/typescript/assets/images/icons/facebook_rounded.svg b/lib/typescript/assets/images/icons/facebook_rounded.svg index 0624728476..bba0df1b82 100644 --- a/lib/typescript/assets/images/icons/facebook_rounded.svg +++ b/lib/typescript/assets/images/icons/facebook_rounded.svg @@ -1 +1,9 @@ - \ No newline at end of file + + + + + + + + + \ No newline at end of file diff --git a/lib/typescript/assets/images/icons/filter-alt.svg b/lib/typescript/assets/images/icons/filter-alt.svg index df7e735a4e..9582c9beca 100644 --- a/lib/typescript/assets/images/icons/filter-alt.svg +++ b/lib/typescript/assets/images/icons/filter-alt.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/lib/typescript/assets/images/icons/git-merge.svg b/lib/typescript/assets/images/icons/git-merge.svg index 75cb49d077..3146be8956 100644 --- a/lib/typescript/assets/images/icons/git-merge.svg +++ b/lib/typescript/assets/images/icons/git-merge.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/lib/typescript/assets/images/icons/google-messages.svg b/lib/typescript/assets/images/icons/google-messages.svg index 8a78456fdd..925e6a20f0 100644 --- a/lib/typescript/assets/images/icons/google-messages.svg +++ b/lib/typescript/assets/images/icons/google-messages.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/lib/typescript/assets/images/icons/google_avatar.svg b/lib/typescript/assets/images/icons/google_avatar.svg index 26c5f2baab..9e94944172 100644 --- a/lib/typescript/assets/images/icons/google_avatar.svg +++ b/lib/typescript/assets/images/icons/google_avatar.svg @@ -1 +1,6 @@ - \ No newline at end of file + + + + + + \ No newline at end of file diff --git a/lib/typescript/assets/images/icons/inbox.svg b/lib/typescript/assets/images/icons/inbox.svg index 4f6ce4e679..d6fea5c440 100644 --- a/lib/typescript/assets/images/icons/inbox.svg +++ b/lib/typescript/assets/images/icons/inbox.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/lib/typescript/assets/images/icons/info-circle.svg b/lib/typescript/assets/images/icons/info-circle.svg new file mode 100644 index 0000000000..11d29a0ba8 --- /dev/null +++ b/lib/typescript/assets/images/icons/info-circle.svg @@ -0,0 +1,2 @@ + diff --git a/lib/typescript/assets/images/icons/leftArrow.svg b/lib/typescript/assets/images/icons/leftArrow.svg index 61788f5f8f..bb9406ffc6 100644 --- a/lib/typescript/assets/images/icons/leftArrow.svg +++ b/lib/typescript/assets/images/icons/leftArrow.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/lib/typescript/assets/images/icons/lightbulb.svg b/lib/typescript/assets/images/icons/lightbulb.svg index fb90e47237..f0426a43c0 100644 --- a/lib/typescript/assets/images/icons/lightbulb.svg +++ b/lib/typescript/assets/images/icons/lightbulb.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/lib/typescript/assets/images/icons/link.svg b/lib/typescript/assets/images/icons/link.svg index cc608d258c..404b772516 100644 --- a/lib/typescript/assets/images/icons/link.svg +++ b/lib/typescript/assets/images/icons/link.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/lib/typescript/assets/images/icons/messenger_avatar.svg b/lib/typescript/assets/images/icons/messenger_avatar.svg index ec2d30e5f8..96e439f1c9 100644 --- a/lib/typescript/assets/images/icons/messenger_avatar.svg +++ b/lib/typescript/assets/images/icons/messenger_avatar.svg @@ -1 +1,6 @@ - \ No newline at end of file + + + + + + \ No newline at end of file diff --git a/lib/typescript/assets/images/icons/minimize-button.svg b/lib/typescript/assets/images/icons/minimize-button.svg index 97c0829be8..d77291116a 100644 --- a/lib/typescript/assets/images/icons/minimize-button.svg +++ b/lib/typescript/assets/images/icons/minimize-button.svg @@ -1,3 +1,3 @@ - + diff --git a/lib/typescript/assets/images/icons/paperplane.svg b/lib/typescript/assets/images/icons/paperplane.svg index c050a605f3..d9291a2599 100644 --- a/lib/typescript/assets/images/icons/paperplane.svg +++ b/lib/typescript/assets/images/icons/paperplane.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/lib/typescript/assets/images/icons/phone.svg b/lib/typescript/assets/images/icons/phone.svg index 4520742646..122891d376 100644 --- a/lib/typescript/assets/images/icons/phone.svg +++ b/lib/typescript/assets/images/icons/phone.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/lib/typescript/assets/images/icons/plus-circle.svg b/lib/typescript/assets/images/icons/plus-circle.svg index 6b76f444e4..030430b303 100644 --- a/lib/typescript/assets/images/icons/plus-circle.svg +++ b/lib/typescript/assets/images/icons/plus-circle.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/lib/typescript/assets/images/icons/plus.svg b/lib/typescript/assets/images/icons/plus.svg index 7f97fc52c4..42e774b9ba 100644 --- a/lib/typescript/assets/images/icons/plus.svg +++ b/lib/typescript/assets/images/icons/plus.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/lib/typescript/assets/images/icons/price-tag.svg b/lib/typescript/assets/images/icons/price-tag.svg index 938c792daf..8530cad5f4 100644 --- a/lib/typescript/assets/images/icons/price-tag.svg +++ b/lib/typescript/assets/images/icons/price-tag.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/lib/typescript/assets/images/icons/richCardIcon.svg b/lib/typescript/assets/images/icons/richCardIcon.svg index ec84e1029b..b90cd918ac 100644 --- a/lib/typescript/assets/images/icons/richCardIcon.svg +++ b/lib/typescript/assets/images/icons/richCardIcon.svg @@ -1,3 +1,3 @@ - + diff --git a/lib/typescript/assets/images/icons/rightArrow.svg b/lib/typescript/assets/images/icons/rightArrow.svg index bf01afdf01..94d4290220 100644 --- a/lib/typescript/assets/images/icons/rightArrow.svg +++ b/lib/typescript/assets/images/icons/rightArrow.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/lib/typescript/assets/images/icons/search.svg b/lib/typescript/assets/images/icons/search.svg index 08de0f90f2..b8343adc03 100644 --- a/lib/typescript/assets/images/icons/search.svg +++ b/lib/typescript/assets/images/icons/search.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/lib/typescript/assets/images/icons/shortcut.svg b/lib/typescript/assets/images/icons/shortcut.svg index d4315404e2..fb374ff170 100644 --- a/lib/typescript/assets/images/icons/shortcut.svg +++ b/lib/typescript/assets/images/icons/shortcut.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/lib/typescript/assets/images/icons/sign-out.svg b/lib/typescript/assets/images/icons/sign-out.svg index dfea91911e..e73ee7b82a 100644 --- a/lib/typescript/assets/images/icons/sign-out.svg +++ b/lib/typescript/assets/images/icons/sign-out.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/lib/typescript/assets/images/icons/smiley.svg b/lib/typescript/assets/images/icons/smiley.svg index 40f4d666c0..fc29cbca52 100644 --- a/lib/typescript/assets/images/icons/smiley.svg +++ b/lib/typescript/assets/images/icons/smiley.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/lib/typescript/assets/images/icons/sms-channel.svg b/lib/typescript/assets/images/icons/sms-channel.svg deleted file mode 100644 index e092ab6870..0000000000 --- a/lib/typescript/assets/images/icons/sms-channel.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/lib/typescript/assets/images/icons/sms-icon.svg b/lib/typescript/assets/images/icons/sms-icon.svg index 66da2976d4..e96b2b17bd 100644 --- a/lib/typescript/assets/images/icons/sms-icon.svg +++ b/lib/typescript/assets/images/icons/sms-icon.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/lib/typescript/assets/images/icons/sms.svg b/lib/typescript/assets/images/icons/sms.svg index 5476cf9511..90552d7b04 100644 --- a/lib/typescript/assets/images/icons/sms.svg +++ b/lib/typescript/assets/images/icons/sms.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/lib/typescript/assets/images/icons/sms_avatar.svg b/lib/typescript/assets/images/icons/sms_avatar.svg index c229b04d18..651c0f5c28 100644 --- a/lib/typescript/assets/images/icons/sms_avatar.svg +++ b/lib/typescript/assets/images/icons/sms_avatar.svg @@ -1 +1,6 @@ - \ No newline at end of file + + + + + + \ No newline at end of file diff --git a/lib/typescript/assets/images/icons/speak-bubble.svg b/lib/typescript/assets/images/icons/speak-bubble.svg deleted file mode 100644 index e7a7fd36f4..0000000000 --- a/lib/typescript/assets/images/icons/speak-bubble.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/lib/typescript/assets/images/icons/template-alt.svg b/lib/typescript/assets/images/icons/template-alt.svg index 797a886915..d6e9265462 100644 --- a/lib/typescript/assets/images/icons/template-alt.svg +++ b/lib/typescript/assets/images/icons/template-alt.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/lib/typescript/assets/images/icons/trash.svg b/lib/typescript/assets/images/icons/trash.svg index c15a33afb3..2de8577ddf 100644 --- a/lib/typescript/assets/images/icons/trash.svg +++ b/lib/typescript/assets/images/icons/trash.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/lib/typescript/assets/images/icons/whatsapp-icon.svg b/lib/typescript/assets/images/icons/whatsapp-icon.svg index 496814b5d5..1bd9e58739 100644 --- a/lib/typescript/assets/images/icons/whatsapp-icon.svg +++ b/lib/typescript/assets/images/icons/whatsapp-icon.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/lib/typescript/assets/images/icons/whatsapp_avatar.svg b/lib/typescript/assets/images/icons/whatsapp_avatar.svg index 2bcce60b5f..fa90a8b4fd 100644 --- a/lib/typescript/assets/images/icons/whatsapp_avatar.svg +++ b/lib/typescript/assets/images/icons/whatsapp_avatar.svg @@ -1 +1,6 @@ - \ No newline at end of file + + + + + + \ No newline at end of file diff --git a/lib/typescript/assets/images/logo/airy_primary_rgb.svg b/lib/typescript/assets/images/logo/airy_primary_rgb.svg index 2ac7dc1a20..2c86ed1feb 100644 --- a/lib/typescript/assets/images/logo/airy_primary_rgb.svg +++ b/lib/typescript/assets/images/logo/airy_primary_rgb.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/lib/typescript/components/alerts/SettingsModal/ModalHeader.module.scss b/lib/typescript/components/alerts/SettingsModal/ModalHeader.module.scss index b40d9a283a..9728b7d6cb 100644 --- a/lib/typescript/components/alerts/SettingsModal/ModalHeader.module.scss +++ b/lib/typescript/components/alerts/SettingsModal/ModalHeader.module.scss @@ -28,9 +28,9 @@ } .closeIcon { + width: 10px; + height: 10px; svg { - width: 16px; - height: 16px; path { stroke: #aaa; } diff --git a/lib/typescript/components/cta/InfoButton/index.tsx b/lib/typescript/components/cta/InfoButton/index.tsx new file mode 100644 index 0000000000..70d563743d --- /dev/null +++ b/lib/typescript/components/cta/InfoButton/index.tsx @@ -0,0 +1,24 @@ +import React from 'react'; +import {ReactComponent as InfoCircle} from 'assets/images/icons/info-circle.svg'; + +import styles from './style.module.scss'; + +type Props = { + text: string; + link: string; + color: 'blue' | 'grey'; + dataCy?: string; +}; + +export const InfoButton = ({text, link, color, dataCy}: Props) => ( + +); diff --git a/lib/typescript/components/cta/InfoButton/style.module.scss b/lib/typescript/components/cta/InfoButton/style.module.scss new file mode 100644 index 0000000000..f7f0aad1cc --- /dev/null +++ b/lib/typescript/components/cta/InfoButton/style.module.scss @@ -0,0 +1,54 @@ +@import 'assets/scss/colors.scss'; +@import 'assets/scss/fonts.scss'; + +.circleIcon { + width: 18px; + height: 18px; + margin-right: 5px; +} + +.blueIcon { + fill: var(--color-airy-blue); +} + +.greyIcon { + fill: var(--color-text-gray); +} + +.button { + display: flex; + align-items: center; + padding: 8px 8px; + margin-bottom: 8px; + background-color: transparent; + border-radius: 8px; + cursor: pointer; +} + +.greyButton { + border: 1px solid var(--color-text-gray); +} + +.blueButton { + border: 1px solid var(--color-airy-blue); +} + +.link, +.link:hover, +.link:active, +.link:visited { + text-decoration: none; + font-weight: 400; + @include font-s; +} + +.greyLink, +.greyLink:hover, +.greyLink:active, +.greyLink:visited { + color: var(--color-text-gray); +} + +.blueLink { + color: var(--color-airy-blue); +} diff --git a/lib/typescript/components/cta/index.ts b/lib/typescript/components/cta/index.ts index e5d8a8a7a9..9699b59ba2 100644 --- a/lib/typescript/components/cta/index.ts +++ b/lib/typescript/components/cta/index.ts @@ -1,3 +1,4 @@ export * from './Button'; export * from './LinkButton'; export * from './HrefButton'; +export * from './InfoButton'; diff --git a/lib/typescript/components/inputs/Input/index.tsx b/lib/typescript/components/inputs/Input/index.tsx index 201d05ed2d..89314f1cdc 100644 --- a/lib/typescript/components/inputs/Input/index.tsx +++ b/lib/typescript/components/inputs/Input/index.tsx @@ -183,7 +183,7 @@ class InputComponent extends Component { } return ( -
+
); @@ -370,7 +370,7 @@ class InputComponent extends Component { onClick={this.handleEmojiDrawer} disabled={this.props.maxLength - value.length <= 0} className={`${styles.emojiIcon} ${this.state.isShowingEmojiDrawer && styles.emojiIconActive}`}> - +
) : null} diff --git a/lib/typescript/components/inputs/Input/style.module.scss b/lib/typescript/components/inputs/Input/style.module.scss index 36c4c76118..82bddb63e7 100644 --- a/lib/typescript/components/inputs/Input/style.module.scss +++ b/lib/typescript/components/inputs/Input/style.module.scss @@ -80,10 +80,20 @@ } .icon { + height: 24px; + width: 24px; display: flex; align-items: center; } +.closeIcon { + @extend .icon; + svg { + width: 10px; + height: 10px; + } +} + .inputTitleRow { display: flex; flex-direction: row; @@ -181,3 +191,8 @@ display: flex; align-items: center; } + +.smileyIcon { + height: 20px; + width: 20px; +} diff --git a/lib/typescript/components/inputs/SearchField/style.module.scss b/lib/typescript/components/inputs/SearchField/style.module.scss index f870274c67..22d5c1e96f 100644 --- a/lib/typescript/components/inputs/SearchField/style.module.scss +++ b/lib/typescript/components/inputs/SearchField/style.module.scss @@ -6,6 +6,7 @@ border: 1px solid var(--color-light-gray); border-radius: 8px; padding: 4px 8px; + height: 38px; input { @include font-base; @@ -31,13 +32,13 @@ } .searchIcon { + display: flex; + align-self: center; + margin-right: 6px; svg { - width: 22px; - height: 22px; - padding-top: 2px; - margin-right: 4px; - position: relative; - top: 2px; + width: 18px; + height: 18px; + margin: 0px; path { fill: var(--color-text-gray); @@ -49,15 +50,16 @@ background-color: rgba(0, 0, 0, 0); border: none; cursor: pointer; - padding: 0px 4px; + display: flex; + align-self: center; + padding: 0px; + margin-right: 4px; } .closeIcon { + height: 10px; + width: 10px; svg { - margin: 0; - width: 10px; - height: 10px; - path { fill: var(--color-text-gray); } diff --git a/lib/typescript/model/Config.ts b/lib/typescript/model/Config.ts index ea7ea22f8e..6760152cb1 100644 --- a/lib/typescript/model/Config.ts +++ b/lib/typescript/model/Config.ts @@ -1,6 +1,21 @@ import {User} from './User'; export interface Config { - components: {[key: string]: {enabled: boolean}}; + services: {[key: string]: {enabled: boolean; healthy: boolean; component: string}}; userProfile?: User; } + +export const getComponents = (config: Config) => { + const {services} = config; + return Object.keys(services).reduce((agg, key) => { + const {healthy, enabled, component} = services[key]; + return { + ...agg, + [component]: { + enabled, + // A component is only healthy if all its services are healthy + healthy: agg[component] ? agg[component].healthy && healthy : healthy, + }, + }; + }, {}); +}; diff --git a/lib/typescript/model/ConversationFilter.ts b/lib/typescript/model/ConversationFilter.ts deleted file mode 100644 index 6cd3eefde5..0000000000 --- a/lib/typescript/model/ConversationFilter.ts +++ /dev/null @@ -1,10 +0,0 @@ -export interface ConversationFilter { - readOnly?: boolean; - unreadOnly?: boolean; - displayName?: string; - createdAt?: string; - byTags?: string[]; - byChannels?: string[]; - bySources?: string[]; - isStateOpen?: boolean; -} diff --git a/lib/typescript/model/Source.ts b/lib/typescript/model/Source.ts index 33bb5573b1..fca192570f 100644 --- a/lib/typescript/model/Source.ts +++ b/lib/typescript/model/Source.ts @@ -5,3 +5,9 @@ export enum Source { twilioSMS = 'twilio.sms', twilioWhatsApp = 'twilio.whatsapp', } + +export const prettifySource = (source: string) => + source + .split('.') + .map(word => `${word[0].toUpperCase()}${word.slice(1)}`) + .join(' '); diff --git a/lib/typescript/model/index.ts b/lib/typescript/model/index.ts index 61f620cef1..361f47db4d 100644 --- a/lib/typescript/model/index.ts +++ b/lib/typescript/model/index.ts @@ -3,7 +3,6 @@ export * from './Config'; export * from './Contact'; export * from './Content'; export * from './Conversation'; -export * from './ConversationFilter'; export * from './Message'; export * from './Metadata'; export * from './Pagination'; diff --git a/lib/typescript/render/index.tsx b/lib/typescript/render/SourceMessage.tsx similarity index 94% rename from lib/typescript/render/index.tsx rename to lib/typescript/render/SourceMessage.tsx index 41f91d72ec..6b356e2b4b 100644 --- a/lib/typescript/render/index.tsx +++ b/lib/typescript/render/SourceMessage.tsx @@ -4,8 +4,6 @@ import {renderProviders} from './renderProviders'; import {Text} from './components/Text'; import {RenderPropsUnion} from './props'; -export * from './props'; - type SourceMessageState = { hasError: boolean; }; @@ -42,5 +40,3 @@ export class SourceMessage extends React.Component { + if (message?.content) { + return <>{message.content.message?.text || message.content.text}; + } + return
; +}; + +export const SourceMessagePreview = (props: SourceMessagePreviewProps) => { + const {conversation} = props; + + const lastMessageIsText = (conversation: Conversation) => { + const lastMessageContent = conversation.lastMessage.content; + + if (typeof lastMessageContent === 'string') { + if (lastMessageContent.includes('&Body=' && '&FromCountry=')) { + const startText = lastMessageContent.search('&Body='); + const endText = lastMessageContent.search('&FromCountry='); + const textLength = endText - startText; + const enCodedText = lastMessageContent.substring(startText + 6, startText + textLength); + const replaced = enCodedText.split('+').join(' '); + const text = decodeURIComponent(replaced); + return text; + } else if (lastMessageContent.includes('&Body=' && '&To=whatsapp')) { + const startText = lastMessageContent.search('&Body='); + const endText = lastMessageContent.search('&To=whatsapp'); + const textLength = endText - startText; + const enCodedText = lastMessageContent.substring(startText + 6, startText + textLength); + const replaced = enCodedText.split('+').join(' '); + const text = decodeURIComponent(replaced); + return text; + } + } + if (lastMessageContent.text || lastMessageContent.message?.text) { + return ; + } else if (lastMessageContent.suggestionResponse) { + return <>{conversation.lastMessage.content.suggestionResponse.text}; + } + }; + + const lastMessageIsIcon = (conversation: Conversation) => { + const lastMessageContent = conversation.lastMessage.content; + + if (!lastMessageContent.attachment) { + if (lastMessageContent.message?.attachments?.[0].type === 'image') { + return ; + } else if (lastMessageContent.message?.attachments?.[0].type === 'video') { + return ; + } else if (lastMessageContent.suggestionResponse) { + return <>{conversation.lastMessage.content.suggestionResponse.text}; + } else if (lastMessageContent.image) { + return ; + } else if (lastMessageContent.richCard) { + return ; + } + } + return ; + }; + + return <>{lastMessageIsText(conversation) || lastMessageIsIcon(conversation)}; +}; diff --git a/lib/typescript/render/components/Text/index.module.scss b/lib/typescript/render/components/Text/index.module.scss index 66ec23c309..ff7cb0d5f3 100644 --- a/lib/typescript/render/components/Text/index.module.scss +++ b/lib/typescript/render/components/Text/index.module.scss @@ -1,4 +1,5 @@ @import 'assets/scss/colors.scss'; +@import 'assets/scss/fonts.scss'; .textMessage { display: inline-block; @@ -17,6 +18,7 @@ .contactContent:active, .contactContent:visited, .contactContent:focus { + @extend .textMessage; background: var(--color-background-blue); color: var(--color-text-contrast); } @@ -26,6 +28,7 @@ .memberContent:active, .memberContent:visited, .memberContent:focus { + @extend .textMessage; background: var(--color-airy-blue); color: white; } @@ -34,3 +37,14 @@ text-decoration: underline; font-weight: normal; } + +.conversationListItemText { + @include font-base; + color: var(--color-text-gray); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + margin-top: 4px; + margin-bottom: 8px; + max-height: 24px; +} diff --git a/lib/typescript/render/components/Text/index.tsx b/lib/typescript/render/components/Text/index.tsx index 3d608f6c6b..e0281c3d91 100644 --- a/lib/typescript/render/components/Text/index.tsx +++ b/lib/typescript/render/components/Text/index.tsx @@ -10,7 +10,7 @@ type TextRenderProps = { export const Text = ({text, fromContact}: TextRenderProps) => ( { const message: Message = props.content; @@ -37,8 +45,6 @@ function render(content: ContentUnion, props: RenderPropsUnion) { case 'image': return ( <> - {content.text && } - ); @@ -46,8 +52,6 @@ function render(content: ContentUnion, props: RenderPropsUnion) { case 'video': return ( <> - {content.text && } -
))} diff --git a/lib/typescript/render/providers/facebook/components/MediaTemplate/index.module.scss b/lib/typescript/render/providers/facebook/components/MediaTemplate/index.module.scss new file mode 100644 index 0000000000..5ac20f85b0 --- /dev/null +++ b/lib/typescript/render/providers/facebook/components/MediaTemplate/index.module.scss @@ -0,0 +1,20 @@ +@import 'assets/scss/colors.scss'; +@import 'assets/scss/fonts.scss'; + +.mediaTemplate { + max-width: 500px; + border-radius: 8px; + border: 1px solid var(--color-light-gray); +} + +.media { + padding: 20px 10px; +} + +.mediaBorder { + border-bottom: 1px solid var(--color-light-gray); +} + +.mediaInfo { + color: var(--color-airy-blue); +} diff --git a/lib/typescript/render/providers/facebook/components/MediaTemplate/index.tsx b/lib/typescript/render/providers/facebook/components/MediaTemplate/index.tsx new file mode 100644 index 0000000000..6f5c4a9cb0 --- /dev/null +++ b/lib/typescript/render/providers/facebook/components/MediaTemplate/index.tsx @@ -0,0 +1,25 @@ +import React from 'react'; +import {MediaTemplate as MediaTemplateModel} from '../../facebookModel'; +import {Buttons} from '../Buttons'; +import styles from './index.module.scss'; + +type MediaTemplateProps = { + template: MediaTemplateModel; +}; + +export const MediaTemplate = ({template: {media_type, url, attachment_id, buttons}}: MediaTemplateProps) => { + return ( +
+
+ {url && ( + + see the {media_type} on Facebook + + )} + + {attachment_id && {media_type} posted on Facebook} +
+ {buttons && } +
+ ); +}; diff --git a/lib/typescript/render/providers/facebook/facebookModel.ts b/lib/typescript/render/providers/facebook/facebookModel.ts index 70a1501968..d2e1b2e97a 100644 --- a/lib/typescript/render/providers/facebook/facebookModel.ts +++ b/lib/typescript/render/providers/facebook/facebookModel.ts @@ -3,9 +3,45 @@ export interface Attachment { } export interface SimpleAttachment { type: 'image' | 'video' | 'audio' | 'file' | 'fallback'; - payload: { - title?: string; - url?: string; + title?: string; + url?: string; + payload?: {title?: string; url?: string} | null; +} + +export interface URLButton extends Content { + type: 'web_url'; + url: string; + title: string; +} + +export interface PostbackButton extends Content { + type: 'postback'; + title: string; + payload: string; +} + +export interface CallButton extends Content { + type: 'phone_number'; + title: string; + payload: string; +} + +export interface LoginButton extends Content { + type: 'account_link'; + url: string; +} + +export interface LogoutButton extends Content { + type: 'account_unlink'; +} + +export interface GamePlayButton extends Content { + type: 'game_play'; + title: 'Play'; + payload?: string; + game_metadata?: { + player_id?: string; + context_id?: string; }; } @@ -14,7 +50,7 @@ export interface ButtonAttachment extends Attachment { payload: { text: string; template_type: 'button'; - buttons: Button[]; + buttons: (URLButton | PostbackButton | CallButton | LoginButton | LogoutButton | GamePlayButton)[]; }; } export interface GenericAttachment extends Attachment { @@ -26,6 +62,22 @@ export interface GenericAttachment extends Attachment { }; } +export interface MediaTemplate extends Content { + type: 'mediaTemplate'; + media_type: 'video' | 'image'; + url?: string; + attachment_id?: string; + buttons: (URLButton | PostbackButton | CallButton | LoginButton | LogoutButton | GamePlayButton)[]; +} + +export interface MediaAttachment extends Attachment { + type: 'template'; + payload: { + template_type: 'media'; + elements: MediaTemplate[]; + }; +} + export interface Element { title: string; subtitle?: string; @@ -34,33 +86,25 @@ export interface Element { type: string; url?: string; }; - buttons: Button[]; + buttons: (URLButton | PostbackButton | CallButton | LoginButton | LogoutButton | GamePlayButton)[]; } export interface Content { type: string; + text?: string; } export interface TextContent extends Content { type: 'text'; - text: string; -} - -export interface Postback extends Content { - type: 'postback'; - title: string; - payload: string; } export interface ImageContent extends Content { type: 'image'; - text?: string; imageUrl: string; } export interface VideoContent extends Content { type: 'video'; - text?: string; videoUrl: string; } @@ -78,16 +122,10 @@ export interface QuickRepliesContent extends Content { quickReplies: QuickReply[]; } -export interface Button { - type: 'web_url'; - url: string; - title: string; -} - export interface ButtonTemplate extends Content { type: 'buttonTemplate'; text: string; - buttons: Button[]; + buttons: (URLButton | PostbackButton | CallButton | LoginButton | LogoutButton | GamePlayButton)[]; } export interface GenericTemplate extends Content { @@ -106,11 +144,20 @@ export interface Fallback extends Content { // Add a new facebook content model here: export type ContentUnion = | TextContent - | Postback + | PostbackButton | ImageContent | VideoContent | ButtonTemplate | GenericTemplate | QuickRepliesContent + | MediaTemplate + | Fallback; + +export type AttachmentUnion = + | TextContent + | ImageContent + | VideoContent + | ButtonTemplate + | GenericTemplate + | MediaTemplate | Fallback; -export type AttachmentUnion = TextContent | ImageContent | VideoContent | ButtonTemplate | GenericTemplate | Fallback; diff --git a/package.json b/package.json index 0c64691b95..aa272b2606 100644 --- a/package.json +++ b/package.json @@ -6,13 +6,13 @@ "@crello/react-lottie": "^0.0.11", "@reduxjs/toolkit": "^1.5.1", "@stomp/stompjs": "^6.1.0", - "@types/node": "15.3.0", - "@types/react": "17.0.5", + "@types/node": "15.6.1", + "@types/react": "17.0.8", "@types/react-dom": "17.0.5", "@types/react-redux": "7.1.16", "@types/react-router-dom": "^5.1.7", "camelcase-keys": "^6.2.2", - "core-js": "3.12.1", + "core-js": "3.13.1", "emoji-mart": "3.0.1", "linkifyjs": "^2.1.9", "lodash-es": "^4.17.21", @@ -37,35 +37,35 @@ "@babel/plugin-proposal-class-properties": "^7.13.0", "@babel/plugin-proposal-object-rest-spread": "^7.13.8", "@babel/plugin-transform-spread": "^7.13.0", - "@babel/preset-env": "^7.14.2", + "@babel/preset-env": "^7.14.4", "@babel/preset-react": "^7.13.13", "@babel/preset-typescript": "^7.13.0", - "@bazel/bazelisk": "^1.8.1", - "@bazel/typescript": "^3.5.0", + "@bazel/bazelisk": "^1.9.0", + "@bazel/typescript": "^3.5.1", "@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.24.0", - "@typescript-eslint/parser": "^4.24.0", + "@typescript-eslint/eslint-plugin": "^4.26.0", + "@typescript-eslint/parser": "^4.26.0", "babel-loader": "^8.0.6", - "copy-webpack-plugin": "^8.1.1", - "css-loader": "^5.2.4", - "cypress": "^7.3.0", - "eslint": "^7.26.0", - "eslint-plugin-react": "^7.23.2", + "copy-webpack-plugin": "^9.0.0", + "css-loader": "^5.2.6", + "cypress": "^7.4.0", + "eslint": "^7.27.0", + "eslint-plugin-react": "^7.24.0", "file-loader": "^6.2.0", "html-webpack-plugin": "^5.3.1", "minimist": "^1.2.5", "prettier": "^2.3.0", "react-hot-loader": "^4.13.0", - "sass": "^1.32.13", + "sass": "^1.34.0", "sass-loader": "^11.1.1", "style-loader": "^2.0.0", "terser-webpack-plugin": "^5.1.2", "typescript": "4.2.4", "url-loader": "^4.1.1", - "webpack": "^5.37.0", + "webpack": "^5.38.1", "webpack-bundle-analyzer": "^4.4.2", "webpack-cli": "^4.7.0", "webpack-dev-server": "^3.11.2" diff --git a/yarn.lock b/yarn.lock index a889f09528..de29b17e05 100644 --- a/yarn.lock +++ b/yarn.lock @@ -16,10 +16,10 @@ dependencies: "@babel/highlight" "^7.12.13" -"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.13.15", "@babel/compat-data@^7.14.0": - version "7.14.0" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.14.0.tgz#a901128bce2ad02565df95e6ecbf195cf9465919" - integrity sha512-vu9V3uMM/1o5Hl5OekMUowo3FqXLJSw+s+66nt0fSWVWTtmosdzn45JHOB3cPtZoe6CTBDzvSw0RdOY85Q37+Q== +"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.14.4": + version "7.14.4" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.14.4.tgz#45720fe0cecf3fd42019e1d12cc3d27fadc98d58" + integrity sha512-i2wXrWQNkH6JplJQGn3Rd2I4Pij8GdHkXwHMxm+zV5YG/Jci+bCNrWZEWC4o+umiDkRrRs4dVzH3X4GP7vyjQQ== "@babel/core@7.14.3", "@babel/core@^7.12.3": version "7.14.3" @@ -66,14 +66,14 @@ "@babel/helper-explode-assignable-expression" "^7.12.13" "@babel/types" "^7.12.13" -"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.13.16": - version "7.13.16" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.16.tgz#6e91dccf15e3f43e5556dffe32d860109887563c" - integrity sha512-3gmkYIrpqsLlieFwjkGgLaSHmhnvlAYzZLlYVjlW+QwI+1zE17kGxuJGmIqDQdYp56XdmGeD+Bswx0UTyG18xA== +"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.13.16", "@babel/helper-compilation-targets@^7.14.4": + version "7.14.4" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.14.4.tgz#33ebd0ffc34248051ee2089350a929ab02f2a516" + integrity sha512-JgdzOYZ/qGaKTVkn5qEDV/SXAh8KcyUVkCoSWGN8T3bwrgd6m+/dJa2kVGi6RJYJgEYPBdZ84BZp9dUjNWkBaA== dependencies: - "@babel/compat-data" "^7.13.15" + "@babel/compat-data" "^7.14.4" "@babel/helper-validator-option" "^7.12.17" - browserslist "^4.14.5" + browserslist "^4.16.6" semver "^6.3.0" "@babel/helper-create-class-features-plugin@^7.13.0": @@ -99,6 +99,18 @@ "@babel/helper-replace-supers" "^7.13.12" "@babel/helper-split-export-declaration" "^7.12.13" +"@babel/helper-create-class-features-plugin@^7.14.3": + version "7.14.4" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.14.4.tgz#abf888d836a441abee783c75229279748705dc42" + integrity sha512-idr3pthFlDCpV+p/rMgGLGYIVtazeatrSOQk8YzO2pAepIjQhCN3myeihVg58ax2bbbGK9PUE1reFi7axOYIOw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.12.13" + "@babel/helper-function-name" "^7.14.2" + "@babel/helper-member-expression-to-functions" "^7.13.12" + "@babel/helper-optimise-call-expression" "^7.12.13" + "@babel/helper-replace-supers" "^7.14.4" + "@babel/helper-split-export-declaration" "^7.12.13" + "@babel/helper-create-regexp-features-plugin@^7.12.13": version "7.12.17" resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.12.17.tgz#a2ac87e9e319269ac655b8d4415e94d38d663cb7" @@ -244,6 +256,16 @@ "@babel/traverse" "^7.13.0" "@babel/types" "^7.13.12" +"@babel/helper-replace-supers@^7.14.4": + version "7.14.4" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.14.4.tgz#b2ab16875deecfff3ddfcd539bc315f72998d836" + integrity sha512-zZ7uHCWlxfEAAOVDYQpEf/uyi1dmeC7fX4nCf2iz9drnCwi1zvwXL3HwWWNXUQEJ1k23yVn3VbddiI9iJEXaTQ== + dependencies: + "@babel/helper-member-expression-to-functions" "^7.13.12" + "@babel/helper-optimise-call-expression" "^7.12.13" + "@babel/traverse" "^7.14.2" + "@babel/types" "^7.14.4" + "@babel/helper-simple-access@^7.13.12": version "7.13.12" resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.13.12.tgz#dd6c538afb61819d205a012c31792a39c7a5eaf6" @@ -339,11 +361,12 @@ "@babel/helper-create-class-features-plugin" "^7.13.0" "@babel/helper-plugin-utils" "^7.13.0" -"@babel/plugin-proposal-class-static-block@^7.13.11": - version "7.13.11" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.13.11.tgz#6fcbba4a962702c17e5371a0c7b39afde186d703" - integrity sha512-fJTdFI4bfnMjvxJyNuaf8i9mVcZ0UhetaGEUHaHV9KEnibLugJkZAtXikR8KcYj+NYmI4DZMS8yQAyg+hvfSqg== +"@babel/plugin-proposal-class-static-block@^7.14.3": + version "7.14.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.14.3.tgz#5a527e2cae4a4753119c3a3e7f64ecae8ccf1360" + integrity sha512-HEjzp5q+lWSjAgJtSluFDrGGosmwTgKwCXdDQZvhKsRlwv3YdkUEqxNrrjesJd+B9E9zvr1PVPVBvhYZ9msjvQ== dependencies: + "@babel/helper-create-class-features-plugin" "^7.14.3" "@babel/helper-plugin-utils" "^7.13.0" "@babel/plugin-syntax-class-static-block" "^7.12.13" @@ -395,13 +418,13 @@ "@babel/helper-plugin-utils" "^7.13.0" "@babel/plugin-syntax-numeric-separator" "^7.10.4" -"@babel/plugin-proposal-object-rest-spread@^7.13.8", "@babel/plugin-proposal-object-rest-spread@^7.14.2": - version "7.14.2" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.14.2.tgz#e17d418f81cc103fedd4ce037e181c8056225abc" - integrity sha512-hBIQFxwZi8GIp934+nj5uV31mqclC1aYDhctDu5khTi9PCCUOczyy0b34W0oE9U/eJXiqQaKyVsmjeagOaSlbw== +"@babel/plugin-proposal-object-rest-spread@^7.13.8", "@babel/plugin-proposal-object-rest-spread@^7.14.4": + version "7.14.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.14.4.tgz#0e2b4de419915dc0b409378e829412e2031777c4" + integrity sha512-AYosOWBlyyXEagrPRfLJ1enStufsr7D1+ddpj8OLi9k7B6+NdZ0t/9V7Fh+wJ4g2Jol8z2JkgczYqtWrZd4vbA== dependencies: - "@babel/compat-data" "^7.14.0" - "@babel/helper-compilation-targets" "^7.13.16" + "@babel/compat-data" "^7.14.4" + "@babel/helper-compilation-targets" "^7.14.4" "@babel/helper-plugin-utils" "^7.13.0" "@babel/plugin-syntax-object-rest-spread" "^7.8.3" "@babel/plugin-transform-parameters" "^7.14.2" @@ -584,23 +607,23 @@ dependencies: "@babel/helper-plugin-utils" "^7.12.13" -"@babel/plugin-transform-block-scoping@^7.14.2": - version "7.14.2" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.14.2.tgz#761cb12ab5a88d640ad4af4aa81f820e6b5fdf5c" - integrity sha512-neZZcP19NugZZqNwMTH+KoBjx5WyvESPSIOQb4JHpfd+zPfqcH65RMu5xJju5+6q/Y2VzYrleQTr+b6METyyxg== +"@babel/plugin-transform-block-scoping@^7.14.4": + version "7.14.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.14.4.tgz#caf140b0b2e2462c509553d140e6d0abefb61ed8" + integrity sha512-5KdpkGxsZlTk+fPleDtGKsA+pon28+ptYmMO8GBSa5fHERCJWAzj50uAfCKBqq42HO+Zot6JF1x37CRprwmN4g== dependencies: "@babel/helper-plugin-utils" "^7.13.0" -"@babel/plugin-transform-classes@^7.14.2": - version "7.14.2" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.14.2.tgz#3f1196c5709f064c252ad056207d87b7aeb2d03d" - integrity sha512-7oafAVcucHquA/VZCsXv/gmuiHeYd64UJyyTYU+MPfNu0KeNlxw06IeENBO8bJjXVbolu+j1MM5aKQtH1OMCNg== +"@babel/plugin-transform-classes@^7.14.4": + version "7.14.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.14.4.tgz#a83c15503fc71a0f99e876fdce7dadbc6575ec3a" + integrity sha512-p73t31SIj6y94RDVX57rafVjttNr8MvKEgs5YFatNB/xC68zM3pyosuOEcQmYsYlyQaGY9R7rAULVRcat5FKJQ== dependencies: "@babel/helper-annotate-as-pure" "^7.12.13" "@babel/helper-function-name" "^7.14.2" "@babel/helper-optimise-call-expression" "^7.12.13" "@babel/helper-plugin-utils" "^7.13.0" - "@babel/helper-replace-supers" "^7.13.12" + "@babel/helper-replace-supers" "^7.14.4" "@babel/helper-split-export-declaration" "^7.12.13" globals "^11.1.0" @@ -611,10 +634,10 @@ dependencies: "@babel/helper-plugin-utils" "^7.13.0" -"@babel/plugin-transform-destructuring@^7.13.17": - version "7.13.17" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.13.17.tgz#678d96576638c19d5b36b332504d3fd6e06dea27" - integrity sha512-UAUqiLv+uRLO+xuBKKMEpC+t7YRNVRqBsWWq1yKXbBZBje/t3IXCiSinZhjn/DC3qzBfICeYd2EFGEbHsh5RLA== +"@babel/plugin-transform-destructuring@^7.14.4": + version "7.14.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.14.4.tgz#acbec502e9951f30f4441eaca1d2f29efade59ed" + integrity sha512-JyywKreTCGTUsL1OKu1A3ms/R1sTP0WxbpXlALeGzF53eB3bxtNkYdMj9SDgK7g6ImPy76J5oYYKoTtQImlhQA== dependencies: "@babel/helper-plugin-utils" "^7.13.0" @@ -869,26 +892,26 @@ "@babel/helper-create-regexp-features-plugin" "^7.12.13" "@babel/helper-plugin-utils" "^7.12.13" -"@babel/preset-env@^7.12.1", "@babel/preset-env@^7.14.2": - version "7.14.2" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.14.2.tgz#e80612965da73579c84ad2f963c2359c71524ed5" - integrity sha512-7dD7lVT8GMrE73v4lvDEb85cgcQhdES91BSD7jS/xjC6QY8PnRhux35ac+GCpbiRhp8crexBvZZqnaL6VrY8TQ== +"@babel/preset-env@^7.12.1", "@babel/preset-env@^7.14.4": + version "7.14.4" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.14.4.tgz#73fc3228c59727e5e974319156f304f0d6685a2d" + integrity sha512-GwMMsuAnDtULyOtuxHhzzuSRxFeP0aR/LNzrHRzP8y6AgDNgqnrfCCBm/1cRdTU75tRs28Eh76poHLcg9VF0LA== dependencies: - "@babel/compat-data" "^7.14.0" - "@babel/helper-compilation-targets" "^7.13.16" + "@babel/compat-data" "^7.14.4" + "@babel/helper-compilation-targets" "^7.14.4" "@babel/helper-plugin-utils" "^7.13.0" "@babel/helper-validator-option" "^7.12.17" "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.13.12" "@babel/plugin-proposal-async-generator-functions" "^7.14.2" "@babel/plugin-proposal-class-properties" "^7.13.0" - "@babel/plugin-proposal-class-static-block" "^7.13.11" + "@babel/plugin-proposal-class-static-block" "^7.14.3" "@babel/plugin-proposal-dynamic-import" "^7.14.2" "@babel/plugin-proposal-export-namespace-from" "^7.14.2" "@babel/plugin-proposal-json-strings" "^7.14.2" "@babel/plugin-proposal-logical-assignment-operators" "^7.14.2" "@babel/plugin-proposal-nullish-coalescing-operator" "^7.14.2" "@babel/plugin-proposal-numeric-separator" "^7.14.2" - "@babel/plugin-proposal-object-rest-spread" "^7.14.2" + "@babel/plugin-proposal-object-rest-spread" "^7.14.4" "@babel/plugin-proposal-optional-catch-binding" "^7.14.2" "@babel/plugin-proposal-optional-chaining" "^7.14.2" "@babel/plugin-proposal-private-methods" "^7.13.0" @@ -911,10 +934,10 @@ "@babel/plugin-transform-arrow-functions" "^7.13.0" "@babel/plugin-transform-async-to-generator" "^7.13.0" "@babel/plugin-transform-block-scoped-functions" "^7.12.13" - "@babel/plugin-transform-block-scoping" "^7.14.2" - "@babel/plugin-transform-classes" "^7.14.2" + "@babel/plugin-transform-block-scoping" "^7.14.4" + "@babel/plugin-transform-classes" "^7.14.4" "@babel/plugin-transform-computed-properties" "^7.13.0" - "@babel/plugin-transform-destructuring" "^7.13.17" + "@babel/plugin-transform-destructuring" "^7.14.4" "@babel/plugin-transform-dotall-regex" "^7.12.13" "@babel/plugin-transform-duplicate-keys" "^7.12.13" "@babel/plugin-transform-exponentiation-operator" "^7.12.13" @@ -941,7 +964,7 @@ "@babel/plugin-transform-unicode-escapes" "^7.12.13" "@babel/plugin-transform-unicode-regex" "^7.12.13" "@babel/preset-modules" "^0.1.4" - "@babel/types" "^7.14.2" + "@babel/types" "^7.14.4" babel-plugin-polyfill-corejs2 "^0.2.0" babel-plugin-polyfill-corejs3 "^0.2.0" babel-plugin-polyfill-regenerator "^0.2.0" @@ -1021,23 +1044,23 @@ debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.12.1", "@babel/types@^7.12.13", "@babel/types@^7.12.17", "@babel/types@^7.12.6", "@babel/types@^7.13.0", "@babel/types@^7.13.12", "@babel/types@^7.14.0", "@babel/types@^7.14.2", "@babel/types@^7.4.4": - version "7.14.2" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.14.2.tgz#4208ae003107ef8a057ea8333e56eb64d2f6a2c3" - integrity sha512-SdjAG/3DikRHpUOjxZgnkbR11xUlyDMUFJdvnIgZEE16mqmY0BINMmc4//JMJglEmn6i7sq6p+mGrFWyZ98EEw== +"@babel/types@^7.12.1", "@babel/types@^7.12.13", "@babel/types@^7.12.17", "@babel/types@^7.12.6", "@babel/types@^7.13.0", "@babel/types@^7.13.12", "@babel/types@^7.14.0", "@babel/types@^7.14.2", "@babel/types@^7.14.4", "@babel/types@^7.4.4": + version "7.14.4" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.14.4.tgz#bfd6980108168593b38b3eb48a24aa026b919bc0" + integrity sha512-lCj4aIs0xUefJFQnwwQv2Bxg7Omd6bgquZ6LGC+gGMh6/s5qDVfjuCMlDmYQ15SLsWHd9n+X3E75lKIhl5Lkiw== dependencies: "@babel/helper-validator-identifier" "^7.14.0" to-fast-properties "^2.0.0" -"@bazel/bazelisk@^1.8.1": - version "1.8.1" - resolved "https://registry.yarnpkg.com/@bazel/bazelisk/-/bazelisk-1.8.1.tgz#b4f1b21c3cf2147bcad1ef9242c50095c5f1ad06" - integrity sha512-hD+93iN6sIiWqLdtikx7bRVScLp51Hn/gYGt7z8xFZAhl7TznC9opDe3gQvT7fwRWzgDkk6dtmJiNXlHVsv72g== +"@bazel/bazelisk@^1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@bazel/bazelisk/-/bazelisk-1.9.0.tgz#e55cc5394b838d85651bceb06abdc95a28c44e2f" + integrity sha512-IHCKlMBHd7VMt3Za7rpTJA8O1WI8TUes5nDjdYv1MM/4OZQQFbM85Zb8ugQaKvy3CzxLpXgxYZUiq7b2TdhQnw== -"@bazel/typescript@^3.5.0": - version "3.5.0" - resolved "https://registry.yarnpkg.com/@bazel/typescript/-/typescript-3.5.0.tgz#605493f4f0a5297df8a7fcccb86a1a80ea2090bb" - integrity sha512-BtGFp4nYFkQTmnONCzomk7dkmOwaINBL3piq+lykBlcc6UxLe9iCAnZpOyPypB1ReN3k3SRNAa53x6oGScQxMg== +"@bazel/typescript@^3.5.1": + version "3.5.1" + resolved "https://registry.yarnpkg.com/@bazel/typescript/-/typescript-3.5.1.tgz#c6027d683adeefa2c3cebfa3ed5efa17c405a63b" + integrity sha512-dU5sGgaGdFWV1dJ1B+9iFbttgcKtmob+BvlM8mY7Nxq4j7/wVbgPjiVLOBeOD7kpzYep8JHXfhAokHt486IG+Q== dependencies: protobufjs "6.8.8" semver "5.6.0" @@ -1378,7 +1401,7 @@ resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz#3c9ee980f1a10d6021ae6632ca3e79ca2ec4fb50" integrity sha512-giAlZwstKbmvMk1OO7WXSj4OZ0keXAcl2TQq4LWHiiPH2ByaH7WeUzng+Qej8UPxxv+8lRTuouo0iaNDBuzIBA== -"@types/json-schema@*", "@types/json-schema@^7.0.3", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.6": +"@types/json-schema@*", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.6", "@types/json-schema@^7.0.7": version "7.0.7" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad" integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA== @@ -1412,10 +1435,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@15.3.0": - version "15.3.0" - resolved "https://registry.yarnpkg.com/@types/node/-/node-15.3.0.tgz#d6fed7d6bc6854306da3dea1af9f874b00783e26" - integrity sha512-8/bnjSZD86ZfpBsDlCIkNXIvm+h6wi9g7IqL+kmFkQ+Wvu3JrasgLElfiPgoo8V8vVfnEi0QVS12gbl94h9YsQ== +"@types/node@*", "@types/node@15.6.1": + version "15.6.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-15.6.1.tgz#32d43390d5c62c5b6ec486a9bc9c59544de39a08" + integrity sha512-7EIraBEyRHEe7CH+Fm1XvgqU6uwZN8Q7jppJGcqjROMT29qhAuuOxYB1uEY5UMYQKEmA5D+5tBnhdaPXSsLONA== "@types/node@^10.1.0": version "10.17.55" @@ -1491,10 +1514,10 @@ dependencies: "@types/react" "*" -"@types/react@*", "@types/react@17.0.5": - version "17.0.5" - resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.5.tgz#3d887570c4489011f75a3fc8f965bf87d09a1bea" - integrity sha512-bj4biDB9ZJmGAYTWSKJly6bMr4BLUiBrx9ujiJEoP9XIDY9CTaPGxE5QWN/1WjpPLzYF7/jRNnV2nNxNe970sw== +"@types/react@*", "@types/react@17.0.8": + version "17.0.8" + resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.8.tgz#fe76e3ba0fbb5602704110fd1e3035cf394778e3" + integrity sha512-3sx4c0PbXujrYAKwXxNONXUtRp9C+hE2di0IuxFyf5BELD+B+AXL8G7QrmSKhVwKZDbv0igiAjQAMhXj8Yg3aw== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" @@ -1525,74 +1548,115 @@ resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.3.tgz#9c088679876f374eb5983f150d4787aa6fb32d7e" integrity sha512-FvUupuM3rlRsRtCN+fDudtmytGO6iHJuuRKS1Ss0pG5z8oX0diNEw94UEL7hgDbpN94rgaK5R7sWm6RrSkZuAQ== -"@typescript-eslint/eslint-plugin@^4.24.0": - version "4.24.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.24.0.tgz#03801ffc25b2af9d08f3dc9bccfc0b7ce3780d0f" - integrity sha512-qbCgkPM7DWTsYQGjx9RTuQGswi+bEt0isqDBeo+CKV0953zqI0Tp7CZ7Fi9ipgFA6mcQqF4NOVNwS/f2r6xShw== +"@types/yauzl@^2.9.1": + version "2.9.1" + resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.9.1.tgz#d10f69f9f522eef3cf98e30afb684a1e1ec923af" + integrity sha512-A1b8SU4D10uoPjwb0lnHmmu8wZhR9d+9o2PKBQT2jU5YPTKsxac6M2qGAdY7VcL+dHHhARVUDmeg0rOrcd9EjA== dependencies: - "@typescript-eslint/experimental-utils" "4.24.0" - "@typescript-eslint/scope-manager" "4.24.0" - debug "^4.1.1" - functional-red-black-tree "^1.0.1" - lodash "^4.17.15" - regexpp "^3.0.0" - semver "^7.3.2" - tsutils "^3.17.1" - -"@typescript-eslint/experimental-utils@4.24.0": - version "4.24.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.24.0.tgz#c23ead9de44b99c3a5fd925c33a106b00165e172" - integrity sha512-IwTT2VNDKH1h8RZseMH4CcYBz6lTvRoOLDuuqNZZoThvfHEhOiZPQCow+5El3PtyxJ1iDr6UXZwYtE3yZQjhcw== - dependencies: - "@types/json-schema" "^7.0.3" - "@typescript-eslint/scope-manager" "4.24.0" - "@typescript-eslint/types" "4.24.0" - "@typescript-eslint/typescript-estree" "4.24.0" - eslint-scope "^5.0.0" - eslint-utils "^2.0.0" - -"@typescript-eslint/parser@^4.24.0": - version "4.24.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.24.0.tgz#2e5f1cc78ffefe43bfac7e5659309a92b09a51bd" - integrity sha512-dj1ZIh/4QKeECLb2f/QjRwMmDArcwc2WorWPRlB8UNTZlY1KpTVsbX7e3ZZdphfRw29aTFUSNuGB8w9X5sS97w== - dependencies: - "@typescript-eslint/scope-manager" "4.24.0" - "@typescript-eslint/types" "4.24.0" - "@typescript-eslint/typescript-estree" "4.24.0" - debug "^4.1.1" + "@types/node" "*" -"@typescript-eslint/scope-manager@4.24.0": - version "4.24.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.24.0.tgz#38088216f0eaf235fa30ed8cabf6948ec734f359" - integrity sha512-9+WYJGDnuC9VtYLqBhcSuM7du75fyCS/ypC8c5g7Sdw7pGL4NDTbeH38eJPfzIydCHZDoOgjloxSAA3+4l/zsA== +"@typescript-eslint/eslint-plugin@^4.26.0": + version "4.26.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.26.0.tgz#12bbd6ebd5e7fabd32e48e1e60efa1f3554a3242" + integrity sha512-yA7IWp+5Qqf+TLbd8b35ySFOFzUfL7i+4If50EqvjT6w35X8Lv0eBHb6rATeWmucks37w+zV+tWnOXI9JlG6Eg== dependencies: - "@typescript-eslint/types" "4.24.0" - "@typescript-eslint/visitor-keys" "4.24.0" - -"@typescript-eslint/types@4.24.0": - version "4.24.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.24.0.tgz#6d0cca2048cbda4e265e0c4db9c2a62aaad8228c" - integrity sha512-tkZUBgDQKdvfs8L47LaqxojKDE+mIUmOzdz7r+u+U54l3GDkTpEbQ1Jp3cNqqAU9vMUCBA1fitsIhm7yN0vx9Q== + "@typescript-eslint/experimental-utils" "4.26.0" + "@typescript-eslint/scope-manager" "4.26.0" + debug "^4.3.1" + functional-red-black-tree "^1.0.1" + lodash "^4.17.21" + regexpp "^3.1.0" + semver "^7.3.5" + tsutils "^3.21.0" -"@typescript-eslint/typescript-estree@4.24.0": - version "4.24.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.24.0.tgz#b49249679a98014d8b03e8d4b70864b950e3c90f" - integrity sha512-kBDitL/by/HK7g8CYLT7aKpAwlR8doshfWz8d71j97n5kUa5caHWvY0RvEUEanL/EqBJoANev8Xc/mQ6LLwXGA== +"@typescript-eslint/experimental-utils@4.26.0": + version "4.26.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.26.0.tgz#ba7848b3f088659cdf71bce22454795fc55be99a" + integrity sha512-TH2FO2rdDm7AWfAVRB5RSlbUhWxGVuxPNzGT7W65zVfl8H/WeXTk1e69IrcEVsBslrQSTDKQSaJD89hwKrhdkw== dependencies: - "@typescript-eslint/types" "4.24.0" - "@typescript-eslint/visitor-keys" "4.24.0" + "@types/json-schema" "^7.0.7" + "@typescript-eslint/scope-manager" "4.26.0" + "@typescript-eslint/types" "4.26.0" + "@typescript-eslint/typescript-estree" "4.26.0" + eslint-scope "^5.1.1" + eslint-utils "^3.0.0" + +"@typescript-eslint/parser@^4.26.0": + version "4.26.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.26.0.tgz#31b6b732c9454f757b020dab9b6754112aa5eeaf" + integrity sha512-b4jekVJG9FfmjUfmM4VoOItQhPlnt6MPOBUL0AQbiTmm+SSpSdhHYlwayOm4IW9KLI/4/cRKtQCmDl1oE2OlPg== + dependencies: + "@typescript-eslint/scope-manager" "4.26.0" + "@typescript-eslint/types" "4.26.0" + "@typescript-eslint/typescript-estree" "4.26.0" + debug "^4.3.1" + +"@typescript-eslint/scope-manager@4.25.0": + version "4.25.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.25.0.tgz#9d86a5bcc46ef40acd03d85ad4e908e5aab8d4ca" + integrity sha512-2NElKxMb/0rya+NJG1U71BuNnp1TBd1JgzYsldsdA83h/20Tvnf/HrwhiSlNmuq6Vqa0EzidsvkTArwoq+tH6w== + dependencies: + "@typescript-eslint/types" "4.25.0" + "@typescript-eslint/visitor-keys" "4.25.0" + +"@typescript-eslint/scope-manager@4.26.0": + version "4.26.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.26.0.tgz#60d1a71df162404e954b9d1c6343ff3bee496194" + integrity sha512-G6xB6mMo4xVxwMt5lEsNTz3x4qGDt0NSGmTBNBPJxNsrTXJSm21c6raeYroS2OwQsOyIXqKZv266L/Gln1BWqg== + dependencies: + "@typescript-eslint/types" "4.26.0" + "@typescript-eslint/visitor-keys" "4.26.0" + +"@typescript-eslint/types@4.25.0": + version "4.25.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.25.0.tgz#0e444a5c5e3c22d7ffa5e16e0e60510b3de5af87" + integrity sha512-+CNINNvl00OkW6wEsi32wU5MhHti2J25TJsJJqgQmJu3B3dYDBcmOxcE5w9cgoM13TrdE/5ND2HoEnBohasxRQ== + +"@typescript-eslint/types@4.26.0": + version "4.26.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.26.0.tgz#7c6732c0414f0a69595f4f846ebe12616243d546" + integrity sha512-rADNgXl1kS/EKnDr3G+m7fB9yeJNnR9kF7xMiXL6mSIWpr3Wg5MhxyfEXy/IlYthsqwBqHOr22boFbf/u6O88A== + +"@typescript-eslint/typescript-estree@4.25.0": + version "4.25.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.25.0.tgz#942e4e25888736bff5b360d9b0b61e013d0cfa25" + integrity sha512-1B8U07TGNAFMxZbSpF6jqiDs1cVGO0izVkf18Q/SPcUAc9LhHxzvSowXDTvkHMWUVuPpagupaW63gB6ahTXVlg== + dependencies: + "@typescript-eslint/types" "4.25.0" + "@typescript-eslint/visitor-keys" "4.25.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.24.0": - version "4.24.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.24.0.tgz#a8fafdc76cad4e04a681a945fbbac4e35e98e297" - integrity sha512-4ox1sjmGHIxjEDBnMCtWFFhErXtKA1Ec0sBpuz0fqf3P+g3JFGyTxxbF06byw0FRsPnnbq44cKivH7Ks1/0s6g== +"@typescript-eslint/typescript-estree@4.26.0": + version "4.26.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.26.0.tgz#aea17a40e62dc31c63d5b1bbe9a75783f2ce7109" + integrity sha512-GHUgahPcm9GfBuy3TzdsizCcPjKOAauG9xkz9TR8kOdssz2Iz9jRCSQm6+aVFa23d5NcSpo1GdHGSQKe0tlcbg== + dependencies: + "@typescript-eslint/types" "4.26.0" + "@typescript-eslint/visitor-keys" "4.26.0" + debug "^4.3.1" + globby "^11.0.3" + is-glob "^4.0.1" + semver "^7.3.5" + tsutils "^3.21.0" + +"@typescript-eslint/visitor-keys@4.25.0": + version "4.25.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.25.0.tgz#863e7ed23da4287c5b469b13223255d0fde6aaa7" + integrity sha512-AmkqV9dDJVKP/TcZrbf6s6i1zYXt5Hl8qOLrRDTFfRNae4+LB8A4N3i+FLZPW85zIxRy39BgeWOfMS3HoH5ngg== + dependencies: + "@typescript-eslint/types" "4.25.0" + eslint-visitor-keys "^2.0.0" + +"@typescript-eslint/visitor-keys@4.26.0": + version "4.26.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.26.0.tgz#26d2583169222815be4dcd1da4fe5459bc3bcc23" + integrity sha512-cw4j8lH38V1ycGBbF+aFiLUls9Z0Bw8QschP3mkth50BbWzgFS33ISIgBzUMuQ2IdahoEv/rXstr8Zhlz4B1Zg== dependencies: - "@typescript-eslint/types" "4.24.0" + "@typescript-eslint/types" "4.26.0" eslint-visitor-keys "^2.0.0" "@webassemblyjs/ast@1.11.0": @@ -1791,10 +1855,10 @@ ajv@^6.1.0, ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^7.0.2: - version "7.2.1" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-7.2.1.tgz#a5ac226171912447683524fa2f1248fcf8bac83d" - integrity sha512-+nu0HDv7kNSOua9apAVc979qd932rrZeb3WOvoiD31A/p1mIE5/9bN2027pE2rOPYEdS3UHzsvof4hY+lM9/WQ== +ajv@^8.0.1: + version "8.5.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.5.0.tgz#695528274bcb5afc865446aa275484049a18ae4b" + integrity sha512-Y2l399Tt1AguU3BPRP9Fn4eN+Or+StUGWCUpbnFyXSo8NZ9S4uj+AG2pjs5apK+ZMOwYOz1+a+VKvKH7CudXgQ== dependencies: fast-deep-equal "^3.1.1" json-schema-traverse "^1.0.0" @@ -2207,16 +2271,16 @@ braces@^3.0.1, braces@~3.0.2: dependencies: fill-range "^7.0.1" -browserslist@^4.14.5, browserslist@^4.16.3: - version "4.16.3" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.3.tgz#340aa46940d7db878748567c5dea24a48ddf3717" - integrity sha512-vIyhWmIkULaq04Gt93txdh+j02yX/JzlyhLYbV3YQCn/zvES3JnY7TifHHvvr1w5hTDluNKMkV05cs4vy8Q7sw== +browserslist@^4.14.5, browserslist@^4.16.3, browserslist@^4.16.6: + version "4.16.6" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.6.tgz#d7901277a5a88e554ed305b183ec9b0c08f66fa2" + integrity sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ== dependencies: - caniuse-lite "^1.0.30001181" - colorette "^1.2.1" - electron-to-chromium "^1.3.649" + caniuse-lite "^1.0.30001219" + colorette "^1.2.2" + electron-to-chromium "^1.3.723" escalade "^3.1.1" - node-releases "^1.1.70" + node-releases "^1.1.71" buffer-crc32@~0.2.3: version "0.2.13" @@ -2303,10 +2367,10 @@ camelcase@^6.2.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809" integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== -caniuse-lite@^1.0.30001181: - version "1.0.30001202" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001202.tgz#4cb3bd5e8a808e8cd89e4e66c549989bc8137201" - integrity sha512-ZcijQNqrcF8JNLjzvEiXqX4JUYxoZa7Pvcsd9UD8Kz4TvhTonOSNRsK+qtvpVL4l6+T1Rh4LFtLfnNWg6BGWCQ== +caniuse-lite@^1.0.30001219: + version "1.0.30001228" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001228.tgz#bfdc5942cd3326fa51ee0b42fbef4da9d492a7fa" + integrity sha512-QQmLOGJ3DEgokHbMSA8cj2a+geXqmnpyOFT0lhQV6P3/YOJvGDEwoedcwxEQ30gJIwIIunHIicunJ2rzK5gB2A== caseless@~0.12.0: version "0.12.0" @@ -2612,16 +2676,6 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= -concat-stream@^1.6.2: - version "1.6.2" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" - integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== - dependencies: - buffer-from "^1.0.0" - inherits "^2.0.3" - readable-stream "^2.2.2" - typedarray "^0.0.6" - connect-history-api-fallback@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz#8b32089359308d111115d81cad3fceab888f97bc" @@ -2661,13 +2715,13 @@ copy-descriptor@^0.1.0: resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= -copy-webpack-plugin@^8.1.1: - version "8.1.1" - resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-8.1.1.tgz#3f697e162764925c2f0d235f380676125508fd26" - integrity sha512-rYM2uzRxrLRpcyPqGceRBDpxxUV8vcDqIKxAUKfcnFpcrPxT5+XvhTxv7XLjo5AvEJFPdAE3zCogG2JVahqgSQ== +copy-webpack-plugin@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-9.0.0.tgz#2bf592785d2fcdde9342dfed3676490fe0aa7ce8" + integrity sha512-k8UB2jLIb1Jip2nZbCz83T/XfhfjX6mB1yLJNYKrpYi7FQimfOoFv/0//iT6HV1K8FwUB5yUbCcnpLebJXJTug== dependencies: fast-glob "^3.2.5" - glob-parent "^5.1.1" + glob-parent "^6.0.0" globby "^11.0.3" normalize-path "^3.0.0" p-limit "^3.1.0" @@ -2682,10 +2736,10 @@ core-js-compat@^3.9.0, core-js-compat@^3.9.1: browserslist "^4.16.3" semver "7.0.0" -core-js@3.12.1: - version "3.12.1" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.12.1.tgz#6b5af4ff55616c08a44d386f1f510917ff204112" - integrity sha512-Ne9DKPHTObRuB09Dru5AjwKjY4cJHVGu+y5f7coGn1E9Grkc3p2iBwE9AI/nJzsE29mQF7oq+mhYYRqOMFN1Bw== +core-js@3.13.1: + version "3.13.1" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.13.1.tgz#30303fabd53638892062d8b4e802cac7599e9fb7" + integrity sha512-JqveUc4igkqwStL2RTRn/EPFGBOfEZHxJl/8ej1mXJR75V3go2mFF4bmUYkEIT1rveHKnkUlcJX/c+f1TyIovQ== core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" @@ -2723,15 +2777,14 @@ 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.4: - version "5.2.4" - resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-5.2.4.tgz#e985dcbce339812cb6104ef3670f08f9893a1536" - integrity sha512-OFYGyINCKkdQsTrSYxzGSFnGS4gNjcXkKkQgWxK138jgnPt+lepxdjSZNc8sHAl5vP3DhsJUxufWIjOwI8PMMw== +css-loader@^5.2.6: + version "5.2.6" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-5.2.6.tgz#c3c82ab77fea1f360e587d871a6811f4450cc8d1" + integrity sha512-0wyN5vXMQZu6BvjbrPdUJvkCzGEO24HC7IS7nW4llc6BBFC+zwR9CKtYGv63Puzsg10L/o12inMY5/2ByzfD6w== dependencies: - camelcase "^6.2.0" icss-utils "^5.1.0" loader-utils "^2.0.0" - postcss "^8.2.10" + postcss "^8.2.15" postcss-modules-extract-imports "^3.0.0" postcss-modules-local-by-default "^4.0.0" postcss-modules-scope "^3.0.0" @@ -2793,10 +2846,10 @@ csstype@^3.0.2: resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.7.tgz#2a5fb75e1015e84dd15692f71e89a1450290950b" integrity sha512-KxnUB0ZMlnUWCsx2Z8MUsr6qV6ja1w9ArPErJaJaF8a5SOWoHLIszeCTKGRGRgtLgYrs1E8CHkNSP1VZTTPc9g== -cypress@^7.3.0: - version "7.3.0" - resolved "https://registry.yarnpkg.com/cypress/-/cypress-7.3.0.tgz#17345b8d18681c120f033e7d8fd0f0271e9d0d51" - integrity sha512-aseRCH1tRVCrM6oEfja6fR/bo5l6e4SkHRRSATh27UeN4f/ANC8U7tGIulmrISJVy9xuOkOdbYKbUb2MNM+nrw== +cypress@^7.4.0: + version "7.4.0" + resolved "https://registry.yarnpkg.com/cypress/-/cypress-7.4.0.tgz#679bfe75335b9a4873d44f0d989e9f0367f00665" + integrity sha512-+CmSoT5DS88e92YDfc6aDA3Zf3uCBRKVB92caWsjXMilz0tf6NpByFvIbLLVWXiYOwrhtWV0m/k93+rzodYwRQ== dependencies: "@cypress/listr-verbose-renderer" "^0.4.1" "@cypress/request" "^2.88.5" @@ -2818,7 +2871,7 @@ cypress@^7.3.0: eventemitter2 "^6.4.3" execa "4.1.0" executable "^4.1.1" - extract-zip "^1.7.0" + extract-zip "2.0.1" fs-extra "^9.1.0" getos "^3.2.1" is-ci "^3.0.0" @@ -2855,14 +2908,14 @@ dayjs@^1.10.4: resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.10.4.tgz#8e544a9b8683f61783f570980a8a80eaf54ab1e2" integrity sha512-RI/Hh4kqRc1UKLOAf/T5zdMMX5DQIlDxwUe3wSyMMnEbGunnpENCdbUgM+dW7kXidZqCttBrmw7BhN4TMddkCw== -debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: +debug@2.6.9, debug@^2.2.0, debug@^2.3.3: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== dependencies: ms "2.0.0" -debug@4.3.2, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: +debug@4.3.2, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: version "4.3.2" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b" integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw== @@ -2991,9 +3044,9 @@ dns-equal@^1.0.0: integrity sha1-s55/HabrCnW6nBcySzR1PEfgZU0= dns-packet@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-1.3.1.tgz#12aa426981075be500b910eedcd0b47dd7deda5a" - integrity sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg== + version "1.3.4" + resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-1.3.4.tgz#e3455065824a2507ba886c55a89963bb107dec6f" + integrity sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA== dependencies: ip "^1.1.0" safe-buffer "^5.0.1" @@ -3090,10 +3143,10 @@ ee-first@1.1.1: resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= -electron-to-chromium@^1.3.649: - version "1.3.691" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.691.tgz#a671eaf135a3ccec0915eb8d844a0952aba79f3b" - integrity sha512-ZqiO69KImmOGCyoH0icQPU3SndJiW93juEvf63gQngyhODO6SpQIPMTOHldtCs5DS5GMKvAkquk230E2zt2vpw== +electron-to-chromium@^1.3.723: + version "1.3.737" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.737.tgz#196f2e9656f4f3c31930750e1899c091b72d36b5" + integrity sha512-P/B84AgUSQXaum7a8m11HUsYL8tj9h/Pt5f7Hg7Ty6bm5DxlFq+e5+ouHUoNQMsKDJ7u4yGfI8mOErCmSH9wyg== elegant-spinner@^1.0.1: version "1.0.1" @@ -3201,6 +3254,28 @@ es-abstract@^1.17.2, es-abstract@^1.18.0-next.1, es-abstract@^1.18.0-next.2: string.prototype.trimstart "^1.0.4" unbox-primitive "^1.0.0" +es-abstract@^1.18.2: + version "1.18.3" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.3.tgz#25c4c3380a27aa203c44b2b685bba94da31b63e0" + integrity sha512-nQIr12dxV7SSxE6r6f1l3DtAeEYdsGpps13dR0TwJg1S8gyp4ZPgy3FZcHBgbiQqnoqSTb+oC+kO4UQ0C/J8vw== + dependencies: + call-bind "^1.0.2" + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + get-intrinsic "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.2" + is-callable "^1.2.3" + is-negative-zero "^2.0.1" + is-regex "^1.1.3" + is-string "^1.0.6" + object-inspect "^1.10.3" + object-keys "^1.1.1" + object.assign "^4.1.2" + string.prototype.trimend "^1.0.4" + string.prototype.trimstart "^1.0.4" + unbox-primitive "^1.0.1" + es-module-lexer@^0.4.0: version "0.4.1" resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.4.1.tgz#dda8c6a14d8f340a24e34331e0fab0cb50438e0e" @@ -3230,10 +3305,15 @@ escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= -eslint-plugin-react@^7.23.2: - version "7.23.2" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.23.2.tgz#2d2291b0f95c03728b55869f01102290e792d494" - integrity sha512-AfjgFQB+nYszudkxRkTFu0UR1zEQig0ArVMPloKhxwlwkzaw/fBiH0QWcBBhZONlXqQC51+nfqFrkn4EzHcGBw== +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +eslint-plugin-react@^7.24.0: + version "7.24.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.24.0.tgz#eadedfa351a6f36b490aa17f4fa9b14e842b9eb4" + integrity sha512-KJJIx2SYx7PBx3ONe/mEeMz4YE0Lcr7feJTCMyyKb/341NcjuAgim3Acgan89GfPv7nxXK2+0slu0CWXYM4x+Q== dependencies: array-includes "^3.1.3" array.prototype.flatmap "^1.2.4" @@ -3241,14 +3321,14 @@ eslint-plugin-react@^7.23.2: has "^1.0.3" jsx-ast-utils "^2.4.1 || ^3.0.0" minimatch "^3.0.4" - object.entries "^1.1.3" + object.entries "^1.1.4" object.fromentries "^2.0.4" - object.values "^1.1.3" + object.values "^1.1.4" prop-types "^15.7.2" resolve "^2.0.0-next.3" - string.prototype.matchall "^4.0.4" + string.prototype.matchall "^4.0.5" -eslint-scope@^5.0.0, eslint-scope@^5.1.1: +eslint-scope@5.1.1, eslint-scope@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== @@ -3256,13 +3336,20 @@ eslint-scope@^5.0.0, eslint-scope@^5.1.1: esrecurse "^4.3.0" estraverse "^4.1.1" -eslint-utils@^2.0.0, eslint-utils@^2.1.0: +eslint-utils@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== dependencies: eslint-visitor-keys "^1.1.0" +eslint-utils@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672" + integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== + dependencies: + eslint-visitor-keys "^2.0.0" + eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" @@ -3273,10 +3360,10 @@ eslint-visitor-keys@^2.0.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8" integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ== -eslint@^7.26.0: - version "7.26.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.26.0.tgz#d416fdcdcb3236cd8f282065312813f8c13982f6" - integrity sha512-4R1ieRf52/izcZE7AlLy56uIHHDLT74Yzz2Iv2l6kDaYvEu9x+wMB5dZArVL8SYGXSYV2YAg70FcW5Y5nGGNIg== +eslint@^7.27.0: + version "7.27.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.27.0.tgz#665a1506d8f95655c9274d84bd78f7166b07e9c7" + integrity sha512-JZuR6La2ZF0UD384lcbnd0Cgg6QJjiCwhMD6eU4h/VGPcVGwawNNzKU41tgokGXnfjOOyI6QIffthhJTPzzuRA== dependencies: "@babel/code-frame" "7.12.11" "@eslint/eslintrc" "^0.4.1" @@ -3286,12 +3373,14 @@ eslint@^7.26.0: debug "^4.0.1" doctrine "^3.0.0" enquirer "^2.3.5" + escape-string-regexp "^4.0.0" eslint-scope "^5.1.1" eslint-utils "^2.1.0" eslint-visitor-keys "^2.0.0" espree "^7.3.1" esquery "^1.4.0" esutils "^2.0.2" + fast-deep-equal "^3.1.3" file-entry-cache "^6.0.1" functional-red-black-tree "^1.0.1" glob-parent "^5.0.0" @@ -3303,7 +3392,7 @@ eslint@^7.26.0: js-yaml "^3.13.1" json-stable-stringify-without-jsonify "^1.0.1" levn "^0.4.1" - lodash "^4.17.21" + lodash.merge "^4.6.2" minimatch "^3.0.4" natural-compare "^1.4.0" optionator "^0.9.1" @@ -3312,7 +3401,7 @@ eslint@^7.26.0: semver "^7.2.1" strip-ansi "^6.0.0" strip-json-comments "^3.1.0" - table "^6.0.4" + table "^6.0.9" text-table "^0.2.0" v8-compile-cache "^2.0.3" @@ -3529,15 +3618,16 @@ extglob@^2.0.4: snapdragon "^0.8.1" to-regex "^3.0.1" -extract-zip@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.7.0.tgz#556cc3ae9df7f452c493a0cfb51cc30277940927" - integrity sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA== +extract-zip@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-2.0.1.tgz#663dca56fe46df890d5f131ef4a06d22bb8ba13a" + integrity sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg== dependencies: - concat-stream "^1.6.2" - debug "^2.6.9" - mkdirp "^0.5.4" + debug "^4.1.1" + get-stream "^5.1.0" yauzl "^2.10.0" + optionalDependencies: + "@types/yauzl" "^2.9.1" extsprintf@1.3.0: version "1.3.0" @@ -3549,7 +3639,7 @@ extsprintf@^1.2.0: resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= -fast-deep-equal@^3.1.1: +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== @@ -3809,7 +3899,7 @@ get-stream@^4.0.0: dependencies: pump "^3.0.0" -get-stream@^5.0.0: +get-stream@^5.0.0, get-stream@^5.1.0: version "5.2.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== @@ -3848,13 +3938,20 @@ glob-parent@^3.1.0: is-glob "^3.1.0" path-dirname "^1.0.0" -glob-parent@^5.0.0, glob-parent@^5.1.0, glob-parent@^5.1.1, glob-parent@~5.1.0: +glob-parent@^5.0.0, glob-parent@^5.1.0, glob-parent@~5.1.0: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" +glob-parent@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.0.tgz#f851b59b388e788f3a44d63fab50382b2859c33c" + integrity sha512-Hdd4287VEJcZXUwv1l8a+vXC1GjOQqXe+VS30w/ypihpcnu9M1n3xeYeJu5CBpeEQj2nAab2xxz28GuA3vp4Ww== + dependencies: + is-glob "^4.0.1" + glob-to-regexp@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" @@ -3966,7 +4063,7 @@ has-ansi@^2.0.0: dependencies: ansi-regex "^2.0.0" -has-bigints@^1.0.0: +has-bigints@^1.0.0, has-bigints@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA== @@ -4590,6 +4687,14 @@ is-regex@^1.0.4, is-regex@^1.1.2: call-bind "^1.0.2" has-symbols "^1.0.1" +is-regex@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.3.tgz#d029f9aff6448b93ebbe3f33dac71511fdcbef9f" + integrity sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ== + dependencies: + call-bind "^1.0.2" + has-symbols "^1.0.2" + is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" @@ -4605,6 +4710,11 @@ is-string@^1.0.5: resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6" integrity sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ== +is-string@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.6.tgz#3fe5d5992fb0d93404f32584d4b0179a71b54a5f" + integrity sha512-2gdzbKUuqtQ3lYNrUTQYoClPhm7oQu4UdpSZMp1/DGgkHBT8E2Z1l0yMdb6D4zNAxwDiMv8MdulKROJGNl0Q0w== + is-symbol@^1.0.2, is-symbol@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" @@ -4933,16 +5043,31 @@ 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.clonedeep@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" + integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= + lodash.debounce@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + lodash.once@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= +lodash.truncate@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" + integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM= + lodash@^4.0.1, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" @@ -5234,7 +5359,7 @@ mixin-deep@^1.2.0: for-in "^1.0.2" is-extendable "^1.0.1" -mkdirp@^0.5.1, mkdirp@^0.5.4, mkdirp@^0.5.5, mkdirp@~0.5.1: +mkdirp@^0.5.1, mkdirp@^0.5.5, mkdirp@~0.5.1: version "0.5.5" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== @@ -5279,10 +5404,10 @@ nan@^2.12.1: resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19" integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ== -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== +nanoid@^3.1.23: + version "3.1.23" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.23.tgz#f744086ce7c2bc47ee0a8472574d5c78e4183a81" + integrity sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw== nanomatch@^1.2.9: version "1.2.13" @@ -5334,10 +5459,10 @@ node-forge@^0.10.0: resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3" integrity sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA== -node-releases@^1.1.70: - version "1.1.71" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.71.tgz#cb1334b179896b1c89ecfdd4b725fb7bbdfc7dbb" - integrity sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg== +node-releases@^1.1.71: + version "1.1.72" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.72.tgz#14802ab6b1039a79a0c7d662b610a5bbd76eacbe" + integrity sha512-LLUo+PpH3dU6XizX3iVoubUNheF/owjXCZZ5yACDxNnPtgFuludV1ZL3ayK1kVep42Rmm0+R9/Y60NQbZ2bifw== normalize-path@^2.1.1: version "2.1.1" @@ -5396,6 +5521,11 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" +object-inspect@^1.10.3: + version "1.10.3" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.10.3.tgz#c2aa7d2d09f50c99375704f7a0adf24c5782d369" + integrity sha512-e5mCJlSH7poANfC8z8S9s9S2IN5/4Zb3aZ33f5s8YqoazCFzNLloLU8r5VCG+G7WoqLvAAZoVMcy3tp/3X0Plw== + object-inspect@^1.9.0: version "1.9.0" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.9.0.tgz#c90521d74e1127b67266ded3394ad6116986533a" @@ -5431,15 +5561,14 @@ object.assign@^4.1.0, object.assign@^4.1.2: has-symbols "^1.0.1" object-keys "^1.1.1" -object.entries@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.3.tgz#c601c7f168b62374541a07ddbd3e2d5e4f7711a6" - integrity sha512-ym7h7OZebNS96hn5IJeyUmaWhaSM4SVtAPPfNLQEI2MYWCO2egsITb9nab2+i/Pwibx+R0mtn+ltKJXRSeTMGg== +object.entries@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.4.tgz#43ccf9a50bc5fd5b649d45ab1a579f24e088cafd" + integrity sha512-h4LWKWE+wKQGhtMjZEBud7uLGhqyLwj8fpHOarZhD2uY3C9cRtk57VQ89ke3moByLXMedqs3XCHzyb4AmA2DjA== dependencies: - call-bind "^1.0.0" + call-bind "^1.0.2" define-properties "^1.1.3" - es-abstract "^1.18.0-next.1" - has "^1.0.3" + es-abstract "^1.18.2" object.fromentries@^2.0.4: version "2.0.4" @@ -5467,15 +5596,14 @@ object.pick@^1.3.0: dependencies: isobject "^3.0.1" -object.values@^1.1.0, object.values@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.3.tgz#eaa8b1e17589f02f698db093f7c62ee1699742ee" - integrity sha512-nkF6PfDB9alkOUxpf1HNm/QlkeW3SReqL5WXeBLpEJJnlPSvRaDQpW3gQTksTN3fgJX4hL42RzKyOin6ff3tyw== +object.values@^1.1.0, object.values@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.4.tgz#0d273762833e816b693a637d30073e7051535b30" + integrity sha512-TnGo7j4XSnKQoK3MfvkzqKCi0nVe/D9I9IjwTNYdb/fxYHpjrluHVOgw0AF6jrRFGMPHdfuidR09tIDiIvnaSg== dependencies: call-bind "^1.0.2" define-properties "^1.1.3" - es-abstract "^1.18.0-next.2" - has "^1.0.3" + es-abstract "^1.18.2" obuf@^1.0.0, obuf@^1.1.2: version "1.1.2" @@ -5826,14 +5954,14 @@ 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.10: - version "8.2.10" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.2.10.tgz#ca7a042aa8aff494b334d0ff3e9e77079f6f702b" - integrity sha512-b/h7CPV7QEdrqIxtAf2j31U5ef05uBDuvoXv6L51Q4rcS1jdlXAVKJv+atCFdUXYl9dyTHGyoMzIepwowRJjFw== +postcss@^8.2.15: + version "8.3.0" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.3.0.tgz#b1a713f6172ca427e3f05ef1303de8b65683325f" + integrity sha512-+ogXpdAjWGa+fdYY5BQ96V/6tAo+TdSSIMP5huJBIygdWwKtVoB5JWZ7yUd4xZ8r+8Kvvx4nyg/PQ071H4UtcQ== dependencies: colorette "^1.2.2" - nanoid "^3.1.22" - source-map "^0.6.1" + nanoid "^3.1.23" + source-map-js "^0.6.2" preact@^10.5.12: version "10.5.13" @@ -6170,7 +6298,7 @@ reactcss@^1.2.0: dependencies: lodash "^4.0.1" -readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.2.2: +readable-stream@^2.0.1, readable-stream@^2.0.2: version "2.3.7" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== @@ -6267,7 +6395,7 @@ regexp.prototype.flags@^1.2.0, regexp.prototype.flags@^1.3.1: call-bind "^1.0.2" define-properties "^1.1.3" -regexpp@^3.0.0, regexpp@^3.1.0: +regexpp@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2" integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q== @@ -6517,10 +6645,10 @@ sass-loader@^11.1.1: klona "^2.0.4" neo-async "^2.6.2" -sass@^1.32.13: - version "1.32.13" - resolved "https://registry.yarnpkg.com/sass/-/sass-1.32.13.tgz#8d29c849e625a415bce71609c7cf95e15f74ed00" - integrity sha512-dEgI9nShraqP7cXQH+lEXVf73WOPCse0QlFzSD8k+1TcOxCMwVXfQlr0jtoluZysQOyJGnfr21dLvYKDJq8HkA== +sass@^1.34.0: + version "1.34.0" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.34.0.tgz#e46d5932d8b0ecc4feb846d861f26a578f7f7172" + integrity sha512-rHEN0BscqjUYuomUEaqq3BMgsXqQfkcMVR7UhscsAVub0/spUrZGBMxQXFS2kfiDsPLZw5yuU9iJEFNC2x38Qw== dependencies: chokidar ">=3.0.0 <4.0.0" @@ -6811,6 +6939,11 @@ source-list-map@^2.0.1: resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== +source-map-js@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-0.6.2.tgz#0bb5de631b41cfbda6cfba8bd05a80efdfd2385e" + integrity sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug== + source-map-resolve@^0.5.0: version "0.5.3" resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" @@ -6966,15 +7099,16 @@ string-width@^4.2.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.0" -string.prototype.matchall@^4.0.4: - version "4.0.4" - resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.4.tgz#608f255e93e072107f5de066f81a2dfb78cf6b29" - integrity sha512-pknFIWVachNcyqRfaQSeu/FUfpvJTe4uskUSZ9Wc1RijsPuzbZ8TyYT8WCNnntCjUEqQ3vUHMAfVj2+wLAisPQ== +string.prototype.matchall@^4.0.5: + version "4.0.5" + resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.5.tgz#59370644e1db7e4c0c045277690cf7b01203c4da" + integrity sha512-Z5ZaXO0svs0M2xd/6By3qpeKpLKd9mO4v4q3oMEQrk8Ck4xOD5d5XeBOOjGrmVZZ/AHB1S0CgG4N5r1G9N3E2Q== dependencies: call-bind "^1.0.2" define-properties "^1.1.3" - es-abstract "^1.18.0-next.2" - has-symbols "^1.0.1" + es-abstract "^1.18.2" + get-intrinsic "^1.1.1" + has-symbols "^1.0.2" internal-slot "^1.0.3" regexp.prototype.flags "^1.3.1" side-channel "^1.0.4" @@ -7129,15 +7263,17 @@ symbol-observable@^1.1.0: resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ== -table@^6.0.4: - version "6.0.7" - resolved "https://registry.yarnpkg.com/table/-/table-6.0.7.tgz#e45897ffbcc1bcf9e8a87bf420f2c9e5a7a52a34" - integrity sha512-rxZevLGTUzWna/qBLObOe16kB2RTnnbhciwgPbMMlazz1yZGVEgnZK762xyVdVznhqxrfCeBMmMkgOOaPwjH7g== +table@^6.0.9: + version "6.7.1" + resolved "https://registry.yarnpkg.com/table/-/table-6.7.1.tgz#ee05592b7143831a8c94f3cee6aae4c1ccef33e2" + integrity sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg== dependencies: - ajv "^7.0.2" - lodash "^4.17.20" + ajv "^8.0.1" + lodash.clonedeep "^4.5.0" + lodash.truncate "^4.4.2" slice-ansi "^4.0.0" string-width "^4.2.0" + strip-ansi "^6.0.0" tapable@^2.0.0, tapable@^2.1.1, tapable@^2.2.0: version "2.2.0" @@ -7288,7 +7424,7 @@ tsutils@2.27.2: dependencies: tslib "^1.8.1" -tsutils@^3.17.1: +tsutils@^3.17.1, tsutils@^3.21.0: version "3.21.0" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== @@ -7332,11 +7468,6 @@ type-is@~1.6.17, type-is@~1.6.18: media-typer "0.3.0" mime-types "~2.1.24" -typedarray@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" - integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= - typesafe-actions@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/typesafe-actions/-/typesafe-actions-5.1.0.tgz#9afe8b1e6a323af1fd59e6a57b11b7dd6623d2f1" @@ -7357,6 +7488,16 @@ unbox-primitive@^1.0.0: has-symbols "^1.0.0" which-boxed-primitive "^1.0.1" +unbox-primitive@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471" + integrity sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw== + dependencies: + function-bind "^1.1.1" + has-bigints "^1.0.1" + has-symbols "^1.0.2" + which-boxed-primitive "^1.0.2" + unicode-canonical-property-names-ecmascript@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818" @@ -7605,10 +7746,10 @@ warning@^4.0.3: dependencies: loose-envify "^1.0.0" -watchpack@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.1.1.tgz#e99630550fca07df9f90a06056987baa40a689c7" - integrity sha512-Oo7LXCmc1eE1AjyuSBmtC3+Wy4HcV8PxWh2kP6fOl8yTlNS7r0K9l1ao2lrrUza7V39Y3D/BbJgY8VeSlc5JKw== +watchpack@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.2.0.tgz#47d78f5415fe550ecd740f99fe2882323a58b1ce" + integrity sha512-up4YAn/XHgZHIxFBVCdlMiWDj6WaLKpwVeGQk2I5thdYxF/KmF0aaz6TfJZ/hfl1h/XlcDr7k1KH7ThDagpFaA== dependencies: glob-to-regexp "^0.4.1" graceful-fs "^4.1.2" @@ -7720,18 +7861,18 @@ webpack-merge@^5.7.3: clone-deep "^4.0.1" wildcard "^2.0.0" -webpack-sources@^2.1.1: - version "2.2.0" - resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-2.2.0.tgz#058926f39e3d443193b6c31547229806ffd02bac" - integrity sha512-bQsA24JLwcnWGArOKUxYKhX3Mz/nK1Xf6hxullKERyktjNMC4x8koOeaDNTA2fEJ09BdWLbM/iTW0ithREUP0w== +webpack-sources@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-2.3.0.tgz#9ed2de69b25143a4c18847586ad9eccb19278cfa" + integrity sha512-WyOdtwSvOML1kbgtXbTDnEW0jkJ7hZr/bDByIwszhWd/4XX1A3XMkrbFMsuH4+/MfLlZCUzlAdg4r7jaGKEIgQ== dependencies: source-list-map "^2.0.1" source-map "^0.6.1" -webpack@^5.37.0: - version "5.37.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.37.0.tgz#2ab00f613faf494504eb2beef278dab7493cc39d" - integrity sha512-yvdhgcI6QkQkDe1hINBAJ1UNevqNGTVaCkD2SSJcB8rcrNNl922RI8i2DXUAuNfANoxwsiXXEA4ZPZI9q2oGLA== +webpack@^5.38.1: + version "5.38.1" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.38.1.tgz#5224c7f24c18e729268d3e3bc97240d6e880258e" + integrity sha512-OqRmYD1OJbHZph6RUMD93GcCZy4Z4wC0ele4FXyYF0J6AxO1vOSuIlU1hkS/lDlR9CDYBz64MZRmdbdnFFoT2g== dependencies: "@types/eslint-scope" "^3.7.0" "@types/estree" "^0.0.47" @@ -7743,7 +7884,7 @@ webpack@^5.37.0: chrome-trace-event "^1.0.2" enhanced-resolve "^5.8.0" es-module-lexer "^0.4.0" - eslint-scope "^5.1.1" + eslint-scope "5.1.1" events "^3.2.0" glob-to-regexp "^0.4.1" graceful-fs "^4.2.4" @@ -7754,8 +7895,8 @@ webpack@^5.37.0: schema-utils "^3.0.0" tapable "^2.1.1" terser-webpack-plugin "^5.1.1" - watchpack "^2.0.0" - webpack-sources "^2.1.1" + watchpack "^2.2.0" + webpack-sources "^2.3.0" websocket-driver@>=0.5.1, websocket-driver@^0.7.4: version "0.7.4" @@ -7771,7 +7912,7 @@ websocket-extensions@>=0.1.1: resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42" integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== -which-boxed-primitive@^1.0.1: +which-boxed-primitive@^1.0.1, which-boxed-primitive@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==