Browse Source

Merge branch 'master' of github.com:provectus/kafka-ui

German Osin 4 năm trước cách đây
mục cha
commit
58f3421d0b

+ 1 - 1
kafka-ui-api/src/main/java/com/provectus/kafka/ui/controller/StaticController.java

@@ -50,6 +50,6 @@ public class StaticController {
     return ResourceUtil.readAsString(indexFile)
         .replace("href=\"./static", "href=\"" + staticPath)
         .replace("src=\"./static", "src=\"" + staticPath)
-        .replace("window.basePath=\"/\"", "window.basePath=\"" + contextPath + "\"");
+        .replace("window.basePath=\"\"", "window.basePath=\"" + contextPath + "\"");
   }
 }

+ 1 - 1
kafka-ui-api/src/main/java/com/provectus/kafka/ui/service/ClusterService.java

@@ -43,7 +43,7 @@ import reactor.util.function.Tuples;
 @Service
 @RequiredArgsConstructor
 public class ClusterService {
-  private static final Integer DEFAULT_PAGE_SIZE = 20;
+  private static final Integer DEFAULT_PAGE_SIZE = 25;
 
   private final ClustersStorage clustersStorage;
   private final ClusterMapper clusterMapper;

+ 11 - 11
kafka-ui-api/src/test/java/com/provectus/kafka/ui/service/ClusterServiceTest.java

@@ -33,7 +33,7 @@ class ClusterServiceTest {
   private ClustersStorage clustersStorage;
 
   @Test
-  public void shouldListFirst20Topics() {
+  public void shouldListFirst25Topics() {
     var topicName = UUID.randomUUID().toString();
 
     when(clustersStorage.getClusterByName(topicName))
@@ -51,8 +51,8 @@ class ClusterServiceTest {
     var topics = clusterService.getTopics(topicName,
         Optional.empty(), Optional.empty(), Optional.empty(),
         Optional.empty(), Optional.empty());
-    assertThat(topics.getPageCount()).isEqualTo(5);
-    assertThat(topics.getTopics()).hasSize(20);
+    assertThat(topics.getPageCount()).isEqualTo(4);
+    assertThat(topics.getTopics()).hasSize(25);
     assertThat(topics.getTopics()).map(Topic::getName).isSorted();
   }
 
@@ -97,8 +97,8 @@ class ClusterServiceTest {
 
     var topics = clusterService.getTopics(topicName, Optional.of(0), Optional.of(-1),
         Optional.empty(), Optional.empty(), Optional.empty());
-    assertThat(topics.getPageCount()).isEqualTo(5);
-    assertThat(topics.getTopics()).hasSize(20);
+    assertThat(topics.getPageCount()).isEqualTo(4);
+    assertThat(topics.getTopics()).hasSize(25);
     assertThat(topics.getTopics()).map(Topic::getName).isSorted();
   }
 
@@ -122,8 +122,8 @@ class ClusterServiceTest {
     var topics = clusterService.getTopics(topicName,
         Optional.empty(), Optional.empty(), Optional.of(true),
         Optional.empty(), Optional.empty());
-    assertThat(topics.getPageCount()).isEqualTo(5);
-    assertThat(topics.getTopics()).hasSize(20);
+    assertThat(topics.getPageCount()).isEqualTo(4);
+    assertThat(topics.getTopics()).hasSize(25);
     assertThat(topics.getTopics()).map(Topic::getName).isSorted();
   }
 
@@ -148,8 +148,8 @@ class ClusterServiceTest {
     var topics = clusterService.getTopics(topicName,
         Optional.empty(), Optional.empty(), Optional.of(true),
         Optional.empty(), Optional.empty());
-    assertThat(topics.getPageCount()).isEqualTo(5);
-    assertThat(topics.getTopics()).hasSize(20);
+    assertThat(topics.getPageCount()).isEqualTo(4);
+    assertThat(topics.getTopics()).hasSize(25);
     assertThat(topics.getTopics()).map(Topic::getName).isSorted();
   }
 
@@ -198,8 +198,8 @@ class ClusterServiceTest {
     var topics = clusterService.getTopics(topicName,
         Optional.empty(), Optional.empty(), Optional.empty(),
         Optional.empty(), Optional.of(TopicColumnsToSort.TOTAL_PARTITIONS));
-    assertThat(topics.getPageCount()).isEqualTo(5);
-    assertThat(topics.getTopics()).hasSize(20);
+    assertThat(topics.getPageCount()).isEqualTo(4);
+    assertThat(topics.getTopics()).hasSize(25);
     assertThat(topics.getTopics()).map(Topic::getPartitionCount).isSorted();
   }
 }

+ 1 - 1
kafka-ui-e2e-checks/src/test/java/com/provectus/kafka/ui/SmokeTests.java

@@ -13,7 +13,7 @@ public class SmokeTests extends BaseTest {
     @DisplayName("main page should load")
     @Issue("380")
     void mainPageLoads() {
-        pages.goTo("")
+        pages.open()
             .mainPage.shouldBeOnPage();
         compareScreenshots("main");
     }

+ 5 - 4
kafka-ui-e2e-checks/src/test/java/com/provectus/kafka/ui/base/BaseTest.java

@@ -4,6 +4,7 @@ import com.codeborne.selenide.Configuration;
 import com.codeborne.selenide.logevents.SelenideLogger;
 import com.provectus.kafka.ui.pages.Pages;
 import com.provectus.kafka.ui.screenshots.Screenshooter;
+import com.provectus.kafka.ui.steps.Steps;
 import io.github.cdimascio.dotenv.Dotenv;
 import io.qameta.allure.selenide.AllureSelenide;
 import lombok.SneakyThrows;
@@ -18,12 +19,11 @@ import org.testcontainers.utility.DockerImageName;
 import java.io.File;
 import java.util.Arrays;
 
-import static com.codeborne.selenide.Selenide.closeWebDriver;
-
 @Slf4j
 @DisplayNameGeneration(CamelCaseToSpacedDisplayNameGenerator.class)
 public class BaseTest {
 
+  protected Steps steps = Steps.INSTANCE;
   protected Pages pages = Pages.INSTANCE;
 
   private Screenshooter screenshooter = new Screenshooter();
@@ -59,8 +59,8 @@ public class BaseTest {
 
   @AfterAll
   public static void afterAll() {
-    closeWebDriver();
-    selenoid.close();
+//    closeWebDriver();
+//    selenoid.close();
   }
 
   @SneakyThrows
@@ -86,6 +86,7 @@ public class BaseTest {
     Configuration.baseUrl = TestConfiguration.BASE_URL;
     Configuration.browserSize = TestConfiguration.BROWSER_SIZE;
     var capabilities = new DesiredCapabilities();
+//    DesiredCapabilities capabilities = DesiredCapabilities.chrome();
     capabilities.setCapability("enableVNC", TestConfiguration.ENABLE_VNC);
     Configuration.browserCapabilities = capabilities;
 

+ 48 - 5
kafka-ui-e2e-checks/src/test/java/com/provectus/kafka/ui/pages/MainPage.java

@@ -2,15 +2,58 @@ package com.provectus.kafka.ui.pages;
 
 import com.codeborne.selenide.Condition;
 import io.qameta.allure.Step;
+import lombok.SneakyThrows;
 import org.openqa.selenium.By;
 
-import static com.codeborne.selenide.Selenide.$;
+import static com.codeborne.selenide.Selenide.*;
 
 public class MainPage {
 
-    @Step
-    public void shouldBeOnPage(){
-        $(By.xpath("//*[contains(text(),'Loading')]")).shouldBe(Condition.disappear);
-        $(By.xpath("//h5[text()='Clusters']")).shouldBe(Condition.visible);
+  private static final long TIMEOUT = 25000;
+
+  @Step
+  public MainPage shouldBeOnPage() {
+    $(By.xpath("//*[contains(text(),'Loading')]")).shouldBe(Condition.disappear);
+    $(By.xpath("//h5[text()='Clusters']")).shouldBe(Condition.visible);
+    return this;
+  }
+
+
+  private void refreshUntil(By by){
+    int i =0;
+    do
+    {
+      refresh();
+      i++;
+      sleep(2000);
+    } while(getElements(by).size()<1 && i!=20);
+    $(by).shouldBe(Condition.visible);
+  }
+
+  @SneakyThrows
+  public void shouldBeTopic(String topicName) {
+    refreshUntil(By.xpath("//div[contains(@class,'section')]//table//a[text()='%s']".formatted(topicName)));
+  }
+
+
+
+  public enum SideMenuOptions {
+    BROKERS("Brokers"),
+    TOPICS("Topics"),
+    CONSUMERS("Consumers"),
+    SCHEMA_REGISTRY("Schema registry");
+
+    String value;
+
+    SideMenuOptions(String value) {
+      this.value = value;
     }
+  }
+
+  @Step
+  public MainPage goToSideMenu(String clusterName, SideMenuOptions option) {
+    $(By.xpath("//aside//*[a[text()='%s']]//a[text()='%s']".formatted(clusterName, option.value)))
+        .click();
+    return this;
+  }
 }

+ 4 - 1
kafka-ui-e2e-checks/src/test/java/com/provectus/kafka/ui/pages/Pages.java

@@ -9,8 +9,11 @@ public class Pages {
 
     public MainPage mainPage = new MainPage();
 
-    public Pages goTo(String path) {
+    private Pages goTo(String path) {
         Selenide.open(TestConfiguration.BASE_URL+path);
         return this;
     }
+    public Pages open() {
+       return goTo("");
+    }
 }

+ 12 - 0
kafka-ui-e2e-checks/src/test/java/com/provectus/kafka/ui/steps/Steps.java

@@ -0,0 +1,12 @@
+package com.provectus.kafka.ui.steps;
+
+import com.provectus.kafka.ui.steps.kafka.KafkaSteps;
+
+public class Steps {
+
+    public static final Steps INSTANCE = new Steps();
+
+    private Steps(){}
+
+    public KafkaSteps kafka = new KafkaSteps();
+}

+ 62 - 0
kafka-ui-e2e-checks/src/test/java/com/provectus/kafka/ui/steps/kafka/KafkaSteps.java

@@ -0,0 +1,62 @@
+package com.provectus.kafka.ui.steps.kafka;
+
+import lombok.SneakyThrows;
+import org.apache.kafka.clients.admin.*;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class KafkaSteps {
+
+  int partitions = 2;
+  short replicationFactor = 1;
+  public enum Cluster{
+    SECOND_LOCAL("secondLocal","localhost:9093"),LOCAL("local","localhost:9092");
+    private String name;
+    private String server;
+    private  Map<String, Object> config = new HashMap<>();
+    Cluster(String name,String server) {
+      this.name = name;
+      this.server = server;
+      this.config.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, server);
+      this.config.put(AdminClientConfig.REQUEST_TIMEOUT_MS_CONFIG, "5000");
+    }
+
+    public String getName() {
+      return name;
+    }
+  }
+
+
+  @SneakyThrows
+  public void createTopic(Cluster cluster,String topicName) {
+    try (AdminClient client = AdminClient.create(cluster.config)) {
+      client
+          .createTopics(
+              Collections.singleton(new NewTopic(topicName, partitions, replicationFactor)),
+              new CreateTopicsOptions().timeoutMs(1000))
+          .all()
+          .get();
+
+      assertTrue(client
+              .listTopics()
+              .names().get().contains(topicName));
+
+    }
+  }
+
+  @SneakyThrows
+  public void deleteTopic(Cluster cluster,String topicName) {
+    try (AdminClient client = AdminClient.create(cluster.config)) {
+      assertTrue(client.listTopics().names().get().contains(topicName));
+      client
+          .deleteTopics(
+              Collections.singleton(topicName), new DeleteTopicsOptions().timeoutMs(1000))
+          .all()
+          .get();
+    }
+  }
+}

+ 32 - 0
kafka-ui-e2e-checks/src/test/java/com/provectus/kafka/ui/topics/TopicTests.java

@@ -0,0 +1,32 @@
+package com.provectus.kafka.ui.topics;
+
+import com.provectus.kafka.ui.base.BaseTest;
+import com.provectus.kafka.ui.pages.MainPage;
+import com.provectus.kafka.ui.steps.kafka.KafkaSteps;
+import lombok.SneakyThrows;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+public class TopicTests extends BaseTest {
+
+
+    public static final String NEW_TOPIC = "new-topic";
+
+    @AfterEach
+    @SneakyThrows
+    void  afterEach(){
+        steps.kafka.deleteTopic(KafkaSteps.Cluster.SECOND_LOCAL,NEW_TOPIC);
+    }
+
+    @SneakyThrows
+    @DisplayName("should create a topic")
+    @Test
+    void createTopic(){
+        steps.kafka.createTopic(KafkaSteps.Cluster.SECOND_LOCAL,NEW_TOPIC);
+        pages.open()
+                .mainPage.shouldBeOnPage()
+        .goToSideMenu(KafkaSteps.Cluster.SECOND_LOCAL.getName(), MainPage.SideMenuOptions.TOPICS)
+        .shouldBeTopic(NEW_TOPIC);
+    }
+}

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 429 - 202
kafka-ui-react-app/package-lock.json


+ 3 - 3
kafka-ui-react-app/package.json

@@ -20,7 +20,7 @@
     "pretty-ms": "^7.0.1",
     "react": "^17.0.1",
     "react-ace": "^9.3.0",
-    "react-datepicker": "^3.7.0",
+    "react-datepicker": "^4.1.1",
     "react-dom": "^17.0.1",
     "react-hook-form": "^7.6.9",
     "react-json-tree": "^0.15.0",
@@ -91,7 +91,7 @@
     "@typescript-eslint/eslint-plugin": "^4.20.0",
     "@typescript-eslint/parser": "^4.20.0",
     "@wojtekmaj/enzyme-adapter-react-17": "^0.6.0",
-    "dotenv": "^9.0.1",
+    "dotenv": "^10.0.0",
     "enzyme": "^3.11.0",
     "enzyme-to-json": "^3.6.1",
     "eslint": "^7.22.0",
@@ -114,7 +114,7 @@
     "react-test-renderer": "^17.0.2",
     "redux-mock-store": "^1.5.4",
     "ts-jest": "^26.5.4",
-    "ts-node": "^9.1.1",
+    "ts-node": "^10.0.0",
     "typescript": "^4.2.3"
   },
   "engines": {

+ 1 - 1
kafka-ui-react-app/public/index.html

@@ -8,7 +8,7 @@
     <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
     <title>UI for Apache Kafka</title>
     <script type="text/javascript">
-      window.basePath = "/";
+      window.basePath = "";
     </script>
   </head>
   <body>

+ 7 - 5
kafka-ui-react-app/src/components/Connect/List/ListItem.tsx

@@ -64,11 +64,13 @@ const ListItem: React.FC<ListItemProps> = ({
       <td>{type}</td>
       <td>{connectorClass}</td>
       <td>
-        {topics?.map((t) => (
-          <Link className="mr-1" key={t} to={clusterTopicPath(clusterName, t)}>
-            {t}
-          </Link>
-        ))}
+        <div className="is-flex is-flex-wrap-wrap">
+          {topics?.map((t) => (
+            <span key={t} className="tag is-info is-light mr-1 mb-1">
+              <Link to={clusterTopicPath(clusterName, t)}>{t}</Link>
+            </span>
+          ))}
+        </div>
       </td>
       <td>{status && <StatusTag status={status.state} />}</td>
       <td>

+ 21 - 16
kafka-ui-react-app/src/components/Connect/List/__tests__/__snapshots__/ListItem.spec.tsx.snap

@@ -106,25 +106,30 @@ exports[`Connectors ListItem matches snapshot 1`] = `
                 FileStreamSource
               </td>
               <td>
-                <Link
-                  className="mr-1"
-                  key="test-topic"
-                  to="/ui/clusters/local/topics/test-topic"
+                <div
+                  className="is-flex is-flex-wrap-wrap"
                 >
-                  <LinkAnchor
-                    className="mr-1"
-                    href="/ui/clusters/local/topics/test-topic"
-                    navigate={[Function]}
+                  <span
+                    className="tag is-info is-light mr-1 mb-1"
+                    key="test-topic"
                   >
-                    <a
-                      className="mr-1"
-                      href="/ui/clusters/local/topics/test-topic"
-                      onClick={[Function]}
+                    <Link
+                      to="/ui/clusters/local/topics/test-topic"
                     >
-                      test-topic
-                    </a>
-                  </LinkAnchor>
-                </Link>
+                      <LinkAnchor
+                        href="/ui/clusters/local/topics/test-topic"
+                        navigate={[Function]}
+                      >
+                        <a
+                          href="/ui/clusters/local/topics/test-topic"
+                          onClick={[Function]}
+                        >
+                          test-topic
+                        </a>
+                      </LinkAnchor>
+                    </Link>
+                  </span>
+                </div>
               </td>
               <td>
                 <StatusTag

+ 55 - 51
kafka-ui-react-app/src/components/Topics/shared/Form/TopicForm.tsx

@@ -29,61 +29,65 @@ const TopicForm: React.FC<Props> = ({
 
   return (
     <form onSubmit={onSubmit}>
-      <fieldset disabled={isEditing || isSubmitting}>
-        <div className="columns">
-          <div className="column is-three-quarters">
-            <label className="label">Topic Name *</label>
-            <input
-              className="input"
-              placeholder="Topic Name"
-              defaultValue={topicName}
-              {...register('name', {
-                required: 'Topic Name is required.',
-                pattern: {
-                  value: TOPIC_NAME_VALIDATION_PATTERN,
-                  message: 'Only alphanumeric, _, -, and . allowed',
-                },
-              })}
-              autoComplete="off"
-            />
-            <p className="help is-danger">
-              <ErrorMessage errors={errors} name="name" />
-            </p>
-          </div>
+      <fieldset disabled={isSubmitting}>
+        <fieldset disabled={isEditing}>
+          <div className="columns">
+            <div className="column is-three-quarters">
+              <label className="label">Topic Name *</label>
+              <input
+                className="input"
+                placeholder="Topic Name"
+                defaultValue={topicName}
+                {...register('name', {
+                  required: 'Topic Name is required.',
+                  pattern: {
+                    value: TOPIC_NAME_VALIDATION_PATTERN,
+                    message: 'Only alphanumeric, _, -, and . allowed',
+                  },
+                })}
+                autoComplete="off"
+              />
+              <p className="help is-danger">
+                <ErrorMessage errors={errors} name="name" />
+              </p>
+            </div>
 
-          <div className="column">
-            <label className="label">Number of partitions *</label>
-            <input
-              className="input"
-              type="number"
-              placeholder="Number of partitions"
-              defaultValue="1"
-              {...register('partitions', {
-                required: 'Number of partitions is required.',
-              })}
-            />
-            <p className="help is-danger">
-              <ErrorMessage errors={errors} name="partitions" />
-            </p>
+            <div className="column">
+              <label className="label">Number of partitions *</label>
+              <input
+                className="input"
+                type="number"
+                placeholder="Number of partitions"
+                defaultValue="1"
+                {...register('partitions', {
+                  required: 'Number of partitions is required.',
+                })}
+              />
+              <p className="help is-danger">
+                <ErrorMessage errors={errors} name="partitions" />
+              </p>
+            </div>
           </div>
-        </div>
+        </fieldset>
 
         <div className="columns">
-          <div className="column">
-            <label className="label">Replication Factor *</label>
-            <input
-              className="input"
-              type="number"
-              placeholder="Replication Factor"
-              defaultValue="1"
-              {...register('replicationFactor', {
-                required: 'Replication Factor is required.',
-              })}
-            />
-            <p className="help is-danger">
-              <ErrorMessage errors={errors} name="replicationFactor" />
-            </p>
-          </div>
+          <fieldset disabled={isEditing}>
+            <div className="column">
+              <label className="label">Replication Factor *</label>
+              <input
+                className="input"
+                type="number"
+                placeholder="Replication Factor"
+                defaultValue="1"
+                {...register('replicationFactor', {
+                  required: 'Replication Factor is required.',
+                })}
+              />
+              <p className="help is-danger">
+                <ErrorMessage errors={errors} name="replicationFactor" />
+              </p>
+            </div>
+          </fieldset>
 
           <div className="column">
             <label className="label">Min In Sync Replicas *</label>

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác