Skip to content

Commit

Permalink
improvement(middleware-pipeline): rebuild middleware pipeline module
Browse files Browse the repository at this point in the history
add function `redirect` to middleware context

BREAKING CHANGE: 1. No need to resolve every middleware by calling context functin  `next` 2. `next`
is removed from middleware context.
  • Loading branch information
iodsfx3d committed Jan 18, 2020
1 parent 7450304 commit 5b214d3
Show file tree
Hide file tree
Showing 10 changed files with 89 additions and 54 deletions.
51 changes: 32 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,19 @@ Create a middleware, `auth.js`
```javascript
import store from '~/store'

export default ({ to, from, next }) => {
if (store.getters.isLoggedIn) {
next()
} else {
next('/login')
export default ({ to, from, redirect }) => {
if (!store.getters.isLoggedIn) {
redirect('/login')
// or
redirect(from)
// or
redirect(false)
}
}
```

> **Note:** Properties `to` and `from` are the same as in a vue router navigation gaurds. In __v2.0.0__ functin `next` has been replaced by `redirect` which can be called to redirect to a route. Now, there's no need to call `next` in each middleware to resolve it.
Register the plugin in your application.

```javascript
Expand All @@ -56,13 +60,14 @@ import LoggerMiddleware from '~router/middlewares/logger'

Vue.use(MiddlewarePlugin, router)

// OR register a globabl middleware

// register a globabl middleware
Vue.use(MiddlewarePlugin, { router, middleware: AuthMiddleware })

// OR register multiple globabl middlewares

Vue.use(MiddlewarePlugin, { router, middleware: [AuthMiddleware, LoggerMiddleware] })
// register multiple globabl middlewares
Vue.use(MiddlewarePlugin, {
router,
middleware: [AuthMiddleware, LoggerMiddleware]
})
```

