Feat: News in the client app (#34392)

This commit is contained in:
Stuart Taylor
2018-11-29 12:12:15 +00:00
committed by Valeriy
parent 28798dc008
commit d327a5c36b
87 changed files with 2334 additions and 1403 deletions

View File

@ -1,214 +1,259 @@
// import React from 'react';
// import { renderToString } from 'react-dom/server';
// // import { StaticRouter } from 'react-router-dom';
// import { has } from 'lodash';
import { has, pick, isEmpty } from 'lodash';
import debug from 'debug';
import { reportError } from '../middlewares/error-reporter';
// import NewsApp from '../../news/NewsApp';
const routerLog = debug('fcc:boot:news:router');
const apiLog = debug('fcc:boot:news:api');
const log = debug('fcc:boot:news');
export default function newsBoot(app) {
// const router = app.loopback.Router();
// const api = app.loopback.Router();
const api = app.loopback.Router();
// router.get('/n', (req, res) => res.redirect('/news'));
// router.get('/n/:shortId', createShortLinkHandler(app));
api.get('/n/:shortId', createShortLinkHandler(app));
// router.get('/news', serveNewsApp);
// router.get('/news/*', serveNewsApp);
api.post('/p', createPopularityHandler(app));
// api.post('/p', createPopularityHandler(app));
// app.use(api);
// app.use(router);
app.use('/internal', api);
}
// function serveNewsApp(req, res) {
// const context = {};
// const markup = renderToString(
// <StaticRouter basename='/news' context={context} location={req.url}>
// <NewsApp />
// </StaticRouter>
// );
// if (context.url) {
// routerLog('redirect found in `renderToString`');
// // 'client-side' routing hit on a redirect
// return res.redirect(context.url);
// }
// routerLog('news markup sending');
// return res.render('layout-news', { title: 'News | freeCodeCamp', markup });
// }
function createShortLinkHandler(app) {
const { Article } = app.models;
// function createShortLinkHandler(app) {
// const { Article } = app.models;
const referralHandler = createReferralHandler(app);
// const referralHandler = createRerralHandler(app);
return function shortLinkHandler(req, res, next) {
const { query, user } = req;
const { shortId } = req.params;
// return function shortLinkHandler(req, res, next) {
// const { query, user } = req;
// const { shortId } = req.params;
// We manually report the error here as it should not affect this request
referralHandler(query, shortId, !!user).catch(err => reportError(err));
// referralHandler(query, shortId, !!user);
if (!shortId) {
return res.sendStatus(400);
}
return Article.findOne(
{
where: {
or: [{ shortId }, { slugPart: shortId }]
}
},
(err, article) => {
if (err) {
next(err);
}
if (!article) {
return res.status(404).send('Could not find article by shortId');
}
const {
slugPart,
shortId,
author: { username }
} = article;
const slug = `/news/${username}/${slugPart}--${shortId}`;
const articleData = {
...pick(article, [
'author',
'renderableContent',
'firstPublishedDate',
'viewCount',
'title',
'featureImage',
'slugPart',
'shortId',
'meta'
]),
slug
};
return res.json(articleData);
}
);
};
}
// routerLog(req.origin);
// routerLog(query.refsource);
// if (!shortId) {
// return res.redirect('/news');
// }
// routerLog('shortId', shortId);
// return Article.findOne(
// {
// where: {
// or: [{ shortId }, { slugPart: shortId }]
// }
// },
// (err, article) => {
// if (err) {
// next(err);
// }
// if (!article) {
// return res.redirect('/news');
// }
// const {
// slugPart,
// shortId,
// author: { username }
// } = article;
// const slug = `/news/${username}/${slugPart}--${shortId}`;
// return res.redirect(slug);
// }
// );
// };
// }
function createPopularityHandler(app) {
const { Article, Popularity } = app.models;
// function createPopularityHandler(app) {
// const { Article, Popularity } = app.models;
function findArticleByShortId(shortId) {
return new Promise((resolve, reject) =>
Article.findOne({ where: { shortId } }, (err, article) => {
if (err) {
log('Error returned from Article.findOne(shortId)');
return reject(err);
}
log('article found');
return resolve(article);
})
);
}
// return function handlePopularityStats(req, res, next) {
// const { body, user } = req;
function findPopularityByShortId(shortId) {
return new Promise((resolve, reject) =>
Popularity.findOne(
{ where: { articleId: shortId } },
(err, popularity) => {
if (err) {
log('Error returned from Popularity.findOne(shortId)');
return reject(err);
}
log('popularity found');
return resolve(popularity);
}
)
);
}
// if (
// !has(body, 'event') ||
// !has(body, 'timestamp') ||
// !has(body, 'shortId')
// ) {
// console.warn('Popularity event recieved from client is malformed');
// console.log(JSON.stringify(body, null, 2));
// // sending 200 because the client shouldn't care for this
// return res.sendStatus(200);
// }
// res.sendStatus(200);
// const { shortId } = body;
// apiLog('shortId', shortId);
// const populartiyUpdate = {
// ...body,
// byAuthenticatedUser: !!user
// };
// Popularity.findOne({ where: { articleId: shortId } }, (err, popularity) => {
// if (err) {
// apiLog(err);
// return next(err);
// }
// if (popularity) {
// return popularity.updateAttribute(
// 'events',
// [populartiyUpdate, ...popularity.events],
// err => {
// if (err) {
// apiLog(err);
// return next(err);
// }
// return apiLog('poplarity updated');
// }
// );
// }
// return Popularity.create(
// {
// events: [populartiyUpdate],
// articleId: shortId
// },
// err => {
// if (err) {
// apiLog(err);
// return next(err);
// }
// return apiLog('poulartiy created');
// }
// );
// });
// return body.event === 'view'
// ? Article.findOne({ where: { shortId } }, (err, article) => {
// if (err) {
// apiLog(err);
// next(err);
// }
// return article.updateAttributes(
// { viewCount: article.viewCount + 1 },
// err => {
// if (err) {
// apiLog(err);
// return next(err);
// }
// return apiLog('article views updated');
// }
// );
// })
// : null;
// };
// }
function createPopularity(popularityUpdate, shortId) {
return new Promise((resolve, reject) =>
Popularity.create(
{
events: [popularityUpdate],
articleId: shortId
},
err => {
if (err) {
return reject(err);
}
log('poulartiy created');
return resolve();
}
)
);
}
// function createRerralHandler(app) {
// const { Popularity } = app.models;
function updatePopularity(popularity, popularityUpdate) {
return new Promise((resolve, reject) =>
popularity.updateAttribute(
'events',
[popularityUpdate, ...popularity.events],
err => {
if (err) {
log('Error returned from popularity.updateAttribute()');
return reject(err);
}
log('poplarity updated');
return resolve();
}
)
);
}
// return function referralHandler(query, shortId, byAuthenticatedUser) {
// if (!query.refsource) {
// return null;
// }
// const eventUpdate = {
// event: `referral - ${query.refsource}`,
// timestamp: new Date(Date.now()),
// byAuthenticatedUser
// };
// return Popularity.findOne(
// { where: { articleId: shortId } },
// (err, popularity) => {
// if (err) {
// console.error(
// 'Failed finding a `Popularity` in a referral handler',
// err
// );
// return null;
// }
function incrementArticleViews(article) {
return new Promise((resolve, reject) =>
article.updateAttributes({ $inc: { viewCount: 1 } }, err => {
if (err) {
log(err);
return reject(err);
}
log('article views updated');
return resolve();
})
);
}
// if (popularity) {
// return popularity.updateAttribute(
// 'events',
// [eventUpdate, ...popularity.events],
// err => {
// if (err) {
// console.error(
// 'Failed in updating the `events` attribute of a `popularity`',
// err
// );
// }
// }
// );
// }
// return Popularity.create(
// {
// events: [eventUpdate],
// articleId: shortId
// },
// err => {
// if (err) {
// return console.error('Failed creating a new `Popularity`', err);
// }
// return apiLog('poulartiy created');
// }
// );
// }
// );
// };
// }
return async function handlePopularityStats(req, res, next) {
const { body, user } = req;
if (
!has(body, 'event') ||
!has(body, 'timestamp') ||
!has(body, 'shortId')
) {
console.warn('Popularity event recieved from client is malformed');
console.log(JSON.stringify(body, null, 2));
// sending 200 because the client shouldn't care for this
return res.sendStatus(200);
}
const { shortId } = body;
log('shortId', shortId);
const articlePromise = findArticleByShortId(shortId);
const popularityPromise = findPopularityByShortId(shortId);
const [article, popularity] = await Promise.all([
articlePromise,
popularityPromise
]).catch(err => {
log('find catch');
return next(err);
});
if (!article || isEmpty(article)) {
log('No article found to handle the populartity update');
// sending 200 because the client shouldn't care for this
return res.sendStatus(200);
}
const populartiyUpdate = {
...body,
byAuthenticatedUser: !!user
};
const populartiyUpdateOrCreatePromise = isEmpty(popularity)
? createPopularity(populartiyUpdate, shortId)
: updatePopularity(popularity, populartiyUpdate);
const maybeUpdateArticlePromise =
body.event === 'view' ? incrementArticleViews(article) : null;
return Promise.all([
populartiyUpdateOrCreatePromise,
maybeUpdateArticlePromise
])
.then(() => res.sendStatus(200))
.catch(err => {
log('updates catch');
return next(err);
});
};
}
function createReferralHandler(app) {
const { Popularity } = app.models;
return function referralHandler(query, shortId, byAuthenticatedUser) {
if (!query.refsource) {
return Promise.resolve();
}
const eventUpdate = {
event: `referral - ${query.refsource}`,
timestamp: new Date(Date.now()),
byAuthenticatedUser
};
return new Promise((resolve, reject) =>
Popularity.findOne(
{ where: { articleId: shortId } },
(err, popularity) => {
if (err) {
console.error(
'Failed finding a `Popularity` in a referral handler',
err
);
return reject(err);
}
if (popularity) {
return popularity.updateAttribute(
'events',
[eventUpdate, ...popularity.events],
err => {
if (err) {
return reject(err);
}
log('populartiy updated');
return resolve();
}
);
}
return Popularity.create(
{
events: [eventUpdate],
articleId: shortId
},
err => {
if (err) {
return reject(err);
}
log('poulartiy created');
return resolve();
}
);
}
)
);
};
}