فهرست منبع

final fixes, topic creation

Zhenya Taran 5 سال پیش
والد
کامیت
2834b9efaa

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

@@ -49,7 +49,7 @@ public class ClusterService {
         return Mono.just(ResponseEntity.ok(Flux.fromIterable(cluster.getTopicConfigsMap().get(topicName))));
     }
 
-    public Mono<ResponseEntity<Void>> createTopic(String clusterId, Mono<TopicFormData> topicFormData) {
+    public Mono<ResponseEntity<Topic>> createTopic(String clusterId, Mono<TopicFormData> topicFormData) {
         KafkaCluster cluster = clustersStorage.getClusterById(clusterId);
         return kafkaService.createTopic(cluster, topicFormData);
     }

+ 3 - 3
kafka-ui-api/src/main/java/com/provectus/kafka/ui/kafka/KafkaService.java

@@ -199,7 +199,7 @@ public class KafkaService {
     private void loadTopicConfig(KafkaCluster kafkaCluster, String topicName) {
         AdminClient adminClient = kafkaCluster.getAdminClient();
 
-        Set<ConfigResource> resources = Collections.singleton(new ConfigResource(ConfigResource.Type.TOPIC, "messages"));
+        Set<ConfigResource> resources = Collections.singleton(new ConfigResource(ConfigResource.Type.TOPIC, topicName));
         final Map<ConfigResource, Config> configs = adminClient.describeConfigs(resources).all().get();
 
         if (configs.isEmpty()) return;
@@ -222,7 +222,7 @@ public class KafkaService {
     }
 
     @SneakyThrows
-    public Mono<ResponseEntity<Void>> createTopic(KafkaCluster cluster, Mono<TopicFormData> topicFormData) {
+    public Mono<ResponseEntity<Topic>> createTopic(KafkaCluster cluster, Mono<TopicFormData> topicFormData) {
         return topicFormData.flatMap(
                 topicData -> {
                     AdminClient adminClient = cluster.getAdminClient();
@@ -239,7 +239,7 @@ public class KafkaService {
 
                     Topic topic = collectTopicData(cluster, topicDescription);
                     cluster.getTopics().add(topic);
-                    return Mono.just(new ResponseEntity<>(HttpStatus.CREATED));
+                    return Mono.just(new ResponseEntity<>(topic, HttpStatus.CREATED));
                 }
         );
     }

+ 8 - 3
kafka-ui-api/src/main/java/com/provectus/kafka/ui/rest/MetricsRestController.java

@@ -1,6 +1,6 @@
 package com.provectus.kafka.ui.rest;
 
-import com.provectus.kafka.ui.api.ClustersApi;
+import com.provectus.kafka.ui.api.ApiClustersApi;
 import com.provectus.kafka.ui.cluster.service.ClusterService;
 import com.provectus.kafka.ui.model.*;
 import lombok.RequiredArgsConstructor;
@@ -11,10 +11,11 @@ import reactor.core.publisher.Flux;
 import reactor.core.publisher.Mono;
 
 import javax.validation.Valid;
+import java.util.ArrayList;
 
 @RestController
 @RequiredArgsConstructor
-public class MetricsRestController implements ClustersApi {
+public class MetricsRestController implements ApiClustersApi {
 
     private final ClusterService clusterService;
 
@@ -44,8 +45,12 @@ public class MetricsRestController implements ClustersApi {
     }
 
     @Override
-    public Mono<ResponseEntity<Void>> createTopic(String clusterId, @Valid Mono<TopicFormData> topicFormData, ServerWebExchange exchange) {
+    public Mono<ResponseEntity<Topic>> createTopic(String clusterId, @Valid Mono<TopicFormData> topicFormData, ServerWebExchange exchange) {
         return clusterService.createTopic(clusterId, topicFormData);
     }
 
+    @Override
+    public Mono<ResponseEntity<Flux<Broker>>> getBrokers(String clusterId, ServerWebExchange exchange) {
+        return Mono.just(ResponseEntity.ok(Flux.fromIterable(new ArrayList<>())));
+    }
 }

+ 45 - 13
kafka-ui-contract/src/main/resources/swagger/kafka-ui-api.yaml

@@ -9,15 +9,15 @@ info:
     name: Apache 2.0
     url: http://www.apache.org/licenses/LICENSE-2.0
 tags:
-  - name: /clusters
+  - name: /api/clusters
 servers:
   - url: /localhost
 
 paths:
-  /clusters:
+  /api/clusters:
     get:
       tags:
-        - /clusters
+        - /api/clusters
       summary: getClusters
       operationId: getClusters
       responses:
@@ -30,10 +30,32 @@ paths:
                 items:
                   $ref: '#/components/schemas/Cluster'
 
-  /clusters/{clusterId}/metrics/broker:
+  /api/clusters/{clusterId}/brokers:
     get:
       tags:
-        - /clusters
+        - /api/clusters
+      summary: getBrokers
+      operationId: getBrokers
+      parameters:
+        - name: clusterId
+          in: path
+          required: true
+          schema:
+            type: string
+      responses:
+        200:
+          description: OK
+          content:
+            application/json:
+              schema:
+                type: array
+                items:
+                  $ref: '#/components/schemas/Broker'
+
+  /api/clusters/{clusterId}/metrics/broker:
+    get:
+      tags:
+        - /api/clusters
       summary: getBrokersMetrics
       operationId: getBrokersMetrics
       parameters:
@@ -50,10 +72,10 @@ paths:
               schema:
                 $ref: '#/components/schemas/BrokersMetrics'
 
-  /clusters/{clusterId}/topics:
+  /api/clusters/{clusterId}/topics:
     get:
       tags:
-        - /clusters
+        - /api/clusters
       summary: getTopics
       operationId: getTopics
       parameters:
@@ -73,7 +95,7 @@ paths:
                   $ref: '#/components/schemas/Topic'
     post:
       tags:
-        - /clusters
+        - /api/clusters
       summary: createTopic
       operationId: createTopic
       parameters:
@@ -90,11 +112,15 @@ paths:
       responses:
         201:
           description: Created
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Topic'
 
-  /clusters/{clusterId}/topics/{topicName}:
+  /api/clusters/{clusterId}/topics/{topicName}:
     get:
       tags:
-        - /clusters
+        - /api/clusters
       summary: getTopicDetails
       operationId: getTopicDetails
       parameters:
@@ -116,10 +142,10 @@ paths:
               schema:
                 $ref: '#/components/schemas/TopicDetails'
 
-  /clusters/{clusterId}/topics/{topicName}/config:
+  /api/clusters/{clusterId}/topics/{topicName}/config:
     get:
       tags:
-        - /clusters
+        - /api/clusters
       summary: getTopicConfigs
       operationId: getTopicConfigs
       parameters:
@@ -295,4 +321,10 @@ components:
         configs:
           type: object
           additionalProperties:
-            type: string
+            type: string
+
+    Broker:
+      type: object
+      properties:
+        id:
+          type: string

+ 1 - 1
kafka-ui-react-app/.env

@@ -1,2 +1,2 @@
 # Kafka REST API
-REACT_APP_API_URL=http://localhost:8080
+REACT_APP_API_URL=http://localhost:8080/api

+ 1 - 1
kafka-ui-react-app/src/components/Topics/Details/Overview/Overview.tsx

@@ -63,7 +63,7 @@ const Overview: React.FC<Props> = ({
             </tr>
           </thead>
           <tbody>
-            {partitions.map(({ partition, leader }) => (
+            {partitions && partitions.map(({ partition, leader }) => (
               <tr key={`partition-list-item-key-${partition}`}>
                 <td>{partition}</td>
                 <td>{leader}</td>

+ 7 - 0
kafka-ui-react-app/src/components/Topics/New/New.tsx

@@ -14,6 +14,7 @@ interface Props {
   isTopicCreated: boolean;
   createTopic: (clusterId: ClusterId, form: TopicFormData) => void;
   redirectToTopicPath: (clusterId: ClusterId, topicName: TopicName) => void;
+  resetUploadedState: () => void;
 }
 
 const New: React.FC<Props> = ({
@@ -21,6 +22,7 @@ const New: React.FC<Props> = ({
   isTopicCreated,
   createTopic,
   redirectToTopicPath,
+  resetUploadedState
 }) => {
   const { register, handleSubmit, errors, getValues } = useForm<TopicFormData>();
   const [isSubmitting, setIsSubmitting] = React.useState<boolean>(false);
@@ -36,6 +38,11 @@ const New: React.FC<Props> = ({
   );
 
   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
+    //loaded, and we will try to get entity immediately after pressing the button, and we will receive null
+    //going to object page on the second creation. Resetting loaded state is workaround, need to tweak loader logic
+    resetUploadedState();
+    isTopicCreated = false;
     setIsSubmitting(true);
     createTopic(clusterId, data);
   }

+ 4 - 2
kafka-ui-react-app/src/components/Topics/New/NewContainer.ts

@@ -6,6 +6,7 @@ import { createTopic } from 'redux/actions';
 import { getTopicCreated } from 'redux/reducers/topics/selectors';
 import { clusterTopicPath } from 'lib/paths';
 import { ThunkDispatch } from 'redux-thunk';
+import * as actions from "../../../redux/actions/actions";
 
 interface RouteProps {
   clusterId: string;
@@ -20,11 +21,12 @@ const mapStateToProps = (state: RootState, { match: { params: { clusterId } } }:
 
 const mapDispatchToProps = (dispatch: ThunkDispatch<RootState, undefined, Action>, { history }: OwnProps) => ({
   createTopic: (clusterId: ClusterId, form: TopicFormData) => {
-    dispatch(createTopic(clusterId, form))
+    dispatch(createTopic(clusterId, form));
   },
   redirectToTopicPath: (clusterId: ClusterId, topicName: TopicName) => {
     history.push(clusterTopicPath(clusterId, topicName));
-  }
+  },
+  resetUploadedState: (() => dispatch(actions.createTopicAction.failure()))
 });
 
 

+ 1 - 1
kafka-ui-react-app/src/redux/actions/actions.ts

@@ -50,4 +50,4 @@ export const createTopicAction = createAsyncAction(
   ActionType.POST_TOPIC__REQUEST,
   ActionType.POST_TOPIC__SUCCESS,
   ActionType.POST_TOPIC__FAILURE,
-)<undefined, undefined, undefined>();
+)<undefined, Topic, undefined>();

+ 3 - 3
kafka-ui-react-app/src/redux/actions/thunks.ts

@@ -5,7 +5,7 @@ import {
   Cluster,
   ClusterId,
   TopicFormData,
-  TopicName,
+  TopicName, Topic,
 } from 'redux/interfaces';
 
 export const fetchBrokers = (clusterId: ClusterId): PromiseThunk<void> => async (dispatch) => {
@@ -71,8 +71,8 @@ export const fetchTopicConfig = (clusterId: ClusterId, topicName: TopicName): Pr
 export const createTopic = (clusterId: ClusterId, form: TopicFormData): PromiseThunk<void> => async (dispatch) => {
   dispatch(actions.createTopicAction.request());
   try {
-    await api.postTopic(clusterId, form);
-    dispatch(actions.createTopicAction.success());
+    const topic: Topic = await api.postTopic(clusterId, form);
+    dispatch(actions.createTopicAction.success(topic));
   } catch (e) {
     dispatch(actions.createTopicAction.failure());
   }

+ 2 - 2
kafka-ui-react-app/src/redux/api/topics.ts

@@ -23,7 +23,7 @@ export const getTopics = (clusterId: ClusterId): Promise<Topic[]> =>
   fetch(`${BASE_URL}/clusters/${clusterId}/topics`, { ...BASE_PARAMS })
     .then(res => res.json());
 
-export const postTopic = (clusterId: ClusterId, form: TopicFormData): Promise<Response> => {
+export const postTopic = (clusterId: ClusterId, form: TopicFormData): Promise<Topic> => {
   const {
     name,
     partitions,
@@ -50,5 +50,5 @@ export const postTopic = (clusterId: ClusterId, form: TopicFormData): Promise<Re
     ...BASE_PARAMS,
     method: 'POST',
     body,
-  });
+  }).then(res => res.json());
 }

+ 17 - 6
kafka-ui-react-app/src/redux/reducers/topics/reducer.ts

@@ -6,15 +6,15 @@ export const initialState: TopicsState = {
   allNames: [],
 };
 
-const updateTopicList = (state: TopicsState, payload: Topic[]) => {
+const updateTopicList = (state: TopicsState, payload: Topic[]): TopicsState => {
   const initialMemo: TopicsState = {
     ...state,
     allNames: [],
-  }
+  };
 
   return payload.reduce(
     (memo: TopicsState, topic) => {
-      const { name } = topic;
+      const {name} = topic;
       memo.byName[name] = {
         ...memo.byName[name],
         ...topic,
@@ -25,7 +25,16 @@ const updateTopicList = (state: TopicsState, payload: Topic[]) => {
     },
     initialMemo,
   );
-}
+};
+
+const addToTopicList = (state: TopicsState, payload: Topic): TopicsState => {
+  const newState: TopicsState = {
+    ...state
+  };
+  newState.allNames.push(payload.name);
+  newState.byName[payload.name] = payload;
+  return newState;
+};
 
 const reducer = (state = initialState, action: Action): TopicsState => {
   switch (action.type) {
@@ -41,7 +50,7 @@ const reducer = (state = initialState, action: Action): TopicsState => {
             ...action.payload.details,
           }
         }
-      }
+      };
     case ActionType.GET_TOPIC_CONFIG__SUCCESS:
       return {
         ...state,
@@ -52,7 +61,9 @@ const reducer = (state = initialState, action: Action): TopicsState => {
             config: action.payload.config,
           }
         }
-      }
+      };
+    case ActionType.POST_TOPIC__SUCCESS:
+      return addToTopicList(state, action.payload);
     default:
       return state;
   }