From b520f3bfa003f66d71c1f134bf8c42eadb7784c0 Mon Sep 17 00:00:00 2001 From: Francesco Bianchi Date: Thu, 27 Sep 2018 16:41:35 +0200 Subject: [PATCH 1/2] feat(routes): render error page if middleware callback error --- package-lock.json | 5 +++++ package.json | 1 + src/Routes.js | 15 +++++++++++---- src/middleware/MiddlewareManager.js | 4 +++- test/routes.test.js | 25 +++++++++++++++++++++++++ 5 files changed, 45 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index d7a2e34c..1e191c08 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7127,6 +7127,11 @@ "lodash.keys": "^3.0.0" } }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" + }, "lodash.flattendeep": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", diff --git a/package.json b/package.json index 81cdf17a..e06b7fea 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "babel-polyfill": "^6.26.0", "eslint": "^5.5.0", "eslint-plugin-jest": "^21.22.0", + "lodash.clonedeep": "^4.5.0", "path-to-regexp": "^1.7.0", "prop-types": "^15.6.1", "remove-trailing-separator": "^1.1.0" diff --git a/src/Routes.js b/src/Routes.js index b15ab9ba..9ec78be8 100644 --- a/src/Routes.js +++ b/src/Routes.js @@ -126,14 +126,21 @@ export default class Routes { req.nextRoute = route req.siteUrl = this.siteUrl req.getMultilanguageUrls = () => this.getMultilanguageUrls(route, query) - - MiddlewareManager(route.middlewares, { req, res, route, query })((err, data) => { - if (err) throw err + + const middleware = MiddlewareManager(route.middlewares, { req, res, route, query }) + middleware((err, data) => { + if (err) { + const { pathname } = parsedUrl + const { statusCode = 500 } = err + res.statusCode = statusCode + app.renderError(err, req, res, pathname, query) + return + } req.nextData = data + renderRoute(app, customHandler, { req, res, route, query }) }) - renderRoute(app, customHandler, { req, res, route, query }) return } diff --git a/src/middleware/MiddlewareManager.js b/src/middleware/MiddlewareManager.js index 50c88e46..3fa4bba2 100644 --- a/src/middleware/MiddlewareManager.js +++ b/src/middleware/MiddlewareManager.js @@ -1,10 +1,12 @@ 'use strict' import async from 'async' +import clonedeep from 'lodash.clonedeep' export default (middlewares = [], initialData = {}) => { + const reversed = clonedeep(middlewares).reverse() return async.compose( - ...middlewares.reverse(), + ...reversed, function (cb) { cb(null, initialData) }, diff --git a/test/routes.test.js b/test/routes.test.js index c7962da5..791b87df 100644 --- a/test/routes.test.js +++ b/test/routes.test.js @@ -227,4 +227,29 @@ describe('middleware()', () => { expect(routes.routes[0].middlewares).toBeInstanceOf(Array) expect(routes.routes[0].middlewares[0]).toBe(testFunct) }) + + test('can render error page if a middleware callback has error', () => { + const testFunct = (params, cb) => { + const error = new Error('this is an error') + error.statusCode = 418 + cb(error) + } + + const routes = nextRoutes({ locale: 'it' }) + routes.add('a', 'en', '/').middleware([testFunct]) + + const app = { + getRequestHandler: () => { }, + renderError: jest.fn() + } + const requestHandler = routes.getRequestHandler(app) + const req = { url: '/en' } + const res = {} + + requestHandler(req, res) + + expect(app.renderError.mock.calls.length).toBe(1) + expect(res.statusCode).toBe(418) + + }) }) From 89afd48bb1ee04693a7b1318669ad6f9ba9ce4b3 Mon Sep 17 00:00:00 2001 From: Francesco Bianchi Date: Thu, 27 Sep 2018 18:33:02 +0200 Subject: [PATCH 2/2] feat(): detect language from browser --- src/Routes.js | 8 +++++--- src/helpers/routeHelper.js | 7 +++++++ test/helpers/routeHelper.test.js | 24 +++++++++++++++++++++++- 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/src/Routes.js b/src/Routes.js index d6716290..16e3dc09 100644 --- a/src/Routes.js +++ b/src/Routes.js @@ -3,7 +3,7 @@ import pick from 'lodash.pick' import NextLink from 'next/link' import NextRouter from 'next/router' import Route from './Route' -import { generateRouteFromObjectName, redirectToLocalizedHome } from './helpers/routeHelper' +import { generateRouteFromObjectName, redirectToLocalizedHome, detectLocale } from './helpers/routeHelper' import MiddlewareManager from './middleware/MiddlewareManager' export default class Routes { @@ -127,7 +127,7 @@ export default class Routes { req.nextRoute = route req.siteUrl = this.siteUrl req.getMultilanguageUrls = () => this.getMultilanguageUrls(route, query) - + const middleware = MiddlewareManager(route.middlewares, { req, res, route, query }) middleware((err, data) => { if (err) { @@ -145,8 +145,10 @@ export default class Routes { return } + if (req.url === '/' && this.forceLocale) { - redirectToLocalizedHome(res, this.locale) + const detectedLocale = detectLocale({ req, routes: this.routes, defaultLocale: this.locale }) + redirectToLocalizedHome(res, detectedLocale) return } diff --git a/src/helpers/routeHelper.js b/src/helpers/routeHelper.js index ffd6e5e9..816f0b03 100644 --- a/src/helpers/routeHelper.js +++ b/src/helpers/routeHelper.js @@ -24,4 +24,11 @@ export const redirectToLocalizedHome = (res, locale) => { res.writeHead(301, { 'Location': `/${locale}` }) res.end() } +} + +export const detectLocale = ({ req, routes, defaultLocale }) => { + const acceptsLangs = typeof req.acceptsLanguages === 'function' && req.acceptsLanguages() + const langs = !acceptsLangs ? [] : acceptsLangs.filter(lang => lang.length === 2) + const routesLangs = routes.map(({ locale }) => locale) + return routesLangs.indexOf(langs[0]) > -1 ? langs[0] : defaultLocale } \ No newline at end of file diff --git a/test/helpers/routeHelper.test.js b/test/helpers/routeHelper.test.js index 7aabea8b..38a459ef 100644 --- a/test/helpers/routeHelper.test.js +++ b/test/helpers/routeHelper.test.js @@ -1,4 +1,4 @@ -import { generateRouteFromObjectName } from './../../src/helpers/routeHelper' +import { generateRouteFromObjectName, detectLocale } from './../../src/helpers/routeHelper' describe('generateRouteFromObject()', () => { it('thrown err if name not exist', () => { @@ -22,4 +22,26 @@ describe('generateRouteFromObject()', () => { expect(result).toHaveProperty('update') expect(result).not.toHaveProperty('foo') }) + + it('return detected locale', () => { + const req = { + acceptsLanguages: () => { return ['it-IT', 'it', 'en', 'es'] } + } + + const routes = [{ locale: 'en' }, { locale: 'it' }] + const result = detectLocale({ req, routes, defaultLocale: 'en' }) + + expect(result).toBe('it') + }) + + it('return default locale if detected is non supoorted', () => { + const req = { + acceptsLanguages: () => { return ['it-IT', 'es'] } + } + + const routes = [{ locale: 'en' }, { locale: 'it' }] + const result = detectLocale({ req, routes, defaultLocale: 'en' }) + + expect(result).toBe('en') + }) }) \ No newline at end of file