Browse Source

[FIX] actions and reducers

Gogs 7 years ago
parent
commit
5a1d6c2b7d

+ 65 - 49
src/actions/index.js

@@ -1,22 +1,68 @@
 import http from '../utils/http'
-import { 
-    REQUEST_START,
-    REQUEST_OK, 
-    REQUEST_KO, 
-    SHOW_SPINNER, 
-    HIDE_SPINNER, 
-    SHOW_NOTIFICATION, 
-    HIDE_NOTIFICATION 
-} from '../constants/ActionTypes'
+import { getToken } from '../utils/auth'
 import { isArray } from 'lodash'
 
+
+/**
+ * 
+ * @param {*} username 
+ * @param {*} password 
+ */
+export const login = (username, password) => async dispatch => {
+    dispatch({ type: 'LOGIN_REQUEST' })
+
+    try {
+        const response = await http.post('auth/get_token/', { username, password })
+
+        if (response.status === 200) {
+            dispatch({ type: 'LOGIN_SUCCESS', payload: response.data })    
+        }
+
+        if (response.status === 401) {
+            dispatch({ type: 'LOGIN_FAILURE' })    
+        }
+    } catch(error) {
+        dispatch({ type: 'LOGIN_FAILURE' })
+    }
+}
+
+/**
+ * 
+ */
+export const checkToken = () => async dispatch => {
+    dispatch({ type: 'CHECK_TOKEN_REQUEST' })
+
+    try {
+        const response = await http.post('auth/check_token/', { token: getToken() })
+
+        if (response.status === 200) {
+            dispatch({ type: 'CHECK_TOKEN_SUCCESS', payload: response.data })
+        }
+
+        if (response.status === 401) {
+            dispatch({ type: 'CHECK_TOKEN_FAILURE' })
+        }
+    } catch (error) {
+        dispatch({ type: 'CHECK_TOKEN_FAILURE' })
+    }
+}
+
+/**
+ * 
+ */
+export const logout = () => {
+    return {
+        type: 'LOGOUT_REQUEST'
+    }
+}
+
 /**
  * 
  * @param {*} message 
  */
 export const spinner = (show, message) => dispatch => {
     dispatch({
-        type: show ? SHOW_SPINNER : HIDE_SPINNER,
+        type: show ? 'SHOW_SPINNER' : 'HIDE_SPINNER',
         payload: message
     })
 }
