Add a generic request function with promises

This commit is contained in:
Grégoire Delattre 2016-11-16 22:43:00 +01:00
parent 408c2fca75
commit b819f988f4
6 changed files with 131 additions and 92 deletions

View File

@ -0,0 +1,48 @@
package web
import (
"html/template"
"net/http"
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/data"
"github.com/gorilla/mux"
)
// TmplFuncs handles global template functions
var tmplFuncs = []template.FuncMap{
map[string]interface{}{
"safeURL": func(s string) template.URL {
return template.URL(s)
},
},
}
// AddTmplFunc adds a template function
func AddTmplFunc(name string, f interface{}) error {
tmplFuncs = append(tmplFuncs, map[string]interface{}{
name: f,
})
return nil
}
// TemplateData represents object passed to template renderer
type TemplateData struct {
Route string
Data map[string]interface{}
}
// Rends a view
func (e *Env) Rends(w http.ResponseWriter, r *http.Request, template string) error {
if r.Header.Get("Accept") == "application/json" {
return e.Render.JSON(w, http.StatusOK, TemplateData{
Route: mux.CurrentRoute(r).GetName(),
Data: data.GetAllData(r),
})
}
return e.Render.HTML(w, http.StatusOK, template, TemplateData{
Route: mux.CurrentRoute(r).GetName(),
Data: data.GetAllData(r),
})
}

View File

@ -1,119 +1,56 @@
import axios from 'axios' import { configureAxios, request } from '../requests'
// Select Movie // Select Movie
export const SELECT_MOVIE = 'SELECT_MOVIE'
export function selectMovie(index) { export function selectMovie(index) {
return { return {
type: SELECT_MOVIE, type: 'SELECT_MOVIE',
index index
} }
} }
export const IS_USER_LOGGED_IN = 'IS_USER_LOGGED_IN'
export function isUserLoggedIn() { export function isUserLoggedIn() {
return { return {
type: IS_USER_LOGGED_IN, type: 'IS_USER_LOGGED_IN',
} }
} }
export const ADD_ERROR = 'ADD_ERROR'
export function addError(message) { export function addError(message) {
return { return {
type: ADD_ERROR, type: 'ADD_ERROR',
payload: { payload: {
message, message,
} }
} }
} }
export const DISMISS_ERROR = 'DISMISS_ERROR'
export function dismissError() { export function dismissError() {
return { return {
type: DISMISS_ERROR, type: 'DISMISS_ERROR',
} }
} }
export const USER_LOGOUT = 'USER_LOGOUT'
export function userLogout() { export function userLogout() {
return { return {
type: USER_LOGOUT, type: 'USER_LOGOUT',
} }
} }
export const USER_LOGIN_FULFILLED = 'USER_LOGIN_FULFILLED',
USER_LOGIN_PENDING = 'USER_LOGIN_PENDING';
export function loginUser(username, password) { export function loginUser(username, password) {
return function(dispatch) { return request(
dispatch({ 'USER_LOGIN',
type: USER_LOGIN_PENDING, configureAxios().post(
})
axios.post(
'/users/login', '/users/login',
{ {
username: username, username: username,
password: password, password: password,
}, },
).then(response => { )
if (response.data.status === 'error') )
{
dispatch({
type: ADD_ERROR,
payload: {
message: response.data.message,
}
})
}
dispatch({
type: USER_LOGIN_FULFILLED,
payload: response.data,
})
}).catch(error => {
console.log(error)
})
}
} }
export const MOVIE_LIST_FETCH_FULFILLED = 'MOVIE_LIST_FETCH_FULFILLED',
MOVIE_LIST_FETCH_PENDING = 'MOVIE_LIST_FETCH_PENDING';
export function fetchMovies() { export function fetchMovies() {
const token = localStorage.getItem('token'); return request(
const header = 'Bearer ' + token; 'MOVIE_LIST_FETCH',
return function(dispatch) { configureAxios().get('/movies/explore/popular')
dispatch({ )
type: MOVIE_LIST_FETCH_PENDING,
})
axios.get('/movies/explore/popular', {
headers: { 'Authorization': header },
})
.then(response => {
if (response.data.status === 'error')
{
dispatch({
type: ADD_ERROR,
payload: {
message: response.data.message,
}
})
} else {
dispatch({
type: MOVIE_LIST_FETCH_FULFILLED,
payload: response.data,
})
}
})
.catch(error => {
// Unauthorized
if (error.response.status == 401) {
dispatch({
type: USER_LOGOUT,
})
}
dispatch({
type: ADD_ERROR,
payload: {
message: error.response.data,
}
})
})
}
} }

