Skip to content

Commit

Permalink
Merge pull request #198 from cofacts/articles
Browse files Browse the repository at this point in the history
Loads previously viewed articles and display in LIFF
  • Loading branch information
MrOrz committed Jun 19, 2020
2 parents f66dd36 + d01f0a6 commit a04a114
Show file tree
Hide file tree
Showing 7 changed files with 219 additions and 0 deletions.
1 change: 1 addition & 0 deletions .env.sample
Expand Up @@ -22,6 +22,7 @@ ROLLBAR_CLIENT_TOKEN=

# This should match rumors-api's RUMORS_LINE_BOT_SECRET
APP_SECRET=CHANGE_ME
APP_ID=RUMORS_LINE_BOT

# Cofacst API URL
API_URL=https://cofacts-api.hacktabl.org/graphql
Expand Down
2 changes: 2 additions & 0 deletions src/liff/.eslintrc.js
Expand Up @@ -30,7 +30,9 @@ module.exports = {
liff: 'readonly',

// Define plugin
APP_ID: 'readonly',
LIFF_ID: 'readonly',
DEBUG_LIFF: 'readonly',
COFACTS_API_URL: 'readonly',
},
};
2 changes: 2 additions & 0 deletions src/liff/App.svelte
@@ -1,11 +1,13 @@
<script>
import { page } from './lib';
import Articles from './pages/Articles.svelte';
import Source from './pages/Source.svelte';
import Reason from './pages/Reason.svelte';
import PositiveFeedback from './pages/PositiveFeedback.svelte';
import NegativeFeedback from './pages/NegativeFeedback.svelte';
const routes = {
articles: Articles,
source: Source,
reason: Reason,
'feedback/yes': PositiveFeedback,
Expand Down
106 changes: 106 additions & 0 deletions src/liff/__tests__/lib.js
Expand Up @@ -345,3 +345,109 @@ describe('assertSameSearchSession', () => {
expect(liff.closeWindow).toHaveBeenCalledTimes(0);
});
});

describe('getArticlesFromCofacts', () => {
let getArticlesFromCofacts;
beforeEach(() => {
global.location = { search: '?foo=bar' };
global.COFACTS_API_URL = 'http://cofacts.api';
global.APP_ID = 'mock_app_id';
global.fetch = jest.fn();
global.rollbar = { error: jest.fn() };

jest.resetModules();
getArticlesFromCofacts = require('../lib').getArticlesFromCofacts;
});

afterEach(() => {
delete global.COFACTS_API_URL;
delete global.fetch;
delete global.APP_ID;
delete global.location;
delete global.rollbar;
});

it('handles empty', async () => {
await expect(getArticlesFromCofacts([])).resolves.toEqual([]);
expect(fetch).not.toHaveBeenCalled();
});

it('rejects on GraphQL error', async () => {
fetch.mockReturnValueOnce(
Promise.resolve({
status: 400,
json: jest.fn().mockReturnValueOnce(
Promise.resolve({
errors: [{ message: 'fake error' }],
})
),
})
);

await expect(getArticlesFromCofacts(['id1'])).rejects.toMatchInlineSnapshot(
`[Error: getArticlesFromCofacts Error: fake error]`
);
});

it('converts article ids to GraphQL request and returns result, despite minor errors', async () => {
const ids = ['id1', 'id2', 'id3'];
fetch.mockReturnValueOnce(
Promise.resolve({
status: 200,
json: jest.fn().mockReturnValueOnce(
Promise.resolve({
data: {
a0: {
id: 'id1',
text: 'text1',
},
a1: {
id: 'id2',
text: 'text2',
},
},
errors: [{ message: 'Some error loading id3' }],
})
),
})
);

await expect(getArticlesFromCofacts(ids)).resolves.toMatchInlineSnapshot(`
Array [
Object {
"id": "id1",
"text": "text1",
},
Object {
"id": "id2",
"text": "text2",
},
undefined,
]
`);

expect(rollbar.error).toHaveBeenCalledTimes(1);

// Check sent GraphQL query & variables
expect(JSON.parse(fetch.mock.calls[0][1].body)).toMatchInlineSnapshot(`
Object {
"query": "
query GetArticlesLinkedToUser(
$a0: String!
$a1: String!
$a2: String!
) {
a0: GetArticle(id: $a0) { id text }
a1: GetArticle(id: $a1) { id text }
a2: GetArticle(id: $a2) { id text }
}
",
"variables": Object {
"a0": "id1",
"a1": "id2",
"a2": "id3",
},
}
`);
});
});
61 changes: 61 additions & 0 deletions src/liff/lib.js
Expand Up @@ -145,3 +145,64 @@ export const assertSameSearchSession = async () => {
return;
}
};

