Fix the problem with instant redirect (#390)
* Fix the problem with instant redirect * Rewrite updateSchema thunk
This commit is contained in:
parent
f3c0866940
commit
42a1c97686
12 changed files with 112 additions and 139 deletions
|
@ -33,6 +33,7 @@ const LatestVersionItem: React.FC<LatestVersionProps> = ({
|
||||||
<div className="tile is-parent">
|
<div className="tile is-parent">
|
||||||
<div className="tile is-child box">
|
<div className="tile is-child box">
|
||||||
<JSONEditor
|
<JSONEditor
|
||||||
|
isFixedHeight
|
||||||
name="schema"
|
name="schema"
|
||||||
value={JSON.stringify(JSON.parse(schema), null, '\t')}
|
value={JSON.stringify(JSON.parse(schema), null, '\t')}
|
||||||
showGutter={false}
|
showGutter={false}
|
||||||
|
|
|
@ -15,6 +15,7 @@ const SchemaVersion: React.FC<SchemaVersionProps> = ({
|
||||||
<td>{id}</td>
|
<td>{id}</td>
|
||||||
<td>
|
<td>
|
||||||
<JSONEditor
|
<JSONEditor
|
||||||
|
isFixedHeight
|
||||||
name="schema"
|
name="schema"
|
||||||
value={JSON.stringify(JSON.parse(schema), null, '\t')}
|
value={JSON.stringify(JSON.parse(schema), null, '\t')}
|
||||||
showGutter={false}
|
showGutter={false}
|
||||||
|
|
|
@ -49,6 +49,7 @@ exports[`LatestVersionItem matches snapshot 1`] = `
|
||||||
className="tile is-child box"
|
className="tile is-child box"
|
||||||
>
|
>
|
||||||
<JSONEditor
|
<JSONEditor
|
||||||
|
isFixedHeight={true}
|
||||||
name="schema"
|
name="schema"
|
||||||
readOnly={true}
|
readOnly={true}
|
||||||
showGutter={false}
|
showGutter={false}
|
||||||
|
|
|
@ -10,6 +10,7 @@ exports[`SchemaVersion matches snapshot 1`] = `
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<JSONEditor
|
<JSONEditor
|
||||||
|
isFixedHeight={true}
|
||||||
name="schema"
|
name="schema"
|
||||||
readOnly={true}
|
readOnly={true}
|
||||||
showGutter={false}
|
showGutter={false}
|
||||||
|
|
|
@ -43,7 +43,7 @@ const Edit = ({
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
formState: { isSubmitting },
|
formState: { isSubmitting, isDirty },
|
||||||
control,
|
control,
|
||||||
} = useForm<NewSchemaSubjectRaw>({ mode: 'onChange' });
|
} = useForm<NewSchemaSubjectRaw>({ mode: 'onChange' });
|
||||||
|
|
||||||
|
@ -62,15 +62,19 @@ const Edit = ({
|
||||||
compatibilityLevel: CompatibilityLevelCompatibilityEnum;
|
compatibilityLevel: CompatibilityLevelCompatibilityEnum;
|
||||||
newSchema: string;
|
newSchema: string;
|
||||||
}) => {
|
}) => {
|
||||||
await updateSchema(
|
try {
|
||||||
schema,
|
await updateSchema(
|
||||||
newSchema,
|
schema,
|
||||||
schemaType,
|
newSchema,
|
||||||
compatibilityLevel,
|
schemaType,
|
||||||
clusterName,
|
compatibilityLevel,
|
||||||
subject
|
clusterName,
|
||||||
);
|
subject
|
||||||
history.push(clusterSchemaPath(clusterName, subject));
|
);
|
||||||
|
history.push(clusterSchemaPath(clusterName, subject));
|
||||||
|
} catch (e) {
|
||||||
|
// do not redirect
|
||||||
|
}
|
||||||
},
|
},
|
||||||
[schema, register, control, clusterName, subject, updateSchema, history]
|
[schema, register, control, clusterName, subject, updateSchema, history]
|
||||||
);
|
);
|
||||||
|
@ -96,7 +100,7 @@ const Edit = ({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{schemasAreFetched && !isSubmitting ? (
|
{schemasAreFetched ? (
|
||||||
<div className="box">
|
<div className="box">
|
||||||
<form
|
<form
|
||||||
onSubmit={handleSubmit(onSubmit)}
|
onSubmit={handleSubmit(onSubmit)}
|
||||||
|
@ -111,6 +115,7 @@ const Edit = ({
|
||||||
required: 'Schema Type is required.',
|
required: 'Schema Type is required.',
|
||||||
})}
|
})}
|
||||||
defaultValue={schema.schemaType}
|
defaultValue={schema.schemaType}
|
||||||
|
disabled={isSubmitting}
|
||||||
>
|
>
|
||||||
{Object.keys(SchemaType).map((type: string) => (
|
{Object.keys(SchemaType).map((type: string) => (
|
||||||
<option key={type} value={type}>
|
<option key={type} value={type}>
|
||||||
|
@ -128,6 +133,7 @@ const Edit = ({
|
||||||
name="compatibilityLevel"
|
name="compatibilityLevel"
|
||||||
ref={register()}
|
ref={register()}
|
||||||
defaultValue={schema.compatibilityLevel}
|
defaultValue={schema.compatibilityLevel}
|
||||||
|
disabled={isSubmitting}
|
||||||
>
|
>
|
||||||
{Object.keys(CompatibilityLevelCompatibilityEnum).map(
|
{Object.keys(CompatibilityLevelCompatibilityEnum).map(
|
||||||
(level: string) => (
|
(level: string) => (
|
||||||
|
@ -143,7 +149,9 @@ const Edit = ({
|
||||||
<div className="column is-one-half">
|
<div className="column is-one-half">
|
||||||
<h4 className="title is-5 mb-2">Latest Schema</h4>
|
<h4 className="title is-5 mb-2">Latest Schema</h4>
|
||||||
<JSONEditor
|
<JSONEditor
|
||||||
|
isFixedHeight
|
||||||
readOnly
|
readOnly
|
||||||
|
height="500px"
|
||||||
value={getFormattedSchema()}
|
value={getFormattedSchema()}
|
||||||
name="latestSchema"
|
name="latestSchema"
|
||||||
highlightActiveLine={false}
|
highlightActiveLine={false}
|
||||||
|
@ -154,8 +162,10 @@ const Edit = ({
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="newSchema"
|
name="newSchema"
|
||||||
|
disabled={isSubmitting}
|
||||||
render={({ name, onChange }) => (
|
render={({ name, onChange }) => (
|
||||||
<JSONEditor
|
<JSONEditor
|
||||||
|
readOnly={isSubmitting}
|
||||||
defaultValue={getFormattedSchema()}
|
defaultValue={getFormattedSchema()}
|
||||||
name={name}
|
name={name}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
|
@ -164,7 +174,11 @@ const Edit = ({
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" className="button is-primary">
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="button is-primary"
|
||||||
|
disabled={!isDirty || isSubmitting}
|
||||||
|
>
|
||||||
Submit
|
Submit
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -48,6 +48,7 @@ exports[`Edit Component when schemas are fetched matches the snapshot 1`] = `
|
||||||
>
|
>
|
||||||
<select
|
<select
|
||||||
defaultValue="AVRO"
|
defaultValue="AVRO"
|
||||||
|
disabled={false}
|
||||||
name="schemaType"
|
name="schemaType"
|
||||||
>
|
>
|
||||||
<option
|
<option
|
||||||
|
@ -84,6 +85,7 @@ exports[`Edit Component when schemas are fetched matches the snapshot 1`] = `
|
||||||
>
|
>
|
||||||
<select
|
<select
|
||||||
defaultValue="BACKWARD"
|
defaultValue="BACKWARD"
|
||||||
|
disabled={false}
|
||||||
name="compatibilityLevel"
|
name="compatibilityLevel"
|
||||||
>
|
>
|
||||||
<option
|
<option
|
||||||
|
@ -143,7 +145,9 @@ exports[`Edit Component when schemas are fetched matches the snapshot 1`] = `
|
||||||
Latest Schema
|
Latest Schema
|
||||||
</h4>
|
</h4>
|
||||||
<JSONEditor
|
<JSONEditor
|
||||||
|
height="500px"
|
||||||
highlightActiveLine={false}
|
highlightActiveLine={false}
|
||||||
|
isFixedHeight={true}
|
||||||
name="latestSchema"
|
name="latestSchema"
|
||||||
readOnly={true}
|
readOnly={true}
|
||||||
value="{
|
value="{
|
||||||
|
@ -261,6 +265,7 @@ exports[`Edit Component when schemas are fetched matches the snapshot 1`] = `
|
||||||
"watchInternal": [Function],
|
"watchInternal": [Function],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
disabled={false}
|
||||||
name="newSchema"
|
name="newSchema"
|
||||||
render={[Function]}
|
render={[Function]}
|
||||||
/>
|
/>
|
||||||
|
@ -268,6 +273,7 @@ exports[`Edit Component when schemas are fetched matches the snapshot 1`] = `
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
className="button is-primary"
|
className="button is-primary"
|
||||||
|
disabled={true}
|
||||||
type="submit"
|
type="submit"
|
||||||
>
|
>
|
||||||
Submit
|
Submit
|
||||||
|
|
|
@ -1,16 +1,10 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { RootState } from 'redux/interfaces';
|
|
||||||
import { createSchema } from 'redux/actions';
|
import { createSchema } from 'redux/actions';
|
||||||
import { getSchemaCreated } from 'redux/reducers/schemas/selectors';
|
|
||||||
|
|
||||||
import New from './New';
|
import New from './New';
|
||||||
|
|
||||||
const mapStateToProps = (state: RootState) => ({
|
|
||||||
isSchemaCreated: getSchemaCreated(state),
|
|
||||||
});
|
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
const mapDispatchToProps = {
|
||||||
createSchema,
|
createSchema,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(New);
|
export default connect(null, mapDispatchToProps)(New);
|
||||||
|
|
|
@ -4,15 +4,25 @@ import 'ace-builds/src-noconflict/mode-json5';
|
||||||
import 'ace-builds/src-noconflict/theme-textmate';
|
import 'ace-builds/src-noconflict/theme-textmate';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
const JSONEditor: React.FC<IAceEditorProps> = (props) => (
|
interface JSONEditorProps extends IAceEditorProps {
|
||||||
<AceEditor
|
isFixedHeight?: boolean;
|
||||||
mode="json5"
|
}
|
||||||
theme="textmate"
|
|
||||||
tabSize={2}
|
const JSONEditor: React.FC<JSONEditorProps> = (props) => {
|
||||||
width="100%"
|
const { isFixedHeight, value } = props;
|
||||||
wrapEnabled
|
return (
|
||||||
{...props}
|
<AceEditor
|
||||||
/>
|
mode="json5"
|
||||||
);
|
theme="textmate"
|
||||||
|
tabSize={2}
|
||||||
|
width="100%"
|
||||||
|
height={
|
||||||
|
isFixedHeight ? `${(value?.split('\n').length || 32) * 16}px` : '500px'
|
||||||
|
}
|
||||||
|
wrapEnabled
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default JSONEditor;
|
export default JSONEditor;
|
||||||
|
|
|
@ -122,53 +122,22 @@ describe('Thunks', () => {
|
||||||
expect(error.status).toEqual(404);
|
expect(error.status).toEqual(404);
|
||||||
expect(store.getActions()).toEqual([
|
expect(store.getActions()).toEqual([
|
||||||
actions.createSchemaAction.request(),
|
actions.createSchemaAction.request(),
|
||||||
actions.createSchemaAction.failure({}),
|
actions.createSchemaAction.failure({
|
||||||
|
alert: {
|
||||||
|
response: {
|
||||||
|
body: undefined,
|
||||||
|
status: 404,
|
||||||
|
statusText: 'Not Found',
|
||||||
|
},
|
||||||
|
subject: 'schema-NewSchema',
|
||||||
|
title: 'Schema NewSchema',
|
||||||
|
},
|
||||||
|
}),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('updateSchemaCompatibilityLevel', () => {
|
|
||||||
it('creates UPDATE_SCHEMA__SUCCESS when patching a schema', async () => {
|
|
||||||
fetchMock.putOnce(
|
|
||||||
`/api/clusters/${clusterName}/schemas/${subject}/compatibility`,
|
|
||||||
200
|
|
||||||
);
|
|
||||||
await store.dispatch(
|
|
||||||
thunks.updateSchemaCompatibilityLevel(
|
|
||||||
clusterName,
|
|
||||||
subject,
|
|
||||||
CompatibilityLevelCompatibilityEnum.BACKWARD
|
|
||||||
)
|
|
||||||
);
|
|
||||||
expect(store.getActions()).toEqual([
|
|
||||||
actions.updateSchemaCompatibilityLevelAction.request(),
|
|
||||||
actions.updateSchemaCompatibilityLevelAction.success(),
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('creates UPDATE_SCHEMA__SUCCESS when failing to patch a schema', async () => {
|
|
||||||
fetchMock.putOnce(
|
|
||||||
`/api/clusters/${clusterName}/schemas/${subject}/compatibility`,
|
|
||||||
404
|
|
||||||
);
|
|
||||||
try {
|
|
||||||
await store.dispatch(
|
|
||||||
thunks.updateSchemaCompatibilityLevel(
|
|
||||||
clusterName,
|
|
||||||
subject,
|
|
||||||
CompatibilityLevelCompatibilityEnum.BACKWARD
|
|
||||||
)
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
expect(error.status).toEqual(404);
|
|
||||||
expect(store.getActions()).toEqual([
|
|
||||||
actions.updateSchemaCompatibilityLevelAction.request(),
|
|
||||||
actions.updateSchemaCompatibilityLevelAction.failure({}),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
describe('deleteSchema', () => {
|
describe('deleteSchema', () => {
|
||||||
it('fires DELETE_SCHEMA__SUCCESS on success', async () => {
|
it('fires DELETE_SCHEMA__SUCCESS on success', async () => {
|
||||||
fetchMock.deleteOnce(
|
fetchMock.deleteOnce(
|
||||||
|
@ -203,7 +172,7 @@ describe('Thunks', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('updateSchema', () => {
|
describe('updateSchema', () => {
|
||||||
it('calls createSchema', () => {
|
it('calls PATCH_SCHEMA__REQUEST', () => {
|
||||||
store.dispatch(
|
store.dispatch(
|
||||||
thunks.updateSchema(
|
thunks.updateSchema(
|
||||||
fixtures.schema,
|
fixtures.schema,
|
||||||
|
@ -215,23 +184,7 @@ describe('Thunks', () => {
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
expect(store.getActions()).toEqual([
|
expect(store.getActions()).toEqual([
|
||||||
actions.createSchemaAction.request(),
|
actions.updateSchemaAction.request(),
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('calls updateSchema and does not call createSchema when schema does not change', () => {
|
|
||||||
store.dispatch(
|
|
||||||
thunks.updateSchema(
|
|
||||||
fixtures.schema,
|
|
||||||
fixtures.schema.schema,
|
|
||||||
SchemaType.JSON,
|
|
||||||
CompatibilityLevelCompatibilityEnum.FORWARD,
|
|
||||||
clusterName,
|
|
||||||
subject
|
|
||||||
)
|
|
||||||
);
|
|
||||||
expect(store.getActions()).toEqual([
|
|
||||||
actions.updateSchemaCompatibilityLevelAction.request(),
|
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -130,11 +130,11 @@ export const createSchemaAction = createAsyncAction(
|
||||||
'POST_SCHEMA__FAILURE'
|
'POST_SCHEMA__FAILURE'
|
||||||
)<undefined, SchemaSubject, { alert?: FailurePayload }>();
|
)<undefined, SchemaSubject, { alert?: FailurePayload }>();
|
||||||
|
|
||||||
export const updateSchemaCompatibilityLevelAction = createAsyncAction(
|
export const updateSchemaAction = createAsyncAction(
|
||||||
'PATCH_SCHEMA_COMPATIBILITY__REQUEST',
|
'PATCH_SCHEMA__REQUEST',
|
||||||
'PATCH_SCHEMA_COMPATIBILITY__SUCCESS',
|
'PATCH_SCHEMA__SUCCESS',
|
||||||
'PATCH_SCHEMA_COMPATIBILITY__FAILURE'
|
'PATCH_SCHEMA__FAILURE'
|
||||||
)<undefined, undefined, { alert?: FailurePayload }>();
|
)<undefined, SchemaSubject, { alert?: FailurePayload }>();
|
||||||
|
|
||||||
export const deleteSchemaAction = createAsyncAction(
|
export const deleteSchemaAction = createAsyncAction(
|
||||||
'DELETE_SCHEMA__REQUEST',
|
'DELETE_SCHEMA__REQUEST',
|
||||||
|
|
|
@ -68,32 +68,7 @@ export const createSchema = (
|
||||||
response,
|
response,
|
||||||
};
|
};
|
||||||
dispatch(actions.createSchemaAction.failure({ alert }));
|
dispatch(actions.createSchemaAction.failure({ alert }));
|
||||||
}
|
throw error;
|
||||||
};
|
|
||||||
|
|
||||||
export const updateSchemaCompatibilityLevel = (
|
|
||||||
clusterName: ClusterName,
|
|
||||||
subject: string,
|
|
||||||
compatibilityLevel: CompatibilityLevelCompatibilityEnum
|
|
||||||
): PromiseThunkResult => async (dispatch) => {
|
|
||||||
dispatch(actions.updateSchemaCompatibilityLevelAction.request());
|
|
||||||
try {
|
|
||||||
await schemasApiClient.updateSchemaCompatibilityLevel({
|
|
||||||
clusterName,
|
|
||||||
subject,
|
|
||||||
compatibilityLevel: {
|
|
||||||
compatibility: compatibilityLevel,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
dispatch(actions.updateSchemaCompatibilityLevelAction.success());
|
|
||||||
} catch (error) {
|
|
||||||
const response = await getResponse(error);
|
|
||||||
const alert: FailurePayload = {
|
|
||||||
subject: 'compatibilityLevel',
|
|
||||||
title: `Compatibility level ${subject}`,
|
|
||||||
response,
|
|
||||||
};
|
|
||||||
dispatch(actions.updateSchemaCompatibilityLevelAction.failure({ alert }));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -105,27 +80,42 @@ export const updateSchema = (
|
||||||
clusterName: string,
|
clusterName: string,
|
||||||
subject: string
|
subject: string
|
||||||
): PromiseThunkResult => async (dispatch) => {
|
): PromiseThunkResult => async (dispatch) => {
|
||||||
if (
|
dispatch(actions.updateSchemaAction.request());
|
||||||
(newSchema &&
|
try {
|
||||||
!isEqual(JSON.parse(latestSchema.schema), JSON.parse(newSchema))) ||
|
let schema: SchemaSubject = latestSchema;
|
||||||
newSchemaType !== latestSchema.schemaType
|
if (
|
||||||
) {
|
(newSchema &&
|
||||||
await dispatch(
|
!isEqual(JSON.parse(latestSchema.schema), JSON.parse(newSchema))) ||
|
||||||
createSchema(clusterName, {
|
newSchemaType !== latestSchema.schemaType
|
||||||
...latestSchema,
|
) {
|
||||||
schema: newSchema || latestSchema.schema,
|
schema = await schemasApiClient.createNewSchema({
|
||||||
schemaType: newSchemaType || latestSchema.schemaType,
|
clusterName,
|
||||||
})
|
newSchemaSubject: {
|
||||||
);
|
...latestSchema,
|
||||||
}
|
schema: newSchema || latestSchema.schema,
|
||||||
if (newCompatibilityLevel !== latestSchema.compatibilityLevel) {
|
schemaType: newSchemaType || latestSchema.schemaType,
|
||||||
await dispatch(
|
},
|
||||||
updateSchemaCompatibilityLevel(
|
});
|
||||||
|
}
|
||||||
|
if (newCompatibilityLevel !== latestSchema.compatibilityLevel) {
|
||||||
|
await schemasApiClient.updateSchemaCompatibilityLevel({
|
||||||
clusterName,
|
clusterName,
|
||||||
subject,
|
subject,
|
||||||
newCompatibilityLevel
|
compatibilityLevel: {
|
||||||
)
|
compatibility: newCompatibilityLevel,
|
||||||
);
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
actions.updateSchemaAction.success(schema);
|
||||||
|
} catch (e) {
|
||||||
|
const response = await getResponse(e);
|
||||||
|
const alert: FailurePayload = {
|
||||||
|
subject: ['schema', subject].join('-'),
|
||||||
|
title: `Schema ${subject}`,
|
||||||
|
response,
|
||||||
|
};
|
||||||
|
dispatch(actions.updateSchemaAction.failure({ alert }));
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
export const deleteSchema = (
|
export const deleteSchema = (
|
||||||
|
|
|
@ -66,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 'PATCH_SCHEMA__SUCCESS':
|
||||||
|
return addToSchemaList(state, action.payload);
|
||||||
case getType(actions.deleteSchemaAction.success):
|
case getType(actions.deleteSchemaAction.success):
|
||||||
return deleteFromSchemaList(state, action.payload);
|
return deleteFromSchemaList(state, action.payload);
|
||||||
default:
|
default:
|
||||||
|
|
Loading…
Add table
Reference in a new issue