Remove immutable in webhooks page

Signed-off-by: soupette <cyril.lpz@gmail.com>
This commit is contained in:
soupette 2021-05-17 20:34:59 +02:00
parent c3f5509de7
commit a58f59ab8f
7 changed files with 226 additions and 168 deletions

View File

@ -33,7 +33,10 @@ function EditView() {
const { formatMessage } = useGlobalContext();
const [submittedOnce, setSubmittedOnce] = useState(false);
const [isSubmitting, setIsSubmitting] = useState(false);
const [reducerState, dispatch] = useReducer(reducer, initialState);
const [
{ formErrors, modifiedData, initialData, isLoading, isTriggering, triggerResponse },
dispatch,
] = useReducer(reducer, initialState);
const { push, replace } = useHistory();
const {
params: { id },
@ -43,14 +46,14 @@ function EditView() {
const { signal } = abortController;
const isCreating = id === 'create';
const {
formErrors,
modifiedData,
initialData,
isLoading,
isTriggering,
triggerResponse,
} = reducerState.toJS();
// const {
// formErrors,
// modifiedData,
// initialData,
// isLoading,
// isTriggering,
// triggerResponse,
// } = reducerState.toJS();
useEffect(() => {
isMounted.current = true;
@ -195,6 +198,7 @@ function EditView() {
try {
lockApp();
setIsSubmitting(true);
console.log(cleanData(modifiedData), modifiedData);
const { data } = await request('/admin/webhooks', {
method: 'POST',
body: cleanData(modifiedData),

View File

@ -1,5 +1,6 @@
import { fromJS } from 'immutable';
import { get } from 'lodash';
import produce from 'immer';
import get from 'lodash/get';
import set from 'lodash/set';
const header = { key: '', value: '' };
@ -10,67 +11,90 @@ const initialWebhook = {
url: '',
};
const initialState = fromJS({
const initialState = {
formErrors: {},
initialData: initialWebhook,
isTriggering: false,
modifiedData: initialWebhook,
isLoading: true,
triggerResponse: {},
});
};
const reducer = (state, action) => {
switch (action.type) {
case 'ADD_NEW_HEADER':
return state.updateIn(['modifiedData', ...action.keys], arr => arr.push(fromJS(header)));
case 'GET_DATA_SUCCEEDED': {
const headers = get(action, ['data', 'headers'], {});
let formattedHeaders = [header];
if (Object.keys(headers).length > 0) {
formattedHeaders = Object.keys(headers).map(key => {
return { key, value: headers[key] };
});
const reducer = (state, action) =>
// eslint-disable-next-line consistent-return
produce(state, draftState => {
switch (action.type) {
case 'ADD_NEW_HEADER': {
draftState.modifiedData.headers.push(header);
break;
}
case 'GET_DATA_SUCCEEDED': {
const headers = get(action, ['data', 'headers'], {});
let formattedHeaders = [header];
const data = fromJS(action.data).update('headers', () => fromJS(formattedHeaders));
return state
.update('isLoading', () => false)
.update('initialData', () => data)
.update('modifiedData', () => data);
}
case 'ON_CHANGE':
return state.updateIn(['modifiedData', ...action.keys], () => action.value);
case 'ON_HEADER_REMOVE': {
return state.updateIn(['modifiedData', 'headers'], headers => {
if (headers.size === 1) {
return fromJS([header]);
if (Object.keys(headers).length > 0) {
formattedHeaders = Object.keys(headers).map(key => {
return { key, value: headers[key] };
});
}
return headers.remove(action.index);
});
const data = { ...action.data, headers: formattedHeaders };
draftState.isLoading = false;
draftState.initialData = data;
draftState.modifiedData = data;
break;
}
case 'ON_CHANGE': {
set(draftState, ['modifiedData', ...action.keys], action.value);
break;
}
case 'ON_HEADER_REMOVE': {
const nextHeaders = state.modifiedData.headers.filter((_, index) => index !== action.index);
if (!nextHeaders.length) {
nextHeaders.push(header);
}
draftState.modifiedData.headers = nextHeaders;
break;
}
case 'ON_TRIGGER_CANCELED': {
draftState.isTriggering = false;
draftState.triggerResponse = {};
break;
}
case 'RESET_FORM': {
draftState.modifiedData = state.initialData;
break;
}
case 'SET_ERRORS': {
draftState.formErrors = action.errors;
break;
}
case 'SET_IS_TRIGGERING': {
draftState.isTriggering = !state.isTriggering;
break;
}
case 'SUBMIT_SUCCEEDED': {
draftState.initialData = state.modifiedData;
break;
}
case 'TRIGGER_SUCCEEDED': {
draftState.triggerResponse = action.response;
draftState.isTriggering = false;
break;
}
case 'UNSET_LOADER': {
draftState.isLoading = false;
break;
}
default:
return draftState;
}
case 'ON_TRIGGER_CANCELED':
return state.update('isTriggering', () => false).set('triggerResponse', fromJS({}));
case 'RESET_FORM':
return state.update('modifiedData', () => state.get('initialData'));
case 'SET_ERRORS':
return state.update('formErrors', () => fromJS(action.errors));
case 'SET_IS_TRIGGERING':
return state.update('isTriggering', isTriggering => !isTriggering);
case 'SUBMIT_SUCCEEDED':
return state.update('initialData', () => state.get('modifiedData'));
case 'TRIGGER_SUCCEEDED':
return state
.update('triggerResponse', () => fromJS(action.response))
.update('isTriggering', () => false);
case 'UNSET_LOADER':
return state.update('isLoading', () => false);
default:
return state;
}
};
});
export default reducer;
export { initialState };

View File

@ -1,4 +1,3 @@
import { fromJS } from 'immutable';
import reducer from '../reducer';
describe('Admin | containers | Webhooks | EditView | reducer', () => {
@ -11,14 +10,14 @@ describe('Admin | containers | Webhooks | EditView | reducer', () => {
url: '',
};
const initialState = fromJS({
const initialState = {
formErrors: {},
initialData: initialWebhook,
isTriggering: false,
modifiedData: initialWebhook,
isLoading: true,
triggerResponse: {},
});
};
describe('Format headers object', () => {
it('should convert headers object to an array of an empty object if it is empty', () => {
@ -39,10 +38,7 @@ describe('Admin | containers | Webhooks | EditView | reducer', () => {
};
const data = { ...receivedData, headers: [header] };
const expectedState = state
.set('isLoading', false)
.set('initialData', fromJS(data))
.set('modifiedData', fromJS(data));
const expectedState = { ...state, isLoading: false, initialData: data, modifiedData: data };
expect(reducer(state, action)).toEqual(expectedState);
});
@ -74,10 +70,7 @@ describe('Admin | containers | Webhooks | EditView | reducer', () => {
const data = { ...receivedData, headers: formattedHeaders };
const expectedState = state
.set('isLoading', false)
.update('initialData', () => fromJS(data))
.update('modifiedData', () => fromJS(data));
const expectedState = { ...state, isLoading: false, initialData: data, modifiedData: data };
expect(reducer(state, action)).toEqual(expectedState);
});
@ -93,7 +86,10 @@ describe('Admin | containers | Webhooks | EditView | reducer', () => {
value: 'new webhook name',
};
const expectedState = state.setIn(['modifiedData', ...action.keys], action.value);
const expectedState = {
...state,
modifiedData: { ...state.modifiedData, name: action.value },
};
expect(reducer(state, action)).toEqual(expectedState);
});
@ -106,13 +102,16 @@ describe('Admin | containers | Webhooks | EditView | reducer', () => {
keys: ['headers'],
};
const expectedState = state.setIn(
['modifiedData', 'headers'],
fromJS([
{ key: '', value: '' },
{ key: '', value: '' },
])
);
const expectedState = {
...state,
modifiedData: {
...state.modifiedData,
headers: [
{ key: '', value: '' },
{ key: '', value: '' },
],
},
};
expect(reducer(state, action)).toEqual(expectedState);
});
@ -122,9 +121,16 @@ describe('Admin | containers | Webhooks | EditView | reducer', () => {
{ key: 'accept', value: 'text/html' },
{ key: 'authorization', value: 'Basic YWxhZGRpbjpvcGVuc2VzYW1l' },
];
const state = initialState
.setIn(['initialData', 'headers'], fromJS(initialHeaders))
.setIn(['modifiedData', 'headers'], fromJS(initialHeaders));
const state = {
...initialState,
initialData: {
headers: initialHeaders,
},
modifiedData: {
ok: true,
headers: initialHeaders,
},
};
const action = {
type: 'ON_HEADER_REMOVE',
@ -132,26 +138,25 @@ describe('Admin | containers | Webhooks | EditView | reducer', () => {
};
const updatedHeaders = [{ key: 'accept', value: 'text/html' }];
const expectedState = state.setIn(['modifiedData', 'headers'], fromJS(updatedHeaders));
const expectedState = {
...state,
modifiedData: { ...state.modifiedData, headers: updatedHeaders },
};
expect(reducer(state, action)).toEqual(expectedState);
});
it('should clear a header to modifiedData if it is the last', () => {
const initialHeaders = [{ key: 'accept', value: 'text/html' }];
const state = initialState
.setIn(['initialData', 'headers'], fromJS(initialHeaders))
.setIn(['modifiedData', 'headers'], fromJS(initialHeaders));
const initialData = { headers: initialHeaders, ok: true };
const state = { ...initialState, initialData, modifiedData: initialData };
const action = {
type: 'ON_HEADER_REMOVE',
index: 0,
};
const updatedHeaders = [header];
const expectedState = state.setIn(['modifiedData', 'headers'], fromJS(updatedHeaders));
const expectedState = { ...state, modifiedData: { headers: [header], ok: true } };
expect(reducer(state, action)).toEqual(expectedState);
});
@ -159,28 +164,31 @@ describe('Admin | containers | Webhooks | EditView | reducer', () => {
describe('Trigger actions', () => {
it('should set isTriggering to false when trigger action is canceled', () => {
const state = initialState.set('isTriggering', true);
const state = { ...initialState, isTriggering: true };
const action = {
type: 'ON_TRIGGER_CANCELED',
};
const expectedState = state.update('isTriggering', () => false);
const expectedState = { ...state, isTriggering: false };
expect(reducer(state, action)).toEqual(expectedState);
});
it('should clear triggerResponse when trigger action is canceled', () => {
const state = initialState.set('triggerResponse', {
statusCode: 200,
message: 'succeed',
});
const state = {
...initialState,
triggerResponse: {
statusCode: 200,
message: 'succeed',
},
};
const action = {
type: 'ON_TRIGGER_CANCELED',
};
const expectedState = state.update('triggerResponse', () => fromJS({}));
const expectedState = { ...state, triggerResponse: {} };
expect(reducer(state, action)).toEqual(expectedState);
});
@ -192,13 +200,13 @@ describe('Admin | containers | Webhooks | EditView | reducer', () => {
type: 'SET_IS_TRIGGERING',
};
const expectedState = state.set('isTriggering', true);
const expectedState = { ...state, isTriggering: true };
expect(reducer(state, action)).toEqual(expectedState);
});
it('should update isTriggering and triggerResponse if trigger succeed', () => {
const state = initialState.set('isTriggering', true);
const state = { ...initialState, isTriggering: true };
const action = {
type: 'TRIGGER_SUCCEEDED',
@ -208,9 +216,7 @@ describe('Admin | containers | Webhooks | EditView | reducer', () => {
},
};
const expectedState = state
.update('triggerResponse', () => fromJS(action.response))
.update('isTriggering', () => false);
const expectedState = { ...state, triggerResponse: action.response, isTriggering: false };
expect(reducer(state, action)).toEqual(expectedState);
});
@ -218,15 +224,13 @@ describe('Admin | containers | Webhooks | EditView | reducer', () => {
describe('Reset form', () => {
it('should reset modifiedData with initialData values', () => {
const state = initialState
.setIn(['modifiedData', 'name'], 'updated name')
.setIn(['modifiedData', 'url'], 'updated url');
const state = { ...initialState, modifiedData: { name: 'updated name', url: 'updated url' } };
const action = {
type: 'RESET_FORM',
};
const expectedState = state.update('modifiedData', () => state.get('initialData'));
const expectedState = initialState;
expect(reducer(state, action)).toEqual(expectedState);
});
@ -243,7 +247,7 @@ describe('Admin | containers | Webhooks | EditView | reducer', () => {
},
};
const expectedState = state.update('formErrors', () => fromJS(action.errors));
const expectedState = { ...state, formErrors: action.errors };
expect(reducer(state, action)).toEqual(expectedState);
});

View File

@ -1,7 +1,7 @@
import { set } from 'lodash';
const cleanData = data => {
const webhooks = data;
const webhooks = { ...data };
set(webhooks, 'headers', unformatHeaders(data.headers));

View File

@ -35,12 +35,13 @@ function ListView() {
const isMounted = useRef(true);
const { formatMessage } = useIntl();
const [showModal, setShowModal] = useState(false);
const [reducerState, dispatch] = useReducer(reducer, initialState);
const [{ webhooks, webhooksToDelete, webhookToDelete }, dispatch] = useReducer(
reducer,
initialState
);
const { push } = useHistory();
const { pathname } = useLocation();
const { webhooks, webhooksToDelete, webhookToDelete } = reducerState.toJS();
useEffect(() => {
isMounted.current = true;

View File

@ -1,43 +1,56 @@
import { fromJS } from 'immutable';
import produce from 'immer';
import set from 'lodash/set';
const initialState = fromJS({
const initialState = {
webhooks: [],
webhooksToDelete: [],
webhookToDelete: null,
});
};
const reducer = (state, action) => {
switch (action.type) {
case 'GET_DATA_SUCCEEDED':
return state.update('webhooks', () => fromJS(action.data));
case 'SET_WEBHOOK_ENABLED':
return state.updateIn(['webhooks', ...action.keys], () => action.value);
case 'SET_WEBHOOK_TO_DELETE':
return state.update('webhookToDelete', () => action.id);
case 'SET_WEBHOOKS_TO_DELETE':
return state.update('webhooksToDelete', list => {
const reducer = (state, action) =>
// eslint-disable-next-line consistent-return
produce(state, draftState => {
switch (action.type) {
case 'GET_DATA_SUCCEEDED': {
draftState.webhooks = action.data;
break;
}
case 'SET_WEBHOOK_ENABLED': {
set(draftState, ['webhooks', ...action.keys], action.value);
break;
}
case 'SET_WEBHOOK_TO_DELETE': {
draftState.webhookToDelete = action.id;
break;
}
case 'SET_WEBHOOKS_TO_DELETE': {
if (action.value) {
return list.push(action.id);
draftState.webhooksToDelete.push(action.id);
} else {
draftState.webhooksToDelete = state.webhooksToDelete.filter(id => id !== action.id);
}
return list.filter(data => data !== action.id);
});
case 'WEBHOOKS_DELETED':
return state
.update('webhooks', webhooks =>
webhooks.filter(webhook => {
return !state.get('webhooksToDelete').includes(webhook.get('id'));
})
)
.update('webhooksToDelete', () => []);
case 'WEBHOOK_DELETED':
return state
.update('webhooks', webhooks => webhooks.remove(action.index))
.update('webhookToDelete', () => null);
default:
return state;
}
};
break;
}
case 'WEBHOOKS_DELETED': {
draftState.webhooks = state.webhooks.filter(
webhook => !state.webhooksToDelete.includes(webhook.id)
);
draftState.webhooksToDelete = [];
break;
}
case 'WEBHOOK_DELETED': {
draftState.webhooks = state.webhooks.filter((_, index) => index !== action.index);
draftState.webhookToDelete = null;
break;
}
default:
return draftState;
}
});
export default reducer;
export { initialState };

View File

@ -1,12 +1,11 @@
import { fromJS } from 'immutable';
import reducer from '../reducer';
describe('Admin | containers | Webhooks | ListView | reducer', () => {
const initialState = fromJS({
const initialState = {
webhooks: [],
webhooksToDelete: [],
webhookToDelete: null,
});
};
describe('Load webhooks', () => {
it('should update webhooks with received data', () => {
@ -35,7 +34,7 @@ describe('Admin | containers | Webhooks | ListView | reducer', () => {
data: receivedData,
};
const expectedState = state.set('webhooks', fromJS(receivedData));
const expectedState = { ...state, webhooks: receivedData };
expect(reducer(state, action)).toEqual(expectedState);
});
@ -61,15 +60,35 @@ describe('Admin | containers | Webhooks | ListView | reducer', () => {
isEnabled: false,
},
];
const state = initialState.set('webhooks', fromJS(webhooks));
const state = { ...initialState, webhooks };
const action = {
type: 'SET_WEBHOOK_ENABLED',
keys: [1, 'isEnabled'],
value: false,
value: true,
};
const expectedState = state.setIn(['webhooks', 1, 'isEnabled'], false);
const expectedState = {
...state,
webhooks: [
{
id: 1,
name: 'webhook 1',
url: 'http://localhost:5000',
headers: {},
events: ['entry.create', 'entry.update', 'entry.delete'],
isEnabled: true,
},
{
id: 2,
name: 'webhook 2',
url: 'http://localhost:4000',
headers: {},
events: ['media.create', 'media.update'],
isEnabled: true,
},
],
};
expect(reducer(state, action)).toEqual(expectedState);
});
@ -95,13 +114,13 @@ describe('Admin | containers | Webhooks | ListView | reducer', () => {
isEnabled: false,
},
];
const state = initialState.set('webhooks', fromJS(webhooks));
const state = { ...initialState, webhooks };
const action = {
type: 'SET_WEBHOOK_TO_DELETE',
id: 1,
};
const expectedState = state.set('webhookToDelete', 1);
const expectedState = { ...state, webhookToDelete: 1 };
expect(reducer(state, action)).toEqual(expectedState);
});
@ -125,14 +144,14 @@ describe('Admin | containers | Webhooks | ListView | reducer', () => {
isEnabled: false,
},
];
const state = initialState.set('webhooks', fromJS(webhooks));
const state = { ...initialState, webhooks };
const action = {
type: 'SET_WEBHOOKS_TO_DELETE',
id: 1,
value: true,
};
const expectedState = state.set('webhooksToDelete', fromJS([1]));
const expectedState = { ...state, webhooksToDelete: [1] };
expect(reducer(state, action)).toEqual(expectedState);
});
@ -156,16 +175,15 @@ describe('Admin | containers | Webhooks | ListView | reducer', () => {
isEnabled: false,
},
];
const state = initialState
.set('webhooks', fromJS(webhooks))
.set('webhooksToDelete', fromJS([1, 2]));
const state = { ...initialState, webhooks, webhooksToDelete: [1, 2] };
const action = {
type: 'SET_WEBHOOKS_TO_DELETE',
id: 1,
value: false,
};
const expectedState = state.set('webhooksToDelete', fromJS([2]));
const expectedState = { ...state, webhooksToDelete: [2] };
expect(reducer(state, action)).toEqual(expectedState);
});
@ -200,14 +218,12 @@ describe('Admin | containers | Webhooks | ListView | reducer', () => {
},
];
const state = initialState.set('webhooksToDelete', [1]).set('webhooks', fromJS(webhooks));
const state = { ...initialState, webhooksToDelete: [1], webhooks };
const action = {
type: 'WEBHOOKS_DELETED',
};
const expectedState = state
.set('webhooks', fromJS(updatedWebhooks))
.set('webhooksToDelete', []);
const expectedState = { ...state, webhooks: updatedWebhooks, webhooksToDelete: [] };
expect(reducer(state, action)).toEqual(expectedState);
});
@ -260,18 +276,14 @@ describe('Admin | containers | Webhooks | ListView | reducer', () => {
const webhookIdToDelete = 4;
const state = initialState
.set('webhookToDelete', webhookIdToDelete)
.set('webhooks', fromJS(webhooks));
const state = { ...initialState, webhookToDelete: webhookIdToDelete, webhooks };
const action = {
type: 'WEBHOOK_DELETED',
index: 1,
};
const expectedState = state
.set('webhooks', fromJS(updatedWebhooks))
.set('webhookToDelete', null);
const expectedState = { ...state, webhooks: updatedWebhooks, webhookToDelete: null };
expect(reducer(state, action)).toEqual(expectedState);
});