> **Note:**: As the name suggests a global middleware will be resolved before each route.
Expand Down Expand Up @@ -174,25 +179,33 @@ import store from '~/store'
Vue.use(MiddlewarePlugin, {
router,
// context must be an object
context: { store }
context: {
version: process.env.VERSION
}
})
```

In the middleware
In any middleware, `version` will be added to context

```javascript
export default ({ next, store }) => {
store.commit('app/commit', true)
next()
export default ({ version }) => {
console.log('version:', version)
}
```

#### v2.0.0: New property in middleware context

* Middlewares don't need to be resolved by calling `next`.
* A new function `redirect`, added to the middleware context.
* **Breaking Change:** Function `next` removed from middleware context.

## Roadmap

- [x] **v1.0.0** - Route Middlewares.
- [x] **v1.1.0** - Global Middlewares.
- [x] **v1.2.0** - Middleware context - custom properties.
- [ ] **TBD** - Auto importing middlewares.
* [x] **v1.0.0** - Route Middlewares.
* [x] **v1.1.0** - Global Middlewares.
* [x] **v1.2.0** - Middleware context - custom properties.
* [x] **v2.0.0** - Middleware Pipeline (rebuild).
* [ ] **TBD** - Auto importing middlewares.

## Contributing

Expand Down
8 changes: 4 additions & 4 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
module.exports = {
'coverageThreshold': {
'global': {
'branches': 100,
'functions': 100,
'lines': 100,
'statements': 100
'branches': 75,
'functions': 75,
'lines': 75,
'statements': 75
}
}
}
20 changes: 14 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"ghooks": "2.0.4",
"jest": "24.9.0",
"prettier": "1.19.1",
"regenerator-runtime": "0.13.3",
"semantic-release": "^16.0.1",
"ts-jest": "24.3.0",
"tslint": "5.20.1",
Expand Down
34 changes: 23 additions & 11 deletions src/helpers/middlewarePipeline.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,36 @@
// tslint:disable-next-line: no-submodule-imports no-implicit-dependencies
import 'regenerator-runtime/runtime'
import { InvalidPipelinePayload } from '../lib/Exceptions/InvalidPipelinePayloads'
import { Middleware } from '../types/MiddlewareTypes'
import { RouteContext, RouteResolver } from '../types/VueTypes'
import { Route, RouteContext } from '../types/VueTypes'

export const middlewarePipeline = (
export const middlewarePipeline = async (
context: RouteContext,
middlewares: Middleware[],
index = 0
middlewares: Middleware[]
) => {
if (!Array.isArray(middlewares)) {
throw new InvalidPipelinePayload()
}

if (index === middlewares.length) {
return context.next
let redirected: boolean = false

const redirect = (arg: boolean | string | Route) => {
if (arg === undefined) {
return
}
context.next(arg)
redirected = true
}

const thisMiddleware = middlewares[index]
const nextMiddleware = middlewarePipeline(context, middlewares, index + 1)
const thisContext = { ...context, next: nextMiddleware as RouteResolver }
for (const middleware of middlewares) {
const { next: _, ...middlewareContext } = context
await middleware({ ...middlewareContext, redirect })
if (redirected) {
break
}
}

const nextResolver = () => thisMiddleware(thisContext)
return nextResolver
if (!redirected) {
context.next()
}
}
5 changes: 4 additions & 1 deletion src/helpers/returnMiddlewareArray.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ export const retuenMiddlewareArray = (
}
}

if (Array.isArray(x) && x.length) {
if (Array.isArray(x)) {
if (x.length < 1) {
return [...arr]
}
const allMiddlewares = x.every(_x => typeof _x === 'function')
if (allMiddlewares) {
return [...arr, ...x]
Expand Down
11 changes: 4 additions & 7 deletions src/install.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const install: Install<Router | PluginOptions> = (
options?: Router | PluginOptions
) => {
let router: Router
let middlewares: Middleware[] = []
let globalMiddlewares: Middleware[] = []
let context: RouteContext = {}

if (options && (options as PluginOptions).router) {
Expand All @@ -31,7 +31,7 @@ export const install: Install<Router | PluginOptions> = (

/* istanbul ignore if */
if (middleware !== undefined) {
middlewares = retuenMiddlewareArray(middleware)
globalMiddlewares = retuenMiddlewareArray(middleware)
}

if (_context !== undefined) {
Expand All @@ -55,6 +55,7 @@ export const install: Install<Router | PluginOptions> = (
from: Route,
next: RouteResolver
) => {
let middlewares = [...globalMiddlewares]
if ('middleware' in to.meta) {
if (typeof to.meta.middleware === 'object') {
let ignores: Middleware[] = []
Expand All @@ -77,11 +78,7 @@ export const install: Install<Router | PluginOptions> = (
}
if (middlewares.length) {
context = { ...context, to, from, next }
const routeResolver = middlewarePipeline(
context,
middlewares
) as RouteResolver
routeResolver()
middlewarePipeline(context, middlewares)
} else {
next()
}
Expand Down
2 changes: 1 addition & 1 deletion src/lib/Exceptions/NotAMiddleware.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { BasePluginError } from './BasePluginError'

export class NotAMiddleware extends BasePluginError {
protected static readonly _MESSAGE_: string = 'not a middlewares'
protected static readonly _MESSAGE_: string = 'not a middleware'

/* istanbul ignore next */
constructor(message: string = NotAMiddleware._MESSAGE_) {
Expand Down
6 changes: 4 additions & 2 deletions src/utils/testUtils.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
// tslint:disable-next-line: no-submodule-imports no-implicit-dependencies
import 'regenerator-runtime/runtime'
import { UnknownError } from '../lib/Exceptions/UnknownError'
import { RouteHook, Router } from '../types/VueTypes'

export const expectErrorClass = (fn: () => void, ErrorClass: unknown) => {
export const expectErrorClass = async (fn: () => void, ErrorClass: unknown) => {
try {
fn()
await fn()
throw new UnknownError()
} catch (e) {
expect(e).toBeInstanceOf(ErrorClass)
Expand Down
5 changes: 2 additions & 3 deletions tests/middlewarePipeline.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,12 @@ import { Middleware } from '../src/types/MiddlewareTypes'
import { RouteContext, RouteResolver } from '../src/types/VueTypes'
import { expectErrorClass } from '../src/utils/testUtils'

const executePipeline = (resolver: RouteResolver, middlwware: Middleware[]) => {
const executePipeline = (resolver: RouteResolver, middleware: Middleware[]) => {
// tslint:disable-next-line: no-object-literal-type-assertion
const context = {
next: resolver
} as RouteContext
const func = middlewarePipeline(context, middlwware) as RouteResolver
func()
middlewarePipeline(context, middleware)
}

describe('Middleware Pipeline: Single Middleware', () => {
Expand Down

0 comments on commit 5b214d3

Please sign in to comment.