Skip to content
Permalink
Browse files

Feat: News in the client app (#34392)

  • Loading branch information...
Bouncey authored and ValeraS committed Nov 29, 2018
1 parent 28798dc commit d327a5c36b03d019df7766b267f7d271abdab505
Showing with 2,345 additions and 1,414 deletions.
  1. +249 −204 api-server/server/boot/news.js
  2. +2 −1 api-server/server/datasources.production.js
  3. +1 −38 api-server/server/middlewares/error-handlers.js
  4. +16 −5 api-server/server/middlewares/error-reporter.js
  5. +9 −1 api-server/server/middlewares/jwt-authorization.js
  6. +8 −1 client/gatsby-config.js
  7. +33 −2 client/gatsby-node.js
  8. +51 −21 client/gatsby-ssr.js
  9. +77 −24 client/package-lock.json
  10. +2 −1 client/package.json
  11. +218 −0 client/plugins/fcc-create-nav-data/package-lock.json
  12. +0 −170 client/plugins/fcc-create-nav-data/yarn.lock
  13. +26 −0 client/plugins/fcc-source-news/create-news-node.js
  14. +55 −0 client/plugins/fcc-source-news/gatsby-node.js
  15. +80 −0 client/plugins/fcc-source-news/package-lock.json
  16. +6 −0 client/plugins/fcc-source-news/package.json
  17. +16 −0 client/src/__mocks__/news-article.js
  18. +42 −0 client/src/__tests__/integration/handled-error.test.js
  19. +33 −0 client/src/__tests__/integration/news-slug.test.js
  20. +89 −0 client/src/client-only-routes/ShowDynamicNewsOrFourOhFour.js
  21. +4 −12 client/src/client-only-routes/ShowProfileOrFourOhFour.js
  22. +1 −1 client/src/components/Flash/redux/index.js
  23. +35 −7 client/src/components/Header/header.css
  24. +3 −0 client/src/components/Header/index.js
  25. +1 −1 client/src/components/Map/redux/index.js
  26. +2 −9 client/src/components/RedirectHome.js
  27. +3 −0 client/src/components/RedirectNews.js
  28. +10 −0 client/src/components/createRedirect.js
  29. +9 −2 client/src/pages/404.js
  30. +15 −0 client/src/pages/n.js
  31. +96 −0 client/src/pages/news.js
  32. +25 −0 client/src/redux/error-saga.js
  33. +7 −9 client/src/redux/index.js
  34. +22 −11 client/src/redux/rootReducer.js
  35. +10 −2 client/src/redux/rootSaga.js
  36. +1 −1 client/src/redux/settings/index.js
  37. +1 −1 client/src/templates/Challenges/redux/index.js
  38. +62 −0 client/src/templates/News/Featured/Featured.js
  39. +33 −0 client/src/templates/News/Featured/featured.css
  40. 0 {news/routes → client/src/templates/News}/Featured/index.js
  41. 0 {news → client/src/templates/News}/NewsApp.js
  42. +61 −0 client/src/templates/News/NewsReferalLinkHandler/index.js
  43. +1 −1 {news/routes/Show → client/src/templates/News/ShowArticle}/components/Author.js
  44. +187 −0 client/src/templates/News/ShowArticle/index.js
  45. +26 −0 client/src/templates/News/ShowArticle/proptypes.js
  46. +34 −0 client/src/templates/News/ShowArticle/show-article.css
  47. 0 {news → client/src/templates/News}/components/ArticleMeta.js
  48. 0 {news → client/src/templates/News}/components/BannerWide.js
  49. +67 −0 client/src/templates/News/redux/index.js
  50. +27 −0 client/src/templates/News/redux/shortId-saga.js
  51. +24 −0 client/src/utils/ajax.js
  52. +84 −0 client/src/utils/handled-error.js
  53. +150 −0 client/src/utils/handled-error.test.js
  54. +16 −0 client/src/utils/index.js
  55. +7 −0 client/src/utils/report-error.js
  56. +6 −0 client/src/utils/reportedErrorMessage.js
  57. +45 −0 client/src/utils/utils.test.js
  58. +114 −0 client/utils/gatsby/challengePageCreator.js
  59. +41 −0 client/utils/gatsby/guidePageCreator.js
  60. +8 −153 client/utils/gatsby/index.js
  61. +22 −0 client/utils/gatsby/newsPageCreator.js
  62. +18 −0 client/utils/news.js
  63. +32 −0 client/utils/news.test.js
  64. +8 −0 config/secrets.js
  65. +1 −0 lerna.json
  66. +0 −18 news/client.js
  67. +0 −38 news/components/Nav/LargeNav.js
  68. +0 −42 news/components/Nav/MediumNav.js
  69. +0 −31 news/components/Nav/Nav.js
  70. +0 −44 news/components/Nav/SmallNav.js
  71. +0 −38 news/components/Nav/components/NavLinks.js
  72. +0 −41 news/components/Nav/components/NavLogo.js
  73. +0 −1 news/components/Nav/index.js
  74. +0 −3 news/components/Nav/navPropTypes.js
  75. +0 −14 news/routes/EditArticle/EditArticle.js
  76. +0 −1 news/routes/EditArticle/index.js
  77. +0 −1 news/routes/Editor/index.js
  78. +0 −166 news/routes/Featured/Featured.js
  79. +0 −14 news/routes/Latest/Latest.js
  80. +0 −1 news/routes/Latest/index.js
  81. +0 −237 news/routes/Show/Show.js
  82. +0 −1 news/routes/Show/index.js
  83. +0 −13 news/routes/index.js
  84. +0 −27 news/utils/ajax.js
  85. +1 −0 sample.env
  86. +10 −4 tools/scripts/seed/seedNewsArticles.js
  87. +2 −1 tools/scripts/start-develop.js

Large diffs are not rendered by default.

Oops, something went wrong.
@@ -5,7 +5,8 @@ module.exports = {
connector: 'mongodb',
connectionTimeout: 10000,
url: secrets.db,
useNewUrlParser: true
useNewUrlParser: true,
allowExtendedOperators: true
},
mail: {
connector: 'mail',
@@ -8,47 +8,10 @@ import { unwrapHandledError } from '../utils/create-handled-error.js';

const isDev = process.env.NODE_ENV !== 'production';

// const toString = Object.prototype.toString;
// is full error or just trace
// _.toString(new Error('foo')) => "Error: foo
// Object.prototype.toString.call(new Error('foo')) => "[object Error]"
// const isInspect = val => !val.stack && _.toString(val) === toString.call(val);
// const stringifyErr = val => {
// if (val.stack) {
// return String(val.stack);
// }

// const str = String(val);

// return isInspect(val) ?
// inspect(val) :
// str;
// };

// const createStackHtml = _.flow(
// _.cond([
// [isInspect, err => [err]],
// // may be stack or just err.msg
// [_.stubTrue, _.flow(stringifyErr, _.split('\n'), _.tail) ]
// ]),
// _.map(_.escape),
// _.map(line => `<li>${line}</lin>`),
// _.join('')
// );

// const createErrorTitle = _.cond([
// [
// _.negate(isInspect),
// _.flow(stringifyErr, _.split('\n'), _.head, _.defaultTo('Error'))
// ],
// [_.stubTrue, _.constant('Error')]
// ]);

export default function prodErrorHandler() {
// error handling in production.
// disabling eslint due to express parity rules for error handlers
// eslint-disable-next-line no-unused-vars
return function(err, req, res, next) {
// eslint-disable-line
const handled = unwrapHandledError(err);
// respect handled error status
let status = handled.status || err.status || res.statusCode;
@@ -5,20 +5,30 @@ import {
unwrapHandledError
} from '../utils/create-handled-error.js';

const { ROLLBAR_APP_ID } = process.env;
import { rollbar } from '../../../config/secrets';

const rollbar = new Rollbar(ROLLBAR_APP_ID);
const { appId } = rollbar;
const reporter = new Rollbar(appId);
const log = debug('fcc:middlewares:error-reporter');

const errTemplate = ({message, ...restError}, req) => `
const errTemplate = (error, req) => {
const { message, stack } = error;
return `
Time: ${new Date(Date.now()).toISOString()}
Error: ${message}
Is authenticated user: ${!!req.user}
Route: ${JSON.stringify(req.route, null, 2)}
Stack: ${stack}
${JSON.stringify(restError, null, 2)}
// raw
${JSON.stringify(error, null, 2)}
`;
};

export function reportError(err) {
return reporter.error(err.message, err);
}

export default function errrorReporter() {
if (process.env.NODE_ENV !== 'production' && process.env.ERROR_REPORTER) {
@@ -44,6 +54,7 @@ export default function errrorReporter() {
// logging the error provides us with more information,
// i.e isAuthenticatedUser, req.route
console.error(errTemplate(err, req));
return rollbar.error(err.message, err);
reportError(err);
return next(err);
};
}
@@ -6,9 +6,17 @@ import { homeLocation } from '../../../config/env';

import { wrapHandledError } from '../utils/create-handled-error';

// We need to tunnel through a proxy path set up within
// the gatsby app, at this time, that path is /internal
const whiteListRE = new RegExp([
'^/internal/n/',
'^/internal/p\??'
].join('|'));


export default () => function authorizeByJWT(req, res, next) {
const path = req.path.split('/')[1];
if (/^external$|^internal$/.test(path)) {
if (/^external$|^internal$/.test(path) && !whiteListRE.test(req.path)) {
const cookie = req.signedCookies && req.signedCookies['jwt_access_token'] ||
req.cookie && req.cookie['jwt_access_token'];
if (!cookie) {
@@ -32,7 +32,8 @@ module.exports = {
'/certification/*',
'/unsubscribed/*',
'/user/*',
'/settings/*'
'/settings/*',
'/n/*'
]
}
},
@@ -45,6 +46,12 @@ module.exports = {
curriculumPath: localeChallengesRootDir
}
},
{
resolve: 'fcc-source-news',
options: {
maximumStaticRenderCount: 100
}
},
{
resolve: 'gatsby-source-filesystem',
options: {
@@ -8,8 +8,10 @@ const {
createChallengePages,
createBlockIntroPages,
createSuperBlockIntroPages,
createGuideArticlePages
createGuideArticlePages,
createNewsArticle
} = require('./utils/gatsby');
const { createArticleSlug } = require('./utils/news');

const createByIdentityMap = {
guideMarkdown: createGuideArticlePages,
@@ -35,6 +37,15 @@ exports.onCreateNode = function onCreateNode({ node, actions, getNode }) {
createNodeField({ node, name: 'slug', value: slug });
}
}
if (node.internal.type === 'NewsArticleNode') {
const {
author: { username },
slugPart,
shortId
} = node;
const slug = createArticleSlug({ username, shortId, slugPart });
createNodeField({ node, name: 'slug', value: slug });
}
};

exports.createPages = function createPages({ graphql, actions }) {
@@ -86,6 +97,19 @@ exports.createPages = function createPages({ graphql, actions }) {
}
}
}
allNewsArticleNode(
sort: { fields: firstPublishedDate, order: DESC }
) {
edges {
node {
id
shortId
fields {
slug
}
}
}
}
}
`).then(result => {
if (result.errors) {
@@ -126,6 +150,11 @@ exports.createPages = function createPages({ graphql, actions }) {
return null;
});

// Create news article pages
result.data.allNewsArticleNode.edges.forEach(
createNewsArticle(createPage)
);

return null;
})
);
@@ -161,7 +190,9 @@ exports.onCreateWebpackConfig = ({ stage, rules, plugins, actions }) => {
HOME_PATH: JSON.stringify(
process.env.HOME_PATH || 'http://localhost:3000'
),
STRIPE_PUBLIC_KEY: JSON.stringify(process.env.STRIPE_PUBLIC_KEY || '')
STRIPE_PUBLIC_KEY: JSON.stringify(process.env.STRIPE_PUBLIC_KEY || ''),
ROLLBAR_CLIENT_ID: JSON.stringify(process.env.ROLLBAR_CLIENT_ID || ''),
ENVIRONMENT: JSON.stringify(process.env.NODE_ENV || 'development')
}),
new RmServiceWorkerPlugin()
]
@@ -1,18 +1,19 @@
/* global ROLLBAR_CLIENT_ID ENVIRONMENT */
import React from 'react';
import PropTypes from 'prop-types';
import { Provider } from 'react-redux';

