src/infra/service/graph.js
// @flow
import _ from 'lodash';
import { reportError } from 'infra/service/reporter';
const get = (url: string): Promise<any> => new Promise((resolve, reject) => {
window.FB.api(url, fbRes => {
if (fbRes.error) {
const code = fbRes.error.code;
const type = fbRes.error.type;
const id = fbRes.error.fbtrace_id;
const message = fbRes.error.message;
switch (code) {
default:
return reject(new Error(`${type}:${code}:${message}:${id}`));
}
}
return resolve(fbRes);
});
});
class GraphList {
data: {}[] = [];
next: string = '';
previous: string = '';
fetch(url: string) {
return get(url)
.then(res => {
this.data = [...this.data, ...res.data];
if (res.paging) {
this.previous = res.paging.previous;
this.next = res.paging.next;
} else {
this.previous = '';
this.next = '';
}
});
}
fetchForward(url: string, pages: number = 10, currentPage: number = 1) {
return this.fetch(currentPage === 1 ? url : this.next)
.then(() => {
if (this.next && currentPage < pages) {
return this.fetchForward(url, pages, currentPage + 1);
} else {
return this.data;
}
});
}
}
const login = () => new Promise((resolve, reject) => {
window.FB.login(response => {
if (response.authResponse) {
resolve(response);
} else {
reject(new Error('User cancelled login or did not fully authorize.'));
}
}, { scope: 'public_profile, email, user_managed_groups' });
});
const logout = () => new Promise((resolve) => {
window.FB.logout();
resolve(true);
});
const getLoginStatus = () => new Promise((resolve, reject) => {
window.FB.getLoginStatus((response) => {
if (response.status === 'connected') {
resolve(response);
} else {
reject(new Error('User not connected.'))
}
});
});
const getUser = () => get('/me?fields=id,name,email,picture')
.then(res => {
const user: UserProfile = {
facebookId: res.id,
name: res.name,
email: res.email,
picture: res.picture.data.url,
};
return user;
})
.catch(err => {
const errors = err.message.split(':');
switch (errors[1]) {
case '2500':
case '190':
case '104':
throw new Error('Access token required. Try to refresh your token with re-login.');
default:
reportError(new Error(err.message));
throw err;
}
});
const getUserManagedGroups = (): Promise<Group[]> => get('/me/groups?fields=id,name,privacy,cover,description,owner&limit=100')
.then(res => {
const rawGroups = res.data;
if (rawGroups.length < 1) return [];
const groups: Group[] = rawGroups.map(raw => ({
id: raw.id,
name: raw.name,
privacy: raw.privacy,
cover: (raw.cover || {}).source,
description: raw.description,
owner: raw.owner,
}));
return groups;
})
.catch(err => {
const errors = err.message.split(':');
switch (errors[1]) {
case '2500':
case '190':
case '104':
throw new Error('Access token required. Try to refresh your token with re-login.');
default:
reportError(new Error(err.message));
throw err;
}
});
const getGroup = (groupId: string): Promise<Group> => get(`/${groupId}?fields=id,name,privacy,cover,description,owner`)
.then(res => {
const group: Group = {
id: res.id,
name: res.name,
privacy: res.privacy,
cover: (res.cover || {}).source,
description: res.description,
owner: res.owner,
}
return group;
})
.catch(err => {
const errors = err.message.split(':');
switch (errors[1]) {
case '190':
case '104':
throw new Error('Access token required. Try to refresh your token with re-login.');
case '100':
case '803':
throw new Error('Group ID is not valid.');
default:
reportError(new Error(err.message));
throw err;
}
});
const getGroupFeed = (groupId: string, pages: number): Promise<{}[]> => {
const url = `/${groupId}/feed?fields=created_time,id,message,updated_time,caption,story,description,from,link,name,picture,status_type,type,shares,permalink_url,likes.limit(50),comments.limit(50){id,from,message,created_time,likes.limit(50),comments.limit(50){id,from,message,created_time,likes.limit(50)}}&limit=50`;
const list = new GraphList();
return list.fetchForward(url, pages)
.catch(err => {
const errors = err.message.split(':');
switch (errors[1]) {
case '1':
throw new Error('oAuth exception, try to refresh your browser manually.');
default:
reportError(new Error(err.message));
throw err;
}
});
};
const getGroupComments = (posts: Object[]): Promise<any> => {
// currently just put existing comments without fetching next page
const firstComments = [];
const secondComments = [];
// get post with comments
posts.forEach(post => {
const comments = ((post.comments || {}).data || []).map(comment => ({
parent: post.id,
hierarchy: 0,
...comment,
}));
if (comments) firstComments.push(...comments);
});
// get second comments
firstComments.forEach(com => {
const comments = ((com.comments || {}).data || []).map(comment => ({
parent: com.id,
hierarchy: 1,
...comment,
}));
if (comments) secondComments.push(...comments);
});
const comments = [...firstComments.map(com => _.omit(com, 'comments')), ...secondComments];
return Promise.resolve(comments)
.catch(err => {
const errors = err.message.split(':');
switch (errors[1]) {
default:
reportError(new Error(err.message));
throw err;
}
});
};
const getGroupMembers = (groupId: string, pages: number): Promise<{}[]> => {
const url = `/${groupId}/members?fields=id,name,administrator,picture,link&limit=100`;
const list = new GraphList();
return list.fetchForward(url, pages)
.catch(err => {
const errors = err.message.split(':');
switch (errors[1]) {
default:
reportError(new Error(err.message));
throw err;
}
});
};
export default {
login,
logout,
getLoginStatus,
getUser,
getGroup,
getUserManagedGroups,
getGroupFeed,
getGroupComments,
getGroupMembers,
};