diff --git a/frontend/package.json b/frontend/package.json index f44dd856b2..88a7544a8c 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -67,6 +67,7 @@ "fetch-mock": "9.11.0", "http-proxy-middleware": "2.0.2", "immutable": "4.0.0", + "@types/lodash.clonedeep": "^4.5.6", "lodash.clonedeep": "4.5.0", "lodash.flow": "3.5.0", "node-fetch": "2.6.7", diff --git a/frontend/src/component/addons/AddonForm/AddonEvents/AddonEvents.tsx b/frontend/src/component/addons/AddonForm/AddonEvents/AddonEvents.tsx new file mode 100644 index 0000000000..3beb70432c --- /dev/null +++ b/frontend/src/component/addons/AddonForm/AddonEvents/AddonEvents.tsx @@ -0,0 +1,43 @@ +import React from 'react'; +import { Grid, FormControlLabel, Checkbox } from '@material-ui/core'; + +import { styles as commonStyles } from '../../../common'; +import { IAddonProvider } from '../../../../interfaces/addons'; + +interface IAddonProps { + provider: IAddonProvider; + checkedEvents: string[]; + setEventValue: (name: string) => void; + error: Record; +} + +export const AddonEvents = ({ + provider, + checkedEvents, + setEventValue, + error, +}: IAddonProps) => { + if (!provider) return null; + + return ( + +

Events

+ {error} + + {provider.events.map(e => ( + + + } + label={e} + /> + + ))} + +
+ ); +}; diff --git a/frontend/src/component/addons/form-addon-component.jsx b/frontend/src/component/addons/AddonForm/AddonForm.jsx similarity index 87% rename from frontend/src/component/addons/form-addon-component.jsx rename to frontend/src/component/addons/AddonForm/AddonForm.jsx index e4483d6593..d47e76c6d1 100644 --- a/frontend/src/component/addons/form-addon-component.jsx +++ b/frontend/src/component/addons/AddonForm/AddonForm.jsx @@ -1,23 +1,29 @@ import React, { useState, useEffect } from 'react'; import PropTypes from 'prop-types'; import { TextField, FormControlLabel, Switch } from '@material-ui/core'; - -import { FormButtons, styles as commonStyles } from '../common'; -import { trim } from '../common/util'; -import AddonParameters from './form-addon-parameters'; -import AddonEvents from './form-addon-events'; +import { FormButtons, styles as commonStyles } from '../../common'; +import { trim } from '../../common/util'; +import { AddonParameters } from './AddonParameters/AddonParameters'; +import { AddonEvents } from './AddonEvents/AddonEvents'; import cloneDeep from 'lodash.clonedeep'; - -import styles from './form-addon-component.module.scss'; -import PageContent from '../common/PageContent/PageContent'; -import useAddonsApi from '../../hooks/api/actions/useAddonsApi/useAddonsApi'; -import useToast from '../../hooks/useToast'; +import PageContent from '../../common/PageContent/PageContent'; import { useHistory } from 'react-router-dom'; +import useAddonsApi from '../../../hooks/api/actions/useAddonsApi/useAddonsApi'; +import useToast from '../../../hooks/useToast'; +import { makeStyles } from '@material-ui/styles'; -const AddonFormComponent = ({ editMode, provider, addon, fetch }) => { +const useStyles = makeStyles(theme => ({ + nameInput: { + marginRight: '1.5rem', + }, + formSection: { padding: '10px 28px' }, +})); + +export const AddonForm = ({ editMode, provider, addon, fetch }) => { const { createAddon, updateAddon } = useAddonsApi(); const { setToastData, setToastApiError } = useToast(); const history = useHistory(); + const styles = useStyles(); const [config, setConfig] = useState(addon); const [errors, setErrors] = useState({ @@ -116,6 +122,7 @@ const AddonFormComponent = ({ editMode, provider, addon, fetch }) => { history.push('/addons'); setToastData({ type: 'success', + confetti: true, title: 'Addon created successfully', }); } @@ -196,14 +203,17 @@ const AddonFormComponent = ({ editMode, provider, addon, fetch }) => { />
- +
); }; -AddonFormComponent.propTypes = { +AddonForm.propTypes = { provider: PropTypes.object, addon: PropTypes.object.isRequired, fetch: PropTypes.func.isRequired, @@ -211,5 +221,3 @@ AddonFormComponent.propTypes = { cancel: PropTypes.func.isRequired, editMode: PropTypes.bool.isRequired, }; - -export default AddonFormComponent; diff --git a/frontend/src/component/addons/AddonForm/AddonParameters/AddonParameter/AddonParameter.tsx b/frontend/src/component/addons/AddonForm/AddonParameters/AddonParameter/AddonParameter.tsx new file mode 100644 index 0000000000..40441955f8 --- /dev/null +++ b/frontend/src/component/addons/AddonForm/AddonParameters/AddonParameter/AddonParameter.tsx @@ -0,0 +1,60 @@ +import { TextField } from '@material-ui/core'; +import { + IAddonConfig, + IAddonProvider, + IAddonProviderParams, +} from '../../../../../interfaces/addons'; + +const resolveType = ({ type = 'text', sensitive = false }, value: string) => { + if (sensitive && value === MASKED_VALUE) { + return 'text'; + } + if (type === 'textfield') { + return 'text'; + } + return type; +}; + +const MASKED_VALUE = '*****'; + +interface IAddonParameterProps { + provider: IAddonProvider; + errors: Record; + definition: IAddonProviderParams; + setParameterValue: (param: string) => void; + config: IAddonConfig; +} + +export const AddonParameter = ({ + definition, + config, + errors, + setParameterValue, +}: IAddonParameterProps) => { + const value = config.parameters[definition.name] || ''; + const type = resolveType(definition, value); + const error = errors.parameters[definition.name]; + + return ( +
+ +
+ ); +}; diff --git a/frontend/src/component/addons/AddonForm/AddonParameters/AddonParameters.tsx b/frontend/src/component/addons/AddonForm/AddonParameters/AddonParameters.tsx new file mode 100644 index 0000000000..10536f5bd2 --- /dev/null +++ b/frontend/src/component/addons/AddonForm/AddonParameters/AddonParameters.tsx @@ -0,0 +1,43 @@ +import React from 'react'; +import { IAddonConfig, IAddonProvider } from '../../../../interfaces/addons'; +import { AddonParameter } from './AddonParameter/AddonParameter'; + +interface IAddonParametersProps { + provider: IAddonProvider; + errors: Record; + editMode: boolean; + setParameterValue: (param: string) => void; + config: IAddonConfig; +} + +export const AddonParameters = ({ + provider, + config, + errors, + setParameterValue, + editMode, +}: IAddonParametersProps) => { + if (!provider) return null; + + return ( + +

Parameters

+ {editMode ? ( +

+ Sensitive parameters will be masked with value "***** + ". If you don't change the value they will not be updated + when saving. +

+ ) : null} + {provider.parameters.map(parameter => ( + + ))} +
+ ); +}; diff --git a/frontend/src/component/addons/AddonList/AddonList.jsx b/frontend/src/component/addons/AddonList/AddonList.tsx similarity index 69% rename from frontend/src/component/addons/AddonList/AddonList.jsx rename to frontend/src/component/addons/AddonList/AddonList.tsx index a849d2672b..8d96e5c3d0 100644 --- a/frontend/src/component/addons/AddonList/AddonList.jsx +++ b/frontend/src/component/addons/AddonList/AddonList.tsx @@ -1,12 +1,9 @@ -import { useContext, useEffect } from 'react'; -import ConfiguredAddons from './ConfiguredAddons'; -import AvailableAddons from './AvailableAddons'; +import { ReactElement } from 'react'; +import { ConfiguredAddons } from './ConfiguredAddons/ConfiguredAddons'; +import { AvailableAddons } from './AvailableAddons/AvailableAddons'; import { Avatar } from '@material-ui/core'; import { DeviceHub } from '@material-ui/icons'; - import ConditionallyRender from '../../common/ConditionallyRender/ConditionallyRender'; -import AccessContext from '../../../contexts/AccessContext'; - import slackIcon from '../../../assets/icons/slack.svg'; import jiraIcon from '../../../assets/icons/jira.svg'; import webhooksIcon from '../../../assets/icons/webhooks.svg'; @@ -14,7 +11,6 @@ import teamsIcon from '../../../assets/icons/teams.svg'; import dataDogIcon from '../../../assets/icons/datadog.svg'; import { formatAssetPath } from '../../../utils/format-path'; import useAddons from '../../../hooks/api/getters/useAddons/useAddons'; -import { useHistory } from 'react-router-dom'; const style = { width: '40px', @@ -23,7 +19,7 @@ const style = { float: 'left', }; -const getIcon = name => { +const getAddonIcon = (name: string): ReactElement => { switch (name) { case 'slack': return ( @@ -74,40 +70,21 @@ const getIcon = name => { } }; -const AddonList = () => { - const { hasAccess } = useContext(AccessContext); - const { addons, providers, refetchAddons } = useAddons(); - const history = useHistory(); - - useEffect(() => { - if (addons.length === 0) { - refetchAddons(); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [addons.length]); +export const AddonList = () => { + const { providers, addons } = useAddons(); return ( <> 0} - show={ - - } + show={} />
); }; - -export default AddonList; diff --git a/frontend/src/component/addons/AddonList/AvailableAddons/AvailableAddons.jsx b/frontend/src/component/addons/AddonList/AvailableAddons/AvailableAddons.jsx deleted file mode 100644 index 07adcafc07..0000000000 --- a/frontend/src/component/addons/AddonList/AvailableAddons/AvailableAddons.jsx +++ /dev/null @@ -1,57 +0,0 @@ -import React from 'react'; -import PageContent from '../../../common/PageContent/PageContent'; -import { - Button, - List, - ListItem, - ListItemAvatar, - ListItemSecondaryAction, - ListItemText, -} from '@material-ui/core'; -import ConditionallyRender from '../../../common/ConditionallyRender/ConditionallyRender'; -import { CREATE_ADDON } from '../../../providers/AccessProvider/permissions'; -import PropTypes from 'prop-types'; - -const AvailableAddons = ({ providers, getIcon, hasAccess, history }) => { - - const renderProvider = provider => ( - - {getIcon(provider.name)} - - - - history.push(`/addons/create/${provider.name}`) - } - title="Configure" - > - Configure - - } - /> - - - ); - return ( - - {providers.map(provider => renderProvider(provider))} - - ); -}; - -AvailableAddons.propTypes = { - providers: PropTypes.array.isRequired, - getIcon: PropTypes.func.isRequired, - hasAccess: PropTypes.func.isRequired, - history: PropTypes.object.isRequired, -}; - -export default AvailableAddons; diff --git a/frontend/src/component/addons/AddonList/AvailableAddons/AvailableAddons.tsx b/frontend/src/component/addons/AddonList/AvailableAddons/AvailableAddons.tsx new file mode 100644 index 0000000000..9632b0e9e5 --- /dev/null +++ b/frontend/src/component/addons/AddonList/AvailableAddons/AvailableAddons.tsx @@ -0,0 +1,63 @@ +import { ReactElement } from 'react'; +import PageContent from '../../../common/PageContent/PageContent'; +import { + List, + ListItem, + ListItemAvatar, + ListItemSecondaryAction, + ListItemText, +} from '@material-ui/core'; +import { CREATE_ADDON } from '../../../providers/AccessProvider/permissions'; +import { useHistory } from 'react-router-dom'; +import PermissionButton from '../../../common/PermissionButton/PermissionButton'; + +interface IProvider { + name: string; + displayName: string; + description: string; + documentationUrl: string; + parameters: object[]; + events: string[]; +} + +interface IAvailableAddonsProps { + getAddonIcon: (name: string) => ReactElement; + providers: IProvider[]; +} + +export const AvailableAddons = ({ + providers, + getAddonIcon, +}: IAvailableAddonsProps) => { + const history = useHistory(); + + const renderProvider = (provider: IProvider) => ( + + {getAddonIcon(provider.name)} + + + + history.push(`/addons/create/${provider.name}`) + } + tooltip={`Configure ${provider.name} Addon`} + > + Configure + + + + ); + return ( + + + {providers.map((provider: IProvider) => + renderProvider(provider) + )} + + + ); +}; diff --git a/frontend/src/component/addons/AddonList/AvailableAddons/index.jsx b/frontend/src/component/addons/AddonList/AvailableAddons/index.jsx deleted file mode 100644 index 00f3612923..0000000000 --- a/frontend/src/component/addons/AddonList/AvailableAddons/index.jsx +++ /dev/null @@ -1,3 +0,0 @@ -import AvailableAddons from './AvailableAddons'; - -export default AvailableAddons; diff --git a/frontend/src/component/addons/AddonList/ConfiguredAddons/ConfiguredAddons.jsx b/frontend/src/component/addons/AddonList/ConfiguredAddons/ConfiguredAddons.tsx similarity index 51% rename from frontend/src/component/addons/AddonList/ConfiguredAddons/ConfiguredAddons.jsx rename to frontend/src/component/addons/AddonList/ConfiguredAddons/ConfiguredAddons.tsx index 63db383913..233d57354b 100644 --- a/frontend/src/component/addons/AddonList/ConfiguredAddons/ConfiguredAddons.jsx +++ b/frontend/src/component/addons/AddonList/ConfiguredAddons/ConfiguredAddons.tsx @@ -1,6 +1,4 @@ -import React from 'react'; import { - IconButton, List, ListItem, ListItemAvatar, @@ -8,7 +6,6 @@ import { ListItemText, } from '@material-ui/core'; import { Visibility, VisibilityOff, Delete } from '@material-ui/icons'; - import ConditionallyRender from '../../../common/ConditionallyRender/ConditionallyRender'; import { DELETE_ADDON, @@ -16,17 +13,43 @@ import { } from '../../../providers/AccessProvider/permissions'; import { Link } from 'react-router-dom'; import PageContent from '../../../common/PageContent/PageContent'; -import PropTypes from 'prop-types'; import useAddons from '../../../../hooks/api/getters/useAddons/useAddons'; import useToast from '../../../../hooks/useToast'; import useAddonsApi from '../../../../hooks/api/actions/useAddonsApi/useAddonsApi'; +import { ReactElement, useContext, useState } from 'react'; +import AccessContext from '../../../../contexts/AccessContext'; +import { IAddon } from '../../../../interfaces/addons'; +import PermissionIconButton from '../../../common/PermissionIconButton/PermissionIconButton'; +import Dialogue from '../../../common/Dialogue'; -const ConfiguredAddons = ({ addons, hasAccess, getIcon }) => { - const { refetchAddons } = useAddons(); +interface IConfigureAddonsProps { + getAddonIcon: (name: string) => ReactElement; +} + +export const ConfiguredAddons = ({ getAddonIcon }: IConfigureAddonsProps) => { + const { refetchAddons, addons } = useAddons(); const { updateAddon, removeAddon } = useAddonsApi(); const { setToastData, setToastApiError } = useToast(); + const { hasAccess } = useContext(AccessContext); + const [showDelete, setShowDelete] = useState(false); + const [deletedAddon, setDeletedAddon] = useState({ + id: 0, + provider: '', + description: '', + enabled: false, + events: [], + parameters: {}, + }); - const toggleAddon = async addon => { + const sortAddons = (addons: IAddon[]) => { + if (!addons) return []; + + return addons.sort((addonA: IAddon, addonB: IAddon) => { + return addonA.id - addonB.id; + }); + }; + + const toggleAddon = async (addon: IAddon) => { try { await updateAddon({ ...addon, enabled: !addon.enabled }); refetchAddons(); @@ -35,12 +58,12 @@ const ConfiguredAddons = ({ addons, hasAccess, getIcon }) => { title: 'Success', text: 'Addon state switched successfully', }); - } catch (e) { + } catch (e: any) { setToastApiError(e.toString()); } }; - const onRemoveAddon = addon => async () => { + const onRemoveAddon = async (addon: IAddon) => { try { await removeAddon(addon.id); refetchAddons(); @@ -58,9 +81,9 @@ const ConfiguredAddons = ({ addons, hasAccess, getIcon }) => { } }; - const renderAddon = addon => ( + const renderAddon = (addon: IAddon) => ( - {getIcon(addon.provider)} + {getAddonIcon(addon.provider)} @@ -85,50 +108,48 @@ const ConfiguredAddons = ({ addons, hasAccess, getIcon }) => { secondary={addon.description} /> - toggleAddon(addon)} - > - } - elseShow={} - /> - - } - /> - - - - } - /> + toggleAddon(addon)} + > + } + elseShow={} + /> + + { + setDeletedAddon(addon); + setShowDelete(true); + }} + > + + ); return ( - {addons.map(addon => renderAddon(addon))} + + {sortAddons(addons).map((addon: IAddon) => renderAddon(addon))} + + { + onRemoveAddon(deletedAddon); + setShowDelete(false); + }} + onClose={() => { + setShowDelete(false); + }} + title="Confirm deletion" + > +
Are you sure you want to delete this Addon?
+
); }; -ConfiguredAddons.propTypes = { - addons: PropTypes.array.isRequired, - hasAccess: PropTypes.func.isRequired, - toggleAddon: PropTypes.func.isRequired, - getIcon: PropTypes.func.isRequired, -}; - -export default ConfiguredAddons; diff --git a/frontend/src/component/addons/AddonList/ConfiguredAddons/index.jsx b/frontend/src/component/addons/AddonList/ConfiguredAddons/index.jsx deleted file mode 100644 index 8a8cf00375..0000000000 --- a/frontend/src/component/addons/AddonList/ConfiguredAddons/index.jsx +++ /dev/null @@ -1,3 +0,0 @@ -import ConfiguredAddons from './ConfiguredAddons'; - -export default ConfiguredAddons; diff --git a/frontend/src/component/addons/AddonList/index.js b/frontend/src/component/addons/AddonList/index.js deleted file mode 100644 index be5a97ab70..0000000000 --- a/frontend/src/component/addons/AddonList/index.js +++ /dev/null @@ -1,3 +0,0 @@ -import AddonListComponent from './AddonList'; - -export default AddonListComponent; diff --git a/frontend/src/component/addons/CreateAddon/CreateAddon.tsx b/frontend/src/component/addons/CreateAddon/CreateAddon.tsx new file mode 100644 index 0000000000..9d18048388 --- /dev/null +++ b/frontend/src/component/addons/CreateAddon/CreateAddon.tsx @@ -0,0 +1,42 @@ +import { useParams } from 'react-router-dom'; +import useAddons from '../../../hooks/api/getters/useAddons/useAddons'; +import { AddonForm } from '../AddonForm/AddonForm'; +import cloneDeep from 'lodash.clonedeep'; + +interface IAddonCreateParams { + providerId: string; +} + +const DEFAULT_DATA = { + provider: '', + description: '', + enabled: true, + parameters: {}, + events: [], +}; + +export const CreateAddon = () => { + const { providerId } = useParams(); + + const { providers, refetchAddons } = useAddons(); + + const editMode = false; + const provider = providers.find( + (providerItem: any) => providerItem.name === providerId + ); + + const defaultAddon = { + ...cloneDeep(DEFAULT_DATA), + provider: provider ? provider.name : '', + }; + + return ( + // @ts-expect-error + + ); +}; diff --git a/frontend/src/component/addons/EditAddon/EditAddon.tsx b/frontend/src/component/addons/EditAddon/EditAddon.tsx new file mode 100644 index 0000000000..9639757bf0 --- /dev/null +++ b/frontend/src/component/addons/EditAddon/EditAddon.tsx @@ -0,0 +1,41 @@ +import { useParams } from 'react-router-dom'; +import useAddons from '../../../hooks/api/getters/useAddons/useAddons'; +import { AddonForm } from '../AddonForm/AddonForm'; +import cloneDeep from 'lodash.clonedeep'; +import { IAddon } from '../../../interfaces/addons'; + +interface IAddonEditParams { + addonId: string; +} + +const DEFAULT_DATA = { + provider: '', + description: '', + enabled: true, + parameters: {}, + events: [], +}; + +export const EditAddon = () => { + const { addonId } = useParams(); + + const { providers, addons, refetchAddons } = useAddons(); + + const editMode = true; + const addon = addons.find( + (addon: IAddon) => addon.id === Number(addonId) + ) || { ...cloneDeep(DEFAULT_DATA) }; + const provider = addon + ? providers.find(provider => provider.name === addon.provider) + : undefined; + + return ( + // @ts-expect-error + + ); +}; diff --git a/frontend/src/component/addons/form-addon-component.module.scss b/frontend/src/component/addons/form-addon-component.module.scss deleted file mode 100644 index 6145c6cb6e..0000000000 --- a/frontend/src/component/addons/form-addon-component.module.scss +++ /dev/null @@ -1,17 +0,0 @@ -.nameInput { - margin-right: 1.5rem; -} - -.formContainer { - margin-bottom: 1.5rem; - max-width: 350px; -} - -.formSection { - padding: 10px 28px; -} - -.header { - font-size: var(--h1-size); - padding: var(--card-header-padding); -} diff --git a/frontend/src/component/addons/form-addon-container.js b/frontend/src/component/addons/form-addon-container.js deleted file mode 100644 index b2a7c721dc..0000000000 --- a/frontend/src/component/addons/form-addon-container.js +++ /dev/null @@ -1,55 +0,0 @@ -import { connect } from 'react-redux'; -import FormComponent from './form-addon-component'; -import { updateAddon, createAddon, fetchAddons } from '../../store/addons/actions'; -import cloneDeep from 'lodash.clonedeep'; - -// Required for to fill the initial form. -const DEFAULT_DATA = { - provider: '', - description: '', - enabled: true, - parameters: {}, - events: [], -}; - -const mapStateToProps = (state, params) => { - const defaultAddon = cloneDeep(DEFAULT_DATA); - const editMode = !!params.addonId; - const addons = state.addons.get('addons').toJS(); - const providers = state.addons.get('providers').toJS(); - - let addon; - let provider; - - if (editMode) { - addon = addons.find(addon => addon.id === +params.addonId) || defaultAddon; - provider = addon ? providers.find(provider => provider.name === addon.provider) : undefined; - } else { - provider = providers.find(provider => provider.name === params.provider); - addon = { ...defaultAddon, provider: provider ? provider.name : '' }; - } - - return { - provider, - addon, - editMode, - }; -}; - -const mapDispatchToProps = (dispatch, ownProps) => { - const { addonId, history } = ownProps; - const submit = addonId ? updateAddon : createAddon; - - return { - submit: async addonConfig => { - await submit(addonConfig)(dispatch); - history.push('/addons'); - }, - fetch: () => fetchAddons()(dispatch), - cancel: () => history.push('/addons'), - }; -}; - -const FormAddContainer = connect(mapStateToProps, mapDispatchToProps)(FormComponent); - -export default FormAddContainer; diff --git a/frontend/src/component/addons/form-addon-events.jsx b/frontend/src/component/addons/form-addon-events.jsx deleted file mode 100644 index 1b6f7518d3..0000000000 --- a/frontend/src/component/addons/form-addon-events.jsx +++ /dev/null @@ -1,35 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { Grid, FormControlLabel, Checkbox } from '@material-ui/core'; - -import { styles as commonStyles } from '../common'; - -const AddonEvents = ({ provider, checkedEvents, setEventValue, error }) => { - if (!provider) return null; - - return ( - -

Events

- {error} - - {provider.events.map(e => ( - - } - label={e} - /> - - ))} - -
- ); -}; - -AddonEvents.propTypes = { - provider: PropTypes.object, - checkedEvents: PropTypes.array.isRequired, - setEventValue: PropTypes.func.isRequired, - error: PropTypes.string, -}; - -export default AddonEvents; diff --git a/frontend/src/component/addons/form-addon-parameters.jsx b/frontend/src/component/addons/form-addon-parameters.jsx deleted file mode 100644 index edaef3a141..0000000000 --- a/frontend/src/component/addons/form-addon-parameters.jsx +++ /dev/null @@ -1,86 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { TextField } from '@material-ui/core'; - -const MASKED_VALUE = '*****'; - -const resolveType = ({ type = 'text', sensitive = false }, value) => { - if (sensitive && value === MASKED_VALUE) { - return 'text'; - } - if (type === 'textfield') { - return 'text'; - } - return type; -}; - -const AddonParameter = ({ definition, config, errors, setParameterValue }) => { - const value = config.parameters[definition.name] || ''; - const type = resolveType(definition, value); - const error = errors.parameters[definition.name]; - - return ( -
- -
- ); -}; - -AddonParameter.propTypes = { - definition: PropTypes.object.isRequired, - config: PropTypes.object.isRequired, - errors: PropTypes.object.isRequired, - setParameterValue: PropTypes.func.isRequired, -}; - -const AddonParameters = ({ provider, config, errors, setParameterValue, editMode }) => { - if (!provider) return null; - - return ( - -

Parameters

- {editMode ? ( -

- Sensitive parameters will be masked with value "***** - ". If you don't change the value they will not be updated when saving. -

- ) : null} - {provider.parameters.map(p => ( - - ))} -
- ); -}; - -AddonParameters.propTypes = { - provider: PropTypes.object, - config: PropTypes.object.isRequired, - errors: PropTypes.object.isRequired, - setParameterValue: PropTypes.func.isRequired, - editMode: PropTypes.bool, -}; - -export default AddonParameters; diff --git a/frontend/src/component/addons/index.jsx b/frontend/src/component/addons/index.jsx deleted file mode 100644 index c04dbe4966..0000000000 --- a/frontend/src/component/addons/index.jsx +++ /dev/null @@ -1,30 +0,0 @@ -import { connect } from 'react-redux'; -import AddonsListComponent from './AddonList'; -import { fetchAddons, removeAddon, updateAddon } from '../../store/addons/actions'; - -const mapStateToProps = state => { - const list = state.addons.toJS(); - - return { - addons: list.addons, - providers: list.providers, - }; -}; - -const mapDispatchToProps = dispatch => ({ - removeAddon: addon => { - // eslint-disable-next-line no-alert - if (window.confirm('Are you sure you want to remove this addon?')) { - removeAddon(addon)(dispatch); - } - }, - fetchAddons: () => fetchAddons()(dispatch), - toggleAddon: addon => { - const updatedAddon = { ...addon, enabled: !addon.enabled }; - return updateAddon(updatedAddon)(dispatch); - }, -}); - -const AddonsListContainer = connect(mapStateToProps, mapDispatchToProps)(AddonsListComponent); - -export default AddonsListContainer; diff --git a/frontend/src/component/menu/__tests__/__snapshots__/routes-test.jsx.snap b/frontend/src/component/menu/__tests__/__snapshots__/routes-test.jsx.snap index 062bc1ea4c..f643fa4014 100644 --- a/frontend/src/component/menu/__tests__/__snapshots__/routes-test.jsx.snap +++ b/frontend/src/component/menu/__tests__/__snapshots__/routes-test.jsx.snap @@ -286,7 +286,7 @@ Array [ "layout": "main", "menu": Object {}, "parent": "/addons", - "path": "/addons/create/:provider", + "path": "/addons/create/:providerId", "title": "Create", "type": "protected", }, @@ -295,7 +295,7 @@ Array [ "layout": "main", "menu": Object {}, "parent": "/addons", - "path": "/addons/edit/:id", + "path": "/addons/edit/:addonId", "title": "Edit", "type": "protected", }, diff --git a/frontend/src/component/menu/routes.js b/frontend/src/component/menu/routes.js index b473a9ad37..7c74bd8065 100644 --- a/frontend/src/component/menu/routes.js +++ b/frontend/src/component/menu/routes.js @@ -9,9 +9,7 @@ import { ArchiveListContainer } from '../archive/ArchiveListContainer'; import Applications from '../../page/applications'; import ApplicationView from '../../page/applications/view'; import { TagTypeList } from '../tags/TagTypeList/TagTypeList'; -import Addons from '../../page/addons'; -import AddonsCreate from '../../page/addons/create'; -import AddonsEdit from '../../page/addons/edit'; +import { AddonList } from '../addons/AddonList/AddonList'; import Admin from '../admin'; import AdminApi from '../admin/api'; import AdminInvoice from '../admin/invoice/InvoiceAdminPage'; @@ -45,6 +43,8 @@ import CreateFeature from '../feature/CreateFeature/CreateFeature'; import EditFeature from '../feature/EditFeature/EditFeature'; import ContextList from '../context/ContextList/ContextList'; import RedirectFeatureView from '../feature/RedirectFeatureView/RedirectFeatureView'; +import { CreateAddon } from '../addons/CreateAddon/CreateAddon'; +import { EditAddon } from '../addons/EditAddon/EditAddon'; export const routes = [ // Project @@ -322,19 +322,19 @@ export const routes = [ // Addons { - path: '/addons/create/:provider', + path: '/addons/create/:providerId', parent: '/addons', title: 'Create', - component: AddonsCreate, + component: CreateAddon, type: 'protected', layout: 'main', menu: {}, }, { - path: '/addons/edit/:id', + path: '/addons/edit/:addonId', parent: '/addons', title: 'Edit', - component: AddonsEdit, + component: EditAddon, type: 'protected', layout: 'main', menu: {}, @@ -342,7 +342,7 @@ export const routes = [ { path: '/addons', title: 'Addons', - component: Addons, + component: AddonList, hidden: false, type: 'protected', layout: 'main', diff --git a/frontend/src/hooks/api/actions/useAddonsApi/useAddonsApi.ts b/frontend/src/hooks/api/actions/useAddonsApi/useAddonsApi.ts index bf2e8855b6..c26719bff0 100644 --- a/frontend/src/hooks/api/actions/useAddonsApi/useAddonsApi.ts +++ b/frontend/src/hooks/api/actions/useAddonsApi/useAddonsApi.ts @@ -1,4 +1,4 @@ -import { IAddons } from '../../../../interfaces/addons'; +import { IAddon } from '../../../../interfaces/addons'; import useAPI from '../useApi/useApi'; const useAddonsApi = () => { @@ -8,7 +8,7 @@ const useAddonsApi = () => { const URI = 'api/admin/addons'; - const createAddon = async (addonConfig: IAddons) => { + const createAddon = async (addonConfig: IAddon) => { const path = URI; const req = createRequest(path, { method: 'POST', @@ -38,7 +38,7 @@ const useAddonsApi = () => { } }; - const updateAddon = async (addonConfig: IAddons) => { + const updateAddon = async (addonConfig: IAddon) => { const path = `${URI}/${addonConfig.id}`; const req = createRequest(path, { method: 'PUT', diff --git a/frontend/src/interfaces/addons.ts b/frontend/src/interfaces/addons.ts index 4ae31b449d..158fb3c18a 100644 --- a/frontend/src/interfaces/addons.ts +++ b/frontend/src/interfaces/addons.ts @@ -1,4 +1,6 @@ -export interface IAddons { +import { ITagType } from './tags'; + +export interface IAddon { id: number; provider: string; description: string; @@ -6,3 +8,32 @@ export interface IAddons { events: string[]; parameters: object; } + +export interface IAddonProvider { + description: string; + displayName: string; + documentationUrl: string; + events: string[]; + name: string; + parameters: IAddonProviderParams[]; + tagTypes: ITagType[]; +} + +export interface IAddonProviderParams { + name: string; + displayName: string; + type: string; + required: boolean; + sensitive: boolean; + placeholder?: string; + description?: string; +} + +export interface IAddonConfig { + description: string; + enabled: boolean; + events: string[]; + id: number; + parameters: Record; + provider: string; +} diff --git a/frontend/src/page/addons/create.js b/frontend/src/page/addons/create.js deleted file mode 100644 index c9894d3484..0000000000 --- a/frontend/src/page/addons/create.js +++ /dev/null @@ -1,14 +0,0 @@ -import React from 'react'; -import AddonForm from '../../component/addons/form-addon-container'; -import PropTypes from 'prop-types'; - -const render = ({ match: { params }, history }) => ( - -); - -render.propTypes = { - match: PropTypes.object.isRequired, - history: PropTypes.object.isRequired, -}; - -export default render; diff --git a/frontend/src/page/addons/edit.js b/frontend/src/page/addons/edit.js deleted file mode 100644 index ffe64e5387..0000000000 --- a/frontend/src/page/addons/edit.js +++ /dev/null @@ -1,14 +0,0 @@ -import React from 'react'; -import AddonForm from '../../component/addons/form-addon-container'; -import PropTypes from 'prop-types'; - -const render = ({ match: { params }, history }) => ( - -); - -render.propTypes = { - match: PropTypes.object.isRequired, - history: PropTypes.object.isRequired, -}; - -export default render; diff --git a/frontend/src/page/addons/index.js b/frontend/src/page/addons/index.js deleted file mode 100644 index be6ab3f384..0000000000 --- a/frontend/src/page/addons/index.js +++ /dev/null @@ -1,11 +0,0 @@ -import React from 'react'; -import Addons from '../../component/addons'; -import PropTypes from 'prop-types'; - -const render = ({ history }) => ; - -render.propTypes = { - history: PropTypes.object.isRequired, -}; - -export default render; diff --git a/frontend/src/store/addons/__testdata__/data.js b/frontend/src/store/addons/__testdata__/data.js deleted file mode 100644 index 5e18859d85..0000000000 --- a/frontend/src/store/addons/__testdata__/data.js +++ /dev/null @@ -1,69 +0,0 @@ -export const addonSimple = { - addons: [], - providers: [ - { - name: 'webhook', - displayName: 'Webhook', - parameters: [ - { - name: 'url', - displayName: 'Webhook URL', - type: 'string', - }, - { - name: 'unleashUrl', - displayName: 'Unleash Admin UI url', - type: 'text', - }, - { - name: 'bodyTemplate', - displayName: 'Body template', - description: 'You may format the body using a mustache template.', - type: 'text', - }, - ], - events: ['feature-created', 'feature-updated', 'feature-archived', 'feature-revived'], - }, - ], -}; - -export const addonConfig = { - id: 1, - provider: 'webhook', - enabled: true, - description: null, - parameters: { - url: 'http://localhost:4242/webhook', - bodyTemplate: "{'name': '{{event.data.name}}' }", - }, - events: ['feature-updated', 'feature-created'], -}; - -export const addonsWithConfig = { - addons: [addonConfig], - providers: [ - { - name: 'webhook', - displayName: 'Webhook', - parameters: [ - { - name: 'url', - displayName: 'Webhook URL', - type: 'string', - }, - { - name: 'unleashUrl', - displayName: 'Unleash Admin UI url', - type: 'text', - }, - { - name: 'bodyTemplate', - displayName: 'Body template', - description: 'You may format the body using a mustache template.', - type: 'text', - }, - ], - events: ['feature-created', 'feature-updated', 'feature-archived', 'feature-revived'], - }, - ], -}; diff --git a/frontend/src/store/addons/__tests__/__snapshots__/addons-store.test.js.snap b/frontend/src/store/addons/__tests__/__snapshots__/addons-store.test.js.snap deleted file mode 100644 index a417521d15..0000000000 --- a/frontend/src/store/addons/__tests__/__snapshots__/addons-store.test.js.snap +++ /dev/null @@ -1,189 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should add addon-config 1`] = ` -Object { - "addons": Array [ - Object { - "description": null, - "enabled": true, - "events": Array [ - "feature-updated", - "feature-created", - ], - "id": 1, - "parameters": Object { - "bodyTemplate": "{'name': '{{event.data.name}}' }", - "url": "http://localhost:4242/webhook", - }, - "provider": "webhook", - }, - ], - "providers": Array [ - Object { - "displayName": "Webhook", - "events": Array [ - "feature-created", - "feature-updated", - "feature-archived", - "feature-revived", - ], - "name": "webhook", - "parameters": Array [ - Object { - "displayName": "Webhook URL", - "name": "url", - "type": "string", - }, - Object { - "displayName": "Unleash Admin UI url", - "name": "unleashUrl", - "type": "text", - }, - Object { - "description": "You may format the body using a mustache template.", - "displayName": "Body template", - "name": "bodyTemplate", - "type": "text", - }, - ], - }, - ], -} -`; - -exports[`should be default state 1`] = ` -Object { - "addons": Array [], - "providers": Array [], -} -`; - -exports[`should be merged state all 1`] = ` -Object { - "addons": Array [], - "providers": Array [ - Object { - "displayName": "Webhook", - "events": Array [ - "feature-created", - "feature-updated", - "feature-archived", - "feature-revived", - ], - "name": "webhook", - "parameters": Array [ - Object { - "displayName": "Webhook URL", - "name": "url", - "type": "string", - }, - Object { - "displayName": "Unleash Admin UI url", - "name": "unleashUrl", - "type": "text", - }, - Object { - "description": "You may format the body using a mustache template.", - "displayName": "Body template", - "name": "bodyTemplate", - "type": "text", - }, - ], - }, - ], -} -`; - -exports[`should clear addon-config on logout 1`] = ` -Object { - "addons": Array [], - "providers": Array [], -} -`; - -exports[`should remove addon-config 1`] = ` -Object { - "addons": Array [], - "providers": Array [ - Object { - "displayName": "Webhook", - "events": Array [ - "feature-created", - "feature-updated", - "feature-archived", - "feature-revived", - ], - "name": "webhook", - "parameters": Array [ - Object { - "displayName": "Webhook URL", - "name": "url", - "type": "string", - }, - Object { - "displayName": "Unleash Admin UI url", - "name": "unleashUrl", - "type": "text", - }, - Object { - "description": "You may format the body using a mustache template.", - "displayName": "Body template", - "name": "bodyTemplate", - "type": "text", - }, - ], - }, - ], -} -`; - -exports[`should update addon-config 1`] = ` -Object { - "addons": Array [ - Object { - "description": "new desc", - "enabled": false, - "events": Array [ - "feature-updated", - "feature-created", - ], - "id": 1, - "parameters": Object { - "bodyTemplate": "{'name': '{{event.data.name}}' }", - "url": "http://localhost:4242/webhook", - }, - "provider": "webhook", - }, - ], - "providers": Array [ - Object { - "displayName": "Webhook", - "events": Array [ - "feature-created", - "feature-updated", - "feature-archived", - "feature-revived", - ], - "name": "webhook", - "parameters": Array [ - Object { - "displayName": "Webhook URL", - "name": "url", - "type": "string", - }, - Object { - "displayName": "Unleash Admin UI url", - "name": "unleashUrl", - "type": "text", - }, - Object { - "description": "You may format the body using a mustache template.", - "displayName": "Body template", - "name": "bodyTemplate", - "type": "text", - }, - ], - }, - ], -} -`; diff --git a/frontend/src/store/addons/__tests__/addons-actions.test.js b/frontend/src/store/addons/__tests__/addons-actions.test.js deleted file mode 100644 index bfea9ce49a..0000000000 --- a/frontend/src/store/addons/__tests__/addons-actions.test.js +++ /dev/null @@ -1,113 +0,0 @@ -import configureMockStore from 'redux-mock-store'; -import thunk from 'redux-thunk'; -import fetchMock from 'fetch-mock'; - -import { - RECEIVE_ADDON_CONFIG, - ERROR_RECEIVE_ADDON_CONFIG, - REMOVE_ADDON_CONFIG, - UPDATE_ADDON_CONFIG, - ADD_ADDON_CONFIG, - fetchAddons, - removeAddon, - updateAddon, - createAddon, -} from '../actions'; - -const middlewares = [thunk]; -const mockStore = configureMockStore(middlewares); - -afterEach(() => { - fetchMock.restore(); -}); - -test('creates RECEIVE_ADDON_CONFIG when fetching addons has been done', () => { - fetchMock.getOnce('api/admin/addons', { - body: { addons: { providers: [{ name: 'webhook' }] } }, - headers: { 'content-type': 'application/json' }, - }); - - const expectedActions = [{ type: RECEIVE_ADDON_CONFIG, value: { addons: { providers: [{ name: 'webhook' }] } } }]; - const store = mockStore({ addons: [] }); - - return store.dispatch(fetchAddons()).then(() => { - // return of async actions - expect(store.getActions()).toEqual(expectedActions); - }); -}); - -test('creates RECEIVE_ADDON_CONFIG_ when fetching addons has been done', () => { - fetchMock.getOnce('api/admin/addons', { - body: { message: 'Server error' }, - headers: { 'content-type': 'application/json' }, - status: 500, - }); - - const store = mockStore({ addons: [] }); - - return store.dispatch(fetchAddons()).catch(e => { - // return of async actions - expect(store.getActions()[0].type).toEqual(ERROR_RECEIVE_ADDON_CONFIG); - expect(e.message).toEqual('Unexpected exception when talking to unleash-api'); - }); -}); - -test('creates REMOVE_ADDON_CONFIG when delete addon has been done', () => { - const addon = { - id: 1, - provider: 'webhook', - }; - - fetchMock.deleteOnce('api/admin/addons/1', { - status: 200, - }); - - const expectedActions = [{ type: REMOVE_ADDON_CONFIG, value: addon }]; - const store = mockStore({ addons: [] }); - - return store.dispatch(removeAddon(addon)).then(() => { - // return of async actions - expect(store.getActions()).toEqual(expectedActions); - }); -}); - -test('creates UPDATE_ADDON_CONFIG when delete addon has been done', () => { - const addon = { - id: 1, - provider: 'webhook', - }; - - fetchMock.putOnce('api/admin/addons/1', { - headers: { 'content-type': 'application/json' }, - status: 200, - body: addon, - }); - - const expectedActions = [{ type: UPDATE_ADDON_CONFIG, value: addon }]; - const store = mockStore({ addons: [] }); - - return store.dispatch(updateAddon(addon)).then(() => { - // return of async actions - expect(store.getActions()).toEqual(expectedActions); - }); -}); - -test('creates ADD_ADDON_CONFIG when delete addon has been done', () => { - const addon = { - provider: 'webhook', - }; - - fetchMock.postOnce('api/admin/addons', { - headers: { 'content-type': 'application/json' }, - status: 200, - body: addon, - }); - - const expectedActions = [{ type: ADD_ADDON_CONFIG, value: addon }]; - const store = mockStore({ addons: [] }); - - return store.dispatch(createAddon(addon)).then(() => { - // return of async actions - expect(store.getActions()).toEqual(expectedActions); - }); -}); diff --git a/frontend/src/store/addons/__tests__/addons-store.test.js b/frontend/src/store/addons/__tests__/addons-store.test.js deleted file mode 100644 index 9bdabc0199..0000000000 --- a/frontend/src/store/addons/__tests__/addons-store.test.js +++ /dev/null @@ -1,54 +0,0 @@ -import reducer from '../index'; -import { RECEIVE_ADDON_CONFIG, ADD_ADDON_CONFIG, REMOVE_ADDON_CONFIG, UPDATE_ADDON_CONFIG } from '../actions'; -import { addonSimple, addonsWithConfig, addonConfig } from '../__testdata__/data'; -import { USER_LOGOUT } from '../../user/actions'; - -test('should be default state', () => { - const state = reducer(undefined, {}); - expect(state.toJS()).toMatchSnapshot(); -}); - -test('should be merged state all', () => { - const state = reducer(undefined, { type: RECEIVE_ADDON_CONFIG, value: addonSimple }); - expect(state.toJS()).toMatchSnapshot(); -}); - -test('should add addon-config', () => { - let state = reducer(undefined, { type: RECEIVE_ADDON_CONFIG, value: addonSimple }); - state = reducer(state, { type: ADD_ADDON_CONFIG, value: addonConfig }); - - const data = state.toJS(); - expect(data).toMatchSnapshot(); - expect(data.addons.length).toBe(1); -}); - -test('should remove addon-config', () => { - let state = reducer(undefined, { type: RECEIVE_ADDON_CONFIG, value: addonsWithConfig }); - state = reducer(state, { type: REMOVE_ADDON_CONFIG, value: addonConfig }); - - const data = state.toJS(); - expect(data).toMatchSnapshot(); - expect(data.addons.length).toBe(0); -}); - -test('should update addon-config', () => { - const updateAdddonConfig = { ...addonConfig, description: 'new desc', enabled: false }; - - let state = reducer(undefined, { type: RECEIVE_ADDON_CONFIG, value: addonsWithConfig }); - state = reducer(state, { type: UPDATE_ADDON_CONFIG, value: updateAdddonConfig }); - - const data = state.toJS(); - expect(data).toMatchSnapshot(); - expect(data.addons.length).toBe(1); - expect(data.addons[0].description).toBe('new desc'); -}); - -test('should clear addon-config on logout', () => { - let state = reducer(undefined, { type: RECEIVE_ADDON_CONFIG, value: addonsWithConfig }); - state = reducer(state, { type: USER_LOGOUT }); - - const data = state.toJS(); - expect(data).toMatchSnapshot(); - expect(data.addons.length).toBe(0); - expect(data.providers.length).toBe(0); -}); diff --git a/frontend/src/store/addons/actions.js b/frontend/src/store/addons/actions.js deleted file mode 100644 index 2ecc3b2b23..0000000000 --- a/frontend/src/store/addons/actions.js +++ /dev/null @@ -1,51 +0,0 @@ -import api from './api'; -import { dispatchError } from '../util'; - -export const RECEIVE_ADDON_CONFIG = 'RECEIVE_ADDON_CONFIG'; -export const ERROR_RECEIVE_ADDON_CONFIG = 'ERROR_RECEIVE_ADDON_CONFIG'; -export const REMOVE_ADDON_CONFIG = 'REMOVE_ADDON_CONFIG'; -export const ERROR_REMOVING_ADDON_CONFIG = 'ERROR_REMOVING_ADDON_CONFIG'; -export const ADD_ADDON_CONFIG = 'ADD_ADDON_CONFIG'; -export const ERROR_ADD_ADDON_CONFIG = 'ERROR_ADD_ADDON_CONFIG'; -export const UPDATE_ADDON_CONFIG = 'UPDATE_ADDON_CONFIG'; -export const ERROR_UPDATE_ADDON_CONFIG = 'ERROR_UPDATE_ADDON_CONFIG'; - -// const receiveAddonConfig = value => ({ type: RECEIVE_ADDON_CONFIG, value }); -const addAddonConfig = value => ({ type: ADD_ADDON_CONFIG, value }); -const updateAdddonConfig = value => ({ type: UPDATE_ADDON_CONFIG, value }); -const removeAddonconfig = value => ({ type: REMOVE_ADDON_CONFIG, value }); - -const success = (dispatch, type) => value => dispatch({ type, value }); - -export function fetchAddons() { - return dispatch => - api - .fetchAll() - .then(success(dispatch, RECEIVE_ADDON_CONFIG)) - .catch(dispatchError(dispatch, ERROR_RECEIVE_ADDON_CONFIG)); -} - -export function removeAddon(addon) { - return dispatch => - api - .remove(addon) - .then(() => dispatch(removeAddonconfig(addon))) - .catch(dispatchError(dispatch, ERROR_REMOVING_ADDON_CONFIG)); -} - -export function createAddon(addon) { - return dispatch => - api - .create(addon) - .then(res => res.json()) - .then(value => dispatch(addAddonConfig(value))) - .catch(dispatchError(dispatch, ERROR_ADD_ADDON_CONFIG)); -} - -export function updateAddon(addon) { - return dispatch => - api - .update(addon) - .then(() => dispatch(updateAdddonConfig(addon))) - .catch(dispatchError(dispatch, ERROR_UPDATE_ADDON_CONFIG)); -} diff --git a/frontend/src/store/addons/api.js b/frontend/src/store/addons/api.js deleted file mode 100644 index 72decf1494..0000000000 --- a/frontend/src/store/addons/api.js +++ /dev/null @@ -1,44 +0,0 @@ -import { formatApiPath } from '../../utils/format-path'; -import { throwIfNotSuccess, headers } from '../api-helper'; - -const URI = formatApiPath(`api/admin/addons`); - -function fetchAll() { - return fetch(URI, { credentials: 'include' }) - .then(throwIfNotSuccess) - .then(response => response.json()); -} - -function create(addonConfig) { - return fetch(URI, { - method: 'POST', - headers, - body: JSON.stringify(addonConfig), - credentials: 'include', - }).then(throwIfNotSuccess); -} - -function update(addonConfig) { - return fetch(`${URI}/${addonConfig.id}`, { - method: 'PUT', - headers, - body: JSON.stringify(addonConfig), - credentials: 'include', - }).then(throwIfNotSuccess); -} - -function remove(addonConfig) { - return fetch(`${URI}/${addonConfig.id}`, { - method: 'DELETE', - headers, - credentials: 'include', - }).then(throwIfNotSuccess); -} - -const api = { - fetchAll, - create, - update, - remove, -}; -export default api; diff --git a/frontend/src/store/addons/index.js b/frontend/src/store/addons/index.js deleted file mode 100644 index ab31635f7e..0000000000 --- a/frontend/src/store/addons/index.js +++ /dev/null @@ -1,33 +0,0 @@ -import { Map as $Map, List, fromJS } from 'immutable'; -import { RECEIVE_ADDON_CONFIG, ADD_ADDON_CONFIG, REMOVE_ADDON_CONFIG, UPDATE_ADDON_CONFIG } from './actions'; -import { USER_LOGOUT, USER_LOGIN } from '../user/actions'; - -function getInitState() { - return new $Map({ - providers: new List(), - addons: new List(), - }); -} - -const strategies = (state = getInitState(), action) => { - switch (action.type) { - case RECEIVE_ADDON_CONFIG: - return fromJS(action.value); - case ADD_ADDON_CONFIG: { - return state.update('addons', arr => arr.push(fromJS(action.value))); - } - case REMOVE_ADDON_CONFIG: - return state.update('addons', arr => arr.filter(a => a.get('id') !== action.value.id)); - case UPDATE_ADDON_CONFIG: { - const index = state.get('addons').findIndex(item => item.get('id') === action.value.id); - return state.setIn(['addons', index], fromJS(action.value)); - } - case USER_LOGOUT: - case USER_LOGIN: - return getInitState(); - default: - return state; - } -}; - -export default strategies; diff --git a/frontend/src/store/error/index.js b/frontend/src/store/error/index.js index f9ede4f5d4..ed4554436f 100644 --- a/frontend/src/store/error/index.js +++ b/frontend/src/store/error/index.js @@ -22,12 +22,6 @@ import { ERROR_UPDATE_PROJECT, } from '../project/actions'; -import { - ERROR_ADD_ADDON_CONFIG, - ERROR_UPDATE_ADDON_CONFIG, - ERROR_REMOVING_ADDON_CONFIG, -} from '../addons/actions'; - import { UPDATE_APPLICATION_FIELD } from '../application/actions'; import { FORBIDDEN } from '../util'; @@ -59,9 +53,6 @@ const strategies = (state = getInitState(), action) => { case ERROR_RECEIVE_STRATEGIES: case ERROR_REMOVING_PROJECT: case ERROR_UPDATE_PROJECT: - case ERROR_ADD_ADDON_CONFIG: - case ERROR_UPDATE_ADDON_CONFIG: - case ERROR_REMOVING_ADDON_CONFIG: case ERROR_ADD_PROJECT: return addErrorIfNotAlreadyInList(state, action.error.message); case FORBIDDEN: diff --git a/frontend/src/store/index.js b/frontend/src/store/index.js index 5ad7970ca3..13aea0e75a 100644 --- a/frontend/src/store/index.js +++ b/frontend/src/store/index.js @@ -5,7 +5,6 @@ import error from './error'; import user from './user'; import applications from './application'; import projects from './project'; -import addons from './addons'; import apiCalls from './api-calls'; const unleashStore = combineReducers({ @@ -15,7 +14,6 @@ const unleashStore = combineReducers({ user, applications, projects, - addons, apiCalls, }); diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 83a892a0de..56c8d82aff 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -2077,6 +2077,18 @@ resolved "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz" integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= +"@types/lodash.clonedeep@^4.5.6": + version "4.5.6" + resolved "https://registry.yarnpkg.com/@types/lodash.clonedeep/-/lodash.clonedeep-4.5.6.tgz#3b6c40a0affe0799a2ce823b440a6cf33571d32b" + integrity sha512-cE1jYr2dEg1wBImvXlNtp0xDoS79rfEdGozQVgliDZj1uERH4k+rmEMTudP9b4VQ8O6nRb5gPqft0QzEQGMQgA== + dependencies: + "@types/lodash" "*" + +"@types/lodash@*": + version "4.14.178" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.178.tgz#341f6d2247db528d4a13ddbb374bcdc80406f4f8" + integrity sha512-0d5Wd09ItQWH1qFbEyQ7oTQ3GZrMfth5JkbN3EvTKLXcHLRDSXeLnlvlOn0wvxVIwK5o2M8JzP/OWz7T3NRsbw== + "@types/minimatch@*": version "3.0.4" resolved "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.4.tgz"