Browse Source

Schema show page created

Guzel Kafizova 4 years ago
parent
commit
0e05605009

+ 2 - 0
docker/kafka-ui.yaml

@@ -30,6 +30,8 @@ services:
     environment:
       ZOOKEEPER_CLIENT_PORT: 2181
       ZOOKEEPER_TICK_TIME: 2000
+    ports:
+      - 2181:2181
 
   kafka0:
     image: confluentinc/cp-kafka:5.1.0

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

@@ -4813,9 +4813,9 @@
       "dev": true
     },
     "bulma": {
-      "version": "0.8.2",
-      "resolved": "https://registry.npmjs.org/bulma/-/bulma-0.8.2.tgz",
-      "integrity": "sha512-vMM/ijYSxX+Sm+nD7Lmc1UgWDy2JcL2nTKqwgEqXuOMU+IGALbXd5MLt/BcjBAPLIx36TtzhzBcSnOP974gcqA=="
+      "version": "0.9.2",
+      "resolved": "https://registry.npmjs.org/bulma/-/bulma-0.9.2.tgz",
+      "integrity": "sha512-e14EF+3VSZ488yL/lJH0tR8mFWiEQVCMi/BQUMi2TGMBOk+zrDg4wryuwm/+dRSHJw0gMawp2tsW7X1JYUCE3A=="
     },
     "bulma-switch": {
       "version": "2.0.0",
@@ -11528,6 +11528,11 @@
       "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
       "dev": true
     },
+    "json-formatter-js": {
+      "version": "2.3.4",
+      "resolved": "https://registry.npmjs.org/json-formatter-js/-/json-formatter-js-2.3.4.tgz",
+      "integrity": "sha512-gmAzYRtPRmYzeAT4T7+t3NhTF89JOAIioCVDddl9YDb3ls3kWcskirafw/MZGJaRhEU6fRimGJHl7CC7gaAI2Q=="
+    },
     "json-parse-better-errors": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",

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

