Consumer group details view. (#18)
* Added concumer groups list with search. * Added consumer group details view. * Minor tweaks. * Backend group details (#19) * temp result commit * consumer group details api done * fixes * changed calls from loop into vars, changed foreach to map * changes * removed redundant import * Refactoring, fixed contract * Fixed useless group query * fix-consumer-groups-retaining-cluster-on-cluster-switch Co-authored-by: Roman Nedzvetskiy <roman@Romans-MacBook-Pro.local> Co-authored-by: German Osin <german.osin@gmail.com> Co-authored-by: Azat Gataullin <gataniel@gmail.com> * Add loader, fix details * Add empty text for consumer groups Co-authored-by: Sofia Shnaidman <sshnaidman@provectus.com> Co-authored-by: Roman Nedzvetskiy <gmcodemail@gmail.com> Co-authored-by: Roman Nedzvetskiy <roman@Romans-MacBook-Pro.local> Co-authored-by: German Osin <german.osin@gmail.com> Co-authored-by: Azat Gataullin <gataniel@gmail.com> Co-authored-by: Maxim Tereshin <tereshin93@gmail.com>
This commit is contained in:
parent
a0b4b6e1f0
commit
0815739d79
33 changed files with 1156 additions and 110 deletions
|
@ -49,6 +49,11 @@
|
||||||
<artifactId>kafka-clients</artifactId>
|
<artifactId>kafka-clients</artifactId>
|
||||||
<version>${kafka-clients.version}</version>
|
<version>${kafka-clients.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.kafka</groupId>
|
||||||
|
<artifactId>kafka_2.13</artifactId>
|
||||||
|
<version>${kafka.version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.101tec</groupId>
|
<groupId>com.101tec</groupId>
|
||||||
<artifactId>zkclient</artifactId>
|
<artifactId>zkclient</artifactId>
|
||||||
|
|
|
@ -8,6 +8,7 @@ import com.provectus.kafka.ui.cluster.model.KafkaCluster;
|
||||||
import com.provectus.kafka.ui.model.*;
|
import com.provectus.kafka.ui.model.*;
|
||||||
import org.mapstruct.Mapper;
|
import org.mapstruct.Mapper;
|
||||||
import org.mapstruct.Mapping;
|
import org.mapstruct.Mapping;
|
||||||
|
import org.mapstruct.ValueMapping;
|
||||||
|
|
||||||
@Mapper(componentModel = "spring")
|
@Mapper(componentModel = "spring")
|
||||||
public interface ClusterMapper {
|
public interface ClusterMapper {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package com.provectus.kafka.ui.cluster.model;
|
package com.provectus.kafka.ui.cluster.model;
|
||||||
|
|
||||||
|
import com.provectus.kafka.ui.model.ServerStatus;
|
||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
|
@ -22,4 +23,5 @@ public class InternalClusterMetrics {
|
||||||
//TODO: find way to fill
|
//TODO: find way to fill
|
||||||
private final int segmentSize;
|
private final int segmentSize;
|
||||||
private final int segmentCount;
|
private final int segmentCount;
|
||||||
|
private final int zooKeeperStatus;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,14 +8,20 @@ import com.provectus.kafka.ui.kafka.KafkaService;
|
||||||
import com.provectus.kafka.ui.model.*;
|
import com.provectus.kafka.ui.model.*;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
|
import org.apache.kafka.clients.admin.ConsumerGroupListing;
|
||||||
|
import org.apache.kafka.clients.consumer.ConsumerConfig;
|
||||||
|
import org.apache.kafka.clients.consumer.KafkaConsumer;
|
||||||
|
import org.apache.kafka.clients.consumer.OffsetAndMetadata;
|
||||||
|
import org.apache.kafka.common.TopicPartition;
|
||||||
|
import org.apache.kafka.common.serialization.StringDeserializer;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import reactor.core.publisher.Flux;
|
import reactor.core.publisher.Flux;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
|
@ -66,10 +72,57 @@ public class ClusterService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public Mono<List<ConsumerGroup>> getConsumerGroups(String clusterName) {
|
public Mono<ConsumerGroupDetails> getConsumerGroupDetail(String clusterName, String consumerGroupId) {
|
||||||
|
var cluster = clustersStorage.getClusterByName(clusterName).orElseThrow(Throwable::new);
|
||||||
|
|
||||||
|
return kafkaService.getOrCreateAdminClient(cluster).map(ac ->
|
||||||
|
ac.describeConsumerGroups(Collections.singletonList(consumerGroupId)).all()
|
||||||
|
).flatMap(groups ->
|
||||||
|
groupMetadata(cluster, consumerGroupId)
|
||||||
|
.flatMap(offsets -> {
|
||||||
|
Map<TopicPartition, Long> endOffsets = topicPartitionsEndOffsets(cluster, offsets.keySet());
|
||||||
|
return ClusterUtil.toMono(groups).map(s -> s.get(consumerGroupId).members().stream()
|
||||||
|
.flatMap(c -> Stream.of(ClusterUtil.convertToConsumerTopicPartitionDetails(c, offsets, endOffsets)))
|
||||||
|
.collect(Collectors.toList()).stream().flatMap(t -> t.stream().flatMap(Stream::of)).collect(Collectors.toList()));
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.map(c -> new ConsumerGroupDetails().consumers(c).consumerGroupId(consumerGroupId));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public Mono<Map<TopicPartition, OffsetAndMetadata>> groupMetadata(KafkaCluster cluster, String consumerGroupId) {
|
||||||
|
return
|
||||||
|
kafkaService.getOrCreateAdminClient(cluster)
|
||||||
|
.map(ac -> ac.listConsumerGroupOffsets(consumerGroupId).partitionsToOffsetAndMetadata())
|
||||||
|
.flatMap(ClusterUtil::toMono);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<TopicPartition, Long> topicPartitionsEndOffsets(KafkaCluster cluster, Collection<TopicPartition> topicPartitions) {
|
||||||
|
Properties properties = new Properties();
|
||||||
|
properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, cluster.getBootstrapServers());
|
||||||
|
properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
|
||||||
|
properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
|
||||||
|
properties.put(ConsumerConfig.GROUP_ID_CONFIG, UUID.randomUUID().toString());
|
||||||
|
|
||||||
|
try (KafkaConsumer<String, String> consumer = new KafkaConsumer<>(properties)) {
|
||||||
|
return consumer.endOffsets(topicPartitions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
public Mono<List<ConsumerGroup>> getConsumerGroups (String clusterName) {
|
||||||
return clustersStorage.getClusterByName(clusterName)
|
return clustersStorage.getClusterByName(clusterName)
|
||||||
.map(kafkaService::getConsumerGroups)
|
.map(kafkaService::getConsumerGroups)
|
||||||
.orElse(Mono.empty());
|
.orElse(Mono.empty());
|
||||||
|
|
||||||
|
// var cluster = clustersStorage.getClusterByName(clusterName).orElseThrow(Throwable::new);
|
||||||
|
// return kafkaService.getOrCreateAdminClient(cluster).map(ac -> ac.listConsumerGroups().all())
|
||||||
|
// .flatMap(s ->
|
||||||
|
// kafkaService.getOrCreateAdminClient(cluster).flatMap(ac ->
|
||||||
|
// ClusterUtil.toMono(s).map(s1 -> s1.stream().map(ConsumerGroupListing::groupId).collect(Collectors.toList())).map(ac::describeConsumerGroups)
|
||||||
|
// ))
|
||||||
|
// .flatMap(s -> ClusterUtil.toMono(s.all()).map(details -> details.values().stream()
|
||||||
|
// .map(c -> ClusterUtil.convertToConsumerGroup(c, cluster)).collect(Collectors.toList())));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Flux<Broker> getBrokers (String clusterName) {
|
public Flux<Broker> getBrokers (String clusterName) {
|
||||||
|
|
|
@ -2,14 +2,21 @@ package com.provectus.kafka.ui.cluster.util;
|
||||||
|
|
||||||
import com.provectus.kafka.ui.cluster.model.*;
|
import com.provectus.kafka.ui.cluster.model.*;
|
||||||
import com.provectus.kafka.ui.model.ConsumerGroup;
|
import com.provectus.kafka.ui.model.ConsumerGroup;
|
||||||
|
import com.provectus.kafka.ui.model.ConsumerTopicPartitionDetail;
|
||||||
|
import com.provectus.kafka.ui.model.ServerStatus;
|
||||||
import org.apache.kafka.clients.admin.ConfigEntry;
|
import org.apache.kafka.clients.admin.ConfigEntry;
|
||||||
import org.apache.kafka.clients.admin.ConsumerGroupDescription;
|
import org.apache.kafka.clients.admin.ConsumerGroupDescription;
|
||||||
|
import org.apache.kafka.clients.admin.MemberDescription;
|
||||||
import org.apache.kafka.clients.admin.TopicDescription;
|
import org.apache.kafka.clients.admin.TopicDescription;
|
||||||
|
import org.apache.kafka.clients.consumer.OffsetAndMetadata;
|
||||||
import org.apache.kafka.common.KafkaFuture;
|
import org.apache.kafka.common.KafkaFuture;
|
||||||
|
import org.apache.kafka.common.TopicPartition;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import static com.provectus.kafka.ui.kafka.KafkaConstants.TOPIC_DEFAULT_CONFIGS;
|
import static com.provectus.kafka.ui.kafka.KafkaConstants.TOPIC_DEFAULT_CONFIGS;
|
||||||
import static org.apache.kafka.common.config.TopicConfig.MESSAGE_FORMAT_VERSION_CONFIG;
|
import static org.apache.kafka.common.config.TopicConfig.MESSAGE_FORMAT_VERSION_CONFIG;
|
||||||
|
@ -30,11 +37,32 @@ public class ClusterUtil {
|
||||||
ConsumerGroup consumerGroup = new ConsumerGroup();
|
ConsumerGroup consumerGroup = new ConsumerGroup();
|
||||||
consumerGroup.setConsumerGroupId(c.groupId());
|
consumerGroup.setConsumerGroupId(c.groupId());
|
||||||
consumerGroup.setNumConsumers(c.members().size());
|
consumerGroup.setNumConsumers(c.members().size());
|
||||||
int numTopics = c.members().stream().mapToInt( m -> m.assignment().topicPartitions().size()).sum();
|
int numTopics = c.members().stream().flatMap(m -> m.assignment().topicPartitions().stream().flatMap(t -> Stream.of(t.topic()))).collect(Collectors.toSet()).size();
|
||||||
consumerGroup.setNumTopics(numTopics);
|
consumerGroup.setNumTopics(numTopics);
|
||||||
return consumerGroup;
|
return consumerGroup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static List<ConsumerTopicPartitionDetail> convertToConsumerTopicPartitionDetails(
|
||||||
|
MemberDescription consumer,
|
||||||
|
Map<TopicPartition, OffsetAndMetadata> groupOffsets,
|
||||||
|
Map<TopicPartition, Long> endOffsets
|
||||||
|
) {
|
||||||
|
return consumer.assignment().topicPartitions().stream()
|
||||||
|
.map(tp -> {
|
||||||
|
Long currentOffset = groupOffsets.get(tp).offset();
|
||||||
|
Long endOffset = endOffsets.get(tp);
|
||||||
|
ConsumerTopicPartitionDetail cd = new ConsumerTopicPartitionDetail();
|
||||||
|
cd.setConsumerId(consumer.consumerId());
|
||||||
|
cd.setTopic(tp.topic());
|
||||||
|
cd.setPartition(tp.partition());
|
||||||
|
cd.setCurrentOffset(currentOffset);
|
||||||
|
cd.setEndOffset(endOffset);
|
||||||
|
cd.setMessagesBehind(endOffset - currentOffset);
|
||||||
|
return cd;
|
||||||
|
}).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static InternalTopicConfig mapToInternalTopicConfig(ConfigEntry configEntry) {
|
public static InternalTopicConfig mapToInternalTopicConfig(ConfigEntry configEntry) {
|
||||||
InternalTopicConfig.InternalTopicConfigBuilder builder = InternalTopicConfig.builder()
|
InternalTopicConfig.InternalTopicConfigBuilder builder = InternalTopicConfig.builder()
|
||||||
.name(configEntry.name())
|
.name(configEntry.name())
|
||||||
|
@ -95,4 +123,8 @@ public class ClusterUtil {
|
||||||
return topic.build();
|
return topic.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static int convertToIntServerStatus(ServerStatus serverStatus) {
|
||||||
|
return serverStatus.equals(ServerStatus.ONLINE) ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,12 +71,15 @@ public class KafkaService {
|
||||||
|
|
||||||
InternalClusterMetrics clusterMetrics = metricsBuilder
|
InternalClusterMetrics clusterMetrics = metricsBuilder
|
||||||
.activeControllers(brokersMetrics.getActiveControllers())
|
.activeControllers(brokersMetrics.getActiveControllers())
|
||||||
|
.topicCount(topicsMetrics.getTopicCount())
|
||||||
.brokerCount(brokersMetrics.getBrokerCount())
|
.brokerCount(brokersMetrics.getBrokerCount())
|
||||||
.underReplicatedPartitionCount(topicsMetrics.getUnderReplicatedPartitionCount())
|
.underReplicatedPartitionCount(topicsMetrics.getUnderReplicatedPartitionCount())
|
||||||
.inSyncReplicasCount(topicsMetrics.getInSyncReplicasCount())
|
.inSyncReplicasCount(topicsMetrics.getInSyncReplicasCount())
|
||||||
.outOfSyncReplicasCount(topicsMetrics.getOutOfSyncReplicasCount())
|
.outOfSyncReplicasCount(topicsMetrics.getOutOfSyncReplicasCount())
|
||||||
.onlinePartitionCount(topicsMetrics.getOnlinePartitionCount())
|
.onlinePartitionCount(topicsMetrics.getOnlinePartitionCount())
|
||||||
.offlinePartitionCount(topicsMetrics.getOfflinePartitionCount()).build();
|
.offlinePartitionCount(topicsMetrics.getOfflinePartitionCount())
|
||||||
|
.zooKeeperStatus(ClusterUtil.convertToIntServerStatus(zookeeperStatus))
|
||||||
|
.build();
|
||||||
|
|
||||||
return currentCluster.toBuilder()
|
return currentCluster.toBuilder()
|
||||||
.status(ServerStatus.ONLINE)
|
.status(ServerStatus.ONLINE)
|
||||||
|
@ -110,6 +113,7 @@ public class KafkaService {
|
||||||
.outOfSyncReplicasCount(outOfSyncReplicasCount)
|
.outOfSyncReplicasCount(outOfSyncReplicasCount)
|
||||||
.onlinePartitionCount(onlinePartitionCount)
|
.onlinePartitionCount(onlinePartitionCount)
|
||||||
.offlinePartitionCount(offlinePartitionCount)
|
.offlinePartitionCount(offlinePartitionCount)
|
||||||
|
.topicCount(topics.size())
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,7 +142,6 @@ public class KafkaService {
|
||||||
builder.brokerCount(brokers.size()).activeControllers(c != null ? 1 : 0);
|
builder.brokerCount(brokers.size()).activeControllers(c != null ? 1 : 0);
|
||||||
// TODO: fill bytes in/out metrics
|
// TODO: fill bytes in/out metrics
|
||||||
List<Integer> brokerIds = brokers.stream().map(Node::id).collect(Collectors.toList());
|
List<Integer> brokerIds = brokers.stream().map(Node::id).collect(Collectors.toList());
|
||||||
|
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
@ -77,10 +77,15 @@ public class MetricsRestController implements ApiClustersApi {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<ResponseEntity<Flux<ConsumerGroup>>> getConsumerGroup(String clusterName, ServerWebExchange exchange) {
|
public Mono<ResponseEntity<Flux<ConsumerGroup>>> getConsumerGroups(String clusterName, ServerWebExchange exchange) {
|
||||||
return clusterService.getConsumerGroups(clusterName)
|
return clusterService.getConsumerGroups(clusterName)
|
||||||
.map(Flux::fromIterable)
|
.map(Flux::fromIterable)
|
||||||
.map(ResponseEntity::ok)
|
.map(ResponseEntity::ok)
|
||||||
.switchIfEmpty(Mono.just(ResponseEntity.notFound().build())); // TODO: check behaviour on cluster not found and empty groups list
|
.switchIfEmpty(Mono.just(ResponseEntity.notFound().build())); // TODO: check behaviour on cluster not found and empty groups list
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<ResponseEntity<ConsumerGroupDetails>> getConsumerGroup(String clusterName, String consumerGroupId, ServerWebExchange exchange) {
|
||||||
|
return clusterService.getConsumerGroupDetail(clusterName, consumerGroupId).map(ResponseEntity::ok);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,9 @@
|
||||||
"react/jsx-filename-extension": [
|
"react/jsx-filename-extension": [
|
||||||
1,
|
1,
|
||||||
{ "extensions": [".js", ".jsx", ".ts", ".tsx"] }
|
{ "extensions": [".js", ".jsx", ".ts", ".tsx"] }
|
||||||
]
|
],
|
||||||
|
"jsx-a11y/label-has-associated-control": "off",
|
||||||
|
"no-param-reassign": [2, { "props": false }]
|
||||||
},
|
},
|
||||||
"overrides": [
|
"overrides": [
|
||||||
{
|
{
|
||||||
|
@ -52,7 +54,8 @@
|
||||||
"settings": {
|
"settings": {
|
||||||
"import/resolver": {
|
"import/resolver": {
|
||||||
"node": {
|
"node": {
|
||||||
"extensions": [".js", ".jsx", ".ts", ".tsx"]
|
"extensions": [".js", ".jsx", ".ts", ".tsx"],
|
||||||
|
"paths": ["src"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ const topics = require('./payload/topics.json');
|
||||||
const topicDetails = require('./payload/topicDetails.json');
|
const topicDetails = require('./payload/topicDetails.json');
|
||||||
const topicConfigs = require('./payload/topicConfigs.json');
|
const topicConfigs = require('./payload/topicConfigs.json');
|
||||||
const consumerGroups = require('./payload/consumerGroups.json');
|
const consumerGroups = require('./payload/consumerGroups.json');
|
||||||
|
const consumerGroupDetails = require('./payload/consumerGroupDetails.json');
|
||||||
|
|
||||||
const db = {
|
const db = {
|
||||||
clusters,
|
clusters,
|
||||||
|
@ -14,7 +15,8 @@ const db = {
|
||||||
topics: topics.map((topic) => ({...topic, id: topic.name})),
|
topics: topics.map((topic) => ({...topic, id: topic.name})),
|
||||||
topicDetails,
|
topicDetails,
|
||||||
topicConfigs,
|
topicConfigs,
|
||||||
consumerGroups: consumerGroups.map((group) => ({...group, id: group.consumerGroupId}))
|
consumerGroups: consumerGroups.map((group) => ({...group, id: group.consumerGroupId})),
|
||||||
|
consumerGroupDetails
|
||||||
};
|
};
|
||||||
const server = jsonServer.create();
|
const server = jsonServer.create();
|
||||||
const router = jsonServer.router(db);
|
const router = jsonServer.router(db);
|
||||||
|
@ -34,6 +36,7 @@ server.use(
|
||||||
'/clusters/:clusterName/metrics/broker': '/brokerMetrics/:clusterName',
|
'/clusters/:clusterName/metrics/broker': '/brokerMetrics/:clusterName',
|
||||||
'/clusters/:clusterName/topics/:id': '/topicDetails',
|
'/clusters/:clusterName/topics/:id': '/topicDetails',
|
||||||
'/clusters/:clusterName/topics/:id/config': '/topicConfigs',
|
'/clusters/:clusterName/topics/:id/config': '/topicConfigs',
|
||||||
|
'/clusters/:clusterName/consumer-groups/:id': '/consumerGroupDetails',
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
29
kafka-ui-react-app/mock/payload/consumerGroupDetails.json
Normal file
29
kafka-ui-react-app/mock/payload/consumerGroupDetails.json
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
{
|
||||||
|
"consumerGroupId": "_fake.cluster.consumer_1",
|
||||||
|
"consumers": [
|
||||||
|
{
|
||||||
|
"consumerId": "_fake.cluster.consumer_1-1-1",
|
||||||
|
"topic": "my-topic",
|
||||||
|
"partition": 0,
|
||||||
|
"messagesBehind": 1246,
|
||||||
|
"currentOffset": 2834,
|
||||||
|
"endOffset": 2835
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"consumerId": "_fake.cluster.consumer_2-2-2",
|
||||||
|
"topic": "docker-connect-status",
|
||||||
|
"partition": 1,
|
||||||
|
"messagesBehind": 678,
|
||||||
|
"currentOffset": 234,
|
||||||
|
"endOffset": 246
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"consumerId": "_fake.cluster.consumer_2-2-2",
|
||||||
|
"topic": "docker-connect-status",
|
||||||
|
"partition": 2,
|
||||||
|
"messagesBehind": 143,
|
||||||
|
"currentOffset": 123,
|
||||||
|
"endOffset": 134
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
584
kafka-ui-react-app/package-lock.json
generated
584
kafka-ui-react-app/package-lock.json
generated
|
@ -1241,6 +1241,15 @@
|
||||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz",
|
||||||
"integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw=="
|
"integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw=="
|
||||||
},
|
},
|
||||||
|
"@samverschueren/stream-to-observable": {
|
||||||
|
"version": "0.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz",
|
||||||
|
"integrity": "sha512-MI4Xx6LHs4Webyvi6EbspgyAb4D2Q2VtnCQ1blOJcoLS6mVa8lNN2rkIy1CVxfTUpoyIbCTkXES1rLXztFD1lg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"any-observable": "^0.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@sheerun/mutationobserver-shim": {
|
"@sheerun/mutationobserver-shim": {
|
||||||
"version": "0.3.2",
|
"version": "0.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/@sheerun/mutationobserver-shim/-/mutationobserver-shim-0.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/@sheerun/mutationobserver-shim/-/mutationobserver-shim-0.3.2.tgz",
|
||||||
|
@ -2046,6 +2055,12 @@
|
||||||
"color-convert": "^1.9.0"
|
"color-convert": "^1.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"any-observable": {
|
||||||
|
"version": "0.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/any-observable/-/any-observable-0.3.0.tgz",
|
||||||
|
"integrity": "sha512-/FQM1EDkTsf63Ub2C6O7GuYFDsSXUwsaZDurV0np41ocwq0jthUAYCmhBX9f+KwlaCgIuWyr/4WlUQUBfKfZog==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"anymatch": {
|
"anymatch": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
|
||||||
|
@ -3870,6 +3885,60 @@
|
||||||
"restore-cursor": "^3.1.0"
|
"restore-cursor": "^3.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"cli-truncate": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"slice-ansi": "^3.0.0",
|
||||||
|
"string-width": "^4.2.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-styles": {
|
||||||
|
"version": "4.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
|
||||||
|
"integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/color-name": "^1.1.1",
|
||||||
|
"color-convert": "^2.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"astral-regex": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"color-convert": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"color-name": "~1.1.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"color-name": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||||
|
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"slice-ansi": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"ansi-styles": "^4.0.0",
|
||||||
|
"astral-regex": "^2.0.0",
|
||||||
|
"is-fullwidth-code-point": "^3.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"cli-width": {
|
"cli-width": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz",
|
||||||
|
@ -3907,6 +3976,12 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"clone": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
|
||||||
|
"integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"clone-deep": {
|
"clone-deep": {
|
||||||
"version": "0.2.4",
|
"version": "0.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-0.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-0.2.4.tgz",
|
||||||
|
@ -4657,6 +4732,12 @@
|
||||||
"mimic-response": "^1.0.0"
|
"mimic-response": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"dedent": {
|
||||||
|
"version": "0.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz",
|
||||||
|
"integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"deep-equal": {
|
"deep-equal": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz",
|
||||||
|
@ -4689,6 +4770,15 @@
|
||||||
"ip-regex": "^2.1.0"
|
"ip-regex": "^2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"defaults": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz",
|
||||||
|
"integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"clone": "^1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"defer-to-connect": {
|
"defer-to-connect": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.1.tgz",
|
||||||
|
@ -5067,6 +5157,12 @@
|
||||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.322.tgz",
|
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.322.tgz",
|
||||||
"integrity": "sha512-Tc8JQEfGQ1MzfSzI/bTlSr7btJv/FFO7Yh6tanqVmIWOuNCu6/D1MilIEgLtmWqIrsv+o4IjpLAhgMBr/ncNAA=="
|
"integrity": "sha512-Tc8JQEfGQ1MzfSzI/bTlSr7btJv/FFO7Yh6tanqVmIWOuNCu6/D1MilIEgLtmWqIrsv+o4IjpLAhgMBr/ncNAA=="
|
||||||
},
|
},
|
||||||
|
"elegant-spinner": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/elegant-spinner/-/elegant-spinner-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-5YRYHhvhYzV/FC4AiMdeSIg3jAYGq9xFvbhZMpPlJoBsfYgrw2DSCYeXfat6tYBu45PWiyRr3+flaCPPmviPaA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"elliptic": {
|
"elliptic": {
|
||||||
"version": "6.5.2",
|
"version": "6.5.2",
|
||||||
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.2.tgz",
|
||||||
|
@ -5147,6 +5243,15 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"enquirer": {
|
||||||
|
"version": "2.3.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.5.tgz",
|
||||||
|
"integrity": "sha512-BNT1C08P9XD0vNg3J475yIUG+mVdp9T6towYFHUv897X0KoHBjB1shyrNmhmtHWKP17iSWgo7Gqh7BBuzLZMSA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"ansi-colors": "^3.2.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"entities": {
|
"entities": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz",
|
||||||
|
@ -7306,6 +7411,12 @@
|
||||||
"resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz",
|
||||||
"integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM="
|
"integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM="
|
||||||
},
|
},
|
||||||
|
"human-signals": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"husky": {
|
"husky": {
|
||||||
"version": "4.2.5",
|
"version": "4.2.5",
|
||||||
"resolved": "https://registry.npmjs.org/husky/-/husky-4.2.5.tgz",
|
"resolved": "https://registry.npmjs.org/husky/-/husky-4.2.5.tgz",
|
||||||
|
@ -9424,6 +9535,352 @@
|
||||||
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz",
|
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz",
|
||||||
"integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA="
|
"integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA="
|
||||||
},
|
},
|
||||||
|
"lint-staged": {
|
||||||
|
"version": "10.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-10.2.2.tgz",
|
||||||
|
"integrity": "sha512-78kNqNdDeKrnqWsexAmkOU3Z5wi+1CsQmUmfCuYgMTE8E4rAIX8RHW7xgxwAZ+LAayb7Cca4uYX4P3LlevzjVg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"chalk": "^4.0.0",
|
||||||
|
"commander": "^5.0.0",
|
||||||
|
"cosmiconfig": "^6.0.0",
|
||||||
|
"debug": "^4.1.1",
|
||||||
|
"dedent": "^0.7.0",
|
||||||
|
"execa": "^4.0.0",
|
||||||
|
"listr2": "1.3.8",
|
||||||
|
"log-symbols": "^3.0.0",
|
||||||
|
"micromatch": "^4.0.2",
|
||||||
|
"normalize-path": "^3.0.0",
|
||||||
|
"please-upgrade-node": "^3.2.0",
|
||||||
|
"string-argv": "0.3.1",
|
||||||
|
"stringify-object": "^3.3.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-styles": {
|
||||||
|
"version": "4.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
|
||||||
|
"integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/color-name": "^1.1.1",
|
||||||
|
"color-convert": "^2.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"braces": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"fill-range": "^7.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"chalk": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"ansi-styles": "^4.1.0",
|
||||||
|
"supports-color": "^7.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"color-convert": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"color-name": "~1.1.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"color-name": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||||
|
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"commander": {
|
||||||
|
"version": "5.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz",
|
||||||
|
"integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"cosmiconfig": {
|
||||||
|
"version": "6.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz",
|
||||||
|
"integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/parse-json": "^4.0.0",
|
||||||
|
"import-fresh": "^3.1.0",
|
||||||
|
"parse-json": "^5.0.0",
|
||||||
|
"path-type": "^4.0.0",
|
||||||
|
"yaml": "^1.7.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cross-spawn": {
|
||||||
|
"version": "7.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.2.tgz",
|
||||||
|
"integrity": "sha512-PD6G8QG3S4FK/XCGFbEQrDqO2AnMMsy0meR7lerlIOHAAbkuavGU/pOqprrlvfTNjvowivTeBsjebAL0NSoMxw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"path-key": "^3.1.0",
|
||||||
|
"shebang-command": "^2.0.0",
|
||||||
|
"which": "^2.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"execa": {
|
||||||
|
"version": "4.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/execa/-/execa-4.0.1.tgz",
|
||||||
|
"integrity": "sha512-SCjM/zlBdOK8Q5TIjOn6iEHZaPHFsMoTxXQ2nvUvtPnuohz3H2dIozSg+etNR98dGoYUp2ENSKLL/XaMmbxVgw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"cross-spawn": "^7.0.0",
|
||||||
|
"get-stream": "^5.0.0",
|
||||||
|
"human-signals": "^1.1.1",
|
||||||
|
"is-stream": "^2.0.0",
|
||||||
|
"merge-stream": "^2.0.0",
|
||||||
|
"npm-run-path": "^4.0.0",
|
||||||
|
"onetime": "^5.1.0",
|
||||||
|
"signal-exit": "^3.0.2",
|
||||||
|
"strip-final-newline": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fill-range": {
|
||||||
|
"version": "7.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
||||||
|
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"to-regex-range": "^5.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"get-stream": {
|
||||||
|
"version": "5.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz",
|
||||||
|
"integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"pump": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"has-flag": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"import-fresh": {
|
||||||
|
"version": "3.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz",
|
||||||
|
"integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"parent-module": "^1.0.0",
|
||||||
|
"resolve-from": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"is-number": {
|
||||||
|
"version": "7.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||||
|
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"is-stream": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"micromatch": {
|
||||||
|
"version": "4.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz",
|
||||||
|
"integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"braces": "^3.0.1",
|
||||||
|
"picomatch": "^2.0.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"normalize-path": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"npm-run-path": {
|
||||||
|
"version": "4.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
|
||||||
|
"integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"path-key": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"parse-json": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@babel/code-frame": "^7.0.0",
|
||||||
|
"error-ex": "^1.3.1",
|
||||||
|
"json-parse-better-errors": "^1.0.1",
|
||||||
|
"lines-and-columns": "^1.1.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"path-key": {
|
||||||
|
"version": "3.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
|
||||||
|
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"path-type": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"resolve-from": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"shebang-command": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"shebang-regex": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"shebang-regex": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"supports-color": {
|
||||||
|
"version": "7.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
|
||||||
|
"integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"has-flag": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"to-regex-range": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"is-number": "^7.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"which": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"isexe": "^2.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"listr2": {
|
||||||
|
"version": "1.3.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/listr2/-/listr2-1.3.8.tgz",
|
||||||
|
"integrity": "sha512-iRDRVTgSDz44tBeBBg/35TQz4W+EZBWsDUq7hPpqeUHm7yLPNll0rkwW3lIX9cPAK7l+x95mGWLpxjqxftNfZA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@samverschueren/stream-to-observable": "^0.3.0",
|
||||||
|
"chalk": "^3.0.0",
|
||||||
|
"cli-cursor": "^3.1.0",
|
||||||
|
"cli-truncate": "^2.1.0",
|
||||||
|
"elegant-spinner": "^2.0.0",
|
||||||
|
"enquirer": "^2.3.4",
|
||||||
|
"figures": "^3.2.0",
|
||||||
|
"indent-string": "^4.0.0",
|
||||||
|
"log-update": "^4.0.0",
|
||||||
|
"p-map": "^4.0.0",
|
||||||
|
"pad": "^3.2.0",
|
||||||
|
"rxjs": "^6.3.3",
|
||||||
|
"through": "^2.3.8",
|
||||||
|
"uuid": "^7.0.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-styles": {
|
||||||
|
"version": "4.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
|
||||||
|
"integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/color-name": "^1.1.1",
|
||||||
|
"color-convert": "^2.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"chalk": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"ansi-styles": "^4.1.0",
|
||||||
|
"supports-color": "^7.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"color-convert": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"color-name": "~1.1.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"color-name": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||||
|
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"has-flag": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"p-map": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"aggregate-error": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"supports-color": {
|
||||||
|
"version": "7.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
|
||||||
|
"integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"has-flag": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"uuid": {
|
||||||
|
"version": "7.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz",
|
||||||
|
"integrity": "sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"load-json-file": {
|
"load-json-file": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz",
|
||||||
|
@ -9562,6 +10019,97 @@
|
||||||
"resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
|
||||||
"integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M="
|
"integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M="
|
||||||
},
|
},
|
||||||
|
"log-symbols": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"chalk": "^2.4.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"log-update": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"ansi-escapes": "^4.3.0",
|
||||||
|
"cli-cursor": "^3.1.0",
|
||||||
|
"slice-ansi": "^4.0.0",
|
||||||
|
"wrap-ansi": "^6.2.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-regex": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"ansi-styles": {
|
||||||
|
"version": "4.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
|
||||||
|
"integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/color-name": "^1.1.1",
|
||||||
|
"color-convert": "^2.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"astral-regex": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"color-convert": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"color-name": "~1.1.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"color-name": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||||
|
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"slice-ansi": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"ansi-styles": "^4.0.0",
|
||||||
|
"astral-regex": "^2.0.0",
|
||||||
|
"is-fullwidth-code-point": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"strip-ansi": {
|
||||||
|
"version": "6.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
|
||||||
|
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"ansi-regex": "^5.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"wrap-ansi": {
|
||||||
|
"version": "6.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
|
||||||
|
"integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"ansi-styles": "^4.0.0",
|
||||||
|
"string-width": "^4.1.0",
|
||||||
|
"strip-ansi": "^6.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"loglevel": {
|
"loglevel": {
|
||||||
"version": "1.6.6",
|
"version": "1.6.6",
|
||||||
"resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.6.tgz",
|
"resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.6.tgz",
|
||||||
|
@ -10959,6 +11507,15 @@
|
||||||
"semver": "^6.2.0"
|
"semver": "^6.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"pad": {
|
||||||
|
"version": "3.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pad/-/pad-3.2.0.tgz",
|
||||||
|
"integrity": "sha512-2u0TrjcGbOjBTJpyewEl4hBO3OeX5wWue7eIFPzQTg6wFSvoaHcBTTUY5m+n0hd04gmTCPuY0kCpVIVuw5etwg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"wcwidth": "^1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"pako": {
|
"pako": {
|
||||||
"version": "1.0.10",
|
"version": "1.0.10",
|
||||||
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.10.tgz",
|
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.10.tgz",
|
||||||
|
@ -11128,6 +11685,12 @@
|
||||||
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
|
||||||
"integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
|
"integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
|
||||||
},
|
},
|
||||||
|
"picomatch": {
|
||||||
|
"version": "2.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz",
|
||||||
|
"integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"pify": {
|
"pify": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
|
||||||
|
@ -14630,6 +15193,12 @@
|
||||||
"resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
|
||||||
"integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM="
|
"integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM="
|
||||||
},
|
},
|
||||||
|
"string-argv": {
|
||||||
|
"version": "0.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz",
|
||||||
|
"integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"string-length": {
|
"string-length": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/string-length/-/string-length-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/string-length/-/string-length-2.0.0.tgz",
|
||||||
|
@ -14930,6 +15499,12 @@
|
||||||
"resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
|
||||||
"integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8="
|
"integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8="
|
||||||
},
|
},
|
||||||
|
"strip-final-newline": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"strip-indent": {
|
"strip-indent": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz",
|
||||||
|
@ -15833,6 +16408,15 @@
|
||||||
"minimalistic-assert": "^1.0.0"
|
"minimalistic-assert": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"wcwidth": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
|
||||||
|
"integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"defaults": "^1.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"weak-napi": {
|
"weak-napi": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/weak-napi/-/weak-napi-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/weak-napi/-/weak-napi-1.0.3.tgz",
|
||||||
|
|
|
@ -2,7 +2,8 @@ import React from 'react';
|
||||||
import { ClusterName } from 'redux/interfaces';
|
import { ClusterName } from 'redux/interfaces';
|
||||||
import { Switch, Route } from 'react-router-dom';
|
import { Switch, Route } from 'react-router-dom';
|
||||||
import PageLoader from 'components/common/PageLoader/PageLoader';
|
import PageLoader from 'components/common/PageLoader/PageLoader';
|
||||||
import ListContainer from './List/ListContainer';
|
import DetailsContainer from 'components/ConsumerGroups/Details/DetailsContainer';
|
||||||
|
import ListContainer from 'components/ConsumerGroups/List/ListContainer';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
clusterName: ClusterName;
|
clusterName: ClusterName;
|
||||||
|
@ -27,6 +28,10 @@ const ConsumerGroups: React.FC<Props> = ({
|
||||||
path="/ui/clusters/:clusterName/consumer-groups"
|
path="/ui/clusters/:clusterName/consumer-groups"
|
||||||
component={ListContainer}
|
component={ListContainer}
|
||||||
/>
|
/>
|
||||||
|
<Route
|
||||||
|
path="/ui/clusters/:clusterName/consumer-groups/:consumerGroupID"
|
||||||
|
component={DetailsContainer}
|
||||||
|
/>
|
||||||
</Switch>
|
</Switch>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { ClusterName } from 'redux/interfaces';
|
||||||
|
import Breadcrumb from 'components/common/Breadcrumb/Breadcrumb';
|
||||||
|
import { clusterConsumerGroupsPath } from 'lib/paths';
|
||||||
|
import {
|
||||||
|
ConsumerGroupID,
|
||||||
|
ConsumerGroup,
|
||||||
|
ConsumerGroupDetails,
|
||||||
|
Consumer,
|
||||||
|
} from 'redux/interfaces/consumerGroup';
|
||||||
|
|
||||||
|
import PageLoader from 'components/common/PageLoader/PageLoader';
|
||||||
|
import ListItem from './ListItem';
|
||||||
|
|
||||||
|
interface Props extends ConsumerGroup, ConsumerGroupDetails {
|
||||||
|
clusterName: ClusterName;
|
||||||
|
consumerGroupID: ConsumerGroupID;
|
||||||
|
consumers: Consumer[];
|
||||||
|
isFetched: boolean;
|
||||||
|
fetchConsumerGroupDetails: (
|
||||||
|
clusterName: ClusterName,
|
||||||
|
consumerGroupID: ConsumerGroupID
|
||||||
|
) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Details: React.FC<Props> = ({
|
||||||
|
clusterName,
|
||||||
|
consumerGroupID,
|
||||||
|
consumers,
|
||||||
|
isFetched,
|
||||||
|
fetchConsumerGroupDetails,
|
||||||
|
}) => {
|
||||||
|
React.useEffect(() => {
|
||||||
|
fetchConsumerGroupDetails(clusterName, consumerGroupID);
|
||||||
|
}, [fetchConsumerGroupDetails, clusterName, consumerGroupID]);
|
||||||
|
const items = consumers || [];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="section">
|
||||||
|
<div className="level">
|
||||||
|
<div className="level-item level-left">
|
||||||
|
<Breadcrumb
|
||||||
|
links={[
|
||||||
|
{
|
||||||
|
href: clusterConsumerGroupsPath(clusterName),
|
||||||
|
label: 'All Consumer Groups',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
{consumerGroupID}
|
||||||
|
</Breadcrumb>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{isFetched ? (
|
||||||
|
<div className="box">
|
||||||
|
<table className="table is-striped is-fullwidth">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Consumer ID</th>
|
||||||
|
<th>Topic</th>
|
||||||
|
<th>Partition</th>
|
||||||
|
<th>Messages behind</th>
|
||||||
|
<th>Current offset</th>
|
||||||
|
<th>End offset</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{items.map((consumer) => (
|
||||||
|
<ListItem
|
||||||
|
key={consumer.consumerId}
|
||||||
|
clusterName={clusterName}
|
||||||
|
consumer={consumer}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<PageLoader />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Details;
|
|
@ -0,0 +1,29 @@
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import Details from './Details';
|
||||||
|
import {ClusterName, RootState} from 'redux/interfaces';
|
||||||
|
import { withRouter, RouteComponentProps } from 'react-router-dom';
|
||||||
|
import { getIsConsumerGroupDetailsFetched, getConsumerGroupByID } from 'redux/reducers/consumerGroups/selectors';
|
||||||
|
import { ConsumerGroupID } from 'redux/interfaces/consumerGroup';
|
||||||
|
import { fetchConsumerGroupDetails } from 'redux/actions/thunks';
|
||||||
|
|
||||||
|
interface RouteProps {
|
||||||
|
clusterName: ClusterName;
|
||||||
|
consumerGroupID: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface OwnProps extends RouteComponentProps<RouteProps> { }
|
||||||
|
|
||||||
|
const mapStateToProps = (state: RootState, { match: { params: { consumerGroupID, clusterName } } }: OwnProps) => ({
|
||||||
|
clusterName,
|
||||||
|
consumerGroupID,
|
||||||
|
isFetched: getIsConsumerGroupDetailsFetched(state),
|
||||||
|
...getConsumerGroupByID(state, consumerGroupID)
|
||||||
|
});
|
||||||
|
|
||||||
|
const mapDispatchToProps = {
|
||||||
|
fetchConsumerGroupDetails: (clusterName: ClusterName, consumerGroupID: ConsumerGroupID) => fetchConsumerGroupDetails(clusterName, consumerGroupID),
|
||||||
|
};
|
||||||
|
|
||||||
|
export default withRouter(
|
||||||
|
connect(mapStateToProps, mapDispatchToProps)(Details)
|
||||||
|
);
|
|
@ -0,0 +1,33 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { Consumer } from 'redux/interfaces/consumerGroup';
|
||||||
|
import { NavLink } from 'react-router-dom';
|
||||||
|
import { ClusterName } from 'redux/interfaces/cluster';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
clusterName: ClusterName;
|
||||||
|
consumer: Consumer;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ListItem: React.FC<Props> = ({ clusterName, consumer }) => {
|
||||||
|
return (
|
||||||
|
<tr>
|
||||||
|
<td>{consumer.consumerId}</td>
|
||||||
|
<td>
|
||||||
|
<NavLink
|
||||||
|
exact
|
||||||
|
to={`/clusters/${clusterName}/topics/${consumer.topic}`}
|
||||||
|
activeClassName="is-active"
|
||||||
|
className="title is-6"
|
||||||
|
>
|
||||||
|
{consumer.topic}
|
||||||
|
</NavLink>
|
||||||
|
</td>
|
||||||
|
<td>{consumer.partition}</td>
|
||||||
|
<td>{consumer.messagesBehind}</td>
|
||||||
|
<td>{consumer.currentOffset}</td>
|
||||||
|
<td>{consumer.endOffset}</td>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ListItem;
|
|
@ -1,33 +1,31 @@
|
||||||
import React, { ChangeEvent } from 'react';
|
import React from 'react';
|
||||||
import { ConsumerGroup, ClusterName } from 'redux/interfaces';
|
import { ClusterName, ConsumerGroup } from 'redux/interfaces';
|
||||||
import ListItem from './ListItem';
|
|
||||||
import Breadcrumb from 'components/common/Breadcrumb/Breadcrumb';
|
import Breadcrumb from 'components/common/Breadcrumb/Breadcrumb';
|
||||||
|
import ListItem from './ListItem';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
clusterName: ClusterName;
|
clusterName: ClusterName;
|
||||||
consumerGroups: (ConsumerGroup)[];
|
consumerGroups: ConsumerGroup[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const List: React.FC<Props> = ({
|
const List: React.FC<Props> = ({ consumerGroups }) => {
|
||||||
consumerGroups,
|
|
||||||
}) => {
|
|
||||||
|
|
||||||
const [searchText, setSearchText] = React.useState<string>('');
|
const [searchText, setSearchText] = React.useState<string>('');
|
||||||
|
|
||||||
const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
setSearchText(event.target.value);
|
setSearchText(event.target.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
const items = consumerGroups;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="section">
|
<div className="section">
|
||||||
<Breadcrumb>All Consumer Groups</Breadcrumb>
|
<Breadcrumb>All Consumer Groups</Breadcrumb>
|
||||||
|
|
||||||
<div className="box">
|
<div className="box">
|
||||||
<div className="columns">
|
{consumerGroups.length > 0 ? (
|
||||||
<div className="column is-half is-offset-half">
|
<div>
|
||||||
<input id="searchText"
|
<div className="columns">
|
||||||
|
<div className="column is-half is-offset-half">
|
||||||
|
<input
|
||||||
|
id="searchText"
|
||||||
type="text"
|
type="text"
|
||||||
name="searchText"
|
name="searchText"
|
||||||
className="input"
|
className="input"
|
||||||
|
@ -35,27 +33,35 @@ const List: React.FC<Props> = ({
|
||||||
value={searchText}
|
value={searchText}
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<table className="table is-striped is-fullwidth is-hoverable">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Consumer group ID</th>
|
||||||
|
<th>Num of consumers</th>
|
||||||
|
<th>Num of topics</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{consumerGroups
|
||||||
|
.filter(
|
||||||
|
(consumerGroup) =>
|
||||||
|
!searchText ||
|
||||||
|
consumerGroup?.consumerGroupId?.indexOf(searchText) >= 0
|
||||||
|
)
|
||||||
|
.map((consumerGroup) => (
|
||||||
|
<ListItem
|
||||||
|
key={consumerGroup.consumerGroupId}
|
||||||
|
consumerGroup={consumerGroup}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
) : (
|
||||||
<table className="table is-striped is-fullwidth">
|
'No active consumer groups'
|
||||||
<thead>
|
)}
|
||||||
<tr>
|
|
||||||
<th>Consumer group ID</th>
|
|
||||||
<th>Num of consumers</th>
|
|
||||||
<th>Num of topics</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{items
|
|
||||||
.filter( (consumerGroup) => !searchText || consumerGroup?.consumerGroupId?.indexOf(searchText) >= 0)
|
|
||||||
.map((consumerGroup, index) => (
|
|
||||||
<ListItem
|
|
||||||
key={`consumer-group-list-item-key-${index}`}
|
|
||||||
{...consumerGroup}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,24 +1,23 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { NavLink } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
import { ConsumerGroup } from 'redux/interfaces';
|
import { ConsumerGroup } from 'redux/interfaces';
|
||||||
|
|
||||||
const ListItem: React.FC<ConsumerGroup> = ({
|
const ListItem: React.FC<{ consumerGroup: ConsumerGroup }> = ({
|
||||||
consumerGroupId,
|
consumerGroup,
|
||||||
numConsumers,
|
|
||||||
numTopics,
|
|
||||||
}) => {
|
}) => {
|
||||||
|
const history = useHistory();
|
||||||
|
|
||||||
|
function goToConsumerGroupDetails() {
|
||||||
|
history.push(`consumer-groups/${consumerGroup.consumerGroupId}`);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<tr>
|
<tr className="cursor-pointer" onClick={goToConsumerGroupDetails}>
|
||||||
{/* <td>
|
<td>{consumerGroup.consumerGroupId}</td>
|
||||||
<NavLink exact to={`consumer-groups/${consumerGroupId}`} activeClassName="is-active" className="title is-6">
|
<td>{consumerGroup.numConsumers}</td>
|
||||||
{consumerGroupId}
|
<td>{consumerGroup.numTopics}</td>
|
||||||
</NavLink>
|
|
||||||
</td> */}
|
|
||||||
<td>{consumerGroupId}</td>
|
|
||||||
<td>{numConsumers}</td>
|
|
||||||
<td>{numTopics}</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default ListItem;
|
export default ListItem;
|
||||||
|
|
|
@ -1,9 +1,15 @@
|
||||||
import React, { CSSProperties } from 'react';
|
import React, { CSSProperties } from 'react';
|
||||||
import { Cluster } from 'redux/interfaces';
|
import { Cluster } from 'redux/interfaces';
|
||||||
import { NavLink } from 'react-router-dom';
|
import { NavLink } from 'react-router-dom';
|
||||||
import { clusterBrokersPath, clusterTopicsPath, clusterConsumerGroupsPath } from 'lib/paths';
|
import {
|
||||||
|
clusterBrokersPath,
|
||||||
|
clusterTopicsPath,
|
||||||
|
clusterConsumerGroupsPath,
|
||||||
|
} from 'lib/paths';
|
||||||
|
|
||||||
interface Props extends Cluster {}
|
interface Props {
|
||||||
|
cluster: Cluster;
|
||||||
|
}
|
||||||
|
|
||||||
const DefaultIcon: React.FC = () => {
|
const DefaultIcon: React.FC = () => {
|
||||||
const style: CSSProperties = {
|
const style: CSSProperties = {
|
||||||
|
@ -15,29 +21,47 @@ const DefaultIcon: React.FC = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span title="Default Cluster" className="icon has-text-primary is-small">
|
<span title="Default Cluster" className="icon has-text-primary is-small">
|
||||||
<i style={style} data-fa-transform="rotate-340" className="fas fa-thumbtack" />
|
<i
|
||||||
|
style={style}
|
||||||
|
data-fa-transform="rotate-340"
|
||||||
|
className="fas fa-thumbtack"
|
||||||
|
/>
|
||||||
</span>
|
</span>
|
||||||
)
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const ClusterMenu: React.FC<Props> = ({
|
const ClusterMenu: React.FC<Props> = ({ cluster }) => (
|
||||||
name,
|
|
||||||
defaultCluster,
|
|
||||||
}) => (
|
|
||||||
<ul className="menu-list">
|
<ul className="menu-list">
|
||||||
<li>
|
<li>
|
||||||
<NavLink exact to={clusterBrokersPath(name)} title={name} className="has-text-overflow-ellipsis">
|
<NavLink
|
||||||
{defaultCluster && <DefaultIcon />}
|
exact
|
||||||
{name}
|
to={clusterBrokersPath(cluster.name)}
|
||||||
|
title={cluster.name}
|
||||||
|
className="has-text-overflow-ellipsis"
|
||||||
|
>
|
||||||
|
{cluster.defaultCluster && <DefaultIcon />}
|
||||||
|
{cluster.name}
|
||||||
</NavLink>
|
</NavLink>
|
||||||
<ul>
|
<ul>
|
||||||
<NavLink to={clusterBrokersPath(name)} activeClassName="is-active" title="Brokers">
|
<NavLink
|
||||||
|
to={clusterBrokersPath(cluster.name)}
|
||||||
|
activeClassName="is-active"
|
||||||
|
title="Brokers"
|
||||||
|
>
|
||||||
Brokers
|
Brokers
|
||||||
</NavLink>
|
</NavLink>
|
||||||
<NavLink to={clusterTopicsPath(name)} activeClassName="is-active" title="Topics">
|
<NavLink
|
||||||
|
to={clusterTopicsPath(cluster.name)}
|
||||||
|
activeClassName="is-active"
|
||||||
|
title="Topics"
|
||||||
|
>
|
||||||
Topics
|
Topics
|
||||||
</NavLink>
|
</NavLink>
|
||||||
<NavLink to={clusterConsumerGroupsPath(name)} activeClassName="is-active" title="Consumers">
|
<NavLink
|
||||||
|
to={clusterConsumerGroupsPath(cluster.name)}
|
||||||
|
activeClassName="is-active"
|
||||||
|
title="Consumers"
|
||||||
|
>
|
||||||
Consumers
|
Consumers
|
||||||
</NavLink>
|
</NavLink>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -28,8 +28,8 @@ const Nav: React.FC<Props> = ({
|
||||||
{!isClusterListFetched && <div className="loader" />}
|
{!isClusterListFetched && <div className="loader" />}
|
||||||
|
|
||||||
{isClusterListFetched &&
|
{isClusterListFetched &&
|
||||||
clusters.map((cluster, index) => (
|
clusters.map((cluster) => (
|
||||||
<ClusterMenu {...cluster} key={`cluster-list-item-key-${index}`} />
|
<ClusterMenu cluster={cluster} key={cluster.id} />
|
||||||
))}
|
))}
|
||||||
</aside>
|
</aside>
|
||||||
);
|
);
|
||||||
|
|
|
@ -37,12 +37,12 @@ const New: React.FC<Props> = ({
|
||||||
redirectToTopicPath(clusterName, name);
|
redirectToTopicPath(clusterName, name);
|
||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
isSubmitting,
|
isSubmitting,
|
||||||
isTopicCreated,
|
isTopicCreated,
|
||||||
redirectToTopicPath,
|
redirectToTopicPath,
|
||||||
clusterName,
|
clusterName,
|
||||||
methods.getValues,
|
methods.getValues,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const onSubmit = async (data: TopicFormData) => {
|
const onSubmit = async (data: TopicFormData) => {
|
||||||
// TODO: need to fix loader. After success loading the first time, we won't wait for creation any more, because state is
|
// TODO: need to fix loader. After success loading the first time, we won't wait for creation any more, because state is
|
||||||
|
@ -68,6 +68,7 @@ const New: React.FC<Props> = ({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="box">
|
<div className="box">
|
||||||
|
{/* eslint-disable react/jsx-props-no-spreading */}
|
||||||
<FormContext {...methods}>
|
<FormContext {...methods}>
|
||||||
<form onSubmit={methods.handleSubmit(onSubmit)}>
|
<form onSubmit={methods.handleSubmit(onSubmit)}>
|
||||||
<div className="columns">
|
<div className="columns">
|
||||||
|
|
|
@ -30,4 +30,8 @@ export enum ActionType {
|
||||||
GET_CONSUMER_GROUPS__REQUEST = 'GET_CONSUMER_GROUPS__REQUEST',
|
GET_CONSUMER_GROUPS__REQUEST = 'GET_CONSUMER_GROUPS__REQUEST',
|
||||||
GET_CONSUMER_GROUPS__SUCCESS = 'GET_CONSUMER_GROUPS__SUCCESS',
|
GET_CONSUMER_GROUPS__SUCCESS = 'GET_CONSUMER_GROUPS__SUCCESS',
|
||||||
GET_CONSUMER_GROUPS__FAILURE = 'GET_CONSUMER_GROUPS__FAILURE',
|
GET_CONSUMER_GROUPS__FAILURE = 'GET_CONSUMER_GROUPS__FAILURE',
|
||||||
|
|
||||||
|
GET_CONSUMER_GROUP_DETAILS__REQUEST = 'GET_CONSUMER_GROUP_DETAILS__REQUEST',
|
||||||
|
GET_CONSUMER_GROUP_DETAILS__SUCCESS = 'GET_CONSUMER_GROUP_DETAILS__SUCCESS',
|
||||||
|
GET_CONSUMER_GROUP_DETAILS__FAILURE = 'GET_CONSUMER_GROUP_DETAILS__FAILURE',
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { createAsyncAction } from 'typesafe-actions';
|
import { createAsyncAction } from 'typesafe-actions';
|
||||||
import { ActionType } from 'redux/actionType';
|
import { ActionType } from 'redux/actionType';
|
||||||
import { ConsumerGroup } from '../interfaces/consumerGroup';
|
import { ConsumerGroup, ConsumerGroupID, ConsumerGroupDetails } from '../interfaces/consumerGroup';
|
||||||
import {
|
import {
|
||||||
Broker,
|
Broker,
|
||||||
BrokerMetrics,
|
BrokerMetrics,
|
||||||
|
@ -58,3 +58,9 @@ export const fetchConsumerGroupsAction = createAsyncAction(
|
||||||
ActionType.GET_CONSUMER_GROUPS__SUCCESS,
|
ActionType.GET_CONSUMER_GROUPS__SUCCESS,
|
||||||
ActionType.GET_CONSUMER_GROUPS__FAILURE,
|
ActionType.GET_CONSUMER_GROUPS__FAILURE,
|
||||||
)<undefined, ConsumerGroup[], undefined>();
|
)<undefined, ConsumerGroup[], undefined>();
|
||||||
|
|
||||||
|
export const fetchConsumerGroupDetailsAction = createAsyncAction(
|
||||||
|
ActionType.GET_CONSUMER_GROUP_DETAILS__REQUEST,
|
||||||
|
ActionType.GET_CONSUMER_GROUP_DETAILS__SUCCESS,
|
||||||
|
ActionType.GET_CONSUMER_GROUP_DETAILS__FAILURE,
|
||||||
|
)<undefined, { consumerGroupID: ConsumerGroupID, details: ConsumerGroupDetails }, undefined>();
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import * as api from 'redux/api';
|
import * as api from 'redux/api';
|
||||||
import * as actions from './actions';
|
import * as actions from './actions';
|
||||||
|
import { ConsumerGroupID } from '../interfaces/consumerGroup';
|
||||||
import {
|
import {
|
||||||
PromiseThunk,
|
PromiseThunk,
|
||||||
Cluster,
|
Cluster,
|
||||||
|
@ -87,3 +88,13 @@ export const fetchConsumerGroupsList = (clusterName: ClusterName): PromiseThunk<
|
||||||
dispatch(actions.fetchConsumerGroupsAction.failure());
|
dispatch(actions.fetchConsumerGroupsAction.failure());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const fetchConsumerGroupDetails = (clusterName: ClusterName, consumerGroupID: ConsumerGroupID): PromiseThunk<void> => async (dispatch) => {
|
||||||
|
dispatch(actions.fetchConsumerGroupDetailsAction.request());
|
||||||
|
try {
|
||||||
|
const consumerGroupDetails = await api.getConsumerGroupDetails(clusterName, consumerGroupID);
|
||||||
|
dispatch(actions.fetchConsumerGroupDetailsAction.success({ consumerGroupID, details: consumerGroupDetails }));
|
||||||
|
} catch (e) {
|
||||||
|
dispatch(actions.fetchConsumerGroupDetailsAction.failure());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
import { ClusterName } from '../interfaces/cluster';
|
import { ClusterName } from '../interfaces/cluster';
|
||||||
import { ConsumerGroup } from '../interfaces/consumerGroup';
|
import { ConsumerGroup, ConsumerGroupID, ConsumerGroupDetails } from '../interfaces/consumerGroup';
|
||||||
import { BASE_PARAMS, BASE_URL } from '../../lib/constants';
|
import { BASE_PARAMS, BASE_URL } from '../../lib/constants';
|
||||||
|
|
||||||
|
|
||||||
export const getConsumerGroups = (clusterName: ClusterName): Promise<ConsumerGroup[]> =>
|
export const getConsumerGroups = (clusterName: ClusterName): Promise<ConsumerGroup[]> =>
|
||||||
fetch(`${BASE_URL}/clusters/${clusterName}/consumerGroups`, { ...BASE_PARAMS })
|
fetch(`${BASE_URL}/clusters/${clusterName}/consumerGroups`, { ...BASE_PARAMS })
|
||||||
.then(res => res.json());
|
.then(res => res.json());
|
||||||
|
|
||||||
|
export const getConsumerGroupDetails = (clusterName: ClusterName, consumerGroupID: ConsumerGroupID): Promise<ConsumerGroupDetails> =>
|
||||||
|
fetch(`${BASE_URL}/clusters/${clusterName}/consumer-groups/${consumerGroupID}`, { ...BASE_PARAMS })
|
||||||
|
.then(res => res.json());
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
import { reduce } from 'lodash';
|
import { reduce } from 'lodash';
|
||||||
import {
|
import {
|
||||||
TopicName,
|
|
||||||
Topic,
|
|
||||||
ClusterName,
|
ClusterName,
|
||||||
TopicDetails,
|
Topic,
|
||||||
TopicConfig,
|
TopicConfig,
|
||||||
TopicFormData,
|
TopicDetails,
|
||||||
TopicFormCustomParam,
|
TopicFormCustomParam,
|
||||||
|
TopicFormData,
|
||||||
|
TopicName,
|
||||||
} from 'redux/interfaces';
|
} from 'redux/interfaces';
|
||||||
import { BASE_URL, BASE_PARAMS } from 'lib/constants';
|
import { BASE_PARAMS, BASE_URL } from 'lib/constants';
|
||||||
|
|
||||||
export const getTopicConfig = (
|
export const getTopicConfig = (
|
||||||
clusterName: ClusterName,
|
clusterName: ClusterName,
|
||||||
|
|
|
@ -1,5 +1,31 @@
|
||||||
|
export type ConsumerGroupID = string;
|
||||||
|
|
||||||
export interface ConsumerGroup {
|
export interface ConsumerGroup {
|
||||||
consumerGroupId: string;
|
consumerGroupId: ConsumerGroupID;
|
||||||
numConsumers: number;
|
numConsumers: number;
|
||||||
numTopics: number;
|
numTopics: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ConsumerGroupDetails {
|
||||||
|
consumerGroupId: ConsumerGroupID;
|
||||||
|
numConsumers: number;
|
||||||
|
numTopics: number;
|
||||||
|
consumers: Consumer[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Consumer {
|
||||||
|
consumerId: string;
|
||||||
|
topic: string;
|
||||||
|
partition: number;
|
||||||
|
messagesBehind: number;
|
||||||
|
currentOffset: number;
|
||||||
|
endOffset: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ConsumerGroupDetailedInfo extends ConsumerGroup, ConsumerGroupDetails {
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ConsumerGroupsState {
|
||||||
|
byID: { [consumerGroupID: string]: ConsumerGroupDetailedInfo },
|
||||||
|
allIDs: string[]
|
||||||
|
}
|
|
@ -8,7 +8,7 @@ import { TopicsState } from './topic';
|
||||||
import { Cluster } from './cluster';
|
import { Cluster } from './cluster';
|
||||||
import { BrokersState } from './broker';
|
import { BrokersState } from './broker';
|
||||||
import { LoaderState } from './loader';
|
import { LoaderState } from './loader';
|
||||||
import { ConsumerGroup } from './consumerGroup';
|
import { ConsumerGroupsState } from './consumerGroup';
|
||||||
|
|
||||||
export * from './topic';
|
export * from './topic';
|
||||||
export * from './cluster';
|
export * from './cluster';
|
||||||
|
@ -27,7 +27,7 @@ export interface RootState {
|
||||||
topics: TopicsState;
|
topics: TopicsState;
|
||||||
clusters: Cluster[];
|
clusters: Cluster[];
|
||||||
brokers: BrokersState;
|
brokers: BrokersState;
|
||||||
consumerGroups: ConsumerGroup[];
|
consumerGroups: ConsumerGroupsState;
|
||||||
loader: LoaderState;
|
loader: LoaderState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -75,4 +75,4 @@ export interface TopicFormData {
|
||||||
customParams: {
|
customParams: {
|
||||||
[index: string]: TopicFormCustomParam;
|
[index: string]: TopicFormCustomParam;
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
|
@ -8,16 +8,38 @@ const getBrokerListFetchingStatus = createFetchingSelector('GET_BROKERS');
|
||||||
|
|
||||||
export const getIsBrokerListFetched = createSelector(
|
export const getIsBrokerListFetched = createSelector(
|
||||||
getBrokerListFetchingStatus,
|
getBrokerListFetchingStatus,
|
||||||
(status) => status === FetchStatus.fetched,
|
(status) => status === FetchStatus.fetched
|
||||||
);
|
);
|
||||||
|
|
||||||
const getBrokerList = createSelector(brokersState, ({ items }) => items);
|
export const getBrokerCount = createSelector(
|
||||||
|
brokersState,
|
||||||
export const getBrokerCount = createSelector(brokersState, ({ brokerCount }) => brokerCount);
|
({ brokerCount }) => brokerCount
|
||||||
export const getZooKeeperStatus = createSelector(brokersState, ({ zooKeeperStatus }) => zooKeeperStatus);
|
);
|
||||||
export const getActiveControllers = createSelector(brokersState, ({ activeControllers }) => activeControllers);
|
export const getZooKeeperStatus = createSelector(
|
||||||
export const getOnlinePartitionCount = createSelector(brokersState, ({ onlinePartitionCount }) => onlinePartitionCount);
|
brokersState,
|
||||||
export const getOfflinePartitionCount = createSelector(brokersState, ({ offlinePartitionCount }) => offlinePartitionCount);
|
({ zooKeeperStatus }) => zooKeeperStatus
|
||||||
export const getInSyncReplicasCount = createSelector(brokersState, ({ inSyncReplicasCount }) => inSyncReplicasCount);
|
);
|
||||||
export const getOutOfSyncReplicasCount = createSelector(brokersState, ({ outOfSyncReplicasCount }) => outOfSyncReplicasCount);
|
export const getActiveControllers = createSelector(
|
||||||
export const getUnderReplicatedPartitionCount = createSelector(brokersState, ({ underReplicatedPartitionCount }) => underReplicatedPartitionCount);
|
brokersState,
|
||||||
|
({ activeControllers }) => activeControllers
|
||||||
|
);
|
||||||
|
export const getOnlinePartitionCount = createSelector(
|
||||||
|
brokersState,
|
||||||
|
({ onlinePartitionCount }) => onlinePartitionCount
|
||||||
|
);
|
||||||
|
export const getOfflinePartitionCount = createSelector(
|
||||||
|
brokersState,
|
||||||
|
({ offlinePartitionCount }) => offlinePartitionCount
|
||||||
|
);
|
||||||
|
export const getInSyncReplicasCount = createSelector(
|
||||||
|
brokersState,
|
||||||
|
({ inSyncReplicasCount }) => inSyncReplicasCount
|
||||||
|
);
|
||||||
|
export const getOutOfSyncReplicasCount = createSelector(
|
||||||
|
brokersState,
|
||||||
|
({ outOfSyncReplicasCount }) => outOfSyncReplicasCount
|
||||||
|
);
|
||||||
|
export const getUnderReplicatedPartitionCount = createSelector(
|
||||||
|
brokersState,
|
||||||
|
({ underReplicatedPartitionCount }) => underReplicatedPartitionCount
|
||||||
|
);
|
||||||
|
|
|
@ -1,12 +1,48 @@
|
||||||
import { Action, ConsumerGroup } from 'redux/interfaces';
|
import { Action, ConsumerGroup } from 'redux/interfaces';
|
||||||
import { ActionType } from 'redux/actionType';
|
import { ActionType } from 'redux/actionType';
|
||||||
|
import { ConsumerGroupsState } from '../../interfaces/consumerGroup';
|
||||||
|
|
||||||
export const initialState: ConsumerGroup[] = [];
|
export const initialState: ConsumerGroupsState = {
|
||||||
|
byID: {},
|
||||||
|
allIDs: []
|
||||||
|
};
|
||||||
|
|
||||||
const reducer = (state = initialState, action: Action): ConsumerGroup[] => {
|
const updateConsumerGroupsList = (state: ConsumerGroupsState, payload: ConsumerGroup[]): ConsumerGroupsState => {
|
||||||
|
const initialMemo: ConsumerGroupsState = {
|
||||||
|
...state,
|
||||||
|
allIDs: []
|
||||||
|
};
|
||||||
|
|
||||||
|
return payload.reduce(
|
||||||
|
(memo: ConsumerGroupsState, consumerGroup) => {
|
||||||
|
const {consumerGroupId} = consumerGroup;
|
||||||
|
memo.byID[consumerGroupId] = {
|
||||||
|
...memo.byID[consumerGroupId],
|
||||||
|
...consumerGroup,
|
||||||
|
};
|
||||||
|
memo.allIDs.push(consumerGroupId);
|
||||||
|
|
||||||
|
return memo;
|
||||||
|
},
|
||||||
|
initialMemo,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const reducer = (state = initialState, action: Action): ConsumerGroupsState => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case ActionType.GET_CONSUMER_GROUPS__SUCCESS:
|
case ActionType.GET_CONSUMER_GROUPS__SUCCESS:
|
||||||
return action.payload;
|
return updateConsumerGroupsList(state, action.payload);
|
||||||
|
case ActionType.GET_CONSUMER_GROUP_DETAILS__SUCCESS:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
byID: {
|
||||||
|
...state.byID,
|
||||||
|
[action.payload.consumerGroupID]: {
|
||||||
|
...state.byID[action.payload.consumerGroupID],
|
||||||
|
...action.payload.details,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,44 @@
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import { ConsumerGroup, RootState, FetchStatus } from 'redux/interfaces';
|
import { RootState, FetchStatus } from 'redux/interfaces';
|
||||||
import { createFetchingSelector } from 'redux/reducers/loader/selectors';
|
import { createFetchingSelector } from 'redux/reducers/loader/selectors';
|
||||||
|
import { ConsumerGroupID, ConsumerGroupsState } from '../../interfaces/consumerGroup';
|
||||||
|
|
||||||
|
|
||||||
const consumerGroupsState = ({ consumerGroups }: RootState): ConsumerGroup[] => consumerGroups;
|
const consumerGroupsState = ({ consumerGroups }: RootState): ConsumerGroupsState => consumerGroups;
|
||||||
|
|
||||||
|
const getConsumerGroupsMap = (state: RootState) => consumerGroupsState(state).byID;
|
||||||
|
const getConsumerGroupsIDsList = (state: RootState) => consumerGroupsState(state).allIDs;
|
||||||
|
|
||||||
const getConsumerGroupsListFetchingStatus = createFetchingSelector('GET_CONSUMER_GROUPS');
|
const getConsumerGroupsListFetchingStatus = createFetchingSelector('GET_CONSUMER_GROUPS');
|
||||||
|
const getConsumerGroupDetailsFetchingStatus = createFetchingSelector('GET_CONSUMER_GROUP_DETAILS');
|
||||||
|
|
||||||
export const getIsConsumerGroupsListFetched = createSelector(
|
export const getIsConsumerGroupsListFetched = createSelector(
|
||||||
getConsumerGroupsListFetchingStatus,
|
getConsumerGroupsListFetchingStatus,
|
||||||
(status) => status === FetchStatus.fetched,
|
(status) => status === FetchStatus.fetched,
|
||||||
);
|
);
|
||||||
|
|
||||||
export const getConsumerGroupsList = createSelector(consumerGroupsState, (consumerGroups) => consumerGroups);
|
export const getIsConsumerGroupDetailsFetched = createSelector(
|
||||||
|
getConsumerGroupDetailsFetchingStatus,
|
||||||
|
(status) => status === FetchStatus.fetched,
|
||||||
|
);
|
||||||
|
|
||||||
|
export const getConsumerGroupsList = createSelector(
|
||||||
|
getIsConsumerGroupsListFetched,
|
||||||
|
getConsumerGroupsMap,
|
||||||
|
getConsumerGroupsIDsList,
|
||||||
|
(isFetched, byID, ids) => {
|
||||||
|
if (!isFetched) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return ids.map(key => byID[key]);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const getConsumerGroupID = (_: RootState, consumerGroupID: ConsumerGroupID) => consumerGroupID;
|
||||||
|
|
||||||
|
export const getConsumerGroupByID = createSelector(
|
||||||
|
getConsumerGroupsMap,
|
||||||
|
getConsumerGroupID,
|
||||||
|
(consumerGroups, consumerGroupID) => consumerGroups[consumerGroupID],
|
||||||
|
);
|
||||||
|
|
|
@ -31,3 +31,7 @@ code {
|
||||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
||||||
monospace;
|
monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cursor-pointer {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
1
pom.xml
1
pom.xml
|
@ -27,6 +27,7 @@
|
||||||
<openapi-generator-maven-plugin.version>4.2.2</openapi-generator-maven-plugin.version>
|
<openapi-generator-maven-plugin.version>4.2.2</openapi-generator-maven-plugin.version>
|
||||||
<swagger-annotations.version>1.6.0</swagger-annotations.version>
|
<swagger-annotations.version>1.6.0</swagger-annotations.version>
|
||||||
<springdoc-openapi-webflux-ui.version>1.2.32</springdoc-openapi-webflux-ui.version>
|
<springdoc-openapi-webflux-ui.version>1.2.32</springdoc-openapi-webflux-ui.version>
|
||||||
|
<kafka.version>2.4.1</kafka.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<groupId>com.provectus</groupId>
|
<groupId>com.provectus</groupId>
|
||||||
|
|
Loading…
Add table
Reference in a new issue