Implement deleting schemas (#366)

This commit is contained in:
Alexander Krivonosov 2021-04-13 13:30:43 +03:00 committed by GitHub
parent d471759b79
commit 3945845c37
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 134 additions and 6 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -11,7 +11,7 @@ describe('topics reducer', () => {
reducer(
{
byName: {
topic,
[topic.name]: topic,
},
allNames: [topic.name],
messages: [],