@@ -27,7 +73,7 @@ export const spinner = (show, message) => dispatch => {
  */
 export const notify = message => dispatch  => {
     dispatch({
-        type: message ? SHOW_NOTIFICATION : HIDE_NOTIFICATION,
+        type: message ? 'SHOW_NOTIFICATION' : 'HIDE_NOTIFICATION',
         payload: { message }
     })
 }
@@ -37,16 +83,13 @@ export const notify = message => dispatch  => {
  * @param {*} resource 
  */
 export const get = resource => async dispatch => {
-    dispatch({
-        type: REQUEST_START
-    })
+    dispatch({ type: 'REQUEST_INIT' })
 
     try {
-        // const response = await axios.get(`${API_URL}${resource}`)
         const response = await http.get(`${resource}`)
-        dispatch(ok(response.data))
+        dispatch({ type: 'REQUEST_OK', payload: response.data })
     } catch (error) {
-        dispatch(ko(resource, error.response))
+        dispatch({ type: 'REQUEST_KO', payload: error.response.status })
     }
 }
 
@@ -55,9 +98,7 @@ export const get = resource => async dispatch => {
  * @param {*} resource 
  */
 export const post = (resources, data) => async dispatch => {
-    dispatch({
-        type: REQUEST_START
-    })
+    dispatch({ type: 'REQUEST_INIT' })
     
     data = data || {}
 
@@ -65,42 +106,17 @@ export const post = (resources, data) => async dispatch => {
         for (let r of resources) {
             try {
                 const response = await http.post(`${r}`, data)
-                dispatch(ok(response.data))
+                dispatch({ type: 'REQUEST_OK', payload: response.data })
             } catch (error) {
-                dispatch(ko(r, error))
+                dispatch({ type: 'REQUEST_KO', payload: error.response.status })
             }
         }
     } else {
         try {
             const response = await http.post(`${resources}`, data)
-            dispatch(ok(response.data))
+            dispatch({ type: 'REQUEST_OK', payload: response.data })
         } catch (error) {
-            dispatch(ko(resources, error))
+            dispatch({ type: 'REQUEST_KO', payload: error.response.status })
         }
     }
 }
-
-/**
- * 
- * @param {*} resource 
- * @param {*} payload 
- */
-const ok = payload => {
-    return {
-        type: REQUEST_OK,
-        payload
-    }
-}
-
-/**
- * 
- * @param {*} resource 
- * @param {*} payload 
- */
-const ko = (resource, payload) => {
-    return {
-        type: REQUEST_KO,
-        resource,
-        payload
-    }
-}

+ 21 - 11
src/components/common/Topbar.js

@@ -7,7 +7,7 @@ import Avatar from 'material-ui/Avatar'
 import Button from 'material-ui/Button'
 import Menu, { MenuItem } from 'material-ui/Menu'
 import UserIcon from '../icons/UserIcon'
-import { LOGOUT } from '../../constants/ActionTypes'
+import { logout } from '../../actions'
 import { withStyles } from 'material-ui/styles'
 
 const styles = theme => ({
@@ -47,18 +47,19 @@ class Topbar extends Component {
     /**
      * 
      */
-    handleSesionClose = e => {
+    handleSessionClose = e => {
         this.setState({
             anchorEl: null
         })
-        this.props.logout()
+
+        this.props.closeSession()
     }
 
     /**
      * 
      */
     render() {
-        const { classes } = this.props
+        const { username, classes } = this.props
         const { anchorEl } = this.state
         const openMenu = Boolean(anchorEl)
 
@@ -72,7 +73,7 @@ class Topbar extends Component {
                         <Avatar className={classes.avatar}>
                             <UserIcon />
                         </Avatar>
-                        <Button color="inherit" onClick={this.handleOpenMenu}>Anónimo</Button>
+                        <Button color="inherit" onClick={this.handleOpenMenu}>{username}</Button>
                         <Menu 
                             open={openMenu} 
                             anchorEl={anchorEl}
@@ -84,7 +85,7 @@ class Topbar extends Component {
                                 vertical: 'top',
                                 horizontal: 'right',
                             }}>
-                            <MenuItem onClick={this.handleSesionClose}>Cerrar sesión</MenuItem>
+                            <MenuItem onClick={this.handleSessionClose}>Cerrar sesión</MenuItem>
                         </Menu>
                     </div>
                 </Toolbar>
@@ -93,19 +94,28 @@ class Topbar extends Component {
     }
 }
 
+/**
+ * 
+ * @param {*} state 
+ * @param {*} props 
+ */
+const mapStateToProps = (state, props) => {
+    return {
+        username: state.auth.username
+    }
+}
+
 /**
  * 
  * @param {*} dispatch 
  * @param {*} props 
  */
 const mapDispatchToProps = (dispatch, props) => ({
-    logout() {
-        dispatch({
-            type: LOGOUT
-        })
+    closeSession() {
+        dispatch(logout())
     }
 })
 
 Topbar = withStyles(styles)(Topbar)
 
-export default connect(null, mapDispatchToProps)(Topbar)
+export default connect(mapStateToProps, mapDispatchToProps)(Topbar)

+ 10 - 19
src/components/pages/Login.js

@@ -6,9 +6,7 @@ import Typography from 'material-ui/Typography'
 import TextField from 'material-ui/TextField'
 import Button from 'material-ui/Button'
 import Snackbar from 'material-ui/Snackbar'
-import { AUTH } from '../../constants/ResourceNames'
-import { post, notify } from '../../actions'
-import { getToken } from '../../utils/auth'
+import { login, checkToken, notify } from '../../actions'
 import { withStyles } from 'material-ui/styles'
 
 const styles = theme => ({
@@ -61,7 +59,7 @@ class Login extends Component {
      * 
      */
     componentWillMount() {
-        this.props.check()
+        this.props.checkIfLogged()
     }
 
     /**
@@ -102,15 +100,15 @@ class Login extends Component {
      * 
      */
     render() {
+        const { auth, notification, onHideNotification, classes } = this.props
         const { from } = this.props.location.state || { from: { pathname: '/' }}
-        const { isAuthenticated, isLoading, notification, onHideNotification, classes } = this.props
         const { hasError } = this.state
 
-        if (isAuthenticated) {
+        if (auth.isAuthenticated) {
             return <Redirect to={from} />
         }
 
-        if (isLoading) {
+        if (auth.isLogging || auth.isTokenCheck) {
             return <Typography align='center' variant='body1' color='primary'>Redireccionando...</Typography>
         }
 
@@ -142,7 +140,7 @@ class Login extends Component {
                     open={notification.isVisible} 
                     autoHideDuration={2000} 
                     onClose={onHideNotification} 
-                    message={notification.message} 
+                    message={<span>{notification.message}</span>} 
                 />
             </div>
         )
@@ -156,8 +154,7 @@ class Login extends Component {
  */
 const mapStateToProps = (state, props) => {
     return {
-        isAuthenticated: state.auth.isAuthenticated,
-        isLoading: state.spinner.isVisible,
+        auth : state.auth,
         notification: state.notification
     }
 }
@@ -171,14 +168,8 @@ const mapDispatchToProps = (dispatch, props) => ({
     /**
      * 
      */
-    check() {
-        const token = getToken()
-
-        if (!token) {
-            return
-        }
-
-        dispatch(post(`${AUTH}check_token/`, { token }))
+    checkIfLogged() {
+        dispatch(checkToken())
     },
     /**
      * 
@@ -187,7 +178,7 @@ const mapDispatchToProps = (dispatch, props) => ({
      * @param {*} password 
      */
     submit(e, username, password) {
-        dispatch(post(`${AUTH}get_token/`, { username, password }))
+        dispatch(login(username, password ))
     },
     /**
      * 

+ 2 - 8
src/components/pages/Welcome.js

@@ -1,16 +1,11 @@
 import React, { Component } from 'react'
 import Base from '../common/Base'
-import DockerIcon from '../icons/DockerIcon'
 import Typography from 'material-ui/Typography'
 import { withStyles } from 'material-ui/styles'
 
 const styles = theme => ({
     root: {
-
-    },
-    icon: {
-        width: '5em',
-        height: '5em'
+        paddingTop: theme.spacing.unit * 8
     }
 })
 
@@ -23,8 +18,7 @@ class Welcome extends Component {
 
         return (
             <Base title={this.props.title}>
-                <DockerIcon className={classes.icon} />
-                <Typography align='center' variant='headline' color='primary'>Bienvenido al Sistema de Automatización</Typography>
+                <Typography className={classes.root} align='center' variant='headline' color='primary'>Bienvenido al Sistema de Automatización</Typography>
             </Base>
         )
     }

+ 1 - 1
src/constants/ActionTypes.js

@@ -1,4 +1,4 @@
-/**
+/*
  *
  */
 export const REQUEST_START = 'request_start'

+ 78 - 26
src/reducers/auth.js

@@ -1,68 +1,120 @@
 import { createReducer } from '../utils/reducer'
-import { REQUEST_OK, REQUEST_KO, LOGOUT } from '../constants/ActionTypes'
 import { setToken } from '../utils/auth'
-import { has, isEqual } from 'lodash'
 
 const initialState = {
-    isAuthenticated: false
+    isTokenCheck: false,
+    isLogging: false,
+    isAuthenticated: false,
+    token: null,
+    username: null
 }
 
 /**
  * 
- * @param {*} state 
+ * @param {*} isTokenCheck 
  * @param {*} action 
  */
-const checkAuthentication = (isAuthenticated, action) => {
-    if (!has(action.payload, 'status')) {
-        return isAuthenticated
+const isTokenCheck = (isTokenCheck, action) => {
+    if (action.type === 'CHECK_TOKEN_REQUEST') {
+        return true
     }
 
-    // success auth
-    if (isEqual(action.payload.status, 200)) {
-        if (has(action.payload, 'token')) {
-            setToken(action.payload.token)
+    if (action.type === 'CHECK_TOKEN_SUCCESS') {
+        return false
+    }
 
-            return true;
-        }
+    if (action.type === 'CHECK_TOKEN_FAILURE') {
+        setToken(null)
+        return false
+    }
 
+    return isTokenCheck
+}
+
+/**
+ * 
+ */
+const tokenCheckReducer = createReducer(false , {
+    'CHECK_TOKEN_REQUEST': isTokenCheck,
+    'CHECK_TOKEN_SUCCESS': isTokenCheck,
+    'CHECK_TOKEN_FAILURE': isTokenCheck
+})
+
+/**
+ * 
+ * @param {*} isLogging 
+ * @param {*} action 
+ */
+const isLogging = (isLogging, action) => {
+    if (action.type === 'LOGIN_REQUEST') {
         return true
     }
 
-    // error auth
-    if (isEqual(action.payload.status, 401)) {
+    if (action.type === 'LOGIN_SUCCESS' || action.type === 'LOGIN_FAILURE') {
         return false
     }
 
-    return isAuthenticated
+    return isLogging
 }
 
+/**
+ * 
+ */
+const logginReducer = createReducer(false, {
+    'LOGIN_REQUEST': isLogging,
+    'LOGIN_SUCCESS': isLogging,
+    'LOGIN_FAILURE': isLogging
+})
+
 /**
  * 
  * @param {*} isAuthenticated 
  * @param {*} action 
  */
-const revokeAuthentication = (isAuthenticated, action) => {
-    setToken(null)
-    return false
+const isAuthenticated = (isAuthenticated, action) => {
+    if (action.type === 'LOGIN_SUCCESS' || action.type === 'CHECK_TOKEN_SUCCESS') {
+        setToken(action.payload.token)
+        return true
+    }
+
+    if (action.type === 'LOGIN_FAILURE' || action.type === 'CHECK_TOKEN_FAILURE' || action.type === 'LOGOUT_REQUEST') {
+        setToken(null)
+        return false
+    }
+
+    return isAuthenticated
 }
 
 /**
  * 
  */
-const authenticationCheckReducer = createReducer(initialState, {
-    [REQUEST_OK]: checkAuthentication,
-    [REQUEST_KO]: checkAuthentication,
-    [LOGOUT]: revokeAuthentication
-
+const authenticationReducer = createReducer(false, {
+    'LOGIN_SUCCESS': isAuthenticated,
+    'LOGIN_FAILURE': isAuthenticated,
+    'CHECK_TOKEN_SUCCESS': isAuthenticated,
+    'CHECK_TOKEN_FAILURE': isAuthenticated,
+    'LOGOUT_REQUEST': isAuthenticated
 })
 
 /**
  * 
- * @param {*} state 
+ * @param {*} username 
  * @param {*} action 
  */
+const setUsername = (username, action) => {
+    return action.payload.username
+}
+
+const userReducer = createReducer(null, {
+    'LOGIN_SUCCESS': setUsername,
+    'CHECK_TOKEN_SUCCESS': setUsername
+})
+
 export const auth = (state = initialState, action) => {
     return {
-        isAuthenticated: authenticationCheckReducer(state.isAuthenticated, action)
+        isTokenCheck: tokenCheckReducer(state.isTokenCheck, action),
+        isLogging: logginReducer(state.isLogging, action),
+        isAuthenticated: authenticationReducer(state.isAuthenticated, action),
+        username: userReducer(state.username, action)
     }
 }

+ 1 - 2
src/reducers/containers.js

@@ -1,5 +1,4 @@
 import { createReducer } from '../utils/reducer'
-import { REQUEST_OK } from '../constants/ActionTypes'
 import { has, map, isEqual } from 'lodash'
 
 const initialState = {
@@ -31,7 +30,7 @@ const setContainers = (containers, action) => {
  * 
  */
 const containersReducer = createReducer(initialState.containers, {
-    [REQUEST_OK]: setContainers
+    'REQUEST_OK': setContainers
 })
 
 /**

+ 37 - 34
src/reducers/notification.js

@@ -1,6 +1,4 @@
 import { createReducer } from '../utils/reducer'
-import { REQUEST_OK, REQUEST_KO, SHOW_NOTIFICATION, HIDE_NOTIFICATION } from '../constants/ActionTypes'
-import { isEmpty, has, isEqual } from 'lodash'
 
 const initialState = {
     isVisible: false,
@@ -12,28 +10,8 @@ const initialState = {
  * @param {*} state 
  * @param {*} action 
  */
-const showNotification = (state, action) => {
-    // common notification
-    if (!has(action.payload, 'status')) {
-        return {
-            isVisible: !isEmpty(action.payload.message),
-            message: action.payload.message
-        }
-    }
-
-    // error notification
-    if (isEqual(action.payload.status, 401)) {
-        if (has(action.payload, 'error_message')) {
-            return {
-                isVisible: true,
-                message: 'Fallo en la autenticación del usuario'
-            }
-        }
-
-        return state
-    }
-
-    return state
+const showNotification = () => {
+    return true
 }
 
 /**
@@ -41,21 +19,43 @@ const showNotification = (state, action) => {
  * @param {*} state 
  * @param {*} action 
  */
-const hideNotification = (state, action) => {
-    return {
-        isVisible: false,
-        message: null
+const hideNotification = () => {
+    return false
+}
+
+/**
+ * 
+ */
+const notificationVisibilityReducer = createReducer(false, {
+    'LOGIN_FAILURE': showNotification,
+    'CHECK_TOKEN_FAILURE': hideNotification,
+    'HIDE_NOTIFICATION': hideNotification
+})
+
+/**
+ * 
+ * @param {*} message 
+ * @param {*} action 
+ */
+const setNotificationMessage = (message, action) => {
+    if (action.type === 'LOGIN_FAILURE') {
+        return 'Fallo al autenticar el usuario'
     }
+
+    if (action.type === 'HIDE_NOTIFICATION' || action.type === 'CHECK_TOKEN_FAILURE') {
+        return null
+    }
+
+    return null
 }
 
 /**
  * 
  */
-const notificationReducer = createReducer(initialState, {
-    [REQUEST_OK]: showNotification,
-    [REQUEST_KO]: showNotification,
-    [SHOW_NOTIFICATION]: showNotification,
-    [HIDE_NOTIFICATION]: hideNotification
+const notificationMessageReducer = createReducer(null, {
+    'LOGIN_FAILURE': setNotificationMessage,
+    'CHECK_TOKEN_FAILURE': setNotificationMessage,
+    'HIDE_NOTIFICATION': setNotificationMessage
 })
 
 /**
@@ -64,5 +64,8 @@ const notificationReducer = createReducer(initialState, {
  * @param {*} action 
  */
 export const notification = (state = initialState, action) => {
-    return notificationReducer(state, action)
+    return {
+        isVisible: notificationVisibilityReducer(state.isVisible, action),
+        message: notificationMessageReducer(state.message, action)
+    }
 }

+ 1 - 2
src/reducers/requests.js

@@ -1,5 +1,4 @@
 import { createReducer } from '../utils/reducer'
-import { REQUEST_OK } from '../constants/ActionTypes'
 import { has } from 'lodash'
 
 const initialState = {
@@ -23,7 +22,7 @@ const setRequests = (requests, action) => {
  * 
  */
 const requestsReducer = createReducer([], {
-    [REQUEST_OK]: setRequests
+    'REQUEST_OK': setRequests
 })
 
 /**

+ 4 - 5
src/reducers/spinner.js

@@ -1,5 +1,4 @@
 import { createReducer } from '../utils/reducer'
-import { REQUEST_START, REQUEST_OK, REQUEST_KO } from '../constants/ActionTypes'
 
 const initialState = {
     isVisible: false
@@ -10,7 +9,7 @@ const initialState = {
  * @param {*} visibilityState 
  * @param {*} action 
  */
-const setSpinnerVisibility = (visibilityState, action) => {
+const toggleSpinnerVisibility = (visibilityState, action) => {
     return !visibilityState
 }
 
@@ -18,9 +17,9 @@ const setSpinnerVisibility = (visibilityState, action) => {
  * 
  */
 const spinnerVisibilityReducer = createReducer(false, {
-    [REQUEST_START]: setSpinnerVisibility,
-    [REQUEST_OK]: setSpinnerVisibility,
-    [REQUEST_KO]: setSpinnerVisibility
+    'REQUEST_INIT': toggleSpinnerVisibility,
+    'REQUEST_OK': toggleSpinnerVisibility,
+    'REQUEST_KO': toggleSpinnerVisibility
 })
 
 

+ 16 - 0
src/utils/action.js

@@ -0,0 +1,16 @@
+/**
+ * 
+ * @param {*} type 
+ * @param {*} args 
+ */
+export const makeActionCreator = (type, ...argsNames) => {
+    return (...args) => {
+        let action = { type }
+
+        argsNames.forEach((arg, index) => {
+            action[argsNames[index]] = args[index]
+        })
+
+        return action
+    }
+}

+ 5 - 1
src/utils/auth.js

@@ -1,4 +1,4 @@
-import { isEmpty  } from 'lodash'
+import { isEmpty, isNull  } from 'lodash'
 
 const TOKEN_KEY = 'eiruAutomation'
 
@@ -26,4 +26,8 @@ export const setToken = token => {
     }
 
     localStorage.setItem(TOKEN_KEY, token)
+
+    if (isNull(token)) {
+        localStorage.removeItem(TOKEN_KEY)
+    }
 }

+ 1 - 1
src/utils/http.js

@@ -17,7 +17,7 @@ instance.interceptors.request.use(config => {
     const token = localStorage.getItem(TOKEN_KEY)
 
     if (token) {
-        config.headers['Authorization'] = `JWT ${token}`
+        config.headers['Authorization'] = token
     }
 
     return config