import headComponents from './src/head';
import { createStore } from './src/redux/createStore';

import GuideNavigationContextProvider from './src/contexts/GuideNavigationContext';
import GuideNavContextProvider from './src/contexts/GuideNavigationContext';

const store = createStore();

export const wrapRootElement = ({ element }) => {
return (
<Provider store={store}>
<GuideNavigationContextProvider>{element}</GuideNavigationContextProvider>
<GuideNavContextProvider>{element}</GuideNavContextProvider>
</Provider>
);
};
@@ -23,28 +24,57 @@ wrapRootElement.propTypes = {

export const onRenderBody = ({ setHeadComponents, setPostBodyComponents }) => {
setHeadComponents([...headComponents]);
setPostBodyComponents([
<script
async={true}
key='chai-CDN'
src='https://cdnjs.cloudflare.com/ajax/libs/chai/4.1.2/chai.min.js'
/>,
<script
async={true}
key='gtag-script'
src='https://www.googletagmanager.com/gtag/js?id=AW-795617839'
/>,
<script
dangerouslySetInnerHTML={{
__html: `
setPostBodyComponents(
[
/* eslint-disable max-len */
ENVIRONMENT === 'production' ? (
<script
dangerouslySetInnerHTML={{
__html: `
var _rollbarConfig = {
accessToken: "${ROLLBAR_CLIENT_ID}",
captureUncaught: true,
captureUnhandledRejections: true,
payload: {
environment: "${ENVIRONMENT}"
}
};
// Rollbar Snippet
!function(r){function e(n){if(o[n])return o[n].exports;var t=o[n]={exports:{},id:n,loaded:!1};return r[n].call(t.exports,t,t.exports,e),t.loaded=!0,t.exports}var o={};return e.m=r,e.c=o,e.p="",e(0)}([function(r,e,o){"use strict";var n=o(1),t=o(4);_rollbarConfig=_rollbarConfig||{},_rollbarConfig.rollbarJsUrl=_rollbarConfig.rollbarJsUrl||"https://cdnjs.cloudflare.com/ajax/libs/rollbar.js/2.4.6/rollbar.min.js",_rollbarConfig.async=void 0===_rollbarConfig.async||_rollbarConfig.async;var a=n.setupShim(window,_rollbarConfig),l=t(_rollbarConfig);window.rollbar=n.Rollbar,a.loadFull(window,document,!_rollbarConfig.async,_rollbarConfig,l)},function(r,e,o){"use strict";function n(r){return function(){try{return r.apply(this,arguments)}catch(r){try{console.error("[Rollbar]: Internal error",r)}catch(r){}}}}function t(r,e){this.options=r,this._rollbarOldOnError=null;var o=s++;this.shimId=function(){return o},"undefined"!=typeof window&&window._rollbarShims&&(window._rollbarShims[o]={handler:e,messages:[]})}function a(r,e){if(r){var o=e.globalAlias||"Rollbar";if("object"==typeof r[o])return r[o];r._rollbarShims={},r._rollbarWrappedError=null;var t=new p(e);return n(function(){e.captureUncaught&&(t._rollbarOldOnError=r.onerror,i.captureUncaughtExceptions(r,t,!0),i.wrapGlobals(r,t,!0)),e.captureUnhandledRejections&&i.captureUnhandledRejections(r,t,!0);var n=e.autoInstrument;return e.enabled!==!1&&(void 0===n||n===!0||"object"==typeof n&&n.network)&&r.addEventListener&&(r.addEventListener("load",t.captureLoad.bind(t)),r.addEventListener("DOMContentLoaded",t.captureDomContentLoaded.bind(t))),r[o]=t,t})()}}function l(r){return n(function(){var e=this,o=Array.prototype.slice.call(arguments,0),n={shim:e,method:r,args:o,ts:new Date};window._rollbarShims[this.shimId()].messages.push(n)})}var i=o(2),s=0,d=o(3),c=function(r,e){return new t(r,e)},p=function(r){return new d(c,r)};t.prototype.loadFull=function(r,e,o,t,a){var l=function(){var e;if(void 0===r._rollbarDidLoad){e=new Error("rollbar.js did not load");for(var o,n,t,l,i=0;o=r._rollbarShims[i++];)for(o=o.messages||[];n=o.shift();)for(t=n.args||[],i=0;i<t.length;++i)if(l=t[i],"function"==typeof l){l(e);break}}"function"==typeof a&&a(e)},i=!1,s=e.createElement("script"),d=e.getElementsByTagName("script")[0],c=d.parentNode;s.crossOrigin="",s.src=t.rollbarJsUrl,o||(s.async=!0),s.onload=s.onreadystatechange=n(function(){if(!(i||this.readyState&&"loaded"!==this.readyState&&"complete"!==this.readyState)){s.onload=s.onreadystatechange=null;try{c.removeChild(s)}catch(r){}i=!0,l()}}),c.insertBefore(s,d)},t.prototype.wrap=function(r,e,o){try{var n;if(n="function"==typeof e?e:function(){return e||{}},"function"!=typeof r)return r;if(r._isWrap)return r;if(!r._rollbar_wrapped&&(r._rollbar_wrapped=function(){o&&"function"==typeof o&&o.apply(this,arguments);try{return r.apply(this,arguments)}catch(o){var e=o;throw e&&("string"==typeof e&&(e=new String(e)),e._rollbarContext=n()||{},e._rollbarContext._wrappedSource=r.toString(),window._rollbarWrappedError=e),e}},r._rollbar_wrapped._isWrap=!0,r.hasOwnProperty))for(var t in r)r.hasOwnProperty(t)&&(r._rollbar_wrapped[t]=r[t]);return r._rollbar_wrapped}catch(e){return r}};for(var u="log,debug,info,warn,warning,error,critical,global,configure,handleUncaughtException,handleUnhandledRejection,captureEvent,captureDomContentLoaded,captureLoad".split(","),f=0;f<u.length;++f)t.prototype[u[f]]=l(u[f]);r.exports={setupShim:a,Rollbar:p}},function(r,e){"use strict";function o(r,e,o){if(r){var t;if("function"==typeof e._rollbarOldOnError)t=e._rollbarOldOnError;else if(r.onerror){for(t=r.onerror;t._rollbarOldOnError;)t=t._rollbarOldOnError;e._rollbarOldOnError=t}var a=function(){var o=Array.prototype.slice.call(arguments,0);n(r,e,t,o)};o&&(a._rollbarOldOnError=t),r.onerror=a}}function n(r,e,o,n){r._rollbarWrappedError&&(n[4]||(n[4]=r._rollbarWrappedError),n[5]||(n[5]=r._rollbarWrappedError._rollbarContext),r._rollbarWrappedError=null),e.handleUncaughtException.apply(e,n),o&&o.apply(r,n)}function t(r,e,o){if(r){"function"==typeof r._rollbarURH&&r._rollbarURH.belongsToShim&&r.removeEventListener("unhandledrejection",r._rollbarURH);var n=function(r){var o,n,t;try{o=r.reason}catch(r){o=void 0}try{n=r.promise}catch(r){n="[unhandledrejection] error getting \`promise\` from event"}try{t=r.detail,!o&&t&&(o=t.reason,n=t.promise)}catch(r){t="[unhandledrejection] error getting \`detail\` from event"}o||(o="[unhandledrejection] error getting \`reason\` from event"),e&&e.handleUnhandledRejection&&e.handleUnhandledRejection(o,n)};n.belongsToShim=o,r._rollbarURH=n,r.addEventListener("unhandledrejection",n)}}function a(r,e,o){if(r){var n,t,a="EventTarget,Window,Node,ApplicationCache,AudioTrackList,ChannelMergerNode,CryptoOperation,EventSource,FileReader,HTMLUnknownElement,IDBDatabase,IDBRequest,IDBTransaction,KeyOperation,MediaController,MessagePort,ModalWindow,Notification,SVGElementInstance,Screen,TextTrack,TextTrackCue,TextTrackList,WebSocket,WebSocketWorker,Worker,XMLHttpRequest,XMLHttpRequestEventTarget,XMLHttpRequestUpload".split(",");for(n=0;n<a.length;++n)t=a[n],r[t]&&r[t].prototype&&l(e,r[t].prototype,o)}}function l(r,e,o){if(e.hasOwnProperty&&e.hasOwnProperty("addEventListener")){for(var n=e.addEventListener;n._rollbarOldAdd&&n.belongsToShim;)n=n._rollbarOldAdd;var t=function(e,o,t){n.call(this,e,r.wrap(o),t)};t._rollbarOldAdd=n,t.belongsToShim=o,e.addEventListener=t;for(var a=e.removeEventListener;a._rollbarOldRemove&&a.belongsToShim;)a=a._rollbarOldRemove;var l=function(r,e,o){a.call(this,r,e&&e._rollbar_wrapped||e,o)};l._rollbarOldRemove=a,l.belongsToShim=o,e.removeEventListener=l}}r.exports={captureUncaughtExceptions:o,captureUnhandledRejections:t,wrapGlobals:a}},function(r,e){"use strict";function o(r,e){this.impl=r(e,this),this.options=e,n(o.prototype)}function n(r){for(var e=function(r){return function(){var e=Array.prototype.slice.call(arguments,0);if(this.impl[r])return this.impl[r].apply(this.impl,e)}},o="log,debug,info,warn,warning,error,critical,global,configure,handleUncaughtException,handleUnhandledRejection,_createItem,wrap,loadFull,shimId,captureEvent,captureDomContentLoaded,captureLoad".split(","),n=0;n<o.length;n++)r[o[n]]=e(o[n])}o.prototype._swapAndProcessMessages=function(r,e){this.impl=r(this.options);for(var o,n,t;o=e.shift();)n=o.method,t=o.args,this[n]&&"function"==typeof this[n]&&("captureDomContentLoaded"===n||"captureLoad"===n?this[n].apply(this,[t[0],o.ts]):this[n].apply(this,t));return this},r.exports=o},function(r,e){"use strict";r.exports=function(r){return function(e){if(!e&&!window._rollbarInitialized){r=r||{};for(var o,n,t=r.globalAlias||"Rollbar",a=window.rollbar,l=function(r){return new a(r)},i=0;o=window._rollbarShims[i++];)n||(n=o.handler),o.handler._swapAndProcessMessages(l,o.messages);window[t]=n,window._rollbarInitialized=!0}}}}]);
// End Rollbar Snippet
`
}}
key='rollbar-config'
/>
) : null,
/* eslint-enable max-len */
<script
async={true}
key='chai-CDN'
src='https://cdnjs.cloudflare.com/ajax/libs/chai/4.1.2/chai.min.js'
/>,
<script
async={true}
key='gtag-script'
src='https://www.googletagmanager.com/gtag/js?id=AW-795617839'
/>,
<script
dangerouslySetInnerHTML={{
__html: `
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'AW-795617839');
`
}}
key='gtag-dataLayer'
/>,
<script async={true} id='stripe-js' src='https://js.stripe.com/v3/' />
]);
}}
key='gtag-dataLayer'
/>,
<script
async={true}
id='stripe-js'
key='strip-js'
src='https://js.stripe.com/v3/'
/>
].filter(Boolean)
);
};
Oops, something went wrong.

0 comments on commit d327a5c

Please sign in to comment.
You can’t perform that action at this time.