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 { clusterSchemasPath } from 'lib/paths';
|
||||
import ClusterContext from 'components/contexts/ClusterContext';
|
||||
import { useHistory } from 'react-router';
|
||||
import Breadcrumb from '../../common/Breadcrumb/Breadcrumb';
|
||||
import SchemaVersion from './SchemaVersion';
|
||||
import LatestVersionItem from './LatestVersionItem';
|
||||
|
@ -18,6 +19,7 @@ export interface DetailsProps {
|
|||
clusterName: ClusterName,
|
||||
schemaName: SchemaName
|
||||
) => void;
|
||||
deleteSchema: (clusterName: ClusterName, subject: string) => Promise<void>;
|
||||
}
|
||||
|
||||
const Details: React.FC<DetailsProps> = ({
|
||||
|
@ -25,6 +27,7 @@ const Details: React.FC<DetailsProps> = ({
|
|||
schema,
|
||||
clusterName,
|
||||
fetchSchemaVersions,
|
||||
deleteSchema,
|
||||
versions,
|
||||
isFetched,
|
||||
}) => {
|
||||
|
@ -33,6 +36,12 @@ const Details: React.FC<DetailsProps> = ({
|
|||
fetchSchemaVersions(clusterName, subject);
|
||||
}, [fetchSchemaVersions, clusterName]);
|
||||
|
||||
const history = useHistory();
|
||||
const onDelete = async () => {
|
||||
await deleteSchema(clusterName, subject);
|
||||
history.push(clusterSchemasPath(clusterName));
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="section">
|
||||
<div className="level">
|
||||
|
@ -75,7 +84,7 @@ const Details: React.FC<DetailsProps> = ({
|
|||
className="button is-danger is-small level-item"
|
||||
type="button"
|
||||
title="in development"
|
||||
disabled
|
||||
onClick={onDelete}
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
|
|
|
@ -6,7 +6,7 @@ import {
|
|||
getSchema,
|
||||
getSortedSchemaVersions,
|
||||
} from 'redux/reducers/schemas/selectors';
|
||||
import { fetchSchemaVersions } from 'redux/actions';
|
||||
import { fetchSchemaVersions, deleteSchema } from 'redux/actions';
|
||||
import Details from './Details';
|
||||
|
||||
interface RouteProps {
|
||||
|
@ -33,6 +33,7 @@ const mapStateToProps = (
|
|||
|
||||
const mapDispatchToProps = {
|
||||
fetchSchemaVersions,
|
||||
deleteSchema,
|
||||
};
|
||||
|
||||
export default withRouter(
|
||||
|
|
|
@ -30,6 +30,7 @@ describe('Details', () => {
|
|||
schema={schema}
|
||||
clusterName="Test cluster"
|
||||
fetchSchemaVersions={jest.fn()}
|
||||
deleteSchema={jest.fn()}
|
||||
isFetched
|
||||
versions={[]}
|
||||
{...props}
|
||||
|
@ -100,6 +101,17 @@ describe('Details', () => {
|
|||
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', () => {
|
||||
expect(shallow(setupWrapper({ versions }))).toMatchSnapshot();
|
||||
});
|
||||
|
|
|
@ -61,7 +61,7 @@ exports[`Details View Initial state matches snapshot 1`] = `
|
|||
</button>
|
||||
<button
|
||||
className="button is-danger is-small level-item"
|
||||
disabled={true}
|
||||
onClick={[Function]}
|
||||
title="in development"
|
||||
type="button"
|
||||
>
|
||||
|
@ -196,7 +196,7 @@ exports[`Details View when page with schema versions loaded when schema has vers
|
|||
</button>
|
||||
<button
|
||||
className="button is-danger is-small level-item"
|
||||
disabled={true}
|
||||
onClick={[Function]}
|
||||
title="in development"
|
||||
type="button"
|
||||
>
|
||||
|
@ -334,7 +334,7 @@ exports[`Details View when page with schema versions loaded when versions are em
|
|||
</button>
|
||||
<button
|
||||
className="button is-danger is-small level-item"
|
||||
disabled={true}
|
||||
onClick={[Function]}
|
||||
title="in development"
|
||||
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'
|
||||
)<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 fetchConnectsAction = createAsyncAction(
|
||||
|
|
|
@ -68,3 +68,25 @@ export const createSchema = (
|
|||
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 {
|
||||
createSchemaAction,
|
||||
deleteSchemaAction,
|
||||
fetchSchemasByClusterNameAction,
|
||||
fetchSchemaVersionsAction,
|
||||
} from 'redux/actions';
|
||||
|
@ -46,4 +48,31 @@ describe('Schemas reducer', () => {
|
|||
reducer(undefined, createSchemaAction.success(schemaVersionsPayload[0]))
|
||||
).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 { Action, SchemasState } from 'redux/interfaces';
|
||||
import * as actions from 'redux/actions';
|
||||
import { getType } from 'typesafe-actions';
|
||||
|
||||
export const initialState: SchemasState = {
|
||||
byName: {},
|
||||
|
@ -44,6 +46,18 @@ const addToSchemaList = (
|
|||
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 => {
|
||||
switch (action.type) {
|
||||
case 'GET_CLUSTER_SCHEMAS__SUCCESS':
|
||||
|
@ -52,6 +66,8 @@ const reducer = (state = initialState, action: Action): SchemasState => {
|
|||
return { ...state, currentSchemaVersions: action.payload };
|
||||
case 'POST_SCHEMA__SUCCESS':
|
||||
return addToSchemaList(state, action.payload);
|
||||
case getType(actions.deleteSchemaAction.success):
|
||||
return deleteFromSchemaList(state, action.payload);
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ describe('topics reducer', () => {
|
|||
reducer(
|
||||
{
|
||||
byName: {
|
||||
topic,
|
||||
[topic.name]: topic,
|
||||
},
|
||||
allNames: [topic.name],
|
||||
messages: [],
|
||||
|
|
Loading…
Add table
Reference in a new issue