Browse Source

Merge branch 'master' into Topic_custom_params_are_disabled_upon_editing

David 2 years ago
parent
commit
aa19b01be7

+ 19 - 11
.github/ISSUE_TEMPLATE/bug_report.md

@@ -15,33 +15,36 @@ https://github.com/provectus/kafka-ui/discussions
 
 -->
 
-**Describe the bug**
-<!--(A clear and concise description of what the bug is.)-->
+<!--
+Please follow the naming conventions for bugs:
+<Feature/Area/Scope> :  <Compact, but specific problem summary> 
+Avoid generic titles, like “Topics: incorrect layout of message sorting drop-down list”. Better use something like: “Topics: Message sorting drop-down list overlaps the "Submit" button”.
+
+-->
 
+**Describe the bug** (Actual behavior)
+<!--(A clear and concise description of what the bug is.Use a list, if there is more than one problem)-->
+
+**Expected behavior**
+<!--(A clear and concise description of what you expected to happen.)-->
 
 **Set up**
 <!--
+WE MIGHT CLOSE THE ISSUE without further explanation IF YOU DON'T PROVIDE THIS INFORMATION.
+
 How do you run the app? Please provide as much info as possible:
 1. App version (docker image version or check commit hash in the top left corner in UI)
 2. Helm chart version, if you use one
 3. Any IAAC configs
-
-We might close the issue without further explanation if you don't provide such information.
 -->
 
 
 **Steps to Reproduce**
 <!-- We'd like you to provide an example setup (via docker-compose, helm, etc.) 
 to reproduce the problem, especially with a complex setups. -->
-Steps to reproduce the behavior:
 
 1. 
 
-**Expected behavior**
-<!--
-(A clear and concise description of what you expected to happen)
--->
-
 **Screenshots**
 <!--
 (If applicable, add screenshots to help explain your problem)
@@ -50,5 +53,10 @@ Steps to reproduce the behavior:
 
 **Additional context**
 <!--
-(Add any other context about the problem here)
+Add any other context about the problem here. E.g.: 
+1. Are there any alternative scenarios (different data/methods/configuration/setup) you have tried? 
+   Were they successfull or same issue occured? Please provide steps as well.
+2. Related issues (if there are any).
+3. Logs (if available)
+4. Is there any serious impact or behaviour on the end-user because of this issue, that can be overlooked?
 -->

+ 54 - 12
documentation/compose/e2e-tests.yaml

@@ -7,10 +7,18 @@ services:
     image: provectuslabs/kafka-ui:latest
     ports:
       - 8080:8080
+    healthcheck:
+      test: wget --no-verbose --tries=1 --spider  http://localhost:8080/actuator/health
+      interval: 30s
+      timeout: 10s
+      retries: 10  
     depends_on:
-      - kafka0
-      - schemaregistry0
-      - kafka-connect0
+        kafka0:
+          condition: service_healthy
+        schemaregistry0:
+          condition: service_healthy
+        kafka-connect0:
+          condition: service_healthy
     environment:
       KAFKA_CLUSTERS_0_NAME: local
       KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS: kafka0:29092
@@ -24,6 +32,11 @@ services:
     image: confluentinc/cp-kafka:7.2.1
     hostname: kafka0
     container_name: kafka0
+    healthcheck:
+     test: unset JMX_PORT && KAFKA_JMX_OPTS="-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Djava.rmi.server.hostname=kafka0 -Dcom.sun.management.jmxremote.rmi.port=9999" && kafka-broker-api-versions --bootstrap-server=localhost:9092
+     interval: 30s
+     timeout: 10s
+     retries: 10
     ports:
       - "9092:9092"
       - "9997:9997"
@@ -54,7 +67,13 @@ services:
     ports:
       - 8085:8085
     depends_on:
-      - kafka0
+      kafka0:
+          condition: service_healthy
+    healthcheck:
+     test: ["CMD", "timeout", "1", "curl", "--silent", "--fail", "http://schemaregistry0:8085/subjects"]
+     interval: 30s
+     timeout: 10s
+     retries: 10
     environment:
       SCHEMA_REGISTRY_KAFKASTORE_BOOTSTRAP_SERVERS: PLAINTEXT://kafka0:29092
       SCHEMA_REGISTRY_KAFKASTORE_SECURITY_PROTOCOL: PLAINTEXT
@@ -73,8 +92,15 @@ services:
     ports:
       - 8083:8083
     depends_on:
