From 72acf2309c685c77eade58605fb70e2c6c86d10f Mon Sep 17 00:00:00 2001 From: olav Date: Fri, 4 Feb 2022 14:25:56 +0100 Subject: [PATCH] refactor: port EventHistory to TS/SWR (#669) * refactor: port EventHistory to TS/SWR * refactor: fix interface type prefix * refactor: split useEvents and useFeatureEvents hooks Co-authored-by: Fredrik Strand Oseberg --- .../FeatureView/FeatureLog/FeatureLog.tsx | 8 +++- .../history/EventHistory/EventHistory.tsx | 20 +++------ .../component/history/EventHistory/index.js | 16 ------- .../FeatureEventHistory.jsx | 22 +++------- .../history/FeatureEventHistory/index.jsx | 25 ----------- .../hooks/api/getters/useEvents/useEvents.ts | 39 +++++++++++++++++ .../useFeatureEvents/useFeatureEvents.ts | 42 +++++++++++++++++++ frontend/src/interfaces/event.ts | 14 +++++++ frontend/src/page/history/index.js | 6 +-- .../page/history/{toggle.js => toggle.jsx} | 4 +- frontend/src/store/history/actions.js | 33 --------------- frontend/src/store/history/api.js | 21 ---------- frontend/src/store/history/index.js | 23 ---------- frontend/src/store/index.js | 2 - 14 files changed, 117 insertions(+), 158 deletions(-) delete mode 100644 frontend/src/component/history/EventHistory/index.js delete mode 100644 frontend/src/component/history/FeatureEventHistory/index.jsx create mode 100644 frontend/src/hooks/api/getters/useEvents/useEvents.ts create mode 100644 frontend/src/hooks/api/getters/useFeatureEvents/useFeatureEvents.ts create mode 100644 frontend/src/interfaces/event.ts rename frontend/src/page/history/{toggle.js => toggle.jsx} (55%) delete mode 100644 frontend/src/store/history/actions.js delete mode 100644 frontend/src/store/history/api.js delete mode 100644 frontend/src/store/history/index.js diff --git a/frontend/src/component/feature/FeatureView/FeatureLog/FeatureLog.tsx b/frontend/src/component/feature/FeatureView/FeatureLog/FeatureLog.tsx index 6d8185940a..74f4cab009 100644 --- a/frontend/src/component/feature/FeatureView/FeatureLog/FeatureLog.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureLog/FeatureLog.tsx @@ -2,16 +2,20 @@ import { useParams } from 'react-router'; import useFeature from '../../../../hooks/api/getters/useFeature/useFeature'; import { useStyles } from './FeatureLog.styles'; import { IFeatureViewParams } from '../../../../interfaces/params'; -import HistoryComponent from '../../../history/FeatureEventHistory'; +import { FeatureEventHistory } from '../../../history/FeatureEventHistory/FeatureEventHistory'; const FeatureLog = () => { const styles = useStyles(); const { projectId, featureId } = useParams(); const { feature } = useFeature(projectId, featureId); + if (!feature.name) { + return null + } + return (
- +
); }; diff --git a/frontend/src/component/history/EventHistory/EventHistory.tsx b/frontend/src/component/history/EventHistory/EventHistory.tsx index 3821f8cb1d..ec989e9348 100644 --- a/frontend/src/component/history/EventHistory/EventHistory.tsx +++ b/frontend/src/component/history/EventHistory/EventHistory.tsx @@ -1,22 +1,12 @@ -import { useEffect } from 'react'; - import EventLog from '../EventLog'; +import { useEvents } from '../../../hooks/api/getters/useEvents/useEvents'; -interface IEventLogProps { - fetchHistory: () => void; - history: History; -} +export const EventHistory = () => { + const { events } = useEvents(); -const EventHistory = ({ fetchHistory, history }: IEventLogProps) => { - useEffect(() => { - fetchHistory(); - }, [fetchHistory]); - - if (history.length < 0) { + if (events.length < 0) { return null; } - return ; + return ; }; - -export default EventHistory; diff --git a/frontend/src/component/history/EventHistory/index.js b/frontend/src/component/history/EventHistory/index.js deleted file mode 100644 index 2cbef8ad75..0000000000 --- a/frontend/src/component/history/EventHistory/index.js +++ /dev/null @@ -1,16 +0,0 @@ -import { connect } from 'react-redux'; -import EventHistory from './EventHistory'; -import { fetchHistory } from '../../../store/history/actions'; - -const mapStateToProps = state => { - const history = state.history.get('list').toArray(); - return { - history, - }; -}; - -const EventHistoryContainer = connect(mapStateToProps, { fetchHistory })( - EventHistory -); - -export default EventHistoryContainer; diff --git a/frontend/src/component/history/FeatureEventHistory/FeatureEventHistory.jsx b/frontend/src/component/history/FeatureEventHistory/FeatureEventHistory.jsx index 6b767ae72c..eac9a008a7 100644 --- a/frontend/src/component/history/FeatureEventHistory/FeatureEventHistory.jsx +++ b/frontend/src/component/history/FeatureEventHistory/FeatureEventHistory.jsx @@ -1,29 +1,19 @@ import PropTypes from 'prop-types'; -import { useEffect } from 'react'; import EventLog from '../EventLog'; +import { useFeatureEvents } from '../../../hooks/api/getters/useFeatureEvents/useFeatureEvents'; -const FeatureEventHistory = ({ - toggleName, - history, - fetchHistoryForToggle, -}) => { - useEffect(() => { - fetchHistoryForToggle(toggleName); - }, [fetchHistoryForToggle, toggleName]); +export const FeatureEventHistory = ({ toggleName }) => { + const { events } = useFeatureEvents(toggleName); - if (!history || history.length === 0) { - return fetching..; + if (events.length === 0) { + return null; } return ( - + ); }; FeatureEventHistory.propTypes = { toggleName: PropTypes.string.isRequired, - history: PropTypes.array, - fetchHistoryForToggle: PropTypes.func.isRequired, }; - -export default FeatureEventHistory; diff --git a/frontend/src/component/history/FeatureEventHistory/index.jsx b/frontend/src/component/history/FeatureEventHistory/index.jsx deleted file mode 100644 index 797151f55b..0000000000 --- a/frontend/src/component/history/FeatureEventHistory/index.jsx +++ /dev/null @@ -1,25 +0,0 @@ -import { connect } from 'react-redux'; -import FeatureEventHistory from './FeatureEventHistory'; -import { fetchHistoryForToggle } from '../../../store/history/actions'; - -function getHistoryFromToggle(state, toggleName) { - if (!toggleName) { - return []; - } - - if (state.history.hasIn(['toggles', toggleName])) { - return state.history.getIn(['toggles', toggleName]).toArray(); - } - - return []; -} - -const mapStateToProps = (state, props) => ({ - history: getHistoryFromToggle(state, props.toggleName), -}); - -const FeatureEventHistoryContainer = connect(mapStateToProps, { - fetchHistoryForToggle, -})(FeatureEventHistory); - -export default FeatureEventHistoryContainer; diff --git a/frontend/src/hooks/api/getters/useEvents/useEvents.ts b/frontend/src/hooks/api/getters/useEvents/useEvents.ts new file mode 100644 index 0000000000..7c64b21271 --- /dev/null +++ b/frontend/src/hooks/api/getters/useEvents/useEvents.ts @@ -0,0 +1,39 @@ +import useSWR, { mutate, SWRConfiguration } from 'swr'; +import { useCallback } from 'react'; +import { formatApiPath } from '../../../../utils/format-path'; +import handleErrorResponses from '../httpErrorResponseHandler'; +import { IEvent } from '../../../../interfaces/event'; + +const PATH = formatApiPath('api/admin/events'); + +export interface IUseEventsOutput { + events: IEvent[]; + refetchEvents: () => void; + loading: boolean; + error?: Error; +} + +export const useEvents = (options?: SWRConfiguration): IUseEventsOutput => { + const { data, error } = useSWR<{ events: IEvent[] }>( + PATH, + fetchAllEvents, + options + ); + + const refetchEvents = useCallback(() => { + mutate(PATH).catch(console.warn); + }, []); + + return { + events: data?.events || [], + loading: !error && !data, + refetchEvents, + error, + }; +}; + +const fetchAllEvents = () => { + return fetch(PATH, { method: 'GET' }) + .then(handleErrorResponses('Event history')) + .then(res => res.json()); +}; diff --git a/frontend/src/hooks/api/getters/useFeatureEvents/useFeatureEvents.ts b/frontend/src/hooks/api/getters/useFeatureEvents/useFeatureEvents.ts new file mode 100644 index 0000000000..3aa76433ec --- /dev/null +++ b/frontend/src/hooks/api/getters/useFeatureEvents/useFeatureEvents.ts @@ -0,0 +1,42 @@ +import useSWR, { mutate, SWRConfiguration } from 'swr'; +import { useCallback } from 'react'; +import { formatApiPath } from '../../../../utils/format-path'; +import handleErrorResponses from '../httpErrorResponseHandler'; +import { IEvent } from '../../../../interfaces/event'; + +const PATH = formatApiPath('api/admin/events'); + +export interface IUseEventsOutput { + events: IEvent[]; + refetchEvents: () => void; + loading: boolean; + error?: Error; +} + +export const useFeatureEvents = ( + featureName: string, + options?: SWRConfiguration +): IUseEventsOutput => { + const { data, error } = useSWR<{ events: IEvent[] }>( + [PATH, featureName], + () => fetchFeatureEvents(featureName), + options + ); + + const refetchEvents = useCallback(() => { + mutate(PATH).catch(console.warn); + }, []); + + return { + events: data?.events || [], + loading: !error && !data, + refetchEvents, + error, + }; +}; + +const fetchFeatureEvents = (featureName: string) => { + return fetch(`${PATH}/${featureName}`, { method: 'GET' }) + .then(handleErrorResponses('Event history')) + .then(res => res.json()); +}; diff --git a/frontend/src/interfaces/event.ts b/frontend/src/interfaces/event.ts new file mode 100644 index 0000000000..6626cc5d52 --- /dev/null +++ b/frontend/src/interfaces/event.ts @@ -0,0 +1,14 @@ +import { ITag } from './tags'; + +export interface IEvent { + id: number; + createdAt: string; + type: string; + createdBy: string; + project?: string; + environment?: string; + featureName?: string; + data?: any; + preData?: any; + tags?: ITag[]; +} diff --git a/frontend/src/page/history/index.js b/frontend/src/page/history/index.js index a5f86a8c30..36c8a2f149 100644 --- a/frontend/src/page/history/index.js +++ b/frontend/src/page/history/index.js @@ -2,16 +2,16 @@ import { Alert } from '@material-ui/lab'; import React, { useContext } from 'react'; import { ADMIN } from '../../component/providers/AccessProvider/permissions'; import ConditionallyRender from '../../component/common/ConditionallyRender'; -import HistoryComponent from '../../component/history/EventHistory'; +import { EventHistory } from '../../component/history/EventHistory/EventHistory'; import AccessContext from '../../contexts/AccessContext'; -const HistoryPage = ({ history }) => { +const HistoryPage = () => { const { hasAccess } = useContext(AccessContext); return ( } + show={} elseShow={ You need instance admin to access this section. diff --git a/frontend/src/page/history/toggle.js b/frontend/src/page/history/toggle.jsx similarity index 55% rename from frontend/src/page/history/toggle.js rename to frontend/src/page/history/toggle.jsx index 428e298826..b89257af1c 100644 --- a/frontend/src/page/history/toggle.js +++ b/frontend/src/page/history/toggle.jsx @@ -1,9 +1,9 @@ import React from 'react'; import PropTypes from 'prop-types'; -import HistoryListToggle from '../../component/history/FeatureEventHistory'; +import { FeatureEventHistory } from '../../component/history/FeatureEventHistory/FeatureEventHistory'; const render = ({ match: { params } }) => ( - + ); render.propTypes = { diff --git a/frontend/src/store/history/actions.js b/frontend/src/store/history/actions.js deleted file mode 100644 index efbb5f622b..0000000000 --- a/frontend/src/store/history/actions.js +++ /dev/null @@ -1,33 +0,0 @@ -import api from './api'; -import { dispatchError } from '../util'; - -export const RECEIVE_HISTORY = 'RECEIVE_HISTORY'; -export const ERROR_RECEIVE_HISTORY = 'ERROR_RECEIVE_HISTORY'; - -export const RECEIVE_HISTORY_FOR_TOGGLE = 'RECEIVE_HISTORY_FOR_TOGGLE'; - -const receiveHistory = json => ({ - type: RECEIVE_HISTORY, - value: json.events, -}); - -const receiveHistoryforToggle = json => ({ - type: RECEIVE_HISTORY_FOR_TOGGLE, - value: json, -}); - -export function fetchHistory() { - return dispatch => - api - .fetchAll() - .then(json => dispatch(receiveHistory(json))) - .catch(dispatchError(dispatch, ERROR_RECEIVE_HISTORY)); -} - -export function fetchHistoryForToggle(toggleName) { - return dispatch => - api - .fetchHistoryForToggle(toggleName) - .then(json => dispatch(receiveHistoryforToggle(json))) - .catch(dispatchError(dispatch, ERROR_RECEIVE_HISTORY)); -} diff --git a/frontend/src/store/history/api.js b/frontend/src/store/history/api.js deleted file mode 100644 index 8dfabe6f06..0000000000 --- a/frontend/src/store/history/api.js +++ /dev/null @@ -1,21 +0,0 @@ -import { formatApiPath } from '../../utils/format-path'; -import { throwIfNotSuccess } from '../api-helper'; - -const URI = formatApiPath('api/admin/events'); - -function fetchAll() { - return fetch(URI, { credentials: 'include' }) - .then(throwIfNotSuccess) - .then(response => response.json()); -} - -function fetchHistoryForToggle(toggleName) { - return fetch(`${URI}/${toggleName}`, { credentials: 'include' }) - .then(throwIfNotSuccess) - .then(response => response.json()); -} - -export default { - fetchAll, - fetchHistoryForToggle, -}; diff --git a/frontend/src/store/history/index.js b/frontend/src/store/history/index.js deleted file mode 100644 index b58488cc35..0000000000 --- a/frontend/src/store/history/index.js +++ /dev/null @@ -1,23 +0,0 @@ -import { List, Map as $Map } from 'immutable'; -import { RECEIVE_HISTORY, RECEIVE_HISTORY_FOR_TOGGLE } from './actions'; -import { USER_LOGOUT, USER_LOGIN } from '../user/actions'; - -function getInitState() { - return new $Map({ list: new List(), toggles: new $Map() }); -} - -const historyStore = (state = getInitState(), action) => { - switch (action.type) { - case RECEIVE_HISTORY_FOR_TOGGLE: - return state.setIn(['toggles', action.value.toggleName], new List(action.value.events)); - case RECEIVE_HISTORY: - return state.set('list', new List(action.value)); - case USER_LOGOUT: - case USER_LOGIN: - return getInitState(); - default: - return state; - } -}; - -export default historyStore; diff --git a/frontend/src/store/index.js b/frontend/src/store/index.js index 70ecc0b250..b329b9bab4 100644 --- a/frontend/src/store/index.js +++ b/frontend/src/store/index.js @@ -6,7 +6,6 @@ import featureTags from './feature-tags'; import tagTypes from './tag-type'; import tags from './tag'; import strategies from './strategy'; -import history from './history'; // eslint-disable-line import archive from './archive'; import error from './error'; import settings from './settings'; @@ -29,7 +28,6 @@ const unleashStore = combineReducers({ tagTypes, tags, featureTags, - history, archive, error, settings,