Add action user-admin/fetchUsers

Change-Id: I6a956d118ffd352f8155650fee17219cdcefebc6
This commit is contained in:
Manuel Stahl 2019-03-12 14:33:09 +01:00
parent 2aa30c1a36
commit 0254788901
6 changed files with 196 additions and 0 deletions

View File

@ -0,0 +1 @@
export { fetchUsers, dismissFetchUsersError } from './fetchUsers';

View File

@ -0,0 +1,4 @@
export const USER_ADMIN_FETCH_USERS_BEGIN = 'USER_ADMIN_FETCH_USERS_BEGIN';
export const USER_ADMIN_FETCH_USERS_SUCCESS = 'USER_ADMIN_FETCH_USERS_SUCCESS';
export const USER_ADMIN_FETCH_USERS_FAILURE = 'USER_ADMIN_FETCH_USERS_FAILURE';
export const USER_ADMIN_FETCH_USERS_DISMISS_ERROR = 'USER_ADMIN_FETCH_USERS_DISMISS_ERROR';

View File

@ -0,0 +1,89 @@
import {
USER_ADMIN_FETCH_USERS_BEGIN,
USER_ADMIN_FETCH_USERS_SUCCESS,
USER_ADMIN_FETCH_USERS_FAILURE,
USER_ADMIN_FETCH_USERS_DISMISS_ERROR,
} from './constants';
export function fetchUsers(args = {}) {
return (dispatch, getState) => {
dispatch({
type: USER_ADMIN_FETCH_USERS_BEGIN,
});
// Return a promise so that you could control UI flow without states in the store.
// For example: after submit a form, you need to redirect the page to another when succeeds or show some errors message if fails.
// It's hard to use state to manage it, but returning a promise allows you to easily achieve it.
// e.g.: handleSubmit() { this.props.actions.submitForm(data).then(()=> {}).catch(() => {}); }
const promise = new Promise((resolve, reject) => {
const mtx = getState().common.mtx;
const doRequest = mtx._http.authedRequest(undefined, "GET", "/admin/users/" + mtx.credentials.userId)
doRequest.then(
(res) => {
dispatch({
type: USER_ADMIN_FETCH_USERS_SUCCESS,
data: res,
});
resolve(res);
},
// Use rejectHandler as the second argument so that render errors won't be caught.
(err) => {
dispatch({
type: USER_ADMIN_FETCH_USERS_FAILURE,
data: { error: err },
});
reject(err);
},
);
});
return promise;
};
}
// Async action saves request error by default, this method is used to dismiss the error info.
// If you don't want errors to be saved in Redux store, just ignore this method.
export function dismissFetchUsersError() {
return {
type: USER_ADMIN_FETCH_USERS_DISMISS_ERROR,
};
}
export function reducer(state, action) {
switch (action.type) {
case USER_ADMIN_FETCH_USERS_BEGIN:
// Just after a request is sent
return {
...state,
fetchUsersPending: true,
fetchUsersError: null,
};
case USER_ADMIN_FETCH_USERS_SUCCESS:
// The request is success
return {
...state,
userList: action.data,
fetchUsersPending: false,
fetchUsersError: null,
};
case USER_ADMIN_FETCH_USERS_FAILURE:
// The request is failed
return {
...state,
fetchUsersPending: false,
fetchUsersError: action.data.error,
};
case USER_ADMIN_FETCH_USERS_DISMISS_ERROR:
// Dismiss the request failure error
return {
...state,
fetchUsersError: null,
};
default:
return state;
}
}

View File

@ -6,6 +6,9 @@
// NOTE: initialState constant is necessary so that Rekit could auto add initial state when creating async actions.
const initialState = {
userList: [],
fetchUsersPending: false,
fetchUsersError: null,
};
export default initialState;

View File

@ -7,8 +7,10 @@
// https://medium.com/@nate_wang/a-new-approach-for-managing-redux-actions-91c26ce8b5da.
import initialState from './initialState';
import { reducer as fetchUsersReducer } from './fetchUsers';
const reducers = [
fetchUsersReducer,
];
export default function reducer(state = initialState, action) {

View File

@ -0,0 +1,97 @@
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import nock from 'nock';
import {
USER_ADMIN_FETCH_USERS_BEGIN,
USER_ADMIN_FETCH_USERS_SUCCESS,
USER_ADMIN_FETCH_USERS_FAILURE,
USER_ADMIN_FETCH_USERS_DISMISS_ERROR,
} from '../../../../src/features/user-admin/redux/constants';
import {
fetchUsers,
dismissFetchUsersError,
reducer,
} from '../../../../src/features/user-admin/redux/fetchUsers';
const middlewares = [thunk];
const mockStore = configureMockStore(middlewares);
describe('user-admin/redux/fetchUsers', () => {
afterEach(() => {
nock.cleanAll();
});
it('dispatches success action when fetchUsers succeeds', () => {
const store = mockStore({});
return store.dispatch(fetchUsers())
.then(() => {
const actions = store.getActions();
expect(actions[0]).toHaveProperty('type', USER_ADMIN_FETCH_USERS_BEGIN);
expect(actions[1]).toHaveProperty('type', USER_ADMIN_FETCH_USERS_SUCCESS);
});
});
it('dispatches failure action when fetchUsers fails', () => {
const store = mockStore({});
return store.dispatch(fetchUsers({ error: true }))
.catch(() => {
const actions = store.getActions();
expect(actions[0]).toHaveProperty('type', USER_ADMIN_FETCH_USERS_BEGIN);
expect(actions[1]).toHaveProperty('type', USER_ADMIN_FETCH_USERS_FAILURE);
expect(actions[1]).toHaveProperty('data.error', expect.anything());
});
});
it('returns correct action by dismissFetchUsersError', () => {
const expectedAction = {
type: USER_ADMIN_FETCH_USERS_DISMISS_ERROR,
};
expect(dismissFetchUsersError()).toEqual(expectedAction);
});
it('handles action type USER_ADMIN_FETCH_USERS_BEGIN correctly', () => {
const prevState = { listUsersPending: false };
const state = reducer(
prevState,
{ type: USER_ADMIN_FETCH_USERS_BEGIN }
);
expect(state).not.toBe(prevState); // should be immutable
expect(state.listUsersPending).toBe(true);
});
it('handles action type USER_ADMIN_FETCH_USERS_SUCCESS correctly', () => {
const prevState = { listUsersPending: true };
const state = reducer(
prevState,
{ type: USER_ADMIN_FETCH_USERS_SUCCESS, data: {} }
);
expect(state).not.toBe(prevState); // should be immutable
expect(state.listUsersPending).toBe(false);
});
it('handles action type USER_ADMIN_FETCH_USERS_FAILURE correctly', () => {
const prevState = { listUsersPending: true };
const state = reducer(
prevState,
{ type: USER_ADMIN_FETCH_USERS_FAILURE, data: { error: new Error('some error') } }
);
expect(state).not.toBe(prevState); // should be immutable
expect(state.listUsersPending).toBe(false);
expect(state.listUsersError).toEqual(expect.anything());
});
it('handles action type USER_ADMIN_FETCH_USERS_DISMISS_ERROR correctly', () => {
const prevState = { listUsersError: new Error('some error') };
const state = reducer(
prevState,
{ type: USER_ADMIN_FETCH_USERS_DISMISS_ERROR }
);
expect(state).not.toBe(prevState); // should be immutable
expect(state.listUsersError).toBe(null);
});
});