Create Schema Registry form (#209)
* First commit * Create Schema Form. Refactoring * Specs for Create Schema Registry form created * Update thunks.spec.ts * Update actions.spec.ts Co-authored-by: Oleg Shuralev <workshur@gmail.com>
This commit is contained in:
parent
377fa830c6
commit
44cf449a8f
31 changed files with 656 additions and 261 deletions
6
kafka-ui-react-app/package-lock.json
generated
6
kafka-ui-react-app/package-lock.json
generated
|
@ -15794,9 +15794,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"react-hook-form": {
|
||||
"version": "6.15.1",
|
||||
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-6.15.1.tgz",
|
||||
"integrity": "sha512-bL0LQuQ3OlM3JYfbacKtBPLOHhmgYz8Lj6ivMrvu2M6e1wnt4sbGRtPEPYCc/8z3WDbjrMwfAfLX92OsB65pFA=="
|
||||
"version": "6.15.4",
|
||||
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-6.15.4.tgz",
|
||||
"integrity": "sha512-K+Sw33DtTMengs8OdqFJI3glzNl1wBzSefD/ksQw/hJf9CnOHQAU6qy82eOrh0IRNt2G53sjr7qnnw1JDjvx1w=="
|
||||
},
|
||||
"react-is": {
|
||||
"version": "17.0.1",
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
"react": "^17.0.1",
|
||||
"react-datepicker": "^3.5.0",
|
||||
"react-dom": "^17.0.1",
|
||||
"react-hook-form": "^6.15.1",
|
||||
"react-hook-form": "^6.15.4",
|
||||
"react-json-tree": "^0.13.0",
|
||||
"react-multi-select-component": "^2.0.14",
|
||||
"react-redux": "^7.2.2",
|
||||
|
|
|
@ -55,14 +55,6 @@ const Details: React.FC<DetailsProps> = ({
|
|||
</div>
|
||||
</div>
|
||||
<div className="level-right">
|
||||
<button
|
||||
className="button is-primary is-small level-item"
|
||||
type="button"
|
||||
title="in development"
|
||||
disabled
|
||||
>
|
||||
Create Schema
|
||||
</button>
|
||||
<button
|
||||
className="button is-warning is-small level-item"
|
||||
type="button"
|
||||
|
|
|
@ -51,14 +51,6 @@ exports[`Details View Initial state matches snapshot 1`] = `
|
|||
<div
|
||||
className="level-right"
|
||||
>
|
||||
<button
|
||||
className="button is-primary is-small level-item"
|
||||
disabled={true}
|
||||
title="in development"
|
||||
type="button"
|
||||
>
|
||||
Create Schema
|
||||
</button>
|
||||
<button
|
||||
className="button is-warning is-small level-item"
|
||||
disabled={true}
|
||||
|
@ -165,14 +157,6 @@ exports[`Details View when page with schema versions is loading matches snapshot
|
|||
<div
|
||||
className="level-right"
|
||||
>
|
||||
<button
|
||||
className="button is-primary is-small level-item"
|
||||
disabled={true}
|
||||
title="in development"
|
||||
type="button"
|
||||
>
|
||||
Create Schema
|
||||
</button>
|
||||
<button
|
||||
className="button is-warning is-small level-item"
|
||||
disabled={true}
|
||||
|
@ -258,14 +242,6 @@ exports[`Details View when page with schema versions loaded when schema has vers
|
|||
<div
|
||||
className="level-right"
|
||||
>
|
||||
<button
|
||||
className="button is-primary is-small level-item"
|
||||
disabled={true}
|
||||
title="in development"
|
||||
type="button"
|
||||
>
|
||||
Create Schema
|
||||
</button>
|
||||
<button
|
||||
className="button is-warning is-small level-item"
|
||||
disabled={true}
|
||||
|
@ -397,14 +373,6 @@ exports[`Details View when page with schema versions loaded when versions are em
|
|||
<div
|
||||
className="level-right"
|
||||
>
|
||||
<button
|
||||
className="button is-primary is-small level-item"
|
||||
disabled={true}
|
||||
title="in development"
|
||||
type="button"
|
||||
>
|
||||
Create Schema
|
||||
</button>
|
||||
<button
|
||||
className="button is-warning is-small level-item"
|
||||
disabled={true}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import React from 'react';
|
||||
import { SchemaSubject } from 'generated-sources';
|
||||
import Breadcrumb from '../../common/Breadcrumb/Breadcrumb';
|
||||
import { NavLink, useParams } from 'react-router-dom';
|
||||
import { clusterSchemaNewPath } from 'lib/paths';
|
||||
import Breadcrumb from 'components/common/Breadcrumb/Breadcrumb';
|
||||
import ListItem from './ListItem';
|
||||
|
||||
export interface ListProps {
|
||||
|
@ -8,9 +10,24 @@ export interface ListProps {
|
|||
}
|
||||
|
||||
const List: React.FC<ListProps> = ({ schemas }) => {
|
||||
const { clusterName } = useParams<{ clusterName: string }>();
|
||||
|
||||
return (
|
||||
<div className="section">
|
||||
<Breadcrumb>Schema Registry</Breadcrumb>
|
||||
<div className="box">
|
||||
<div className="level">
|
||||
<div className="level-item level-right">
|
||||
<NavLink
|
||||
className="button is-primary"
|
||||
to={clusterSchemaNewPath(clusterName)}
|
||||
>
|
||||
Create Schema
|
||||
</NavLink>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="box">
|
||||
<table className="table is-striped is-fullwidth">
|
||||
<thead>
|
||||
|
@ -21,9 +38,15 @@ const List: React.FC<ListProps> = ({ schemas }) => {
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{schemas.map((subject) => (
|
||||
<ListItem key={subject.id} subject={subject} />
|
||||
))}
|
||||
{schemas.length > 0 ? (
|
||||
schemas.map((subject) => (
|
||||
<ListItem key={subject.id} subject={subject} />
|
||||
))
|
||||
) : (
|
||||
<tr>
|
||||
<td colSpan={10}>No schemas found</td>
|
||||
</tr>
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { connect } from 'react-redux';
|
||||
import { RootState } from 'redux/interfaces';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
import { getSchemaList } from 'redux/reducers/schemas/selectors';
|
||||
import List from './List';
|
||||
|
||||
|
@ -8,4 +7,4 @@ const mapStateToProps = (state: RootState) => ({
|
|||
schemas: getSchemaList(state),
|
||||
});
|
||||
|
||||
export default withRouter(connect(mapStateToProps)(List));
|
||||
export default connect(mapStateToProps)(List);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import React from 'react';
|
||||
import { mount, shallow } from 'enzyme';
|
||||
import { Provider } from 'react-redux';
|
||||
import { shallow } from 'enzyme';
|
||||
import { StaticRouter } from 'react-router';
|
||||
import configureStore from 'redux/store/configureStore';
|
||||
import ListContainer from '../ListContainer';
|
||||
import List, { ListProps } from '../List';
|
||||
|
@ -22,35 +23,31 @@ describe('List', () => {
|
|||
});
|
||||
|
||||
describe('View', () => {
|
||||
const pathname = `/ui/clusters/clusterName/schemas`;
|
||||
|
||||
const setupWrapper = (props: Partial<ListProps> = {}) => (
|
||||
<List schemas={[]} {...props} />
|
||||
<StaticRouter location={{ pathname }} context={{}}>
|
||||
<List schemas={[]} {...props} />
|
||||
</StaticRouter>
|
||||
);
|
||||
|
||||
describe('without schemas', () => {
|
||||
it('renders table heading without ListItem', () => {
|
||||
const wrapper = shallow(setupWrapper());
|
||||
const wrapper = mount(setupWrapper());
|
||||
expect(wrapper.exists('Breadcrumb')).toBeTruthy();
|
||||
expect(wrapper.exists('thead')).toBeTruthy();
|
||||
expect(wrapper.exists('ListItem')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('matches snapshot', () => {
|
||||
expect(shallow(setupWrapper())).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('with schemas', () => {
|
||||
const wrapper = shallow(setupWrapper({ schemas }));
|
||||
const wrapper = mount(setupWrapper({ schemas }));
|
||||
|
||||
it('renders table heading with ListItem', () => {
|
||||
expect(wrapper.exists('Breadcrumb')).toBeTruthy();
|
||||
expect(wrapper.exists('thead')).toBeTruthy();
|
||||
expect(wrapper.find('ListItem').length).toEqual(3);
|
||||
});
|
||||
|
||||
it('matches snapshot', () => {
|
||||
expect(shallow(setupWrapper({ schemas }))).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,102 +0,0 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`List View with schemas matches snapshot 1`] = `
|
||||
<div
|
||||
className="section"
|
||||
>
|
||||
<Breadcrumb>
|
||||
Schema Registry
|
||||
</Breadcrumb>
|
||||
<div
|
||||
className="box"
|
||||
>
|
||||
<table
|
||||
className="table is-striped is-fullwidth"
|
||||
>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
Schema Name
|
||||
</th>
|
||||
<th>
|
||||
Version
|
||||
</th>
|
||||
<th>
|
||||
Compatibility
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<ListItem
|
||||
key="1"
|
||||
subject={
|
||||
Object {
|
||||
"compatibilityLevel": "BACKWARD",
|
||||
"id": 1,
|
||||
"schema": "{\\"type\\":\\"record\\",\\"name\\":\\"MyRecord1\\",\\"namespace\\":\\"com.mycompany\\",\\"fields\\":[{\\"name\\":\\"id\\",\\"type\\":\\"long\\"}]}",
|
||||
"subject": "test",
|
||||
"version": "1",
|
||||
}
|
||||
}
|
||||
/>
|
||||
<ListItem
|
||||
key="1"
|
||||
subject={
|
||||
Object {
|
||||
"compatibilityLevel": "BACKWARD",
|
||||
"id": 1,
|
||||
"schema": "{\\"type\\":\\"record\\",\\"name\\":\\"MyRecord2\\",\\"namespace\\":\\"com.mycompany\\",\\"fields\\":[{\\"name\\":\\"id\\",\\"type\\":\\"long\\"}]}",
|
||||
"subject": "test2",
|
||||
"version": "1",
|
||||
}
|
||||
}
|
||||
/>
|
||||
<ListItem
|
||||
key="1"
|
||||
subject={
|
||||
Object {
|
||||
"compatibilityLevel": "BACKWARD",
|
||||
"id": 1,
|
||||
"schema": "{\\"type\\":\\"record\\",\\"name\\":\\"MyRecord3\\",\\"namespace\\":\\"com.mycompany\\",\\"fields\\":[{\\"name\\":\\"id\\",\\"type\\":\\"long\\"}]}",
|
||||
"subject": "test3",
|
||||
"version": "1",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`List View without schemas matches snapshot 1`] = `
|
||||
<div
|
||||
className="section"
|
||||
>
|
||||
<Breadcrumb>
|
||||
Schema Registry
|
||||
</Breadcrumb>
|
||||
<div
|
||||
className="box"
|
||||
>
|
||||
<table
|
||||
className="table is-striped is-fullwidth"
|
||||
>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
Schema Name
|
||||
</th>
|
||||
<th>
|
||||
Version
|
||||
</th>
|
||||
<th>
|
||||
Compatibility
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody />
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
|
@ -12,7 +12,7 @@ export const schemas: SchemaSubject[] = [
|
|||
{
|
||||
subject: 'test2',
|
||||
version: '1',
|
||||
id: 1,
|
||||
id: 2,
|
||||
schema:
|
||||
'{"type":"record","name":"MyRecord2","namespace":"com.mycompany","fields":[{"name":"id","type":"long"}]}',
|
||||
compatibilityLevel: 'BACKWARD',
|
||||
|
@ -20,7 +20,7 @@ export const schemas: SchemaSubject[] = [
|
|||
{
|
||||
subject: 'test3',
|
||||
version: '1',
|
||||
id: 1,
|
||||
id: 12,
|
||||
schema:
|
||||
'{"type":"record","name":"MyRecord3","namespace":"com.mycompany","fields":[{"name":"id","type":"long"}]}',
|
||||
compatibilityLevel: 'BACKWARD',
|
||||
|
|
115
kafka-ui-react-app/src/components/Schemas/New/New.tsx
Normal file
115
kafka-ui-react-app/src/components/Schemas/New/New.tsx
Normal file
|
@ -0,0 +1,115 @@
|
|||
import React from 'react';
|
||||
import { ClusterName, SchemaName, NewSchemaSubjectRaw } from 'redux/interfaces';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { ErrorMessage } from '@hookform/error-message';
|
||||
import Breadcrumb from 'components/common/Breadcrumb/Breadcrumb';
|
||||
import { clusterSchemaPath, clusterSchemasPath } from 'lib/paths';
|
||||
import { NewSchemaSubject } from 'generated-sources';
|
||||
import { SCHEMA_NAME_VALIDATION_PATTERN } from 'lib/constants';
|
||||
import { useHistory, useParams } from 'react-router';
|
||||
|
||||
export interface NewProps {
|
||||
createSchema: (
|
||||
clusterName: ClusterName,
|
||||
subject: SchemaName,
|
||||
newSchemaSubject: NewSchemaSubject
|
||||
) => void;
|
||||
}
|
||||
|
||||
const New: React.FC<NewProps> = ({ createSchema }) => {
|
||||
const { clusterName } = useParams<{ clusterName: string }>();
|
||||
const history = useHistory();
|
||||
const {
|
||||
register,
|
||||
errors,
|
||||
handleSubmit,
|
||||
formState: { isDirty, isSubmitting },
|
||||
} = useForm<NewSchemaSubjectRaw>();
|
||||
|
||||
const onSubmit = React.useCallback(
|
||||
async ({ subject, schema }: NewSchemaSubjectRaw) => {
|
||||
try {
|
||||
await createSchema(clusterName, subject, { schema });
|
||||
history.push(clusterSchemaPath(clusterName, subject));
|
||||
} catch (e) {
|
||||
// Show Error
|
||||
}
|
||||
},
|
||||
[clusterName]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="section">
|
||||
<div className="level">
|
||||
<div className="level-item level-left">
|
||||
<Breadcrumb
|
||||
links={[
|
||||
{
|
||||
href: clusterSchemasPath(clusterName),
|
||||
label: 'Schema Registry',
|
||||
},
|
||||
]}
|
||||
>
|
||||
New Schema
|
||||
</Breadcrumb>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="box">
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<div>
|
||||
<div className="field">
|
||||
<label className="label">Subject *</label>
|
||||
<div className="control">
|
||||
<input
|
||||
className="input"
|
||||
placeholder="Schema Name"
|
||||
ref={register({
|
||||
required: 'Topic Name is required.',
|
||||
pattern: {
|
||||
value: SCHEMA_NAME_VALIDATION_PATTERN,
|
||||
message: 'Only alphanumeric, _, -, and . allowed',
|
||||
},
|
||||
})}
|
||||
name="subject"
|
||||
autoComplete="off"
|
||||
disabled={isSubmitting}
|
||||
/>
|
||||
</div>
|
||||
<p className="help is-danger">
|
||||
<ErrorMessage errors={errors} name="subject" />
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="field">
|
||||
<label className="label">Schema *</label>
|
||||
<div className="control">
|
||||
<textarea
|
||||
className="textarea"
|
||||
ref={register}
|
||||
name="schema"
|
||||
disabled={isSubmitting}
|
||||
/>
|
||||
</div>
|
||||
<p className="help is-danger">
|
||||
<ErrorMessage errors={errors} name="schema" />
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<div className="field">
|
||||
<div className="control">
|
||||
<input
|
||||
type="submit"
|
||||
className="button is-primary"
|
||||
disabled={isSubmitting || !isDirty}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default New;
|
|
@ -0,0 +1,15 @@
|
|||
import { connect } from 'react-redux';
|
||||
import { RootState } from 'redux/interfaces';
|
||||
import { createSchema } from 'redux/actions';
|
||||
import { getSchemaCreated } from 'redux/reducers/schemas/selectors';
|
||||
import New from './New';
|
||||
|
||||
const mapStateToProps = (state: RootState) => ({
|
||||
isSchemaCreated: getSchemaCreated(state),
|
||||
});
|
||||
|
||||
const mapDispatchToProps = {
|
||||
createSchema,
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(New);
|
|
@ -0,0 +1,37 @@
|
|||
import React from 'react';
|
||||
import configureStore from 'redux/store/configureStore';
|
||||
import { mount, shallow } from 'enzyme';
|
||||
import { Provider } from 'react-redux';
|
||||
import { StaticRouter } from 'react-router-dom';
|
||||
import NewContainer from '../NewContainer';
|
||||
import New, { NewProps } from '../New';
|
||||
|
||||
describe('New', () => {
|
||||
describe('Container', () => {
|
||||
const store = configureStore();
|
||||
|
||||
it('renders view', () => {
|
||||
const component = shallow(
|
||||
<Provider store={store}>
|
||||
<NewContainer />
|
||||
</Provider>
|
||||
);
|
||||
|
||||
expect(component.exists()).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('View', () => {
|
||||
const pathname = '/ui/clusters/clusterName/schemas/new';
|
||||
|
||||
const setupWrapper = (props: Partial<NewProps> = {}) => (
|
||||
<StaticRouter location={{ pathname }} context={{}}>
|
||||
<New createSchema={jest.fn()} {...props} />
|
||||
</StaticRouter>
|
||||
);
|
||||
|
||||
it('matches snapshot', () => {
|
||||
expect(mount(setupWrapper())).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,189 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`New View matches snapshot 1`] = `
|
||||
<StaticRouter
|
||||
context={Object {}}
|
||||
location={
|
||||
Object {
|
||||
"pathname": "/ui/clusters/clusterName/schemas/new",
|
||||
}
|
||||
}
|
||||
>
|
||||
<Router
|
||||
history={
|
||||
Object {
|
||||
"action": "POP",
|
||||
"block": [Function],
|
||||
"createHref": [Function],
|
||||
"go": [Function],
|
||||
"goBack": [Function],
|
||||
"goForward": [Function],
|
||||
"listen": [Function],
|
||||
"location": Object {
|
||||
"hash": "",
|
||||
"pathname": "/ui/clusters/clusterName/schemas/new",
|
||||
"search": "",
|
||||
},
|
||||
"push": [Function],
|
||||
"replace": [Function],
|
||||
}
|
||||
}
|
||||
staticContext={Object {}}
|
||||
>
|
||||
<New
|
||||
createSchema={[MockFunction]}
|
||||
>
|
||||
<div
|
||||
className="section"
|
||||
>
|
||||
<div
|
||||
className="level"
|
||||
>
|
||||
<div
|
||||
className="level-item level-left"
|
||||
>
|
||||
<Breadcrumb
|
||||
links={
|
||||
Array [
|
||||
Object {
|
||||
"href": "/ui/clusters/undefined/schemas",
|
||||
"label": "Schema Registry",
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
<nav
|
||||
aria-label="breadcrumbs"
|
||||
className="breadcrumb"
|
||||
>
|
||||
<ul>
|
||||
<li
|
||||
key="/ui/clusters/undefined/schemas"
|
||||
>
|
||||
<NavLink
|
||||
to="/ui/clusters/undefined/schemas"
|
||||
>
|
||||
<Link
|
||||
aria-current={null}
|
||||
to={
|
||||
Object {
|
||||
"hash": "",
|
||||
"pathname": "/ui/clusters/undefined/schemas",
|
||||
"search": "",
|
||||
"state": null,
|
||||
}
|
||||
}
|
||||
>
|
||||
<LinkAnchor
|
||||
aria-current={null}
|
||||
href="/ui/clusters/undefined/schemas"
|
||||
navigate={[Function]}
|
||||
>
|
||||
<a
|
||||
aria-current={null}
|
||||
href="/ui/clusters/undefined/schemas"
|
||||
onClick={[Function]}
|
||||
>
|
||||
Schema Registry
|
||||
</a>
|
||||
</LinkAnchor>
|
||||
</Link>
|
||||
</NavLink>
|
||||
</li>
|
||||
<li
|
||||
className="is-active"
|
||||
>
|
||||
<span
|
||||
className=""
|
||||
>
|
||||
New Schema
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</Breadcrumb>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="box"
|
||||
>
|
||||
<form
|
||||
onSubmit={[Function]}
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
className="field"
|
||||
>
|
||||
<label
|
||||
className="label"
|
||||
>
|
||||
Subject *
|
||||
</label>
|
||||
<div
|
||||
className="control"
|
||||
>
|
||||
<input
|
||||
autoComplete="off"
|
||||
className="input"
|
||||
disabled={false}
|
||||
name="subject"
|
||||
placeholder="Schema Name"
|
||||
/>
|
||||
</div>
|
||||
<p
|
||||
className="help is-danger"
|
||||
>
|
||||
<Component
|
||||
errors={Object {}}
|
||||
name="subject"
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
className="field"
|
||||
>
|
||||
<label
|
||||
className="label"
|
||||
>
|
||||
Schema *
|
||||
</label>
|
||||
<div
|
||||
className="control"
|
||||
>
|
||||
<textarea
|
||||
className="textarea"
|
||||
disabled={false}
|
||||
name="schema"
|
||||
/>
|
||||
</div>
|
||||
<p
|
||||
className="help is-danger"
|
||||
>
|
||||
<Component
|
||||
errors={Object {}}
|
||||
name="schema"
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<div
|
||||
className="field"
|
||||
>
|
||||
<div
|
||||
className="control"
|
||||
>
|
||||
<input
|
||||
className="button is-primary"
|
||||
disabled={true}
|
||||
type="submit"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</New>
|
||||
</Router>
|
||||
</StaticRouter>
|
||||
`;
|
|
@ -1,43 +1,49 @@
|
|||
import React from 'react';
|
||||
import { ClusterName } from 'redux/interfaces';
|
||||
import { Switch, Route } from 'react-router-dom';
|
||||
import { Switch, Route, useParams } from 'react-router-dom';
|
||||
import PageLoader from 'components/common/PageLoader/PageLoader';
|
||||
import ListContainer from './List/ListContainer';
|
||||
import DetailsContainer from './Details/DetailsContainer';
|
||||
import NewContainer from './New/NewContainer';
|
||||
|
||||
export interface SchemasProps {
|
||||
isFetched: boolean;
|
||||
clusterName: ClusterName;
|
||||
isFetching: boolean;
|
||||
fetchSchemasByClusterName: (clusterName: ClusterName) => void;
|
||||
}
|
||||
|
||||
const Schemas: React.FC<SchemasProps> = ({
|
||||
isFetched,
|
||||
isFetching,
|
||||
fetchSchemasByClusterName,
|
||||
clusterName,
|
||||
}) => {
|
||||
const { clusterName } = useParams<{ clusterName: string }>();
|
||||
|
||||
React.useEffect(() => {
|
||||
fetchSchemasByClusterName(clusterName);
|
||||
}, [fetchSchemasByClusterName, clusterName]);
|
||||
|
||||
if (isFetched) {
|
||||
return (
|
||||
<Switch>
|
||||
<Route
|
||||
exact
|
||||
path="/ui/clusters/:clusterName/schemas"
|
||||
component={ListContainer}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path="/ui/clusters/:clusterName/schemas/:subject/latest"
|
||||
component={DetailsContainer}
|
||||
/>
|
||||
</Switch>
|
||||
);
|
||||
if (isFetching) {
|
||||
return <PageLoader />;
|
||||
}
|
||||
|
||||
return <PageLoader />;
|
||||
return (
|
||||
<Switch>
|
||||
<Route
|
||||
exact
|
||||
path="/ui/clusters/:clusterName/schemas"
|
||||
component={ListContainer}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path="/ui/clusters/:clusterName/schemas/new"
|
||||
component={NewContainer}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path="/ui/clusters/:clusterName/schemas/:subject/latest"
|
||||
component={DetailsContainer}
|
||||
/>
|
||||
</Switch>
|
||||
);
|
||||
};
|
||||
|
||||
export default Schemas;
|
||||
|
|
|
@ -1,32 +1,15 @@
|
|||
import { connect } from 'react-redux';
|
||||
import { ClusterName, RootState } from 'redux/interfaces';
|
||||
import { RootState } from 'redux/interfaces';
|
||||
import { fetchSchemasByClusterName } from 'redux/actions';
|
||||
import { getIsSchemaListFetched } from 'redux/reducers/schemas/selectors';
|
||||
import { RouteComponentProps, withRouter } from 'react-router-dom';
|
||||
import { getIsSchemaListFetching } from 'redux/reducers/schemas/selectors';
|
||||
import Schemas from './Schemas';
|
||||
|
||||
interface RouteProps {
|
||||
clusterName: ClusterName;
|
||||
}
|
||||
|
||||
type OwnProps = RouteComponentProps<RouteProps>;
|
||||
|
||||
const mapStateToProps = (
|
||||
state: RootState,
|
||||
{
|
||||
match: {
|
||||
params: { clusterName },
|
||||
},
|
||||
}: OwnProps
|
||||
) => ({
|
||||
isFetched: getIsSchemaListFetched(state),
|
||||
clusterName,
|
||||
const mapStateToProps = (state: RootState) => ({
|
||||
isFetching: getIsSchemaListFetching(state),
|
||||
});
|
||||
|
||||
const mapDispatchToProps = {
|
||||
fetchSchemasByClusterName,
|
||||
};
|
||||
|
||||
export default withRouter(
|
||||
connect(mapStateToProps, mapDispatchToProps)(Schemas)
|
||||
);
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(Schemas);
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
import React from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import { shallow } from 'enzyme';
|
||||
import { mount } from 'enzyme';
|
||||
import configureStore from 'redux/store/configureStore';
|
||||
import { StaticRouter } from 'react-router-dom';
|
||||
import { match } from 'react-router';
|
||||
import { ClusterName } from 'redux/interfaces';
|
||||
import Schemas, { SchemasProps } from '../Schemas';
|
||||
import SchemasContainer from '../SchemasContainer';
|
||||
|
||||
|
@ -15,7 +13,7 @@ describe('Schemas', () => {
|
|||
const store = configureStore();
|
||||
|
||||
it('renders view', () => {
|
||||
const component = shallow(
|
||||
const component = mount(
|
||||
<Provider store={store}>
|
||||
<StaticRouter location={{ pathname }} context={{}}>
|
||||
<SchemasContainer />
|
||||
|
@ -28,12 +26,13 @@ describe('Schemas', () => {
|
|||
|
||||
describe('View', () => {
|
||||
const setupWrapper = (props: Partial<SchemasProps> = {}) => (
|
||||
<Schemas
|
||||
isFetched
|
||||
clusterName="Test"
|
||||
fetchSchemasByClusterName={jest.fn()}
|
||||
{...props}
|
||||
/>
|
||||
<StaticRouter location={{ pathname }} context={{}}>
|
||||
<Schemas
|
||||
isFetching
|
||||
fetchSchemasByClusterName={jest.fn()}
|
||||
{...props}
|
||||
/>
|
||||
</StaticRouter>
|
||||
);
|
||||
describe('Initial state', () => {
|
||||
let useEffect: jest.SpyInstance<
|
||||
|
@ -43,7 +42,6 @@ describe('Schemas', () => {
|
|||
deps?: React.DependencyList | undefined
|
||||
]
|
||||
>;
|
||||
let wrapper;
|
||||
const mockedFn = jest.fn();
|
||||
|
||||
const mockedUseEffect = () => {
|
||||
|
@ -53,33 +51,20 @@ describe('Schemas', () => {
|
|||
beforeEach(() => {
|
||||
useEffect = jest.spyOn(React, 'useEffect');
|
||||
mockedUseEffect();
|
||||
|
||||
wrapper = shallow(
|
||||
setupWrapper({ fetchSchemasByClusterName: mockedFn })
|
||||
);
|
||||
});
|
||||
|
||||
it('should call fetchSchemasByClusterName every render', () => {
|
||||
mount(setupWrapper({ fetchSchemasByClusterName: mockedFn }));
|
||||
expect(mockedFn).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('matches snapshot', () => {
|
||||
expect(
|
||||
shallow(setupWrapper({ fetchSchemasByClusterName: mockedFn }))
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when page is loading', () => {
|
||||
const wrapper = shallow(setupWrapper({ isFetched: false }));
|
||||
const wrapper = mount(setupWrapper({ isFetching: true }));
|
||||
|
||||
it('renders PageLoader', () => {
|
||||
expect(wrapper.exists('PageLoader')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('matches snapshot', () => {
|
||||
expect(shallow(setupWrapper({ isFetched: false }))).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Schemas Container View Initial state matches snapshot 1`] = `
|
||||
<Switch>
|
||||
<Route
|
||||
component={[Function]}
|
||||
exact={true}
|
||||
path="/ui/clusters/:clusterName/schemas"
|
||||
/>
|
||||
<Route
|
||||
component={[Function]}
|
||||
exact={true}
|
||||
path="/ui/clusters/:clusterName/schemas/:subject/latest"
|
||||
/>
|
||||
</Switch>
|
||||
`;
|
||||
|
||||
exports[`Schemas Container View when page is loading matches snapshot 1`] = `<PageLoader />`;
|
|
@ -9,6 +9,7 @@ export const BASE_PARAMS: ConfigurationParameters = {
|
|||
};
|
||||
|
||||
export const TOPIC_NAME_VALIDATION_PATTERN = RegExp(/^[.,A-Za-z0-9_-]+$/);
|
||||
export const SCHEMA_NAME_VALIDATION_PATTERN = RegExp(/^[.,A-Za-z0-9_-]+$/);
|
||||
|
||||
export const MILLISECONDS_IN_WEEK = 604_800_000;
|
||||
export const MILLISECONDS_IN_DAY = 86_400_000;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { ClusterName, TopicName } from 'redux/interfaces';
|
||||
import { ClusterName, SchemaName, TopicName } from 'redux/interfaces';
|
||||
|
||||
const clusterPath = (clusterName: ClusterName) => `/ui/clusters/${clusterName}`;
|
||||
|
||||
|
@ -12,6 +12,8 @@ export const clusterConsumerGroupsPath = (clusterName: ClusterName) =>
|
|||
`${clusterPath(clusterName)}/consumer-groups`;
|
||||
export const clusterSchemasPath = (clusterName: ClusterName) =>
|
||||
`${clusterPath(clusterName)}/schemas`;
|
||||
export const clusterSchemaNewPath = (clusterName: ClusterName) =>
|
||||
`${clusterPath(clusterName)}/schemas/new`;
|
||||
|
||||
export const clusterTopicPath = (
|
||||
clusterName: ClusterName,
|
||||
|
@ -30,3 +32,8 @@ export const clusterTopicsTopicEditPath = (
|
|||
clusterName: ClusterName,
|
||||
topicName: TopicName
|
||||
) => `${clusterTopicsPath(clusterName)}/${topicName}/edit`;
|
||||
|
||||
export const clusterSchemaPath = (
|
||||
clusterName: ClusterName,
|
||||
subject: SchemaName
|
||||
) => `${clusterSchemasPath(clusterName)}/${subject}/latest`;
|
||||
|
|
|
@ -6,13 +6,13 @@ import * as actions from '../actions';
|
|||
|
||||
describe('Actions', () => {
|
||||
describe('fetchClusterStatsAction', () => {
|
||||
it('creates an REQUEST action', () => {
|
||||
it('creates a REQUEST action', () => {
|
||||
expect(actions.fetchClusterStatsAction.request()).toEqual({
|
||||
type: 'GET_CLUSTER_STATUS__REQUEST',
|
||||
});
|
||||
});
|
||||
|
||||
it('creates an SUCCESS action', () => {
|
||||
it('creates a SUCCESS action', () => {
|
||||
expect(
|
||||
actions.fetchClusterStatsAction.success({ brokerCount: 1 })
|
||||
).toEqual({
|
||||
|
@ -23,7 +23,7 @@ describe('Actions', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('creates an FAILURE action', () => {
|
||||
it('creates a FAILURE action', () => {
|
||||
expect(actions.fetchClusterStatsAction.failure()).toEqual({
|
||||
type: 'GET_CLUSTER_STATUS__FAILURE',
|
||||
});
|
||||
|
@ -75,4 +75,27 @@ describe('Actions', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('createSchemaAction', () => {
|
||||
it('creates a REQUEST action', () => {
|
||||
expect(actions.createSchemaAction.request()).toEqual({
|
||||
type: 'POST_SCHEMA__REQUEST',
|
||||
});
|
||||
});
|
||||
|
||||
it('creates a SUCCESS action', () => {
|
||||
expect(
|
||||
actions.createSchemaAction.success(schemaVersionsPayload[0])
|
||||
).toEqual({
|
||||
type: 'POST_SCHEMA__SUCCESS',
|
||||
payload: schemaVersionsPayload[0],
|
||||
});
|
||||
});
|
||||
|
||||
it('creates a FAILURE action', () => {
|
||||
expect(actions.createSchemaAction.failure()).toEqual({
|
||||
type: 'POST_SCHEMA__FAILURE',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { ClusterStats } from 'generated-sources';
|
||||
import { ClusterStats, NewSchemaSubject } from 'generated-sources';
|
||||
|
||||
export const clusterStats: ClusterStats = {
|
||||
brokerCount: 1,
|
||||
|
@ -11,3 +11,8 @@ export const clusterStats: ClusterStats = {
|
|||
underReplicatedPartitionCount: 0,
|
||||
diskUsage: [{ brokerId: 1, segmentSize: 6538, segmentCount: 6 }],
|
||||
};
|
||||
|
||||
export const schemaPayload: NewSchemaSubject = {
|
||||
schema:
|
||||
'{"type":"record","name":"MyRecord1","namespace":"com.mycompany","fields":[{"name":"id","type":"long"}]}',
|
||||
};
|
||||
|
|
|
@ -105,4 +105,36 @@ describe('Thunks', () => {
|
|||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('createSchema', () => {
|
||||
it('creates POST_SCHEMA__SUCCESS when posting new schema', async () => {
|
||||
fetchMock.postOnce(`/api/clusters/${clusterName}/schemas/${subject}`, {
|
||||
body: schemaFixtures.schemaVersionsPayload[0],
|
||||
});
|
||||
await store.dispatch(
|
||||
thunks.createSchema(clusterName, subject, fixtures.schemaPayload)
|
||||
);
|
||||
expect(store.getActions()).toEqual([
|
||||
actions.createSchemaAction.request(),
|
||||
actions.createSchemaAction.success(
|
||||
schemaFixtures.schemaVersionsPayload[0]
|
||||
),
|
||||
]);
|
||||
});
|
||||
|
||||
// it('creates POST_SCHEMA__FAILURE when posting new schema', async () => {
|
||||
// fetchMock.postOnce(
|
||||
// `/api/clusters/${clusterName}/schemas/${subject}`,
|
||||
// 404
|
||||
// );
|
||||
// await store.dispatch(
|
||||
// thunks.createSchema(clusterName, subject, fixtures.schemaPayload)
|
||||
// );
|
||||
// expect(store.getActions()).toEqual([
|
||||
// actions.createSchemaAction.request(),
|
||||
// actions.createSchemaAction.failure(),
|
||||
// ]);
|
||||
// expect(store.getActions()).toThrow();
|
||||
// });
|
||||
});
|
||||
});
|
||||
|
|
|
@ -109,3 +109,9 @@ export const fetchSchemaVersionsAction = createAsyncAction(
|
|||
'GET_SCHEMA_VERSIONS__SUCCESS',
|
||||
'GET_SCHEMA_VERSIONS__FAILURE'
|
||||
)<undefined, SchemaSubject[], undefined>();
|
||||
|
||||
export const createSchemaAction = createAsyncAction(
|
||||
'POST_SCHEMA__REQUEST',
|
||||
'POST_SCHEMA__SUCCESS',
|
||||
'POST_SCHEMA__FAILURE'
|
||||
)<undefined, SchemaSubject, undefined>();
|
||||
|
|
|
@ -5,6 +5,8 @@ import {
|
|||
Topic,
|
||||
TopicFormData,
|
||||
TopicConfig,
|
||||
NewSchemaSubject,
|
||||
SchemaSubject,
|
||||
} from 'generated-sources';
|
||||
import {
|
||||
ConsumerGroupID,
|
||||
|
@ -280,3 +282,22 @@ export const fetchSchemaVersions = (
|
|||
dispatch(actions.fetchSchemaVersionsAction.failure());
|
||||
}
|
||||
};
|
||||
|
||||
export const createSchema = (
|
||||
clusterName: ClusterName,
|
||||
subject: SchemaName,
|
||||
newSchemaSubject: NewSchemaSubject
|
||||
): PromiseThunkResult => async (dispatch) => {
|
||||
dispatch(actions.createSchemaAction.request());
|
||||
try {
|
||||
const schema: SchemaSubject = await apiClient.createNewSchema({
|
||||
clusterName,
|
||||
subject,
|
||||
newSchemaSubject,
|
||||
});
|
||||
dispatch(actions.createSchemaAction.success(schema));
|
||||
} catch (e) {
|
||||
dispatch(actions.createSchemaAction.failure());
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { SchemaSubject } from 'generated-sources';
|
||||
import { NewSchemaSubject, SchemaSubject } from 'generated-sources';
|
||||
|
||||
export type SchemaName = string;
|
||||
|
||||
|
@ -7,3 +7,7 @@ export interface SchemasState {
|
|||
allNames: SchemaName[];
|
||||
currentSchemaVersions: SchemaSubject[];
|
||||
}
|
||||
|
||||
export interface NewSchemaSubjectRaw extends NewSchemaSubject {
|
||||
subject: string;
|
||||
}
|
||||
|
|
|
@ -56,3 +56,21 @@ Object {
|
|||
],
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Schemas reducer reacts on POST_SCHEMA__SUCCESS and returns payload 1`] = `
|
||||
Object {
|
||||
"allNames": Array [
|
||||
"test",
|
||||
],
|
||||
"byName": Object {
|
||||
"test": Object {
|
||||
"compatibilityLevel": "BACKWARD",
|
||||
"id": 1,
|
||||
"schema": "{\\"type\\":\\"record\\",\\"name\\":\\"MyRecord1\\",\\"namespace\\":\\"com.mycompany\\",\\"fields\\":[{\\"name\\":\\"id\\",\\"type\\":\\"long\\"}]}",
|
||||
"subject": "test",
|
||||
"version": "1",
|
||||
},
|
||||
},
|
||||
"currentSchemaVersions": Array [],
|
||||
}
|
||||
`;
|
||||
|
|
|
@ -52,3 +52,47 @@ export const schemaVersionsPayload: SchemaSubject[] = [
|
|||
compatibilityLevel: 'BACKWARD',
|
||||
},
|
||||
];
|
||||
|
||||
export const newSchemaPayload: SchemaSubject = {
|
||||
subject: 'test4',
|
||||
version: '2',
|
||||
id: 2,
|
||||
schema:
|
||||
'{"type":"record","name":"MyRecord4","namespace":"com.mycompany","fields":[{"name":"id","type":"long"}]}',
|
||||
compatibilityLevel: 'BACKWARD',
|
||||
};
|
||||
|
||||
export const clusterSchemasPayloadWithNewSchema: SchemaSubject[] = [
|
||||
{
|
||||
subject: 'test2',
|
||||
version: '3',
|
||||
id: 4,
|
||||
schema:
|
||||
'{"type":"record","name":"MyRecord4","namespace":"com.mycompany","fields":[{"name":"id","type":"long"}]}',
|
||||
compatibilityLevel: 'BACKWARD',
|
||||
},
|
||||
{
|
||||
subject: 'test3',
|
||||
version: '1',
|
||||
id: 5,
|
||||
schema:
|
||||
'{"type":"record","name":"MyRecord","namespace":"com.mycompany","fields":[{"name":"id","type":"long"}]}',
|
||||
compatibilityLevel: 'BACKWARD',
|
||||
},
|
||||
{
|
||||
subject: 'test',
|
||||
version: '2',
|
||||
id: 2,
|
||||
schema:
|
||||
'{"type":"record","name":"MyRecord2","namespace":"com.mycompany","fields":[{"name":"id","type":"long"}]}',
|
||||
compatibilityLevel: 'BACKWARD',
|
||||
},
|
||||
{
|
||||
subject: 'test4',
|
||||
version: '2',
|
||||
id: 2,
|
||||
schema:
|
||||
'{"type":"record","name":"MyRecord4","namespace":"com.mycompany","fields":[{"name":"id","type":"long"}]}',
|
||||
compatibilityLevel: 'BACKWARD',
|
||||
},
|
||||
];
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import {
|
||||
createSchemaAction,
|
||||
fetchSchemasByClusterNameAction,
|
||||
fetchSchemaVersionsAction,
|
||||
} from 'redux/actions';
|
||||
|
@ -17,6 +18,9 @@ describe('Schemas reducer', () => {
|
|||
expect(reducer(undefined, fetchSchemaVersionsAction.request())).toEqual(
|
||||
initialState
|
||||
);
|
||||
expect(reducer(undefined, createSchemaAction.request())).toEqual(
|
||||
initialState
|
||||
);
|
||||
});
|
||||
|
||||
it('reacts on GET_CLUSTER_SCHEMAS__SUCCESS and returns payload', () => {
|
||||
|
@ -36,4 +40,10 @@ describe('Schemas reducer', () => {
|
|||
)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('reacts on POST_SCHEMA__SUCCESS and returns payload', () => {
|
||||
expect(
|
||||
reducer(undefined, createSchemaAction.success(schemaVersionsPayload[0]))
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,10 +1,16 @@
|
|||
import {
|
||||
createSchemaAction,
|
||||
fetchSchemasByClusterNameAction,
|
||||
fetchSchemaVersionsAction,
|
||||
} from 'redux/actions';
|
||||
import configureStore from 'redux/store/configureStore';
|
||||
import * as selectors from '../selectors';
|
||||
import { clusterSchemasPayload, schemaVersionsPayload } from './fixtures';
|
||||
import {
|
||||
clusterSchemasPayload,
|
||||
clusterSchemasPayloadWithNewSchema,
|
||||
newSchemaPayload,
|
||||
schemaVersionsPayload,
|
||||
} from './fixtures';
|
||||
|
||||
const store = configureStore();
|
||||
|
||||
|
@ -13,6 +19,7 @@ describe('Schemas selectors', () => {
|
|||
it('returns fetch status', () => {
|
||||
expect(selectors.getIsSchemaListFetched(store.getState())).toBeFalsy();
|
||||
expect(selectors.getIsSchemaVersionFetched(store.getState())).toBeFalsy();
|
||||
expect(selectors.getSchemaCreated(store.getState())).toBeFalsy();
|
||||
});
|
||||
|
||||
it('returns schema list', () => {
|
||||
|
@ -34,6 +41,7 @@ describe('Schemas selectors', () => {
|
|||
fetchSchemasByClusterNameAction.success(clusterSchemasPayload)
|
||||
);
|
||||
store.dispatch(fetchSchemaVersionsAction.success(schemaVersionsPayload));
|
||||
store.dispatch(createSchemaAction.success(newSchemaPayload));
|
||||
});
|
||||
|
||||
it('returns fetch status', () => {
|
||||
|
@ -41,11 +49,12 @@ describe('Schemas selectors', () => {
|
|||
expect(
|
||||
selectors.getIsSchemaVersionFetched(store.getState())
|
||||
).toBeTruthy();
|
||||
expect(selectors.getSchemaCreated(store.getState())).toBeTruthy();
|
||||
});
|
||||
|
||||
it('returns schema list', () => {
|
||||
expect(selectors.getSchemaList(store.getState())).toEqual(
|
||||
clusterSchemasPayload
|
||||
clusterSchemasPayloadWithNewSchema
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
@ -32,12 +32,26 @@ const updateSchemaList = (
|
|||
}, initialMemo);
|
||||
};
|
||||
|
||||
const addToSchemaList = (
|
||||
state: SchemasState,
|
||||
payload: SchemaSubject
|
||||
): SchemasState => {
|
||||
const newState: SchemasState = {
|
||||
...state,
|
||||
};
|
||||
newState.allNames.push(payload.subject as string);
|
||||
newState.byName[payload.subject as string] = { ...payload };
|
||||
return newState;
|
||||
};
|
||||
|
||||
const reducer = (state = initialState, action: Action): SchemasState => {
|
||||
switch (action.type) {
|
||||
case 'GET_CLUSTER_SCHEMAS__SUCCESS':
|
||||
return updateSchemaList(state, action.payload);
|
||||
case 'GET_SCHEMA_VERSIONS__SUCCESS':
|
||||
return { ...state, currentSchemaVersions: action.payload };
|
||||
case 'POST_SCHEMA__SUCCESS':
|
||||
return addToSchemaList(state, action.payload);
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
|
@ -15,16 +15,28 @@ const getSchemaVersionsFetchingStatus = createFetchingSelector(
|
|||
'GET_SCHEMA_VERSIONS'
|
||||
);
|
||||
|
||||
const getSchemaCreationStatus = createFetchingSelector('POST_SCHEMA');
|
||||
|
||||
export const getIsSchemaListFetched = createSelector(
|
||||
getSchemaListFetchingStatus,
|
||||
(status) => status === 'fetched'
|
||||
);
|
||||
|
||||
export const getIsSchemaListFetching = createSelector(
|
||||
getSchemaListFetchingStatus,
|
||||
(status) => status === 'fetching' || status === 'notFetched'
|
||||
);
|
||||
|
||||
export const getIsSchemaVersionFetched = createSelector(
|
||||
getSchemaVersionsFetchingStatus,
|
||||
(status) => status === 'fetched'
|
||||
);
|
||||
|
||||
export const getSchemaCreated = createSelector(
|
||||
getSchemaCreationStatus,
|
||||
(status) => status === 'fetched'
|
||||
);
|
||||
|
||||
export const getSchemaList = createSelector(
|
||||
getIsSchemaListFetched,
|
||||
getAllNames,
|
||||
|
|
Loading…
Add table
Reference in a new issue