-      - kafka0
-      - schemaregistry0
+      kafka0:
+          condition: service_healthy
+      schemaregistry0:
+          condition: service_healthy
+    healthcheck:
+      test: ["CMD", "nc", "127.0.0.1", "8083"]
+      interval: 30s
+      timeout: 10s
+      retries: 10
     environment:
       CONNECT_BOOTSTRAP_SERVERS: kafka0:29092
       CONNECT_GROUP_ID: compose-connect-group
@@ -100,7 +126,8 @@ services:
     volumes:
       - ./message.json:/data/message.json
     depends_on:
-      - kafka0
+      kafka0:
+          condition: service_healthy
     command: "bash -c 'echo Waiting for Kafka to be ready... && \
                cub kafka-ready -b kafka0:29092 1 30 && \
                kafka-topics --create --topic users --partitions 3 --replication-factor 1 --if-not-exists --bootstrap-server kafka0:29092 && \
@@ -114,6 +141,11 @@ services:
         image: postgres:9.6.22
     ports:
       - 5432:5432
+    healthcheck:
+      test: ["CMD-SHELL", "pg_isready -U dev_user"]
+      interval: 10s
+      timeout: 5s
+      retries: 5  
     environment:
       POSTGRES_USER: 'dev_user'
       POSTGRES_PASSWORD: '12345'
@@ -121,18 +153,28 @@ services:
   create-connectors:
     image: ellerbrock/alpine-bash-curl-ssl
     depends_on:
-      - postgres-db
-      - kafka-connect0
+      postgres-db:
+          condition: service_healthy
+      kafka-connect0:
+        condition: service_healthy
     volumes:
       - ./connectors:/connectors
     command: bash -c '/connectors/start.sh'
 
   ksqldb:
     image: confluentinc/ksqldb-server:0.18.0
+    healthcheck:
+      test: ["CMD", "timeout", "1", "curl", "--silent", "--fail", "http://localhost:8088/info"]
+      interval: 30s
+      timeout: 10s
+      retries: 10
     depends_on:
-      - kafka0
-      - kafka-connect0
-      - schemaregistry0
+      kafka0:
+        condition: service_healthy
+      kafka-connect0:
+        condition: service_healthy
+      schemaregistry0:
+         condition: service_healthy
     ports:
       - 8088:8088
     environment:

+ 3 - 2
kafka-ui-api/src/main/java/com/provectus/kafka/ui/config/auth/OAuthProperties.java

@@ -1,6 +1,7 @@
 package com.provectus.kafka.ui.config.auth;
 
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
 import javax.annotation.PostConstruct;
@@ -31,13 +32,13 @@ public class OAuthProperties {
     private String clientName;
     private String redirectUri;
     private String authorizationGrantType;
-    private Set<String> scope;
+    private Set<String> scope = new HashSet<>();
     private String issuerUri;
     private String authorizationUri;
     private String tokenUri;
     private String userInfoUri;
     private String jwkSetUri;
     private String userNameAttribute;
-    private Map<String, String> customParams;
+    private Map<String, String> customParams = new HashMap<>();
   }
 }

+ 1 - 1
kafka-ui-api/src/main/java/com/provectus/kafka/ui/config/auth/OAuthPropertiesConverter.java

@@ -71,7 +71,7 @@ public final class OAuthPropertiesConverter {
   }
 
   private static boolean isGoogle(OAuth2Provider provider) {
-    return provider.getCustomParams().get(TYPE).equalsIgnoreCase(GOOGLE);
+    return GOOGLE.equalsIgnoreCase(provider.getCustomParams().get(TYPE));
   }
 }
 

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

@@ -53,9 +53,7 @@ public class StaticController {
   @SneakyThrows
   private String buildFile(Resource file, String contextPath) {
     return ResourceUtil.readAsString(file)
-        .replace("\"/assets/", "\"" + contextPath + "/assets/")
-        .replace("\"/favicon/", "\"" + contextPath + "/favicon/")
-        .replace("/manifest.json", contextPath + "/manifest.json")
-        .replace("window.basePath = ''", "window.basePath=\"" + contextPath + "\"");
+        .replace("\"assets/", "\"" + contextPath + "/assets/")
+        .replace("PUBLIC-PATH-VARIABLE",  contextPath);
   }
 }

+ 9 - 6
kafka-ui-react-app/index.html

@@ -3,7 +3,6 @@
   <head>
     <meta charset="utf-8" />
     <meta name="viewport" content="width=device-width, initial-scale=1" />
-
     <!-- Google fonts -->
     <link rel="preconnect" href="https://fonts.googleapis.com" />
     <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