@@ -5,13 +5,14 @@
   "dependencies": {
     "@types/react-datepicker": "^3.1.1",
     "@types/uuid": "^8.3.0",
-    "bulma": "^0.8.2",
+    "bulma": "^0.9.2",
     "bulma-switch": "^2.0.0",
     "classnames": "^2.2.6",
     "date-fns": "^2.16.1",
     "eslint-import-resolver-node": "^0.3.4",
     "eslint-import-resolver-typescript": "^2.3.0",
     "immer": "^8.0.1",
+    "json-formatter-js": "^2.3.4",
     "lodash": "^4.17.20",
     "pretty-ms": "^6.0.1",
     "react": "^17.0.1",

+ 81 - 24
kafka-ui-react-app/src/components/Schemas/Details/Details.tsx

@@ -1,31 +1,32 @@
 import { SchemaSubject } from 'generated-sources';
 import React from 'react';
-import { ClusterName } from 'redux/interfaces';
+import { ClusterName, SchemaName } from 'redux/interfaces';
+import JSONViewer from 'components/common/JSONViewer/JSONViewer';
 import Breadcrumb from '../../common/Breadcrumb/Breadcrumb';
-import DetailsItem from './DetailsItem';
 import { clusterSchemasPath } from '../../../lib/paths';
+import SchemaVersion from './SchemaVersion';
 
 interface DetailsProps {
   schema: SchemaSubject;
   clusterName: ClusterName;
-  schemaName: SchemaSubject['subject'];
-  versions: string;
+  subject: SchemaName;
+  versions: SchemaSubject[];
   fetchSchemaVersions: (
     clusterName: ClusterName,
-    schemaName: SchemaSubject['subject']
+    schemaName: SchemaName
   ) => void;
 }
 
 const Details: React.FC<DetailsProps> = ({
   schema,
   clusterName,
-  versions,
   fetchSchemaVersions,
-  schemaName,
+  subject,
+  versions,
 }) => {
   React.useEffect(() => {
-    fetchSchemaVersions(clusterName, schemaName);
-  }, [fetchSchemaVersions, clusterName, schemaName]);
+    fetchSchemaVersions(clusterName, subject);
+  }, [fetchSchemaVersions, clusterName]);
 
   return (
     <div className="section">
@@ -44,28 +45,84 @@ const Details: React.FC<DetailsProps> = ({
         </div>
       </div>
       <div className="box">
-        <table className="table is-striped is-fullwidth">
-          <thead>
-            <tr>
-              <th>Latest Version</th>
-            </tr>
-          </thead>
-          <tbody>
-            <DetailsItem schema={schema} />
-          </tbody>
-        </table>
+        <div className="level-left">
+          <div className="level-item">
+            <div className="mr-1">
+              <b>Latest Version</b>
+            </div>
+            <div className="tag is-info is-light" title="Version">
+              #{schema.version}
+            </div>
+          </div>
+          <button
+            className="button is-info is-small level-item"
+            type="button"
+            title="work in progress"
+            disabled
+          >
+            Create Schema
+          </button>
+          <button
+            className="button is-warning is-small level-item"
+            type="button"
+            title="work in progress"
+            disabled
+          >
+            Update Schema
+          </button>
+          <button
+            className="button is-danger is-small level-item"
+            type="button"
+            title="work in progress"
+            disabled
+          >
+            Delete
+          </button>
+        </div>
+        <div className="tile is-ancestor mt-1">
+          <div className="tile is-4 is-parent">
+            <div className="tile is-child">
+              <table className="table is-fullwidth">
+                <tbody>
+                  <tr>
+                    <td>ID</td>
+                    <td>{schema.id}</td>
+                  </tr>
+                  <tr>
+                    <td>Subject</td>
+                    <td>{schema.subject}</td>
+                  </tr>
+                  <tr>
+                    <td>Compatibility</td>
+                    <td>{schema.compatibilityLevel}</td>
+                  </tr>
+                </tbody>
+              </table>
+            </div>
+          </div>
+
+          <div className="tile is-parent">
+            <div className="tile is-child box py-1">
+              <JSONViewer data={JSON.parse(schema.schema as string)} />
+            </div>
+          </div>
+        </div>
       </div>
       <div className="box">
-        <table className="table is-striped is-fullwidth">
+        <table className="table is-fullwidth">
           <thead>
             <tr>
-              <th>Versions</th>
+              <th>Version</th>
+              <th>ID</th>
+              <th>Schema</th>
             </tr>
           </thead>
           <tbody>
-            <tr>
-              <td>{versions}</td>
-            </tr>
+            {versions
+              .sort((a: SchemaSubject, b: SchemaSubject) => a.id - b.id)
+              .map((version) => (
+                <SchemaVersion key={version.id} version={version} />
+              ))}
           </tbody>
         </table>
       </div>

+ 4 - 7
kafka-ui-react-app/src/components/Schemas/Details/DetailsContainer.ts

@@ -1,12 +1,9 @@
 import { connect } from 'react-redux';
-import { ClusterName, RootState, TopicName } from 'redux/interfaces';
+import { ClusterName, RootState } from 'redux/interfaces';
 import { RouteComponentProps, withRouter } from 'react-router-dom';
-import { getSchema, getSchemaList } from 'redux/reducers/schemas/selectors';
+import { getSchema, getSchemaVersions } from 'redux/reducers/schemas/selectors';
 import Details from './Details';
-import {
-  fetchSchemasByClusterName,
-  fetchSchemaVersions,
-} from '../../../redux/actions';
+import { fetchSchemaVersions } from '../../../redux/actions';
 
 interface RouteProps {
   clusterName: ClusterName;
@@ -24,7 +21,7 @@ const mapStateToProps = (
   }: OwnProps
 ) => ({
   schema: getSchema(state, subject),
-  // versions: getSchemasVersions(state, subject),
+  versions: getSchemaVersions(state),
   clusterName,
   subject,
 });

+ 0 - 16
kafka-ui-react-app/src/components/Schemas/Details/DetailsItem.tsx

@@ -1,16 +0,0 @@
-import React from 'react';
-import { SchemaSubject } from 'generated-sources';
-
-interface DetailsItemProps {
-  schema: SchemaSubject;
-}
-
-const DetailsItem: React.FC<DetailsItemProps> = ({ schema }) => {
-  return (
-    <tr>
-      <td>{JSON.stringify(schema)}</td>
-    </tr>
-  );
-};
-
-export default DetailsItem;

+ 44 - 0
kafka-ui-react-app/src/components/Schemas/Details/SchemaVersion.tsx

@@ -0,0 +1,44 @@
+import React from 'react';
+import { SchemaSubject } from 'generated-sources';
+import JSONTree from 'react-json-tree';
+
+interface SchemaVersionProps {
+  version: SchemaSubject;
+}
+
+const SchemaVersion: React.FC<SchemaVersionProps> = ({
+  version: { version, id, schema },
+}) => {
+  const themeJsonTree = {
+    scheme: 'google',
+    author: 'seth wright (http://sethawright.com)',
+    base00: '#1d1f21',
+    base01: '#282a2e',
+    base02: '#373b41',
+    base03: '#969896',
+    base04: '#b4b7b4',
+    base05: '#c5c8c6',
+    base06: '#e0e0e0',
+    base07: '#ffffff',
+    base08: '#CC342B',
+    base09: '#F96A38',
+    base0A: '#FBA922',
+    base0B: '#198844',
+    base0C: '#3971ED',
+    base0D: '#3971ED',
+    base0E: '#A36AC7',
+    base0F: '#3971ED',
+  };
+
+  return (
+    <tr>
+      <td>{version}</td>
+      <td>{id}</td>
+      <td className="py-0">
+        <JSONTree data={JSON.parse(schema as string)} theme={themeJsonTree} />
+      </td>
+    </tr>
+  );
+};
+
+export default SchemaVersion;

+ 4 - 2
kafka-ui-react-app/src/components/Schemas/List/List.tsx

@@ -16,11 +16,13 @@ const List: React.FC<ListProps> = ({ schemas }) => {
           <thead>
             <tr>
               <th>Schema Name</th>
+              <th>Version</th>
+              <th>Compatibility</th>
             </tr>
           </thead>
           <tbody>
-            {schemas.map(({ subject }) => (
-              <ListItem subject={subject} />
+            {schemas.map((subject) => (
+              <ListItem key={subject.id} subject={subject} />
             ))}
           </tbody>
         </table>

+ 9 - 2
kafka-ui-react-app/src/components/Schemas/List/ListItem.tsx

@@ -1,11 +1,14 @@
+import { SchemaSubject } from 'generated-sources';
 import React from 'react';
 import { NavLink } from 'react-router-dom';
 
 interface ListItemProps {
-  subject?: string;
+  subject: SchemaSubject;
 }
 
-const ListItem: React.FC<ListItemProps> = ({ subject }) => {
+const ListItem: React.FC<ListItemProps> = ({
+  subject: { subject, version, compatibilityLevel },
+}) => {
   return (
     <tr>
       <td>
@@ -18,6 +21,10 @@ const ListItem: React.FC<ListItemProps> = ({ subject }) => {
           {subject}
         </NavLink>
       </td>
+      <td>{version}</td>
+      <td>
+        <span className="tag is-link">{compatibilityLevel}</span>
+      </td>
     </tr>
   );
 };

+ 15 - 0
kafka-ui-react-app/src/components/common/JSONViewer/JSONViewer.tsx

@@ -0,0 +1,15 @@
+import React from 'react';
+import JSONTree from 'react-json-tree';
+import theme from './themes/grayscale';
+
+interface JSONViewerProps {
+  data: {
+    [key: string]: string;
+  };
+}
+
+const JSONViewer: React.FC<JSONViewerProps> = ({ data }) => (
+  <JSONTree data={data} theme={theme} invertTheme={false} hideRoot />
+);
+
+export default JSONViewer;

+ 20 - 0
kafka-ui-react-app/src/components/common/JSONViewer/themes/grayscale.ts

@@ -0,0 +1,20 @@
+export default {
+  scheme: 'grayscale',
+  author: 'alexandre gavioli (https://github.com/alexx2/)',
+  base00: '#101010',
+  base01: '#252525',
+  base02: '#464646',
+  base03: '#525252',
+  base04: '#ababab',
+  base05: '#b9b9b9',
+  base06: '#e3e3e3',
+  base07: '#f7f7f7',
+  base08: '#7c7c7c',
+  base09: '#999999',
+  base0A: '#a0a0a0',
+  base0B: '#8e8e8e',
+  base0C: '#868686',
+  base0D: '#686868',
+  base0E: '#747474',
+  base0F: '#5e5e5e',
+};

+ 0 - 5
kafka-ui-react-app/src/lib/paths.ts

@@ -1,5 +1,4 @@
 import { ClusterName, TopicName } from 'redux/interfaces';
-import { SchemaSubject } from '../generated-sources';
 
 const clusterPath = (clusterName: ClusterName) => `/ui/clusters/${clusterName}`;
 
@@ -13,10 +12,6 @@ export const clusterConsumerGroupsPath = (clusterName: ClusterName) =>
   `${clusterPath(clusterName)}/consumer-groups`;
 export const clusterSchemasPath = (clusterName: ClusterName) =>
   `${clusterPath(clusterName)}/schemas`;
-// export const clusterSchemaPath = (
-//   clusterName: ClusterName,
-//   schemaName: string
-// ) => `${clusterSchemaPath(clusterName)}/${schemaName}/latest`;
 
 export const clusterTopicPath = (
   clusterName: ClusterName,

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

@@ -5,7 +5,6 @@ import {
   Topic,
   TopicFormData,
   TopicConfig,
-  SchemaSubject,
 } from 'generated-sources';
 import {
   ConsumerGroupID,
@@ -16,10 +15,10 @@ import {
   TopicMessageQueryParams,
   TopicFormFormattedParams,
   TopicFormDataRaw,
+  SchemaName,
 } from 'redux/interfaces';
 
 import { BASE_PARAMS } from 'lib/constants';
-import { flatten } from 'lodash';
 import * as actions from './actions';
 
 const apiClientConf = new Configuration(BASE_PARAMS);
@@ -258,16 +257,8 @@ export const fetchSchemasByClusterName = (
 ): PromiseThunk<void> => async (dispatch) => {
   dispatch(actions.fetchSchemasByClusterNameAction.request());
   try {
-    const schemaNames = await apiClient.getSchemas({ clusterName });
-
-    // TODO: Remove me after API refactoring
-    const schemas: SchemaSubject[][] = await Promise.all(
-      schemaNames.map((schemaName) =>
-        apiClient.getLatestSchema({ clusterName, schemaName })
-      )
-    );
-
-    dispatch(actions.fetchSchemasByClusterNameAction.success(flatten(schemas)));
+    const schemas = await apiClient.getSchemas({ clusterName });
+    dispatch(actions.fetchSchemasByClusterNameAction.success(schemas));
   } catch (e) {
     dispatch(actions.fetchSchemasByClusterNameAction.failure());
   }
@@ -275,28 +266,17 @@ export const fetchSchemasByClusterName = (
 
 export const fetchSchemaVersions = (
   clusterName: ClusterName,
-  schemaName: SchemaSubject['subject']
+  subject: SchemaName
   // eslint-disable-next-line consistent-return
 ): PromiseThunk<void> => async (dispatch) => {
-  if (!schemaName) return Promise.resolve();
+  if (!subject) return Promise.resolve();
   dispatch(actions.fetchSchemaVersionsAction.request());
   try {
-    const versionIds = await apiClient.getSchemaVersions({
+    const versions = await apiClient.getAllVersionsBySubject({
       clusterName,
-      schemaName,
+      subject,
     });
-
-    console.log(versionIds);
-
-    const versions: SchemaSubject[][] = await Promise.all(
-      versionIds.map((version) =>
-        apiClient.getSchemaByVersion({ clusterName, schemaName, version })
-      )
-    );
-
-    console.log(versions);
-
-    dispatch(actions.fetchSchemaVersionsAction.success(flatten(versions)));
+    dispatch(actions.fetchSchemaVersionsAction.success(versions));
   } catch (e) {
     dispatch(actions.fetchSchemaVersionsAction.failure());
   }

+ 3 - 1
kafka-ui-react-app/src/redux/interfaces/schema.ts

@@ -1,7 +1,9 @@
 import { SchemaSubject } from 'generated-sources';
 
+export type SchemaName = SchemaSubject['subject'];
+
 export interface SchemasState {
   byName: { [subject: string]: SchemaSubject };
-  allNames: string[];
+  allNames: SchemaName[];
   currentSchemaVersions: SchemaSubject[];
 }

+ 5 - 9
kafka-ui-react-app/src/redux/reducers/schemas/selectors.ts

@@ -6,7 +6,6 @@ const schemasState = ({ schemas }: RootState): SchemasState => schemas;
 
 const getAllNames = (state: RootState) => schemasState(state).allNames;
 const getSchemaMap = (state: RootState) => schemasState(state).byName;
-// const getSchemaVersion = (state: RootState) => schemasState(state).versions;
 
 const getSchemaListFetchingStatus = createFetchingSelector(
   'GET_CLUSTER_SCHEMAS'
@@ -25,7 +24,7 @@ export const getSchemaList = createSelector(
     if (!isFetched) {
       return [];
     }
-    return allNames.map((subject) => byName[subject]);
+    return allNames.map((subject) => byName[subject as string]);
   }
 );
 
@@ -37,10 +36,7 @@ export const getSchema = createSelector(
   (schemas, subject) => schemas[subject]
 );
 
-// export const getSchemasVersions = createSelector(
-//   getSchemaVersion,
-//   getSchema,
-//   (versions, subject) => {
-//     return versions.map((version) => subject['version'])
-//   }
-// );
+export const getSchemaVersions = createSelector(
+  schemasState,
+  ({ currentSchemaVersions }) => currentSchemaVersions
+);

+ 1 - 0
kafka-ui-react-app/src/theme/bulma_overrides.scss

@@ -2,6 +2,7 @@
 @import "../../node_modules/bulma/sass/base/_all.sass";
 @import "../../node_modules/bulma/sass/elements/_all.sass";
 @import "../../node_modules/bulma/sass/form/_all.sass";
+@import "../../node_modules/bulma/sass/helpers/_all.sass";
 @import "../../node_modules/bulma/sass/components/_all.sass";
 @import "../../node_modules/bulma/sass/grid/_all.sass";
 @import "../../node_modules/bulma/sass/layout/_all.sass";

+ 0 - 0
testSchema