From a1e6e6c9b3cd6b742de7110fc43296a7d2bebbfc Mon Sep 17 00:00:00 2001 From: Pedro Pereira Date: Tue, 12 Jun 2018 13:11:48 +0100 Subject: [PATCH 01/11] release: v1.2.0 --- CHANGELOG.md | 10 ++++++++++ package-lock.json | 2 +- package.json | 2 +- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a8107cf..f8b5fb3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,16 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. + +# [1.2.0](https://github.com/ezypeeze/nuxt-neo/compare/v1.1.0...v1.2.0) (2018-06-12) + + +### Features + +* **bodyParsers:** add body parsers middleware option (using express/body-parsers lib or custom handler). [#1](https://github.com/ezypeeze/nuxt-neo/issues/1) ([e9cb273](https://github.com/ezypeeze/nuxt-neo/commit/e9cb273)) + + + # [1.1.0](https://github.com/ezypeeze/nuxt-neo/compare/v1.0.2...v1.1.0) (2018-06-06) diff --git a/package-lock.json b/package-lock.json index 626c25d..60e678b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "nuxt-neo", - "version": "1.1.0", + "version": "1.2.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index f80b577..7ba58cb 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nuxt-neo", - "version": "1.1.0", + "version": "1.2.0", "description": "A nuxt.js module that implements a universal api layer, same-way compatible between server and client side.", "keywords": [ "nuxt", From a39d2c3d87972b6b2843a92d7b4739e274564e75 Mon Sep 17 00:00:00 2001 From: Pedro Pereira Date: Tue, 12 Jun 2018 13:31:40 +0100 Subject: [PATCH 02/11] docs: add vulnerabilities badge. add release notes. --- README.md | 10 +++++++--- docs/README.md | 13 ++++++++++--- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index cc79284..19e55af 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,9 @@ # nuxt-neo [![Build Status](https://travis-ci.org/ezypeeze/nuxt-neo.svg?branch=master)](https://travis-ci.org/ezypeeze/nuxt-neo) +[![Current NPM Version](https://badge.fury.io/js/nuxt-neo.svg)](https://badge.fury.io/js/nuxt-neo) [![Dependencies](https://david-dm.org/ezypeeze/nuxt-neo.svg)](https://david-dm.org/ezypeeze/nuxt-neo.svg) -[![npm version](https://badge.fury.io/js/nuxt-neo.svg)](https://badge.fury.io/js/nuxt-neo) -[![npm version](https://img.shields.io/npm/dm/nuxt-neo.svg)](https://img.shields.io/npm/dm/nuxt-neo.svg) +[![Monthly Downloads](https://img.shields.io/npm/dm/nuxt-neo.svg)](https://img.shields.io/npm/dm/nuxt-neo.svg) +[![Known Vulnerabilities](https://snyk.io/test/github/ezypeeze/nuxt-neo/badge.svg)](https://snyk.io/test/github/ezypeeze/nuxt-neo) > This module allows you to make a middleware API between the browser, your server and other private API's. Opinated, yet flexible, you can take care of your data flow in the same way, no matter if you are executing code on server or client side. @@ -19,6 +20,9 @@ I created this module so you can create your API inside nuxt.js easily, based on - Easy middleware injection (for all API or controller/action specific) - One single way to access your API. - Global HTTP Errors for better error handling + +### Release Notes ### +Check all release notes [here](https://github.com/ezypeeze/nuxt-neo/blob/master/CHANGELOG.md). ### License ### -MIT +MIT \ No newline at end of file diff --git a/docs/README.md b/docs/README.md index 195817c..19e55af 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,11 +1,15 @@ # nuxt-neo [![Build Status](https://travis-ci.org/ezypeeze/nuxt-neo.svg?branch=master)](https://travis-ci.org/ezypeeze/nuxt-neo) +[![Current NPM Version](https://badge.fury.io/js/nuxt-neo.svg)](https://badge.fury.io/js/nuxt-neo) [![Dependencies](https://david-dm.org/ezypeeze/nuxt-neo.svg)](https://david-dm.org/ezypeeze/nuxt-neo.svg) -[![npm version](https://badge.fury.io/js/nuxt-neo.svg)](https://badge.fury.io/js/nuxt-neo) -[![npm version](https://img.shields.io/npm/dm/nuxt-neo.svg)](https://img.shields.io/npm/dm/nuxt-neo.svg) +[![Monthly Downloads](https://img.shields.io/npm/dm/nuxt-neo.svg)](https://img.shields.io/npm/dm/nuxt-neo.svg) +[![Known Vulnerabilities](https://snyk.io/test/github/ezypeeze/nuxt-neo/badge.svg)](https://snyk.io/test/github/ezypeeze/nuxt-neo) > This module allows you to make a middleware API between the browser, your server and other private API's. Opinated, yet flexible, you can take care of your data flow in the same way, no matter if you are executing code on server or client side. +### Documentation ### +Check full documentation [here](https://ezypeeze.github.io/nuxt-neo). + ### Description ### One of the things that Nuxt.js doesn't bring out-of-the-box is middleware api support (it can be simply added thanks to its flexibility). @@ -16,6 +20,9 @@ I created this module so you can create your API inside nuxt.js easily, based on - Easy middleware injection (for all API or controller/action specific) - One single way to access your API. - Global HTTP Errors for better error handling + +### Release Notes ### +Check all release notes [here](https://github.com/ezypeeze/nuxt-neo/blob/master/CHANGELOG.md). ### License ### -MIT +MIT \ No newline at end of file From dfb37bbdb2fb6c47807778efe51d806db3d7e591 Mon Sep 17 00:00:00 2001 From: Pedro Pereira Date: Tue, 12 Jun 2018 13:32:45 +0100 Subject: [PATCH 03/11] chore(package.json): add release:patch and release:minor scripts. remove release script. edit push:tags script. --- package.json | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 7ba58cb..8da953b 100755 --- a/package.json +++ b/package.json @@ -32,8 +32,9 @@ "docs:build": "vuepress build docs", "test": "ava", "lint": "eslint --ext .js lib", - "release": "standard-version --no-verify", - "push:tags": "git push --follow-tags" + "release:patch": "standard-version --no-verify --release-as patch", + "release:minor": "standard-version --no-verify --release-as minor", + "push:tags": "git push --tags" }, "pre-commit": [ "lint" @@ -91,10 +92,5 @@ "presets": [ "env" ] - }, - "standard-version": { - "skip": { - "commit": true - } } } From 8c2fa8c6052ed3087a7a56e446e7a64156af01e3 Mon Sep 17 00:00:00 2001 From: Pedro Pereira Date: Tue, 12 Jun 2018 13:55:08 +0100 Subject: [PATCH 04/11] chore(travis): add coverage support --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index bb76f2b..a87afc9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,10 +3,14 @@ sudo: false node_js: - "8" install: + - npm install -g nyc ava codecov - npm install script: - npm run lint - npm test + - nyc ava + - nyc report --reporter=lcov > coverage.lcov + - codecov -t ${CODECOV_TOKEN} deploy: - provider: npm api_key: ${NPM_API_KEY} From 0a8ec0e9725614c91f447f285971877eb21a0778 Mon Sep 17 00:00:00 2001 From: Pedro Pereira Date: Tue, 12 Jun 2018 13:55:57 +0100 Subject: [PATCH 05/11] docs(codecov): add codecov badge --- README.md | 1 + docs/README.md | 1 + 2 files changed, 2 insertions(+) diff --git a/README.md b/README.md index 19e55af..1c7bbd6 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ [![Build Status](https://travis-ci.org/ezypeeze/nuxt-neo.svg?branch=master)](https://travis-ci.org/ezypeeze/nuxt-neo) [![Current NPM Version](https://badge.fury.io/js/nuxt-neo.svg)](https://badge.fury.io/js/nuxt-neo) [![Dependencies](https://david-dm.org/ezypeeze/nuxt-neo.svg)](https://david-dm.org/ezypeeze/nuxt-neo.svg) +[![Codecov](https://codecov.io/gh/ezypeeze/nuxt-neo/branch/master/graph/badge.svg)](https://codecov.io/gh/ezypeeze/nuxt-neo) [![Monthly Downloads](https://img.shields.io/npm/dm/nuxt-neo.svg)](https://img.shields.io/npm/dm/nuxt-neo.svg) [![Known Vulnerabilities](https://snyk.io/test/github/ezypeeze/nuxt-neo/badge.svg)](https://snyk.io/test/github/ezypeeze/nuxt-neo) diff --git a/docs/README.md b/docs/README.md index 19e55af..1c7bbd6 100644 --- a/docs/README.md +++ b/docs/README.md @@ -2,6 +2,7 @@ [![Build Status](https://travis-ci.org/ezypeeze/nuxt-neo.svg?branch=master)](https://travis-ci.org/ezypeeze/nuxt-neo) [![Current NPM Version](https://badge.fury.io/js/nuxt-neo.svg)](https://badge.fury.io/js/nuxt-neo) [![Dependencies](https://david-dm.org/ezypeeze/nuxt-neo.svg)](https://david-dm.org/ezypeeze/nuxt-neo.svg) +[![Codecov](https://codecov.io/gh/ezypeeze/nuxt-neo/branch/master/graph/badge.svg)](https://codecov.io/gh/ezypeeze/nuxt-neo) [![Monthly Downloads](https://img.shields.io/npm/dm/nuxt-neo.svg)](https://img.shields.io/npm/dm/nuxt-neo.svg) [![Known Vulnerabilities](https://snyk.io/test/github/ezypeeze/nuxt-neo/badge.svg)](https://snyk.io/test/github/ezypeeze/nuxt-neo) From a9e22e7e3b694e2567ac0b948d499fe0c26a36b1 Mon Sep 17 00:00:00 2001 From: Pedro Pereira Date: Tue, 12 Jun 2018 13:57:27 +0100 Subject: [PATCH 06/11] chore(deploy_docs): add deploy_docs script to repository. --- .gitignore | 3 ++- deploy_docs.sh | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 deploy_docs.sh diff --git a/.gitignore b/.gitignore index 013ca32..23de917 100755 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ node_modules *.log .nuxt dist -deploy_docs.sh +.nyc_output +coverage diff --git a/deploy_docs.sh b/deploy_docs.sh new file mode 100644 index 0000000..4dd1d15 --- /dev/null +++ b/deploy_docs.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env sh + +# abort on errors +set -e + +# build +npm run docs:build + +# navigate into the build output directory +cd docs/.vuepress/dist + +git init +git config user.name "Pedro Pereira" +git config user.email "pedromdspereira.93@gmail.com" + +git add -A +git commit -m 'Updated documentation' +git push -f git@github.com:ezypeeze/nuxt-neo.git master:gh-pages + +cd - From 426ddcb9eb3e1484b8f9476a69ae07600accd60d Mon Sep 17 00:00:00 2001 From: Pedro Pereira Date: Tue, 12 Jun 2018 15:16:47 +0100 Subject: [PATCH 07/11] refactor: remove unneeded lodash on dependencies (kept in dev-dependencies). --- lib/module.js | 3 +-- package.json | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/module.js b/lib/module.js index b258fb3..0cf2f2c 100755 --- a/lib/module.js +++ b/lib/module.js @@ -1,5 +1,4 @@ const path = require('path'); -const _ = require('lodash/fp/object'); const DEFAULT_MODULE_OPTIONS = { directory: path.resolve(__dirname, '/api'), @@ -49,7 +48,7 @@ const DEFAULT_MODULE_OPTIONS = { }; module.exports = function NeoModule(moduleOptions) { - moduleOptions = _.merge(DEFAULT_MODULE_OPTIONS, moduleOptions); + moduleOptions = Object.assign({}, DEFAULT_MODULE_OPTIONS, moduleOptions); moduleOptions.aliasKey = /^[~|@]/g; moduleOptions.srcDir = this.options.srcDir; diff --git a/package.json b/package.json index 8da953b..361d02a 100755 --- a/package.json +++ b/package.json @@ -49,8 +49,7 @@ "dependencies": { "body-parser": "^1.18.3", "config": "^1.30.0", - "express": "^4.16.3", - "lodash": "^4.17.10" + "express": "^4.16.3" }, "devDependencies": { "ava": "^0.25.0", @@ -71,6 +70,7 @@ "eslint-plugin-standard": "^3.1.0", "eslint-plugin-vue": "^4.5.0", "jsdom": "^11.10.0", + "lodash": "^4.17.10", "nuxt": "^1.4.0", "pre-commit": "^1.2.2", "prettier": "^1.12.1", From 411ef11f482d6071c8e02e68140b92f498b23554 Mon Sep 17 00:00:00 2001 From: Pedro Pereira Date: Tue, 12 Jun 2018 15:18:27 +0100 Subject: [PATCH 08/11] fix: make calls on server and client side arguments uniform. --- lib/plugins/api.template.js | 2 +- lib/utility/controllers.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/plugins/api.template.js b/lib/plugins/api.template.js index 7989137..f690762 100644 --- a/lib/plugins/api.template.js +++ b/lib/plugins/api.template.js @@ -44,7 +44,7 @@ function generateAPI(controllerMapping) { Object.keys(controllerMapping).forEach(function (key) { const context = controllerMapping[key]; if (context && context.path && context.verb) { - api[key] = function ({params, ...values}) { + api[key] = function ({params, ...values} = {}) { return ApiHandler( injectParamsIntoPath( context.path[context.path.length - 1] === '/' ? diff --git a/lib/utility/controllers.js b/lib/utility/controllers.js index 42bd2a7..0428d01 100755 --- a/lib/utility/controllers.js +++ b/lib/utility/controllers.js @@ -127,7 +127,7 @@ function controllerMappingServerSide(directory, req, options) { : ResponseMiddleware; return controllerMapping(options.directory, function (ControllerClass, actionName) { - return function (params, body, query) { + return function ({params, body, query} = {}) { try { return Promise.resolve(new ControllerClass(req)[actionName]({ params: params || {}, From f641798376d52e3d03462aa2abc0291dc896fb22 Mon Sep 17 00:00:00 2001 From: Pedro Pereira Date: Tue, 12 Jun 2018 15:19:32 +0100 Subject: [PATCH 09/11] docs: fix basic-usage vue page example. add docs about function calls arguments format. few internal running notes. --- docs/basic-usage.md | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/docs/basic-usage.md b/docs/basic-usage.md index 75df988..84d38c5 100644 --- a/docs/basic-usage.md +++ b/docs/basic-usage.md @@ -125,7 +125,7 @@ properties for server-side fetching on vue.js pages, we can simply do this: Current todo is: {{currentTodo.title}} -
+
{{todo.title}}
@@ -145,4 +145,23 @@ properties for server-side fetching on vue.js pages, we can simply do this: ``` - ```$api``` is injected into all Vue instances (including root), since ```asyncData``` doesn't have ```this``` -property. \ No newline at end of file +property. +- If ```asyncData``` is called on server-side, it will go directly to your controller action code. If its called on +client-side it uses your ```clientSideApiHandler``` to handle the api request. However, if ```asyncData``` is called on server-side, +api, controller and action middleware will not be triggered, because server-side ```$api``` calls are private and programmatically, +so your code knows what it's doing (or not :D). +- When calling an ```action``` (e.g: ```todos.allActions```), it accepts an object as param: +```json +{ + // the route params, same as express + params: {id: 1}, + + // the request body (in server side is server directly as JSON, + // in client side its your api handler that should define which encode type to use) + body: {foo: true}, + + // the url query string (in server side is server directly as JSON, + // in client side its your api handler that should assemble to url query string (axios does that out-of-the-box) + query: {bar: true} +} +``` \ No newline at end of file From 1ea3075aae07bfcf5194b1f5d17e8564b833c4e9 Mon Sep 17 00:00:00 2001 From: Pedro Pereira Date: Tue, 12 Jun 2018 15:20:04 +0100 Subject: [PATCH 10/11] test: improve tests for better codecov and bug sniffing --- tests/api.flow.test.js | 16 +++++++++++++-- tests/api.response.handlers.test.js | 31 +++++++++++++++++++++++++++++ tests/fixtures/api/users/index.js | 7 +++++++ tests/fixtures/pages/index.vue | 31 ++++++++++++++++++++++++++++- tests/fixtures/test_controller.js | 6 +++++- 5 files changed, 87 insertions(+), 4 deletions(-) diff --git a/tests/api.flow.test.js b/tests/api.flow.test.js index dab8d15..067895e 100644 --- a/tests/api.flow.test.js +++ b/tests/api.flow.test.js @@ -51,18 +51,30 @@ test('Test hybrid api data flow server side.', async (t) => { test('Test hybrid api data flow client side', async (t) => { const window = await nuxt.renderAndGetWindow(URL('/')); - const clickEvent = new window.Event('click'); const path = window.document.querySelector('.index span.path'); const okay = window.document.querySelector('.index span.okay'); const idParam = window.document.querySelector('.index span.id-param'); const changePath = window.document.querySelector('.index .change-path'); const resMiddle = window.document.querySelector('.index span.response-middleware'); + const numOfUsers = window.document.querySelector('.index span.number-of-users'); + const createUser = window.document.querySelector('.index .create-user'); + const firstUser = window.document.querySelector('.index .first-user'); + const ssError = window.document.querySelector('.index .server-side-force-error'); - changePath.dispatchEvent(clickEvent); + changePath.dispatchEvent(new window.Event('click')); + await new Promise(resolve => setTimeout(resolve, 1000)); // wait for API request + createUser.dispatchEvent(new window.Event('click')); + await new Promise(resolve => setTimeout(resolve, 1000)); // wait for API request + createUser.dispatchEvent(new window.Event('click')); await new Promise(resolve => setTimeout(resolve, 1000)); // wait for API request t.is(path.textContent, '/api/v2/users/1'); t.is(okay.textContent, "It's okay!"); t.is(idParam.textContent, '1'); t.is(resMiddle.textContent, "It's okay!"); + t.is(numOfUsers.textContent, '5'); // 3 nuxt.renderAndGetWindow (auto create on asyncData page) and 2 clicks to add user == 5 + t.true(!!ssError); + t.is(ssError.textContent, 'Forced error'); + t.true(!!firstUser); + t.is(firstUser.textContent, 'first'); }); diff --git a/tests/api.response.handlers.test.js b/tests/api.response.handlers.test.js index f8ed359..7b9fe1f 100644 --- a/tests/api.response.handlers.test.js +++ b/tests/api.response.handlers.test.js @@ -14,6 +14,10 @@ test.before(globalBeforeAll({ } }, errorHandler: function (err) { + if (err && err.statusCode) { + throw err; + } + throw new Error('[NEW ERROR] ' + err.message); }, notFoundRouteResponse: function (req, res) { @@ -39,6 +43,33 @@ test('Test error handler', async (t) => { } }); +test('Test bad request response due invalid input data', async (t) => { + try { + await api.post('/products?exception=BadRequestError&message=bad_request'); + } catch (err) { + t.is(err.response.status, 400); + t.is(err.response.data.message, 'bad_request'); + } +}); + +test('Test unauthorized context response due invalid input data', async (t) => { + try { + await api.post('/products?exception=UnauthorizedError&message=no_access'); + } catch (err) { + t.is(err.response.status, 401); + t.is(err.response.data.message, 'no_access'); + } +}); + +test('Test forbidden context response due invalid input data', async (t) => { + try { + await api.post('/products?exception=ForbiddenError'); + } catch (err) { + t.is(err.response.status, 403); + t.is(err.response.data.message, 'Forbidden'); + } +}); + test('Test not found route response', async (t) => { try { await api.get('/products/categories/types'); diff --git a/tests/fixtures/api/users/index.js b/tests/fixtures/api/users/index.js index d55e3ed..aab6fd1 100755 --- a/tests/fixtures/api/users/index.js +++ b/tests/fixtures/api/users/index.js @@ -1,3 +1,5 @@ +const users = []; + class UserController { constructor(request) { @@ -10,6 +12,7 @@ class UserController { path: this.request.originalUrl, params, query, + users } } @@ -19,15 +22,19 @@ class UserController { path: this.request.originalUrl, params, query, + user: users.filter(user => user.id === params.id) } } createAction({params, body}) { + users.push(Object.assign({}, body, {id: users.length + 1})); + return { ok: true, path: this.request.originalUrl, params, body, + user: users[users.length - 1] } } diff --git a/tests/fixtures/pages/index.vue b/tests/fixtures/pages/index.vue index 4e74172..ca4ee7a 100644 --- a/tests/fixtures/pages/index.vue +++ b/tests/fixtures/pages/index.vue @@ -6,18 +6,47 @@ It's not okay... It's okay! It's not okay... + {{ users.length }} + {{ users[0].first_name }} + {{ serverForceError }} +