@@ -13,14 +12,18 @@
     />
 
     <!-- Favicons -->
-    <link rel="icon" href="/favicon/favicon.ico" sizes="any" />
-    <link rel="icon" href="/favicon/icon.svg" type="image/svg+xml" />
-    <link rel="apple-touch-icon" href="/favicon/apple-touch-icon.png" />
-    <link rel="manifest" href="/manifest.json" />
+    <link rel="icon" href="<%= PUBLIC_PATH %>/favicon/favicon.ico" sizes="any" />
+    <link rel="icon" href="<%= PUBLIC_PATH %>/favicon/icon.svg" type="image/svg+xml" />
+    <link rel="apple-touch-icon" href="<%= PUBLIC_PATH %>/favicon/apple-touch-icon.png" />
+    <link rel="manifest" href="<%= PUBLIC_PATH %>/manifest.json" />
 
     <title>UI for Apache Kafka</title>
     <script type="text/javascript">
-      window.basePath = '';
+      window.basePath = '<%= PUBLIC_PATH %>';
+
+      window.__assetsPathBuilder = function (importer) {
+        return window.basePath+ "/" + importer;
+      };
     </script>
   </head>
 

+ 2 - 1
kafka-ui-react-app/package.json

@@ -118,7 +118,8 @@
     "rimraf": "^3.0.2",
     "ts-node": "^10.8.1",
     "ts-prune": "^0.10.3",
-    "typescript": "^4.7.4"
+    "typescript": "^4.7.4",
+    "vite-plugin-ejs": "^1.6.4"
   },
   "engines": {
     "node": "v16.15.0",

+ 50 - 0
kafka-ui-react-app/pnpm-lock.yaml

@@ -85,6 +85,7 @@ specifiers:
   typescript: ^4.7.4
   use-debounce: ^8.0.1
   vite: ^4.0.0
+  vite-plugin-ejs: ^1.6.4
   vite-tsconfig-paths: ^4.0.2
   whatwg-fetch: ^3.6.2
   yup: ^0.32.11
@@ -181,6 +182,7 @@ devDependencies:
   ts-node: 10.8.1_seagpw47opwyivxvtfydnuwcuy
   ts-prune: 0.10.3
   typescript: 4.7.4
+  vite-plugin-ejs: 1.6.4
 
 packages:
 
@@ -4266,6 +4268,10 @@ packages:
     engines: {node: '>=8'}
     dev: true
 
+  /async/3.2.4:
+    resolution: {integrity: sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==}
+    dev: true
+
   /asynckit/0.4.0:
     resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
     dev: true
@@ -4514,6 +4520,12 @@ packages:
       balanced-match: 1.0.2
       concat-map: 0.0.1
 
+  /brace-expansion/2.0.1:
+    resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
+    dependencies:
+      balanced-match: 1.0.2
+    dev: true
+
   /braces/3.0.2:
     resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==}
     engines: {node: '>=8'}
@@ -5040,6 +5052,14 @@ packages:
       wcwidth: 1.0.1
     dev: true
 
+  /ejs/3.1.8:
+    resolution: {integrity: sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==}
+    engines: {node: '>=0.10.0'}
+    hasBin: true
+    dependencies:
+      jake: 10.8.5
+    dev: true
+
   /electron-to-chromium/1.4.151:
     resolution: {integrity: sha512-XaG2LpZi9fdiWYOqJh0dJy4SlVywCvpgYXhzOlZTp4JqSKqxn5URqOjbm9OMYB3aInA2GuHQiem1QUOc1yT0Pw==}
 
@@ -5740,6 +5760,12 @@ packages:
       flat-cache: 3.0.4
     dev: true
 
+  /filelist/1.0.4:
+    resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==}
+    dependencies:
+      minimatch: 5.1.2
+    dev: true
+
   /fill-range/7.0.1:
     resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==}
     engines: {node: '>=8'}
@@ -6340,6 +6366,17 @@ packages:
     engines: {node: '>=6'}
     dev: true
 
+  /jake/10.8.5:
+    resolution: {integrity: sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==}
+    engines: {node: '>=10'}
+    hasBin: true
+    dependencies:
+      async: 3.2.4
+      chalk: 4.1.2
+      filelist: 1.0.4
+      minimatch: 3.1.2
+    dev: true
+
   /jest-changed-files/29.0.0:
     resolution: {integrity: sha512-28/iDMDrUpGoCitTURuDqUzWQoWmOmOKOFST1mi2lwh62X4BFf6khgH3uSuo1e49X/UDjuApAj3w0wLOex4VPQ==}
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
@@ -7176,6 +7213,13 @@ packages:
     dependencies:
       brace-expansion: 1.1.11
 
