diff --git a/packages/core/admin/admin/src/pages/Webhooks/EditView/index.js b/packages/core/admin/admin/src/pages/Webhooks/EditView/index.js index 93bbf72d40..eff6326e30 100644 --- a/packages/core/admin/admin/src/pages/Webhooks/EditView/index.js +++ b/packages/core/admin/admin/src/pages/Webhooks/EditView/index.js @@ -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), diff --git a/packages/core/admin/admin/src/pages/Webhooks/EditView/reducer.js b/packages/core/admin/admin/src/pages/Webhooks/EditView/reducer.js index 670e732ca4..41296d6396 100644 --- a/packages/core/admin/admin/src/pages/Webhooks/EditView/reducer.js +++ b/packages/core/admin/admin/src/pages/Webhooks/EditView/reducer.js @@ -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 }; diff --git a/packages/core/admin/admin/src/pages/Webhooks/EditView/tests/reducer.test.js b/packages/core/admin/admin/src/pages/Webhooks/EditView/tests/reducer.test.js index b5c6a502c0..2f30cb40b4 100644 --- a/packages/core/admin/admin/src/pages/Webhooks/EditView/tests/reducer.test.js +++ b/packages/core/admin/admin/src/pages/Webhooks/EditView/tests/reducer.test.js @@ -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); }); diff --git a/packages/core/admin/admin/src/pages/Webhooks/EditView/utils/formatData.js b/packages/core/admin/admin/src/pages/Webhooks/EditView/utils/formatData.js index 23203001fb..1e47405b2a 100644 --- a/packages/core/admin/admin/src/pages/Webhooks/EditView/utils/formatData.js +++ b/packages/core/admin/admin/src/pages/Webhooks/EditView/utils/formatData.js @@ -1,7 +1,7 @@ import { set } from 'lodash'; const cleanData = data => { - const webhooks = data; + const webhooks = { ...data }; set(webhooks, 'headers', unformatHeaders(data.headers)); diff --git a/packages/core/admin/admin/src/pages/Webhooks/ListView/index.js b/packages/core/admin/admin/src/pages/Webhooks/ListView/index.js index 0f63d51983..32addf54b1 100644 --- a/packages/core/admin/admin/src/pages/Webhooks/ListView/index.js +++ b/packages/core/admin/admin/src/pages/Webhooks/ListView/index.js @@ -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; diff --git a/packages/core/admin/admin/src/pages/Webhooks/ListView/reducer.js b/packages/core/admin/admin/src/pages/Webhooks/ListView/reducer.js index 652ff1233d..fbcd417c97 100644 --- a/packages/core/admin/admin/src/pages/Webhooks/ListView/reducer.js +++ b/packages/core/admin/admin/src/pages/Webhooks/ListView/reducer.js @@ -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 }; diff --git a/packages/core/admin/admin/src/pages/Webhooks/ListView/tests/reducer.test.js b/packages/core/admin/admin/src/pages/Webhooks/ListView/tests/reducer.test.js index c0041b3832..6805746df0 100644 --- a/packages/core/admin/admin/src/pages/Webhooks/ListView/tests/reducer.test.js +++ b/packages/core/admin/admin/src/pages/Webhooks/ListView/tests/reducer.test.js @@ -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); });