Created
January 2, 2018 05:55
-
-
Save gpbaculio/c2d98e900d88f0c322349302b4a3e813 to your computer and use it in GitHub Desktop.
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 { | |
GraphQLSchema, | |
GraphQLObjectType, | |
GraphQLString, | |
GraphQLNonNull, | |
GraphQLBoolean, | |
GraphQLList, | |
GraphQLInt | |
} from 'graphql'; | |
import { | |
connectionArgs, | |
connectionDefinitions, | |
connectionFromArray, | |
cursorForObjectInConnection, | |
fromGlobalId, | |
globalIdField, | |
mutationWithClientMutationId, | |
nodeDefinitions, | |
toGlobalId | |
} from 'graphql-relay'; | |
import pubSub from './pubSub'; | |
import axios from 'axios'; | |
import {getContextUser, generateToken} from './auth'; | |
const users = []; | |
export const getViewer = userEmail => users.find(({email}) => email === userEmail); | |
const getUser = userId => users.find(({id}) => id === userId); | |
class User { // fields in this class MUST MATCH your Graphql Type fields | |
// NOTE! in every class, assign an id, it is used by relay locally on fromGlobalId | |
constructor(email) { | |
this.id = Date.now(); | |
// in a real world app, you would have to assign an id relevant to the id of the | |
// node on the database, then on the nodeInterface, you would call a function | |
// that can retrieve the node to the database, using an id. | |
this.email = email; | |
} | |
} | |
class News { // fields in this class MUST MATCH your Graphql Type fields | |
// NOTE! in every class, assign an id, it is used by relay locally on fromGlobalId | |
constructor({ | |
newsId, | |
title, | |
content, | |
author, | |
tags, | |
link, | |
createdAt, | |
publishedAt, | |
positive, | |
negative, | |
hits, | |
weight, | |
important, | |
saved, | |
pagingState, | |
userRate | |
}) { | |
this.id = newsId; | |
this.title = title; | |
this.content = content; | |
this.author = author; | |
this.tags = tags; | |
this.link = link; | |
this.createdAt = createdAt; | |
this.publishedAt = publishedAt; | |
this.positive = positive; | |
this.negative = negative; | |
this.hits = hits; | |
this.weight = weight; | |
this.important = important; | |
this.saved = saved; | |
this.pagingState = pagingState; | |
this.userRate = userRate; | |
} | |
} | |
const {nodeInterface, nodeField} = nodeDefinitions( // what is this? read more @ https://stackoverflow.com/questions/33399901/in-relay-what-role-do-the-node-interface-and-the-global-id-spec-play | |
async(globalId) => { | |
const {type, id} = fromGlobalId(globalId); | |
if (type === 'User') { | |
const userFound = getUser(id); | |
return userFound; | |
} else if (type === 'News') { | |
let news = await axios | |
.get(`http://www.cryptonewsapi.com/api/v1/news/${id}`) | |
.then(response => { | |
return ({ | |
...response.data | |
}); | |
}) | |
.catch(err => { | |
console.err(`failed to retrieve news! (${id})`); | |
}); | |
console.log('nodeInterface news = ', news) | |
return new News(news); | |
} | |
return null; | |
}, (obj) => { | |
if (obj instanceof User) { | |
return GraphQLUser; | |
} else if (obj instanceof News) { | |
return GraphQLNews; | |
} | |
return null; | |
}); | |
const GraphQLNews = new GraphQLObjectType({ | |
name: 'News', | |
fields: () => ({ | |
id: globalIdField('News'), | |
userRate: { | |
type: new GraphQLObjectType({ | |
name: 'userRate', | |
fields: { | |
rate: { | |
type: GraphQLInt, | |
defaultValue: 0, | |
resolve: ({rate}) => rate | |
}, | |
important: { | |
type: GraphQLBoolean, | |
defaultValue: false, | |
resolve: ({important}) => important | |
}, | |
saved: { | |
type: GraphQLBoolean, | |
defaultValue: false, | |
resolve: ({saved}) => saved | |
} | |
} | |
}), | |
resolve: ({userRate}) => userRate | |
}, | |
title: { | |
type: GraphQLString, | |
resolve: ({title}) => title | |
}, | |
content: { | |
type: GraphQLString, | |
resolve: ({content}) => content | |
}, | |
author: { | |
type: GraphQLString, | |
resolve: ({author}) => author | |
}, | |
tags: { | |
type: new GraphQLList(GraphQLString), | |
resolve: ({tags}) => tags | |
}, | |
link: { | |
type: GraphQLString, | |
resolve: ({link}) => link | |
}, | |
createdAt: { | |
type: GraphQLString, | |
resolve: ({createdAt}) => createdAt | |
}, | |
publishedAt: { | |
type: GraphQLString, | |
resolve: ({publishedAt}) => publishedAt | |
}, | |
positive: { | |
type: GraphQLInt, | |
resolve: ({positive}) => positive | |
}, | |
negative: { | |
type: GraphQLInt, | |
resolve: ({negative}) => negative | |
}, | |
hits: { | |
type: GraphQLInt, | |
resolve: ({hits}) => hits | |
}, | |
weight: { | |
type: GraphQLInt, | |
resolve: ({weight}) => weight | |
}, | |
important: { | |
type: GraphQLInt, | |
resolve: ({important}) => important | |
}, | |
saved: { | |
type: GraphQLInt, | |
resolve: ({saved}) => saved | |
}, | |
pagingState: { | |
type: GraphQLString, | |
resolve: ({pagingState}) => pagingState | |
} | |
}), | |
interfaces: [nodeInterface] | |
}); | |
async function retrieveNews(pagingState, token) { | |
if (!pagingState) { | |
const allNews = await axios | |
.get(`http://www.cryptonewsapi.com/api/v1/news?pagingState`) | |
.then((response) => { | |
const {rows, pagingState} = response.data | |
let allNews = rows.filter(({title}) => title); | |
allNews = allNews.map(async(news) => { | |
const userRate = await axios({ // this method is used to see the user rating on news | |
method: 'get', | |
url: `http://www.cryptonewsapi.com/api/v1/news/${news.newsId}/stat`, | |
headers: { | |
Authorization: `Bearer ${token}` | |
} | |
}).then(response2 => { | |
console.log('response2 = ', response2.data) | |
return ({ | |
...response2.data | |
}); | |
}).catch(err => { | |
return ({rate: 0, important: false, saved: false}); | |
}); | |
return new News({ | |
...news, | |
userRate, | |
pagingState | |
}) | |
}) | |
return allNews; | |
}); | |
console.log('allNews = ', allNews) | |
return allNews; | |
} | |
} | |
const {connectionType: NewsConnection, edgeType: GraphQLNewsEdge} = connectionDefinitions({name: 'News', nodeType: GraphQLNews}); | |
const GraphQLUser = new GraphQLObjectType({ | |
name: 'User', | |
fields: { | |
id: globalIdField('User'), | |
email: { | |
type: GraphQLString, | |
resolve: root => root.email | |
}, | |
allNews: { | |
type: NewsConnection, | |
args: { | |
pagingState: { | |
type: GraphQLString | |
}, | |
first: { | |
type: GraphQLInt | |
}, | |
...connectionArgs, // connectionArgs is a default argument we use for pagination | |
}, | |
resolve: async(root, { | |
pagingState, | |
first, | |
...args | |
}, {token}) => { | |
const news = await retrieveNews(pagingState, token) | |
return connectionFromArray(news, args) | |
} | |
} | |
}, | |
interfaces: [nodeInterface] | |
}); | |
const query = new GraphQLObjectType({ | |
name: 'Query', | |
fields: () => ({ | |
node: nodeField, | |
viewer: { | |
type: GraphQLUser, | |
resolve: async(root, args, {token}) => { | |
const {email} = getContextUser(token); | |
const currentViewer = getViewer(email); // implement this someday, when retrieving user in api is available! | |
console.log('current viewer = ', currentViewer); | |
return currentViewer | |
? currentViewer | |
: 'guest'; | |
}, | |
// it's important to have function to get user from Db. | |
} | |
}) | |
}); | |
const GraphQLUserRegistrationMutation = mutationWithClientMutationId({ | |
name: 'UserRegistration', | |
inputFields: { | |
email: { | |
type: new GraphQLNonNull(GraphQLString) | |
}, | |
password: { | |
type: new GraphQLNonNull(GraphQLString) | |
} | |
}, | |
mutateAndGetPayload: async({email, password}) => { | |
try { | |
await axios | |
.post('www.cryptonewsapi.com/api/v1/users', {email, password}) | |
.then(() => { | |
const newUser = new User(email); | |
console.log(`successfully registered user ${email}`); | |
users.push(newUser); | |
return ({registrationSuccess: true}); | |
}); | |
} catch (e) { | |
console.log(`user ${email} failed to register`, e); | |
} | |
return ({registrationSuccess: false}); | |
}, | |
outputFields: { | |
registrationSuccessful: { | |
type: GraphQLBoolean, | |
resolve: ({registrationSuccessful}) => registrationSuccessful | |
} | |
} | |
}); | |
const GraphQLUserLoginMutation = mutationWithClientMutationId({ | |
name: 'UserLogin', | |
inputFields: { | |
email: { | |
type: new GraphQLNonNull(GraphQLString) | |
}, | |
password: { | |
type: new GraphQLNonNull(GraphQLString) | |
} | |
}, | |
mutateAndGetPayload: async({email, password}) => { | |
return await axios | |
.post('http://www.cryptonewsapi.com/api/v1/auth', {email, password}) | |
.then((response) => { | |
console.log(`user ${email} sucessfully logged in`); | |
return ({ | |
email, | |
refreshToken: response.data.refresh, | |
token: response.data.token, | |
apiKey: response.data.apiKey, | |
error: undefined, | |
auth_token: generateToken(email) // just incase in the future we want a jwt for retrieving user in db | |
}); | |
}) | |
.catch((err) => { | |
console.log(`user ${email} failed to login`, err.response.data); | |
return ({ | |
email: undefined, | |
refreshToken: undefined, | |
token: undefined, | |
apiKey: undefined, | |
error: err.response.data.code, | |
auth_token: undefined | |
}); | |
}); | |
}, | |
outputFields: { | |
token: { | |
type: GraphQLString, | |
resolve: ({token}) => token | |
}, | |
email: { | |
type: GraphQLString, | |
resolve: ({email}) => email | |
}, | |
refreshToken: { | |
type: GraphQLString, | |
resolve: ({refreshToken}) => refreshToken | |
}, | |
apiKey: { | |
type: GraphQLString, | |
resolve: ({apiKey}) => apiKey | |
}, | |
error: { | |
type: GraphQLString, | |
resolve: ({error}) => error | |
}, | |
auth_token: { | |
type: GraphQLString, | |
resolve: ({auth_token}) => auth_token | |
} | |
} | |
}); | |
const GraphQLVoteNewsPositiveMutation = mutationWithClientMutationId({ | |
name: 'VoteNewsPositive', | |
inputFields: { | |
id: { | |
type: GraphQLString | |
}, | |
rate: { | |
type: GraphQLInt | |
} | |
}, | |
mutateAndGetPayload: async({ | |
id, | |
rate | |
}, {token}) => { | |
console.log("mutateAndGetPayload token = ", token); | |
const relayObjectID = id; // relay needs to know its' globalId, return this on the id field of the node: news | |
const {id: newsId} = fromGlobalId(id); | |
console.log('newsId = ', newsId) | |
let query; | |
if (rate === 1) { | |
query = `http://www.cryptonewsapi.com/api/v1/news/${newsId}/votes/rate?value=0` | |
} | |
if (rate === 0) { | |
query = `http://www.cryptonewsapi.com/api/v1/news/${newsId}/votes/rate?value=1` | |
} | |
if (rate === -1) { | |
query = `http://www.cryptonewsapi.com/api/v1/news/${newsId}/votes/rate?value=1` | |
} | |
return await axios({ | |
method: 'post', | |
url: query, | |
headers: { | |
Authorization: `Bearer ${token}` //` // maybe add localstorge.token on context!! | |
} | |
}).then(async(res) => { | |
console.log("vote positive mutation success") | |
let newsData = await axios // retrieve the news | |
.get(`http://www.cryptonewsapi.com/api/v1/news/${newsId}`) | |
.then(response => { | |
return ({ | |
...response.data | |
}); | |
}) | |
.catch(err => { | |
console.err(`failed to retrieve news! (${newsId})`); | |
}); | |
let news = await axios({ // retrieve news stat | |
method: 'get', | |
url: `http://www.cryptonewsapi.com/api/v1/news/${newsId}/stat`, | |
headers: { | |
Authorization: `Bearer ${token}` //` // maybe add localstorge.token on context!! | |
} | |
}).then((response2) => { | |
const {data: userVoteData} = response2; | |
const stats = { | |
userRate: { | |
...userVoteData | |
} | |
}; | |
return new News({ | |
...newsData, | |
...stats, | |
id: relayObjectID | |
}); | |
}).catch(err => { // we should prevent user from casting vote in frontend, like direct the user on login? | |
console.log("failed to retrieve stats on outputfields votenewspositive mutation") | |
return new News({ | |
...newsData, | |
id: relayObjectID | |
}); | |
}); | |
console.log('newsWithStats = ', news) | |
pubSub.publish('voteNewsPositiveSubscription', {voteNewsPositiveSubscription: { | |
news | |
}}) | |
return ({news}) | |
}).catch(err => { | |
console.log("vote positive mutation error = ", err); | |
}); | |
}, | |
outputFields: { | |
news: { | |
type: GraphQLNews, | |
resolve: async({ | |
news | |
}, args) => news | |
} | |
} | |
}); | |
const mutation = new GraphQLObjectType({ | |
name: 'Mutation', | |
fields: { | |
userLogin: GraphQLUserLoginMutation, | |
userRegistration: GraphQLUserRegistrationMutation, | |
voteNewsPositive: GraphQLVoteNewsPositiveMutation | |
} | |
}); | |
const VoteNewsPositiveMutationPayloadType = new GraphQLObjectType({ | |
name: 'VoteNewsPositiveMutationPayload', | |
fields: () => ({ | |
news: { | |
type: GraphQLNews, | |
resolve: ({ | |
news | |
}, args, context) => { | |
console.log('subscription news = ', { | |
...news | |
}) | |
return ({ | |
...news | |
}) | |
} | |
} | |
}) | |
}); | |
const GraphQLVoteNewsPositiveSubscription = { | |
type: VoteNewsPositiveMutationPayloadType, | |
subscribe: () => pubSub.asyncIterator('voteNewsPositiveSubscription') | |
} | |
const subscription = new GraphQLObjectType({ | |
name: 'Subscription', | |
fields: { | |
voteNewsPositiveSubscription: GraphQLVoteNewsPositiveSubscription | |
} | |
}) | |
export const schema = new GraphQLSchema({query, mutation, subscription}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment