Skip to content

Instantly share code, notes, and snippets.

@gpbaculio
Created January 2, 2018 05:55
Show Gist options
  • Save gpbaculio/c2d98e900d88f0c322349302b4a3e813 to your computer and use it in GitHub Desktop.
Save gpbaculio/c2d98e900d88f0c322349302b4a3e813 to your computer and use it in GitHub Desktop.
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