Implement deleting schemas (#366)
This commit is contained in:
parent
d471759b79
commit
3945845c37
10 changed files with 134 additions and 6 deletions
|
@ -3,6 +3,7 @@ import { SchemaSubject } from 'generated-sources';
|
||||||
import { ClusterName, SchemaName } from 'redux/interfaces';
|
import { ClusterName, SchemaName } from 'redux/interfaces';
|
||||||
import { clusterSchemasPath } from 'lib/paths';
|
import { clusterSchemasPath } from 'lib/paths';
|
||||||
import ClusterContext from 'components/contexts/ClusterContext';
|
import ClusterContext from 'components/contexts/ClusterContext';
|
||||||
|
import { useHistory } from 'react-router';
|
||||||
import Breadcrumb from '../../common/Breadcrumb/Breadcrumb';
|
import Breadcrumb from '../../common/Breadcrumb/Breadcrumb';
|
||||||
import SchemaVersion from './SchemaVersion';
|
import SchemaVersion from './SchemaVersion';
|
||||||
import LatestVersionItem from './LatestVersionItem';
|
import LatestVersionItem from './LatestVersionItem';
|
||||||
|
@ -18,6 +19,7 @@ export interface DetailsProps {
|
||||||
clusterName: ClusterName,
|
clusterName: ClusterName,
|
||||||
schemaName: SchemaName
|
schemaName: SchemaName
|
||||||
) => void;
|
) => void;
|
||||||
|
deleteSchema: (clusterName: ClusterName, subject: string) => Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Details: React.FC<DetailsProps> = ({
|
const Details: React.FC<DetailsProps> = ({
|
||||||
|
@ -25,6 +27,7 @@ const Details: React.FC<DetailsProps> = ({
|
||||||
schema,
|
schema,
|
||||||
clusterName,
|
clusterName,
|
||||||
fetchSchemaVersions,
|
fetchSchemaVersions,
|
||||||
|
deleteSchema,
|
||||||
versions,
|
versions,
|
||||||
isFetched,
|
isFetched,
|
||||||
}) => {
|
}) => {
|
||||||
|
@ -33,6 +36,12 @@ const Details: React.FC<DetailsProps> = ({
|
||||||
fetchSchemaVersions(clusterName, subject);
|
fetchSchemaVersions(clusterName, subject);
|
||||||
}, [fetchSchemaVersions, clusterName]);
|
}, [fetchSchemaVersions, clusterName]);
|
||||||
|
|
||||||
|
const history = useHistory();
|
||||||
|
const onDelete = async () => {
|
||||||
|
await deleteSchema(clusterName, subject);
|
||||||
|
history.push(clusterSchemasPath(clusterName));
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="section">
|
<div className="section">
|
||||||
<div className="level">
|
<div className="level">
|
||||||
|
@ -75,7 +84,7 @@ const Details: React.FC<DetailsProps> = ({
|
||||||
className="button is-danger is-small level-item"
|
className="button is-danger is-small level-item"
|
||||||
type="button"
|
type="button"
|
||||||
title="in development"
|
title="in development"
|
||||||
disabled
|
onClick={onDelete}
|
||||||
>
|
>
|
||||||
Delete
|
Delete
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -6,7 +6,7 @@ import {
|
||||||
getSchema,
|
getSchema,
|
||||||
getSortedSchemaVersions,
|
getSortedSchemaVersions,
|
||||||
} from 'redux/reducers/schemas/selectors';
|
} from 'redux/reducers/schemas/selectors';
|
||||||
import { fetchSchemaVersions } from 'redux/actions';
|
import { fetchSchemaVersions, deleteSchema } from 'redux/actions';
|
||||||
import Details from './Details';
|
import Details from './Details';
|
||||||
|
|
||||||
interface RouteProps {
|
interface RouteProps {
|
||||||
|
@ -33,6 +33,7 @@ const mapStateToProps = (
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
const mapDispatchToProps = {
|
||||||
fetchSchemaVersions,
|
fetchSchemaVersions,
|
||||||
|
deleteSchema,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default withRouter(
|
export default withRouter(
|
||||||
|
|
|
@ -30,6 +30,7 @@ describe('Details', () => {
|
||||||
schema={schema}
|
schema={schema}
|
||||||
clusterName="Test cluster"
|
clusterName="Test cluster"
|
||||||
fetchSchemaVersions={jest.fn()}
|
fetchSchemaVersions={jest.fn()}
|
||||||
|
deleteSchema={jest.fn()}
|
||||||
isFetched
|
isFetched
|
||||||
versions={[]}
|
versions={[]}
|
||||||
{...props}
|
{...props}
|
||||||
|
@ -100,6 +101,17 @@ describe('Details', () => {
|
||||||
expect(wrapper.find('SchemaVersion').length).toEqual(2);
|
expect(wrapper.find('SchemaVersion').length).toEqual(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('calls deleteSchema on button click', () => {
|
||||||
|
const mockDelete = jest.fn();
|
||||||
|
const component = mount(
|
||||||
|
<StaticRouter>
|
||||||
|
{setupWrapper({ versions, deleteSchema: mockDelete })}
|
||||||
|
</StaticRouter>
|
||||||
|
);
|
||||||
|
component.find('button').at(1).simulate('click');
|
||||||
|
expect(mockDelete).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
it('matches snapshot', () => {
|
it('matches snapshot', () => {
|
||||||
expect(shallow(setupWrapper({ versions }))).toMatchSnapshot();
|
expect(shallow(setupWrapper({ versions }))).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
|
@ -61,7 +61,7 @@ exports[`Details View Initial state matches snapshot 1`] = `
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className="button is-danger is-small level-item"
|
className="button is-danger is-small level-item"
|
||||||
disabled={true}
|
onClick={[Function]}
|
||||||
title="in development"
|
title="in development"
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
|
@ -196,7 +196,7 @@ exports[`Details View when page with schema versions loaded when schema has vers
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className="button is-danger is-small level-item"
|
className="button is-danger is-small level-item"
|
||||||
disabled={true}
|
onClick={[Function]}
|
||||||
title="in development"
|
title="in development"
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
|
@ -334,7 +334,7 @@ exports[`Details View when page with schema versions loaded when versions are em
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className="button is-danger is-small level-item"
|
className="button is-danger is-small level-item"
|
||||||
disabled={true}
|
onClick={[Function]}
|
||||||
title="in development"
|
title="in development"
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
|
|
|
@ -123,4 +123,37 @@ describe('Thunks', () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('deleteSchema', () => {
|
||||||
|
it('fires DELETE_SCHEMA__SUCCESS on success', async () => {
|
||||||
|
fetchMock.deleteOnce(
|
||||||
|
`/api/clusters/${clusterName}/schemas/${subject}`,
|
||||||
|
200
|
||||||
|
);
|
||||||
|
|
||||||
|
await store.dispatch(thunks.deleteSchema(clusterName, subject));
|
||||||
|
|
||||||
|
expect(store.getActions()).toEqual([
|
||||||
|
actions.deleteSchemaAction.request(),
|
||||||
|
actions.deleteSchemaAction.success(subject),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fires DELETE_SCHEMA__FAILURE on failure', async () => {
|
||||||
|
fetchMock.deleteOnce(
|
||||||
|
`/api/clusters/${clusterName}/schemas/${subject}`,
|
||||||
|
404
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await store.dispatch(thunks.deleteSchema(clusterName, subject));
|
||||||
|
} catch (error) {
|
||||||
|
expect(error.status).toEqual(404);
|
||||||
|
expect(store.getActions()).toEqual([
|
||||||
|
actions.deleteSchemaAction.request(),
|
||||||
|
actions.deleteSchemaAction.failure({}),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -124,6 +124,12 @@ export const createSchemaAction = createAsyncAction(
|
||||||
'POST_SCHEMA__FAILURE'
|
'POST_SCHEMA__FAILURE'
|
||||||
)<undefined, SchemaSubject, { alert?: FailurePayload }>();
|
)<undefined, SchemaSubject, { alert?: FailurePayload }>();
|
||||||
|
|
||||||
|
export const deleteSchemaAction = createAsyncAction(
|
||||||
|
'DELETE_SCHEMA__REQUEST',
|
||||||
|
'DELETE_SCHEMA__SUCCESS',
|
||||||
|
'DELETE_SCHEMA__FAILURE'
|
||||||
|
)<undefined, string, { alert?: FailurePayload }>();
|
||||||
|
|
||||||
export const dismissAlert = createAction('DISMISS_ALERT')<string>();
|
export const dismissAlert = createAction('DISMISS_ALERT')<string>();
|
||||||
|
|
||||||
export const fetchConnectsAction = createAsyncAction(
|
export const fetchConnectsAction = createAsyncAction(
|
||||||
|
|
|
@ -68,3 +68,25 @@ export const createSchema = (
|
||||||
dispatch(actions.createSchemaAction.failure({ alert }));
|
dispatch(actions.createSchemaAction.failure({ alert }));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const deleteSchema = (
|
||||||
|
clusterName: ClusterName,
|
||||||
|
subject: string
|
||||||
|
): PromiseThunkResult => async (dispatch) => {
|
||||||
|
dispatch(actions.deleteSchemaAction.request());
|
||||||
|
try {
|
||||||
|
await schemasApiClient.deleteSchema({
|
||||||
|
clusterName,
|
||||||
|
subject,
|
||||||
|
});
|
||||||
|
dispatch(actions.deleteSchemaAction.success(subject));
|
||||||
|
} catch (error) {
|
||||||
|
const response = await getResponse(error);
|
||||||
|
const alert: FailurePayload = {
|
||||||
|
subject: ['schema', subject].join('-'),
|
||||||
|
title: `Schema ${subject}`,
|
||||||
|
response,
|
||||||
|
};
|
||||||
|
dispatch(actions.deleteSchemaAction.failure({ alert }));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
|
import { SchemaSubject, SchemaType } from 'generated-sources';
|
||||||
import {
|
import {
|
||||||
createSchemaAction,
|
createSchemaAction,
|
||||||
|
deleteSchemaAction,
|
||||||
fetchSchemasByClusterNameAction,
|
fetchSchemasByClusterNameAction,
|
||||||
fetchSchemaVersionsAction,
|
fetchSchemaVersionsAction,
|
||||||
} from 'redux/actions';
|
} from 'redux/actions';
|
||||||
|
@ -46,4 +48,31 @@ describe('Schemas reducer', () => {
|
||||||
reducer(undefined, createSchemaAction.success(schemaVersionsPayload[0]))
|
reducer(undefined, createSchemaAction.success(schemaVersionsPayload[0]))
|
||||||
).toMatchSnapshot();
|
).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('deletes the schema from the list on DELETE_SCHEMA__SUCCESS', () => {
|
||||||
|
const schema: SchemaSubject = {
|
||||||
|
subject: 'name',
|
||||||
|
version: '1',
|
||||||
|
id: 1,
|
||||||
|
schema: '{}',
|
||||||
|
compatibilityLevel: 'BACKWARD',
|
||||||
|
schemaType: SchemaType.AVRO,
|
||||||
|
};
|
||||||
|
expect(
|
||||||
|
reducer(
|
||||||
|
{
|
||||||
|
byName: {
|
||||||
|
[schema.subject]: schema,
|
||||||
|
},
|
||||||
|
allNames: [schema.subject],
|
||||||
|
currentSchemaVersions: [],
|
||||||
|
},
|
||||||
|
deleteSchemaAction.success(schema.subject)
|
||||||
|
)
|
||||||
|
).toEqual({
|
||||||
|
byName: {},
|
||||||
|
allNames: [],
|
||||||
|
currentSchemaVersions: [],
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import { SchemaSubject } from 'generated-sources';
|
import { SchemaSubject } from 'generated-sources';
|
||||||
import { Action, SchemasState } from 'redux/interfaces';
|
import { Action, SchemasState } from 'redux/interfaces';
|
||||||
|
import * as actions from 'redux/actions';
|
||||||
|
import { getType } from 'typesafe-actions';
|
||||||
|
|
||||||
export const initialState: SchemasState = {
|
export const initialState: SchemasState = {
|
||||||
byName: {},
|
byName: {},
|
||||||
|
@ -44,6 +46,18 @@ const addToSchemaList = (
|
||||||
return newState;
|
return newState;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const deleteFromSchemaList = (
|
||||||
|
state: SchemasState,
|
||||||
|
payload: string
|
||||||
|
): SchemasState => {
|
||||||
|
const newState: SchemasState = {
|
||||||
|
...state,
|
||||||
|
};
|
||||||
|
delete newState.byName[payload];
|
||||||
|
newState.allNames = newState.allNames.filter((name) => name !== payload);
|
||||||
|
return newState;
|
||||||
|
};
|
||||||
|
|
||||||
const reducer = (state = initialState, action: Action): SchemasState => {
|
const reducer = (state = initialState, action: Action): SchemasState => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case 'GET_CLUSTER_SCHEMAS__SUCCESS':
|
case 'GET_CLUSTER_SCHEMAS__SUCCESS':
|
||||||
|
@ -52,6 +66,8 @@ const reducer = (state = initialState, action: Action): SchemasState => {
|
||||||
return { ...state, currentSchemaVersions: action.payload };
|
return { ...state, currentSchemaVersions: action.payload };
|
||||||
case 'POST_SCHEMA__SUCCESS':
|
case 'POST_SCHEMA__SUCCESS':
|
||||||
return addToSchemaList(state, action.payload);
|
return addToSchemaList(state, action.payload);
|
||||||
|
case getType(actions.deleteSchemaAction.success):
|
||||||
|
return deleteFromSchemaList(state, action.payload);
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ describe('topics reducer', () => {
|
||||||
reducer(
|
reducer(
|
||||||
{
|
{
|
||||||
byName: {
|
byName: {
|
||||||
topic,
|
[topic.name]: topic,
|
||||||
},
|
},
|
||||||
allNames: [topic.name],
|
allNames: [topic.name],
|
||||||
messages: [],
|
messages: [],
|
||||||
|
|
Loading…
Add table
Reference in a new issue