View File

@ -1,12 +1,10 @@
import { ADD_ERROR, DISMISS_ERROR } from '../actions/actionCreators'
export default function error(state = {}, action) { export default function error(state = {}, action) {
switch (action.type) { switch (action.type) {
case ADD_ERROR: case 'ADD_ERROR':
return Object.assign({}, state, { return Object.assign({}, state, {
message: action.payload.message, message: action.payload.message,
}) })
case DISMISS_ERROR: case 'DISMISS_ERROR':
return {}; return {};
default: default:
return state; return state;

View File

@ -1,5 +1,3 @@
import { SELECT_MOVIE, MOVIE_LIST_FETCH_FULFILLED, MOVIE_LIST_FETCH_PENDING } from '../actions/actionCreators'
const defaultState = { const defaultState = {
movies: [], movies: [],
selectedMovieIndex: 0, selectedMovieIndex: 0,
@ -7,13 +5,13 @@ const defaultState = {
export default function movieStore(state = defaultState, action) { export default function movieStore(state = defaultState, action) {
switch (action.type) { switch (action.type) {
case MOVIE_LIST_FETCH_FULFILLED: case 'MOVIE_LIST_FETCH_FULFILLED':
return Object.assign({}, state, { return Object.assign({}, state, {
movies: action.payload.data, movies: action.payload.data,
}) })
case MOVIE_LIST_FETCH_PENDING: case 'MOVIE_LIST_FETCH_PENDING':
return state return state
case SELECT_MOVIE: case 'SELECT_MOVIE':
return Object.assign({}, state, { return Object.assign({}, state, {
selectedMovieIndex: action.index, selectedMovieIndex: action.index,
}) })

View File

@ -1,4 +1,3 @@
import { IS_USER_LOGGED_IN, USER_LOGIN_PENDING, USER_LOGIN_FULFILLED, USER_LOGOUT } from '../actions/actionCreators'
import jwtDecode from 'jwt-decode' import jwtDecode from 'jwt-decode'
const defaultState = { const defaultState = {
@ -10,22 +9,22 @@ const defaultState = {
export default function userStore(state = defaultState, action) { export default function userStore(state = defaultState, action) {
switch (action.type) { switch (action.type) {
case USER_LOGIN_PENDING: case 'USER_LOGIN_PENDING':
return Object.assign({}, state, { return Object.assign({}, state, {
userLoading: true, userLoading: true,
}) })
case USER_LOGIN_FULFILLED: case 'USER_LOGIN_FULFILLED':
if (action.payload.status === "error") { if (action.payload.status === "error") {
return logoutUser(state) return logoutUser(state)
} }
return updateFromToken(state, action.payload.data.token) return updateFromToken(state, action.payload.data.token)
case IS_USER_LOGGED_IN: case 'IS_USER_LOGGED_IN':
let localToken = localStorage.getItem('token'); let localToken = localStorage.getItem('token');
if (!localToken || localToken === "") { if (!localToken || localToken === "") {
return state; return state;
} }
return updateFromToken(state, localToken) return updateFromToken(state, localToken)
case USER_LOGOUT: case 'USER_LOGOUT':
return logoutUser(state) return logoutUser(state)
default: default:
return state; return state;

59
src/public/js/requests.js Normal file
View File

@ -0,0 +1,59 @@
import axios from 'axios'
// This functions returns an axios instance, the token is added to the
// configuration if found in the localStorage
export function configureAxios(headers = {}) {
// Get the token from the localStorate
const token = localStorage.getItem('token');
if (token) {
headers = { 'Authorization': `Bearer ${token}` };
}
return axios.create({
headers
})
}
// This function takes en event prefix to dispatch evens during the life of the
// request, it also take a promise (axios request)
export function request(eventPrefix, promise) {
// Events
const pending = `${eventPrefix}_PENDING`;
const fulfilled = `${eventPrefix}_FULFILLED`;
return function(dispatch) {
dispatch({
type: pending,
})
promise
.then(response => {
if (response.data.status === 'error')
{
dispatch({
type: 'ADD_ERROR',
payload: {
message: response.data.message,
}
})
}
dispatch({
type: fulfilled,
payload: response.data,
})
})
.catch(error => {
// Unauthorized
if (error.response.status == 401) {
dispatch({
type: 'USER_LOGOUT',
})
}
dispatch({
type: 'ADD_ERROR',
payload: {
message: error.response.data,
}
})
})
}
}