/**
* @param {string[]} articleIds
* @returns {Article} Article object from Cofacts API
*/
export const getArticlesFromCofacts = async articleIds => {
if (articleIds.length === 0) return [];

const variables = articleIds.reduce((agg, articleId, idx) => {
agg[`a${idx}`] = articleId;
return agg;
}, {});

const variableKeys = Object.keys(variables);

const query = `
query GetArticlesLinkedToUser(
${variableKeys.map(k => `$${k}: String!`).join('\n')}
) {
${variableKeys
.map(k => `${k}: GetArticle(id: $${k}) { id text }`)
.join('\n')}
}
`;

let status;
return fetch(COFACTS_API_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-app-id': APP_ID,
},
body: JSON.stringify({ query, variables }),
})
.then(r => {
status = r.status;
return r.json();
})
.then(resp => {
if (status === 400) {
throw new Error(
`getArticlesFromCofacts Error: ${resp.errors
.map(({ message }) => message)
.join('\n')}`
);
}
if (resp.errors) {
// When status is 200 but have error, just print them out.
console.error(
'getArticlesFromCofacts operation contains error:',
resp.errors
);
rollbar.error(
'getArticlesFromCofacts error',
{ body: JSON.stringify({ query, variables }) },
{ resp }
);
}
return variableKeys.map(key => resp.data[key]);
});
};
45 changes: 45 additions & 0 deletions src/liff/pages/Articles.svelte
@@ -0,0 +1,45 @@
<script>
import { onMount } from 'svelte';
import { gql, assertInClient, getArticlesFromCofacts } from '../lib';
let articleData = null;
let articleMap = {};
onMount(async () => {
assertInClient();
const {
data: {userArticleLinks},
errors: linksErrors,
} = await gql`
query ListUserArticleLinks {
userArticleLinks {
articleId
createdAt
}
}
`();
if(linksErrors) {
alert(linksErrors[0].message);
return;
}
articleData = userArticleLinks;
const articlesFromCofacts = await getArticlesFromCofacts(userArticleLinks.map(({articleId}) => articleId));
articlesFromCofacts.forEach(article => {
if(!article) return;
articleMap[article.id] = article;
})
})
</script>

{#if articleData === null}
<p>Loading...</p>
{:else}
<ul>
{#each articleData as link}
<li>{link.articleId} / {articleMap[link.articleId] ? articleMap[link.articleId].text : 'Loading...'} / {link.createdAt}</li>
{/each}
</ul>
{/if}
2 changes: 2 additions & 0 deletions webpack.config.js
Expand Up @@ -128,7 +128,9 @@ module.exports = {
LIFF_ID: JSON.stringify(
(process.env.LIFF_URL || '').replace('https://liff.line.me/', '')
),
APP_ID: JSON.stringify(process.env.APP_ID),
DEBUG_LIFF: process.env.DEBUG_LIFF,
COFACTS_API_URL: JSON.stringify(process.env.API_URL),
}),
],
devtool: prod ? false : 'source-map',
Expand Down

0 comments on commit a04a114

Please sign in to comment.