+  /minimatch/5.1.2:
+    resolution: {integrity: sha512-bNH9mmM9qsJ2X4r2Nat1B//1dJVcn3+iBLa3IgqJ7EbGaDNepL9QSHOxN4ng33s52VMMhhIfgCYDk3C4ZmlDAg==}
+    engines: {node: '>=10'}
+    dependencies:
+      brace-expansion: 2.0.1
+    dev: true
+
   /minimist/1.2.6:
     resolution: {integrity: sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==}
     dev: true
@@ -8675,6 +8719,12 @@ packages:
       '@types/istanbul-lib-coverage': 2.0.3
       convert-source-map: 1.7.0
 
+  /vite-plugin-ejs/1.6.4:
+    resolution: {integrity: sha512-23p1RS4PiA0veXY5/gHZ60pl3pPvd8NEqdBsDgxNK8nM1rjFFDcVb0paNmuipzCgNP/Y0f/Id22M7Il4kvZ2jA==}
+    dependencies:
+      ejs: 3.1.8
+    dev: true
+
   /vite-tsconfig-paths/4.0.2_eqmiqdrctagsk5ranq2vs4ssty:
     resolution: {integrity: sha512-UzU8zwbCQrdUkj/Z0tnh293n4ScRcjJLoS8nPme2iB2FHoU5q8rhilb7AbhLlUC1uv4t6jSzVWnENjPnyGseeQ==}
     peerDependencies:

+ 45 - 51
kafka-ui-react-app/src/components/Cluster/Cluster.tsx

@@ -16,23 +16,15 @@ import {
 import ClusterContext from 'components/contexts/ClusterContext';
 import PageLoader from 'components/common/PageLoader/PageLoader';
 import { useClusters } from 'lib/hooks/api/clusters';
-import Brokers from 'components/Brokers/Brokers';
-import Topics from 'components/Topics/Topics';
-import Schemas from 'components/Schemas/Schemas';
-import Connect from 'components/Connect/Connect';
-import KsqlDb from 'components/KsqlDb/KsqlDb';
-import ConsumerGroups from 'components/ConsumerGroups/ConsumerGroups';
 
-// We can't use Lazy loading till we have a better way to update publicPath in runtime
-// Now java app replaces paths in builded index.html file.
-// const Brokers = React.lazy(() => import('components/Brokers/Brokers'));
-// const Topics = React.lazy(() => import('components/Topics/Topics'));
-// const Schemas = React.lazy(() => import('components/Schemas/Schemas'));
-// const Connect = React.lazy(() => import('components/Connect/Connect'));
-// const KsqlDb = React.lazy(() => import('components/KsqlDb/KsqlDb'));
-// const ConsumerGroups = React.lazy(
-//   () => import('components/ConsumerGroups/ConsumerGroups')
-// );
+const Brokers = React.lazy(() => import('components/Brokers/Brokers'));
+const Topics = React.lazy(() => import('components/Topics/Topics'));
+const Schemas = React.lazy(() => import('components/Schemas/Schemas'));
+const Connect = React.lazy(() => import('components/Connect/Connect'));
+const KsqlDb = React.lazy(() => import('components/KsqlDb/KsqlDb'));
+const ConsumerGroups = React.lazy(
+  () => import('components/ConsumerGroups/ConsumerGroups')
+);
 
 const Cluster: React.FC = () => {
   const { clusterName } = useAppParams<ClusterNameRoute>();
@@ -59,49 +51,51 @@ const Cluster: React.FC = () => {
   return (
     <Suspense fallback={<PageLoader />}>
       <ClusterContext.Provider value={contextValue}>
-        <Routes>
-          <Route
-            path={getNonExactPath(clusterBrokerRelativePath)}
-            element={<Brokers />}
-          />
-          <Route
-            path={getNonExactPath(clusterTopicsRelativePath)}
-            element={<Topics />}
-          />
-          <Route
-            path={getNonExactPath(clusterConsumerGroupsRelativePath)}
-            element={<ConsumerGroups />}
-          />
-          {contextValue.hasSchemaRegistryConfigured && (
+        <Suspense fallback={<PageLoader />}>
+          <Routes>
             <Route
-              path={getNonExactPath(clusterSchemasRelativePath)}
-              element={<Schemas />}
+              path={getNonExactPath(clusterBrokerRelativePath)}
+              element={<Brokers />}
             />
-          )}
-          {contextValue.hasKafkaConnectConfigured && (
             <Route
-              path={getNonExactPath(clusterConnectsRelativePath)}
-              element={<Connect />}
+              path={getNonExactPath(clusterTopicsRelativePath)}
+              element={<Topics />}
             />
-          )}
-          {contextValue.hasKafkaConnectConfigured && (
             <Route
-              path={getNonExactPath(clusterConnectorsRelativePath)}
-              element={<Connect />}
+              path={getNonExactPath(clusterConsumerGroupsRelativePath)}
+              element={<ConsumerGroups />}
             />
-          )}
-          {contextValue.hasKsqlDbConfigured && (
+            {contextValue.hasSchemaRegistryConfigured && (
+              <Route
+                path={getNonExactPath(clusterSchemasRelativePath)}
+                element={<Schemas />}
+              />
+            )}
+            {contextValue.hasKafkaConnectConfigured && (
+              <Route
+                path={getNonExactPath(clusterConnectsRelativePath)}
+                element={<Connect />}
+              />
+            )}
+            {contextValue.hasKafkaConnectConfigured && (
+              <Route
+                path={getNonExactPath(clusterConnectorsRelativePath)}
+                element={<Connect />}
+              />
+            )}
+            {contextValue.hasKsqlDbConfigured && (
+              <Route
+                path={getNonExactPath(clusterKsqlDbRelativePath)}
+                element={<KsqlDb />}
+              />
+            )}
             <Route
-              path={getNonExactPath(clusterKsqlDbRelativePath)}
-              element={<KsqlDb />}
+              path="/"
+              element={<Navigate to={clusterBrokerRelativePath} replace />}
             />
-          )}
-          <Route
-            path="/"
-            element={<Navigate to={clusterBrokerRelativePath} replace />}
-          />
-        </Routes>
-        <Outlet />
+          </Routes>
+          <Outlet />
+        </Suspense>
       </ClusterContext.Provider>
     </Suspense>
   );

+ 4 - 1
kafka-ui-react-app/src/components/Cluster/__tests__/Cluster.spec.tsx

@@ -1,7 +1,7 @@
 import React from 'react';
 import { Cluster, ClusterFeaturesEnum } from 'generated-sources';
 import ClusterComponent from 'components/Cluster/Cluster';
-import { screen } from '@testing-library/react';
+import { screen, waitFor } from '@testing-library/react';
 import { render, WithRoute } from 'lib/testHelpers';
 import {
   clusterBrokersPath,
@@ -59,6 +59,9 @@ describe('Cluster', () => {
       </WithRoute>,
       { initialEntries: [pathname] }
     );
+    await waitFor(() => {
+      expect(screen.queryByRole('progressbar')).not.toBeInTheDocument();
+    });
   };
 
   it('renders Brokers', async () => {

+ 29 - 1
kafka-ui-react-app/vite.config.ts

@@ -6,18 +6,46 @@ import {
 } from 'vite';
 import react from '@vitejs/plugin-react-swc';
 import tsconfigPaths from 'vite-tsconfig-paths';
+import { ViteEjsPlugin } from 'vite-plugin-ejs';
 
 export default defineConfig(({ mode }) => {
   process.env = { ...process.env, ...loadEnv(mode, process.cwd()) };
 
   const defaultConfig: UserConfigExport = {
-    plugins: [react(), tsconfigPaths(), splitVendorChunkPlugin()],
+    plugins: [
+      react(),
+      tsconfigPaths(),
+      splitVendorChunkPlugin(),
+      ViteEjsPlugin({
+        PUBLIC_PATH: mode !== 'development' ? 'PUBLIC-PATH-VARIABLE' : '',
+      }),
+    ],
     server: {
       port: 3000,
     },
     build: {
       outDir: 'build',
     },
+    experimental: {
+      renderBuiltUrl(
+        filename: string,
+        {
+          hostType,
+        }: {
+          hostId: string;
+          hostType: 'js' | 'css' | 'html';
+          type: 'asset' | 'public';
+        }
+      ) {
+        if (hostType === 'js') {
+          return {
+            runtime: `window.__assetsPathBuilder(${JSON.stringify(filename)})`,
+          };
+        }
+
+        return filename;
+      },
+    },
     define: {
       'process.env.NODE_ENV': `"${mode}"`,
       'process.env.VITE_TAG': `"${process.env.VITE_TAG}"`,