Schema show page created

This commit is contained in:
Guzel Kafizova 2021-02-17 13:02:29 +03:00
parent cb32dd25ae
commit 0e05605009
17 changed files with 206 additions and 98 deletions

View file

@ -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

View file

@ -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",

View file

@ -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",

View file

@ -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>

View file

@ -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,
});

View file

@ -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;

View file

@ -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;

View file

@ -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>

View file

@ -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>
);
};

View file

@ -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;

View file

@ -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',
};

View file

@ -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,

View file

@ -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());
}

View file

@ -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[];
}

View file

@ -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
);

View file

@ -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
testSchema Normal file
View file