Created
July 23, 2018 03:55
-
-
Save HenrikJoreteg/b7a97a5daf465d87b1973c6dfc2f5220 to your computer and use it in GitHub Desktop.
Light graphQL / redux-bundler example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import gqlFetch from '../helpers/gql-fetch' | |
import config from '../config' | |
export default { | |
name: 'extraArgs', | |
getExtraArgs: store => { | |
return { | |
gqlFetch: gqlFetch({ | |
apiUrl: config.apiUrl, | |
getToken: () => store.selectAuthToken(), | |
handleBadStatusResponse: () => { | |
store.doSignOut() | |
} | |
}) | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const defaults = { | |
apiUrl: null, | |
handleBadStatusResponse: response => response, | |
firstCatch: err => { | |
throw err | |
}, | |
getToken: () => null | |
} | |
export default spec => (query, variables = null) => { | |
const opts = Object.assign({}, defaults, spec) | |
const token = opts.getToken() | |
const encodedQuery = encodeURIComponent(query.trim()) | |
const encodedVars = variables | |
? `&variables=${encodeURIComponent(JSON.stringify(variables))}` | |
: '' | |
const tokenQuery = token ? `&token=${token}` : '' | |
return fetch( | |
`${opts.apiUrl}/graphql?query=${encodedQuery}${encodedVars}${tokenQuery}`, | |
{ | |
method: 'post' | |
} | |
) | |
.then(response => response.json()) | |
.then(parsed => { | |
if (parsed.errors) { | |
parsed.errors.forEach(error => { | |
if ( | |
(error.message === 'NO_AUTH' || error.message === 'NO_USER') && | |
opts.handleBadStatusResponse | |
) { | |
opts.handleBadStatusResponse() | |
} | |
}) | |
throw parsed.errors | |
} | |
return parsed | |
}) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { createSelector } from 'redux-bundler' | |
import { setItem } from '../helpers/ls' | |
import omit from '../helpers/omit' | |
const subscriptionTransform = sub => | |
Object.assign({}, sub, { | |
cancelledAt: Number(sub.cancelledAt), | |
currentPeriodEnd: Number(sub.currentPeriodEnd), | |
currentPeriodStart: Number(sub.currentPeriodStart), | |
start: Number(sub.start) | |
}) | |
export const userResponseSnippet = `{ | |
id | |
name | |
emailVerified | |
token | |
// lots more stuff here | |
... | |
} | |
` | |
const actions = [ | |
'USER_FETCH_STARTED', | |
'USER_FETCH_FINISHED', | |
'USER_FETCH_FAILED', | |
] | |
const STARTED = actions[0] | |
const FINISHED = actions[1] | |
const FAILED = actions[2] | |
export default { | |
name: 'user', | |
// we don't want to include START because we don't want the app | |
// to ever think it's starting in "loading" mode. | |
persistActions: [FAILED, FINISHED], | |
getReducer: () => { | |
const initialState = { | |
data: null, | |
loading: false, | |
error: null | |
} | |
return (state = initialState, { payload, type }) => { | |
if (type === STARTED) { | |
return Object.assign({}, state, { loading: true }) | |
} | |
if (type === FAILED) { | |
return Object.assign({}, state, { loading: false, error: payload }) | |
} | |
if (type === FINISHED) { | |
return Object.assign({}, state, { | |
error: null, | |
loading: false, | |
data: omit(payload, ['inhalationAgents', 'ivDrugs', 'token']) | |
}) | |
} | |
if (type === CLEARED) { | |
return initialState | |
} | |
return state | |
} | |
}, | |
doFetchUser: () => ({ gqlFetch, dispatch }) => { | |
dispatch({ type: STARTED }) | |
return gqlFetch(`{ user ${userResponseSnippet} }`) | |
.then(res => { | |
store.doReceiveUser(res.data.user) | |
}) | |
.catch(err => dispatch({ type: FAILED, payload: err })) | |
}, | |
doUpdateUser: properties => ({ gqlFetch, dispatch }) => { | |
dispatch({ type: STARTED }) | |
return gqlFetch( | |
`mutation updateUser($properties: UserInput!) { | |
updateUser(properties: $properties) ${userResponseSnippet} | |
}`, | |
{ properties } | |
) | |
.then(res => { | |
store.doReceiveUser(res.data.updateUser) | |
}) | |
.catch(err => dispatch({ type: FAILED, payload: err })) | |
}, | |
doUpdatePaymentMethod: token => ({ gqlFetch }) => { | |
return gqlFetch(` | |
mutation { | |
addPaymentMethod(stripeToken:"${token}") ${userResponseSnippet} | |
} | |
`) | |
.then(res => { | |
store.doReceiveUser(res.data.addPaymentMethod) | |
}) | |
.catch(err => { | |
const first = err[0].message | |
if (first) { | |
const [type, receivedMessage] = first.split('|') | |
if (receivedMessage) { | |
throw Error(receivedMessage) | |
} | |
throw Error(type) | |
} | |
}) | |
}, | |
doCancelSubscription: () => ({ gqlFetch, store }) => { | |
const subscription = store.selectUserSubscription() | |
const id = subscription && subscription.id | |
if (!id) { | |
return | |
} | |
return gqlFetch(` | |
mutation { | |
cancelSubscription(id:"${id}") ${userResponseSnippet} | |
} | |
`).then(res => { | |
store.doReceiveUser(res.data.cancelSubscription) | |
}) | |
}, | |
doReceiveUser: user => { | |
setItem('token', user.token) | |
return { type: FINISHED, payload: user } | |
}, | |
selectUserRaw: state => state.user, | |
selectUser: state => state.user.data, | |
selectUserIsLoading: state => state.user.loading, | |
selectUserSubscription: createSelector('selectUser', user => { | |
const sub = user && user.subscription | |
return sub && subscriptionTransform(sub) | |
}), | |
selectUserPaymentMethod: createSelector( | |
'selectUser', | |
user => user && user.paymentMethod | |
), | |
reactShouldFetchUser: createSelector( | |
'selectUserRaw', | |
'selectIsSignedIn', | |
(userRaw, isSignedIn) => { | |
if (!userRaw.loading && !userRaw.data && isSignedIn) { | |
return { actionCreator: 'doFetchUser' } | |
} | |
} | |
) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment