Commit f78eb440 authored by Tamlyn Rhodes's avatar Tamlyn Rhodes

Convert to GraphQL with Apollo Client

Remove redundant actions and reducers
Add withLoader helper to handle component loading state
parent c3c35105
Pipeline #3163 failed with stage
in 2 minutes and 26 seconds
module.exports = {
'pubsweet-client': {
API_ENDPOINT: 'http://localhost:3000/api',
},
}
module.exports = {
'pubsweet-client': {
API_ENDPOINT: 'http://example.com',
// this should be a relative URL but tests use node-fetch which requires absolute URL
API_ENDPOINT: '/graphql',
'update-subscriber': {
visible: true,
},
},
authsome: {
mode: 'fake-mode'
mode: 'fake-mode',
},
}
......@@ -16,11 +16,15 @@
"author": "Collaborative Knowledge Foundation",
"license": "MIT",
"dependencies": {
"apollo-client-preset": "^1.0.3",
"apollo-link": "^1.0.3",
"authsome": "0.0.9",
"config": "^1.21.0",
"eslint-config-prettier": "^2.6.0",
"event-source-polyfill": "^0.0.10",
"global": "^4.3.1",
"graphql": "^0.11.7",
"graphql-tag": "^2.5.0",
"husky": "^0.14.3",
"isomorphic-fetch": "^2.1.1",
"lint-staged": "^4.2.3",
......@@ -29,6 +33,7 @@
"prop-types": "^15.5.8",
"pubsweet-component-login": "^0.5.2",
"react": "^15.4.4",
"react-apollo": "^2.0.1",
"react-css-themr": "^2.1.2",
"react-redux": "^5.0.2",
"react-router-dom": "^4.2.2",
......
import * as api from '../helpers/api'
import * as T from './types'
const collectionUrl = (collection, suffix) => {
let url = '/collections'
if (collection) url += `/${collection.id}`
if (suffix) url += `/${suffix}`
return url
}
function getCollectionsRequest() {
return {
type: T.GET_COLLECTIONS_REQUEST,
}
}
function getCollectionsFailure(error) {
return {
type: T.GET_COLLECTIONS_FAILURE,
error: error,
}
}
function getCollectionsSuccess(collections) {
return {
type: T.GET_COLLECTIONS_SUCCESS,
collections: collections,
receivedAt: Date.now(),
}
}
export function getCollections(options) {
return dispatch => {
dispatch(getCollectionsRequest())
let url = collectionUrl()
if (options && options.fields) {
url += '?fields=' + encodeURIComponent(options.fields.join(','))
}
return api
.get(url)
.then(
collections => dispatch(getCollectionsSuccess(collections)),
err => dispatch(getCollectionsFailure(err)),
)
}
}
function getCollectionTeamsRequest() {
return {
type: T.GET_COLLECTION_TEAMS_REQUEST,
}
}
function getCollectionTeamsFailure(error) {
return {
type: T.GET_COLLECTION_TEAMS_FAILURE,
error: error,
}
}
function getCollectionTeamsSuccess(teams) {
return {
type: T.GET_COLLECTION_TEAMS_SUCCESS,
teams,
receivedAt: Date.now(),
}
}
export function getCollectionTeams(collection) {
return dispatch => {
dispatch(getCollectionTeamsRequest())
let url = collectionUrl(collection, 'teams')
return api
.get(url)
.then(
teams => dispatch(getCollectionTeamsSuccess(teams)),
err => dispatch(getCollectionTeamsFailure(err)),
)
}
}
function createCollectionRequest(collection) {
return {
type: T.CREATE_COLLECTION_REQUEST,
collection: collection,
}
}
function createCollectionSuccess(collection) {
return {
type: T.CREATE_COLLECTION_SUCCESS,
collection: collection,
}
}
function createCollectionFailure(collection, error) {
return {
type: T.CREATE_COLLECTION_FAILURE,
isFetching: false,
collection: collection,
error: error,
}
}
export function createCollection(collection) {
return dispatch => {
dispatch(createCollectionRequest(collection))
const url = collectionUrl()
return api
.create(url, collection)
.then(
collection => dispatch(createCollectionSuccess(collection)),
err => dispatch(createCollectionFailure(collection, err)),
)
}
}
function getCollectionRequest(collection) {
return {
type: T.GET_COLLECTION_REQUEST,
collection: collection,
}
}
function getCollectionSuccess(collection) {
return {
type: T.GET_COLLECTION_SUCCESS,
collection: collection,
receivedAt: Date.now(),
}
}
function getCollectionFailure(collection, error) {
return {
type: T.GET_COLLECTION_FAILURE,
isFetching: false,
collection: collection,
error: error,
}
}
export function getCollection(collection) {
return dispatch => {
dispatch(getCollectionRequest(collection))
const url = collectionUrl(collection)
return api
.get(url)
.then(
collection => dispatch(getCollectionSuccess(collection)),
err => dispatch(getCollectionFailure(collection, err)),
)
}
}
function updateCollectionRequest(collection) {
return {
type: T.UPDATE_COLLECTION_REQUEST,
collection: collection,
}
}
function updateCollectionSuccess(collection, update) {
return {
type: T.UPDATE_COLLECTION_SUCCESS,
collection: collection,
update: update,
receivedAt: Date.now(),
}
}
function updateCollectionFailure(collection, error) {
return {
type: T.UPDATE_COLLECTION_FAILURE,
isFetching: false,
collection: collection,
error: error,
}
}
export function updateCollection(collection) {
return dispatch => {
dispatch(updateCollectionRequest(collection))
const url = collectionUrl(collection)
return api
.update(url, collection)
.then(
update => dispatch(updateCollectionSuccess(collection, update)),
err => dispatch(updateCollectionFailure(collection, err)),
)
}
}
function deleteCollectionRequest(collection) {
return {
type: T.DELETE_COLLECTION_REQUEST,
collection: collection,
update: { deleted: true },
}
}
function deleteCollectionSuccess(collection) {
return {
type: T.DELETE_COLLECTION_SUCCESS,
collection: collection,
}
}
function deleteCollectionFailure(collection, error) {
return {
type: T.DELETE_COLLECTION_FAILURE,
collection: collection,
update: { deleted: undefined },
error: error,
}
}
export function deleteCollection(collection) {
return dispatch => {
dispatch(deleteCollectionRequest(collection))
const url = collectionUrl(collection)
return api
.remove(url)
.then(
() => dispatch(deleteCollectionSuccess(collection)),
err => dispatch(deleteCollectionFailure(collection, err)),
)
}
}
import * as api from '../helpers/api'
import * as T from './types'
function getCurrentUserRequest() {
return {
type: T.GET_CURRENT_USER_REQUEST,
}
}
function getCurrentUserSuccess(user) {
return {
type: T.GET_CURRENT_USER_SUCCESS,
user,
}
}
function getCurrentUserFailure(error) {
return {
type: T.GET_CURRENT_USER_FAILURE,
error,
}
}
export function getCurrentUser() {
return dispatch => {
dispatch(getCurrentUserRequest())
return api
.get('/users/authenticate')
.then(user => dispatch(getCurrentUserSuccess(user)))
.catch(err => dispatch(getCurrentUserFailure(err)))
}
}
import request from '../helpers/api'
import * as T from './types'
function fileUploadRequest() {
return {
type: T.FILE_UPLOAD_REQUEST,
isFetching: true,
}
}
function fileUploadSuccess(file) {
return {
type: T.FILE_UPLOAD_SUCCESS,
isFetching: false,
file: file,
}
}
function fileUploadFailure(message) {
return {
type: T.FILE_UPLOAD_FAILURE,
isFetching: false,
error: message,
}
}
export function fileUpload(file) {
return dispatch => {
dispatch(fileUploadRequest())
const data = new FormData()
data.append('file', file)
let opts = {
method: 'POST',
headers: {
Accept: 'text/plain', // the response is a URL
// TODO: set the Location header of the response instead
},
body: data,
}
return request('/upload', opts).then(
file => dispatch(fileUploadSuccess(file)),
err => dispatch(fileUploadFailure(err)),
)
}
}
import * as api from '../helpers/api'
import * as T from './types'
export const fragmentUrl = (collection, fragment) => {
let url = ''
if (collection) url += `/collections/${collection.id}`
url += '/fragments'
if (fragment && fragment.id) url += `/${fragment.id}`
return url
}
function getFragmentsRequest(collection) {
return {
type: T.GET_FRAGMENTS_REQUEST,
collection: collection,
}
}
function getFragmentsSuccess(collection, fragments) {
return {
type: T.GET_FRAGMENTS_SUCCESS,
collection: collection,
fragments: fragments,
receivedAt: Date.now(),
}
}
function getFragmentsFailure(error) {
return {
type: T.GET_FRAGMENTS_FAILURE,
error: error,
}
}
export function getFragments(collection, options) {
return dispatch => {
dispatch(getFragmentsRequest(collection))
let url = fragmentUrl(collection)
if (options && options.fields) {
url += '?fields=' + encodeURIComponent(options.fields.join(','))
}
return api
.get(url)
.then(
fragments => dispatch(getFragmentsSuccess(collection, fragments)),
err => dispatch(getFragmentsFailure(err)),
)
}
}
function createFragmentRequest(fragment) {
return {
type: T.CREATE_FRAGMENT_REQUEST,
fragment: fragment,
}
}
function createFragmentSuccess(collection, fragment) {
return {
type: T.CREATE_FRAGMENT_SUCCESS,
collection: collection,
fragment: fragment,
}
}
function createFragmentFailure(fragment, error) {
return {
type: T.CREATE_FRAGMENT_FAILURE,
isFetching: false,
fragment: fragment,
error: error,
}
}
export function createFragment(collection, fragment) {
return dispatch => {
dispatch(createFragmentRequest(fragment))
const url = fragmentUrl(collection, fragment)
return api
.create(url, fragment)
.then(
fragment => dispatch(createFragmentSuccess(collection, fragment)),
err => dispatch(createFragmentFailure(fragment, err)),
)
}
}
function getFragmentRequest(fragment) {
return {
type: T.GET_FRAGMENT_REQUEST,
fragment: fragment,
}
}
function getFragmentSuccess(fragment) {
return {
type: T.GET_FRAGMENT_SUCCESS,
fragment: fragment,
receivedAt: Date.now(),
}
}
function getFragmentFailure(fragment, error) {
return {
type: T.GET_FRAGMENT_FAILURE,
isFetching: false,
fragment: fragment,
error: error,
}
}
export function getFragment(collection, fragment) {
return dispatch => {
dispatch(getFragmentRequest(fragment))
const url = fragmentUrl(collection, fragment)
return api
.get(url)
.then(
fragment => dispatch(getFragmentSuccess(fragment)),
err => dispatch(getFragmentFailure(fragment, err)),
)
}
}
function updateFragmentRequest(fragment) {
return {
type: T.UPDATE_FRAGMENT_REQUEST,
fragment: fragment,
}
}
function updateFragmentSuccess(fragment, update) {
return {
type: T.UPDATE_FRAGMENT_SUCCESS,
fragment: fragment,
update: update,
receivedAt: Date.now(),
}
}
function updateFragmentFailure(fragment, error) {
return {
type: T.UPDATE_FRAGMENT_FAILURE,
isFetching: false,
fragment: fragment,
error: error,
}
}
export function updateFragment(collection, fragment) {
return dispatch => {
dispatch(updateFragmentRequest(fragment))
const url = fragmentUrl(collection, fragment)
return api
.update(url, fragment)
.then(
update => dispatch(updateFragmentSuccess(fragment, update)),
err => dispatch(updateFragmentFailure(fragment, err)),
)
}
}
function deleteFragmentRequest(fragment) {
return {
type: T.DELETE_FRAGMENT_REQUEST,
fragment: fragment,
update: { deleted: true },
}
}
function deleteFragmentSuccess(collection, fragment) {
return {
type: T.DELETE_FRAGMENT_SUCCESS,
collection: collection,
fragment: fragment,
}
}
function deleteFragmentFailure(fragment, error) {
return {
type: T.DELETE_FRAGMENT_FAILURE,
fragment: fragment,
update: { deleted: undefined },
error: error,
}
}
export function deleteFragment(collection, fragment) {
return dispatch => {
dispatch(deleteFragmentRequest(fragment))
const url = fragmentUrl(collection, fragment)
return api
.remove(url)
.then(
json => dispatch(deleteFragmentSuccess(collection, fragment)),
err => dispatch(deleteFragmentFailure(fragment, err)),
)
}
}
import { RESET_ERROR_MESSAGE } from './types'
import * as collections from './collections'
import * as currentUser from './currentUser'
import * as fileUpload from './fileUpload'
import * as fragments from './fragments'
import * as teams from './teams'
import * as users from './users'
import componentActions from '../components/actions'
// Resets the currently visible error message.
const resetErrorMessage = () => ({
type: RESET_ERROR_MESSAGE,
})
// Hydrate hydrates the store from a persistent store, the backend.
// It gets collections, fragments and user data (via token).
const hydrate = () => dispatch =>
Promise.all([
dispatch(currentUser.getCurrentUser()),
dispatch(collections.getCollections()),
])
export default {
...collections,
...currentUser,
...fileUpload,
...fragments,
...teams,
...users,
...componentActions,
hydrate,
resetErrorMessage,
}
export default {}
import * as api from '../helpers/api'
import * as T from './types'
const teamUrl = team => {
let url = '/teams'
if (team) url += `/${team.id}`
return url
}
function getTeamsRequest() {
return {
type: T.GET_TEAMS_REQUEST,
isFetching: true,
}
}
function getTeamsSuccess(teams) {
return {
type: T.GET_TEAMS_SUCCESS,
isFetching: false,
teams: teams,
}
}
function getTeamsFailure(message) {
return {
type: T.GET_TEAMS_FAILURE,
isFetching: false,
message,
}
}
export function getTeams() {
return dispatch => {
dispatch(getTeamsRequest())
return api
.get(teamUrl())
.then(
teams => dispatch(getTeamsSuccess(teams)),
err => dispatch(getTeamsFailure(err)),
)
}
}
function createTeamRequest(team) {
return {
type: T.CREATE_TEAM_REQUEST,
team: team,
}
}
function createTeamSuccess(team) {
return {
type: T.CREATE_TEAM_SUCCESS,
team: team,
}
}
function createTeamFailure(team, error) {
return {
type: T.CREATE_TEAM_FAILURE,
isFetching: false,
team: team,
error: error,
}
}
export function createTeam(team) {
return dispatch => {
dispatch(createTeamRequest(team))
const url = teamUrl()
return api
.create(url, team)
.then(
team => dispatch(createTeamSuccess(team)),
err => dispatch(createTeamFailure(team, err)),
)
}
}
function updateTeamRequest(team) {
return {
type: T.UPDATE_TEAM_REQUEST,
team: team,
}
}
function updateTeamSuccess(team) {
return {
type: T.UPDATE_TEAM_SUCCESS,
team: team,
}
}
function updateTeamFailure(team, error) {
return {