├── .babelrc ├── .editorconfig ├── .github ├── contributing.md ├── issue_template.md └── pull_request_template.md ├── .gitignore ├── .istanbul.yml ├── .npmignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── hooks.js ├── index.d.ts ├── mocha.opts ├── package-lock.json ├── package.json ├── src ├── commons.js └── hooks.js └── test ├── after.test.js ├── app.test.js ├── before.test.js ├── error.test.js └── hooks.test.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ "add-module-exports" ], 3 | "presets": [ "es2015" ] 4 | } 5 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.github/contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing to Feathers 2 | 3 | Thank you for contributing to Feathers! :heart: :tada: 4 | 5 | This repo is the main core and where most issues are reported. Feathers embraces modularity and is broken up across many repos. To make this easier to manage we currently use [Zenhub](https://www.zenhub.com/) for issue triage and visibility. They have a free browser plugin you can install so that you can see what is in flight at any time, but of course you also always see current issues in Github. 6 | 7 | ## Report a bug 8 | 9 | Before creating an issue please make sure you have checked out the docs, specifically the [FAQ](https://docs.feathersjs.com/help/faq.html) section. You might want to also try searching Github. It's pretty likely someone has already asked a similar question. 10 | 11 | If you haven't found your answer please feel free to join our [slack channel](http://slack.feathersjs.com), create an issue on Github, or post on [Stackoverflow](http://stackoverflow.com) using the `feathers` or `feathersjs` tag. We try our best to monitor Stackoverflow but you're likely to get more immediate responses in Slack and Github. 12 | 13 | Issues can be reported in the [issue tracker](https://github.com/feathersjs/feathers/issues). Since feathers combines many modules it can be hard for us to assess the root cause without knowing which modules are being used and what your configuration looks like, so **it helps us immensely if you can link to a simple example that reproduces your issue**. 14 | 15 | ## Report a Security Concern 16 | 17 | We take security very seriously at Feathers. We welcome any peer review of our 100% open source code to ensure nobody's Feathers app is ever compromised or hacked. As a web application developer you are responsible for any security breaches. We do our very best to make sure Feathers is as secure as possible by default. 18 | 19 | In order to give the community time to respond and upgrade we strongly urge you report all security issues to us. Send one of the core team members a PM in [Slack](http://slack.feathersjs.com) or email us at hello@feathersjs.com with details and we will respond ASAP. 20 | 21 | For full details refer to our [Security docs](https://docs.feathersjs.com/SECURITY.html). 22 | 23 | ## Pull Requests 24 | 25 | We :heart: pull requests and we're continually working to make it as easy as possible for people to contribute, including a [Plugin Generator](https://github.com/feathersjs/generator-feathers-plugin) and a [common test suite](https://github.com/feathersjs/feathers-service-tests) for database adapters. 26 | 27 | We prefer small pull requests with minimal code changes. The smaller they are the easier they are to review and merge. A core team member will pick up your PR and review it as soon as they can. They may ask for changes or reject your pull request. This is not a reflection of you as an engineer or a person. Please accept feedback graciously as we will also try to be sensitive when providing it. 28 | 29 | Although we generally accept many PRs they can be rejected for many reasons. We will be as transparent as possible but it may simply be that you do not have the same context or information regarding the roadmap that the core team members have. We value the time you take to put together any contributions so we pledge to always be respectful of that time and will try to be as open as possible so that you don't waste it. :smile: 30 | 31 | **All PRs (except documentation) should be accompanied with tests and pass the linting rules.** 32 | 33 | ### Code style 34 | 35 | Before running the tests from the `test/` folder `npm test` will run ESlint. You can check your code changes individually by running `npm run lint`. 36 | 37 | ### ES6 compilation 38 | 39 | Feathers uses [Babel](https://babeljs.io/) to leverage the latest developments of the JavaScript language. All code and samples are currently written in ES2015. To transpile the code in this repository run 40 | 41 | > npm run compile 42 | 43 | __Note:__ `npm test` will run the compilation automatically before the tests. 44 | 45 | ### Tests 46 | 47 | [Mocha](http://mochajs.org/) tests are located in the `test/` folder and can be run using the `npm run mocha` or `npm test` (with ESLint and code coverage) command. 48 | 49 | ### Documentation 50 | 51 | Feathers documentation is contained in Markdown files in the [feathers-docs](https://github.com/feathersjs/feathers-docs) repository. To change the documentation submit a pull request to that repo, referencing any other PR if applicable, and the docs will be updated with the next release. 52 | 53 | ## External Modules 54 | 55 | If you're written something awesome for Feathers, the Feathers ecosystem, or using Feathers please add it to the [showcase](https://docs.feathersjs.com/why/showcase.html). You also might want to check out the [Plugin Generator](https://github.com/feathersjs/generator-feathers-plugin) that can be used to scaffold plugins to be Feathers compliant from the start. 56 | 57 | If you think it would be a good core module then please contact one of the Feathers core team members in [Slack](http://slack.feathersjs.com) and we can discuss whether it belongs in core and how to get it there. :beers: 58 | 59 | ## Contributor Code of Conduct 60 | 61 | As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. 62 | 63 | We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion. 64 | 65 | Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct. 66 | 67 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team. 68 | 69 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers. 70 | 71 | This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/) 72 | -------------------------------------------------------------------------------- /.github/issue_template.md: -------------------------------------------------------------------------------- 1 | ### Steps to reproduce 2 | 3 | (First please check that this issue is not already solved as [described 4 | here](https://github.com/feathersjs/feathers/blob/master/.github/contributing.md#report-a-bug)) 5 | 6 | - [ ] Tell us what broke. The more detailed the better. 7 | - [ ] If you can, please create a simple example that reproduces the issue and link to a gist, jsbin, repo, etc. 8 | 9 | ### Expected behavior 10 | Tell us what should happen 11 | 12 | ### Actual behavior 13 | Tell us what happens instead 14 | 15 | ### System configuration 16 | 17 | Tell us about the applicable parts of your setup. 18 | 19 | **Module versions** (especially the part that's not working): 20 | 21 | **NodeJS version**: 22 | 23 | **Operating System**: 24 | 25 | **Browser Version**: 26 | 27 | **React Native Version**: 28 | 29 | **Module Loader**: -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ### Summary 2 | 3 | (If you have not already please refer to the contributing guideline as [described 4 | here](https://github.com/feathersjs/feathers/blob/master/.github/contributing.md#pull-requests)) 5 | 6 | - [ ] Tell us about the problem your pull request is solving. 7 | - [ ] Are there any open issues that are related to this? 8 | - [ ] Is this PR dependent on PRs in other repos? 9 | 10 | If so, please mention them to keep the conversations linked together. 11 | 12 | ### Other Information 13 | 14 | If there's anything else that's important and relevant to your pull 15 | request, mention that information here. This could include 16 | benchmarks, or other information. 17 | 18 | Your PR will be reviewed by a core team member and they will work with you to get your changes merged in a timely manner. If merged your PR will automatically be added to the changelog in the next release. 19 | 20 | If your changes involve documentation updates please mention that and link the appropriate PR in [feathers-docs](https://github.com/feathersjs/feathers-docs). 21 | 22 | Thanks for contributing to Feathers! :heart: -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # === Node === 2 | lib-cov 3 | *.seed 4 | *.log 5 | *.csv 6 | *.dat 7 | *.out 8 | *.pid 9 | *.gz 10 | 11 | pids 12 | logs 13 | results 14 | 15 | npm-debug.log 16 | node_modules 17 | 18 | # === Mac === 19 | .DS_Store 20 | .AppleDouble 21 | .LSOverride 22 | 23 | # Icon must ends with two \r. 24 | Icon 25 | 26 | # Directory for instrumented libs generated by jscoverage/JSCover 27 | lib-cov 28 | 29 | # Coverage directory used by tools like istanbul 30 | coverage 31 | 32 | # Thumbnails 33 | ._* 34 | 35 | # Files that might appear on external disk 36 | .Spotlight-V100 37 | .Trashes 38 | 39 | # === Vim === 40 | [._]*.s[a-w][a-z] 41 | [._]s[a-w][a-z] 42 | *.un~ 43 | Session.vim 44 | .netrwhist 45 | *~ 46 | 47 | # === Sublime === 48 | # workspace files are user-specific 49 | *.sublime-workspace 50 | 51 | # project files should be checked into the repository, unless a significant 52 | # proportion of contributors will probably not be using SublimeText 53 | # *.sublime-project 54 | 55 | # === Webstorm === 56 | .idea 57 | 58 | # Users Environment Variables 59 | .lock-wscript 60 | 61 | lib/ -------------------------------------------------------------------------------- /.istanbul.yml: -------------------------------------------------------------------------------- 1 | verbose: false 2 | instrumentation: 3 | root: src/ 4 | excludes: 5 | - lib/ 6 | include-all-sources: true 7 | reporting: 8 | print: summary 9 | reports: 10 | - html 11 | - text 12 | - lcov 13 | watermarks: 14 | statements: [50, 80] 15 | lines: [50, 80] 16 | functions: [50, 80] 17 | branches: [50, 80] -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .babelrc 2 | .istanbul.yml 3 | .travis.yml 4 | .editorconfig 5 | .idea/ 6 | src/ 7 | test/ 8 | !lib/ 9 | .github/ 10 | coverage -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | services: mongodb 4 | node_js: 5 | - 'node' 6 | - '6' 7 | - '4' 8 | addons: 9 | code_climate: 10 | repo_token: '121572b57d09b52bdcce7f905e3d5b7423194580423885f508ed5f55511b12dc' 11 | before_script: 12 | - npm install -g codeclimate-test-reporter 13 | after_script: 14 | - codeclimate-test-reporter < coverage/lcov.info 15 | notifications: 16 | email: false 17 | slack: 18 | rooms: 19 | secure: PnejT51VVL7/qV8JEu8ZlyOPbH5BcAPEWq4k2UcHParZF7t/SQ267B2c0G2u23oMyKLsq2nOOsYxrenaY1hEe7+FOgzqzdFLwUCS0EMvWI/1ObeWZ6Gcc6VlPV8tgYWI4HEJ6mffV0lXRzeQUQ0zQuVE1OOkDIyGDeygQnWKEFg= 20 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## [v2.1.2](https://github.com/feathersjs-ecosystem/feathers-hooks/tree/v2.1.2) (2017-10-31) 4 | [Full Changelog](https://github.com/feathersjs-ecosystem/feathers-hooks/compare/v2.1.1...v2.1.2) 5 | 6 | **Closed issues:** 7 | 8 | - Populate hook with array of one ref return object not an array. [\#174](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/174) 9 | 10 | **Merged pull requests:** 11 | 12 | - Add a warning when trying to use hooks with v3 [\#175](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/175) ([daffl](https://github.com/daffl)) 13 | 14 | ## [v2.1.1](https://github.com/feathersjs-ecosystem/feathers-hooks/tree/v2.1.1) (2017-10-22) 15 | [Full Changelog](https://github.com/feathersjs-ecosystem/feathers-hooks/compare/v2.1.0...v2.1.1) 16 | 17 | **Closed issues:** 18 | 19 | - Angular 4 feathers hook build fails - "All declarations of 'Service' must have identical type parameters" [\#172](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/172) 20 | 21 | **Merged pull requests:** 22 | 23 | - Fix TypeScript declarations. [\#173](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/173) ([TimMensch](https://github.com/TimMensch)) 24 | - Update mocha to the latest version 🚀 [\#171](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/171) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) 25 | 26 | ## [v2.1.0](https://github.com/feathersjs-ecosystem/feathers-hooks/tree/v2.1.0) (2017-09-27) 27 | [Full Changelog](https://github.com/feathersjs-ecosystem/feathers-hooks/compare/v2.0.4...v2.1.0) 28 | 29 | **Merged pull requests:** 30 | 31 | - Allow errors to be swallowed in error hooks [\#170](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/170) ([James0x57](https://github.com/James0x57)) 32 | 33 | ## [v2.0.4](https://github.com/feathersjs-ecosystem/feathers-hooks/tree/v2.0.4) (2017-09-25) 34 | [Full Changelog](https://github.com/feathersjs-ecosystem/feathers-hooks/compare/v2.0.3...v2.0.4) 35 | 36 | **Merged pull requests:** 37 | 38 | - \[typings\] Make Service generic types default to any [\#168](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/168) ([j2L4e](https://github.com/j2L4e)) 39 | 40 | ## [v2.0.3](https://github.com/feathersjs-ecosystem/feathers-hooks/tree/v2.0.3) (2017-09-25) 41 | [Full Changelog](https://github.com/feathersjs-ecosystem/feathers-hooks/compare/v2.0.2...v2.0.3) 42 | 43 | **Closed issues:** 44 | 45 | - Hook typings [\#166](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/166) 46 | - Error: 'chck' is not a valid hook method [\#165](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/165) 47 | - \[feature request\] provide a way to swallow error in error hooks [\#151](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/151) 48 | 49 | **Merged pull requests:** 50 | 51 | - \[typings\] add service and id to hook context object [\#169](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/169) ([j2L4e](https://github.com/j2L4e)) 52 | - Update README to point at new documentation location [\#167](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/167) ([tfussell](https://github.com/tfussell)) 53 | 54 | ## [v2.0.2](https://github.com/feathersjs-ecosystem/feathers-hooks/tree/v2.0.2) (2017-07-11) 55 | [Full Changelog](https://github.com/feathersjs-ecosystem/feathers-hooks/compare/v2.0.1...v2.0.2) 56 | 57 | **Fixed bugs:** 58 | 59 | - TypeScript: HookProps.type: add 'error' [\#161](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/161) ([tycho01](https://github.com/tycho01)) 60 | 61 | **Closed issues:** 62 | 63 | - Params are not populated when called internally [\#160](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/160) 64 | - \[discussion\] maybe change the name of the context parameter from 'hook' to 'context' or use 'this' in the future? [\#159](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/159) 65 | - Add hook to the top of others hook [\#158](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/158) 66 | - \_\_hooks being overwritten [\#157](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/157) 67 | - Using a hook.disable\(\) on a service should remove that from the Allow header for REST services [\#107](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/107) 68 | 69 | **Merged pull requests:** 70 | 71 | - Add prepare script [\#164](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/164) ([couac](https://github.com/couac)) 72 | - Update index.d.ts [\#163](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/163) ([j2L4e](https://github.com/j2L4e)) 73 | - Define return type for hooks [\#162](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/162) ([Creiger](https://github.com/Creiger)) 74 | 75 | ## [v2.0.1](https://github.com/feathersjs-ecosystem/feathers-hooks/tree/v2.0.1) (2017-04-28) 76 | [Full Changelog](https://github.com/feathersjs-ecosystem/feathers-hooks/compare/v2.0.0...v2.0.1) 77 | 78 | **Closed issues:** 79 | 80 | - `\_\_hooks` of existing service should not be overwritten [\#154](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/154) 81 | - An in-range update of mongoose is breaking the build 🚨 [\#153](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/153) 82 | 83 | **Merged pull requests:** 84 | 85 | - Test for \#154 and \#155 [\#156](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/156) ([daffl](https://github.com/daffl)) 86 | - Fixed issue with \_\_hooks being overwritten when reusing service [\#155](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/155) ([Mattchewone](https://github.com/Mattchewone)) 87 | - Update semistandard to the latest version 🚀 [\#152](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/152) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) 88 | 89 | ## [v2.0.0](https://github.com/feathersjs-ecosystem/feathers-hooks/tree/v2.0.0) (2017-04-17) 90 | [Full Changelog](https://github.com/feathersjs-ecosystem/feathers-hooks/compare/v1.8.1...v2.0.0) 91 | 92 | **Implemented enhancements:** 93 | 94 | - Population fails on empty values [\#137](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/137) 95 | 96 | **Closed issues:** 97 | 98 | - feathers-hooks-common/lib/populate, feathers-hooks-common/lib/bundled not found. [\#149](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/149) 99 | - Test Hooks [\#146](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/146) 100 | - Gravatar example doesn't work?! [\#145](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/145) 101 | - how to get service name\(path\) in hook functions? [\#143](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/143) 102 | - V1.7.1: Module not found: Error: Can't resolve 'feathers-hooks-common/lib/populate' in '/app/node\_modules/feathers-hooks/lib' @ ./~/feathers-hooks/lib/hooks.js 13:16-61 [\#142](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/142) 103 | - Calling populate\(target, options\) is now DEPRECATED [\#141](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/141) 104 | - Hooks with mutiple entries per request [\#95](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/95) 105 | - Remove ref doc will broke the parent router [\#82](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/82) 106 | 107 | **Merged pull requests:** 108 | 109 | - Remove bundled hooks [\#150](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/150) ([daffl](https://github.com/daffl)) 110 | - Update dependencies to enable Greenkeeper 🌴 [\#148](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/148) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) 111 | 112 | ## [v1.8.1](https://github.com/feathersjs-ecosystem/feathers-hooks/tree/v1.8.1) (2017-03-02) 113 | [Full Changelog](https://github.com/feathersjs-ecosystem/feathers-hooks/compare/v1.8.0...v1.8.1) 114 | 115 | **Merged pull requests:** 116 | 117 | - Additional hook typedefs [\#139](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/139) ([myknbani](https://github.com/myknbani)) 118 | 119 | ## [v1.8.0](https://github.com/feathersjs-ecosystem/feathers-hooks/tree/v1.8.0) (2017-03-01) 120 | [Full Changelog](https://github.com/feathersjs-ecosystem/feathers-hooks/compare/v1.7.1...v1.8.0) 121 | 122 | **Closed issues:** 123 | 124 | - Composing Hooks [\#133](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/133) 125 | - Can I use populate without authentication? [\#132](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/132) 126 | - Remove-hook breaks feathers-mongoose [\#91](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/91) 127 | 128 | **Merged pull requests:** 129 | 130 | - Update feathers-mongoose to version 5.0.0 🚀 [\#138](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/138) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) 131 | - Typescript Definitions [\#135](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/135) ([AbraaoAlves](https://github.com/AbraaoAlves)) 132 | - Update feathers-mongoose to version 4.0.0 🚀 [\#134](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/134) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) 133 | 134 | ## [v1.7.1](https://github.com/feathersjs-ecosystem/feathers-hooks/tree/v1.7.1) (2016-12-16) 135 | [Full Changelog](https://github.com/feathersjs-ecosystem/feathers-hooks/compare/v1.7.0...v1.7.1) 136 | 137 | **Closed issues:** 138 | 139 | - How to mix hooks together \(pluck + populate\) [\#68](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/68) 140 | 141 | **Merged pull requests:** 142 | 143 | - Use legacyPopulate from latest hooks-common [\#131](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/131) ([daffl](https://github.com/daffl)) 144 | 145 | ## [v1.7.0](https://github.com/feathersjs-ecosystem/feathers-hooks/tree/v1.7.0) (2016-11-25) 146 | [Full Changelog](https://github.com/feathersjs-ecosystem/feathers-hooks/compare/v1.6.1...v1.7.0) 147 | 148 | **Implemented enhancements:** 149 | 150 | - Bundled hooks should support a scope [\#63](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/63) 151 | 152 | **Closed issues:** 153 | 154 | - Be able to access deeply nested attributes in bundled hooks [\#79](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/79) 155 | 156 | **Merged pull requests:** 157 | 158 | - Adding hook.path and hook.service [\#129](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/129) ([daffl](https://github.com/daffl)) 159 | - Update feathers-memory to version 1.0.0 🚀 [\#128](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/128) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) 160 | - Node \>= 4 [\#127](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/127) ([daffl](https://github.com/daffl)) 161 | - Update feathers-commons to version 0.8.0 🚀 [\#126](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/126) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) 162 | - feathers-errors@2.5.0 breaks build 🚨 [\#125](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/125) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) 163 | - babel-core@6.18.1 breaks build 🚨 [\#124](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/124) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) 164 | 165 | ## [v1.6.1](https://github.com/feathersjs-ecosystem/feathers-hooks/tree/v1.6.1) (2016-11-02) 166 | [Full Changelog](https://github.com/feathersjs-ecosystem/feathers-hooks/compare/v1.6.0...v1.6.1) 167 | 168 | **Closed issues:** 169 | 170 | - app.hooks\(\) does not pass hook.app [\#123](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/123) 171 | - \[Feature Request\] Hook to disable multi-update/patch/remove [\#98](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/98) 172 | 173 | ## [v1.6.0](https://github.com/feathersjs-ecosystem/feathers-hooks/tree/v1.6.0) (2016-10-31) 174 | [Full Changelog](https://github.com/feathersjs-ecosystem/feathers-hooks/compare/v1.5.8...v1.6.0) 175 | 176 | **Closed issues:** 177 | 178 | - service.hooks\(\) did not run as expected [\#119](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/119) 179 | - Throw an error for unknown hook methods [\#118](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/118) 180 | - no hook result when internal service call [\#106](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/106) 181 | - remove hook should check if hook.result is not null [\#99](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/99) 182 | - afterError hooks [\#83](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/83) 183 | - Should remove hook.params.provider to act as an internal call [\#76](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/76) 184 | 185 | **Merged pull requests:** 186 | 187 | - app.hooks global hooks [\#122](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/122) ([daffl](https://github.com/daffl)) 188 | - Error on invalid hook methods [\#121](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/121) ([daffl](https://github.com/daffl)) 189 | - Use feathers-hooks-common for bundled hooks [\#120](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/120) ([daffl](https://github.com/daffl)) 190 | - Test to verify hooks get chained [\#117](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/117) ([daffl](https://github.com/daffl)) 191 | - jshint —\> semistandard [\#116](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/116) ([corymsmith](https://github.com/corymsmith)) 192 | - feathers-commons@0.7.8 breaks build 🚨 [\#115](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/115) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) 193 | - jshint@2.9.4 breaks build 🚨 [\#113](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/113) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) 194 | - adding code coverage [\#112](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/112) ([ekryski](https://github.com/ekryski)) 195 | - Implement .hooks service method [\#110](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/110) ([daffl](https://github.com/daffl)) 196 | - onError hooks functionality [\#109](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/109) ([daffl](https://github.com/daffl)) 197 | - Rename hooks properties on service [\#108](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/108) ([daffl](https://github.com/daffl)) 198 | 199 | ## [v1.5.8](https://github.com/feathersjs-ecosystem/feathers-hooks/tree/v1.5.8) (2016-09-27) 200 | [Full Changelog](https://github.com/feathersjs-ecosystem/feathers-hooks/compare/v1.5.7...v1.5.8) 201 | 202 | **Closed issues:** 203 | 204 | - Sanitation hooks for each db type [\#62](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/62) 205 | 206 | **Merged pull requests:** 207 | 208 | - Fix/nullable result [\#101](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/101) ([kaiquewdev](https://github.com/kaiquewdev)) 209 | - Update feathers-memory to version 0.8.0 🚀 [\#97](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/97) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) 210 | 211 | ## [v1.5.7](https://github.com/feathersjs-ecosystem/feathers-hooks/tree/v1.5.7) (2016-08-20) 212 | [Full Changelog](https://github.com/feathersjs-ecosystem/feathers-hooks/compare/v1.5.6...v1.5.7) 213 | 214 | **Closed issues:** 215 | 216 | - Customize existing hook - 'this' undefined - TypeError [\#94](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/94) 217 | - c [\#93](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/93) 218 | 219 | **Merged pull requests:** 220 | 221 | - Fix removing fields from feathers-mongoose [\#92](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/92) ([supasate](https://github.com/supasate)) 222 | 223 | ## [v1.5.6](https://github.com/feathersjs-ecosystem/feathers-hooks/tree/v1.5.6) (2016-08-16) 224 | [Full Changelog](https://github.com/feathersjs-ecosystem/feathers-hooks/compare/v1.5.5...v1.5.6) 225 | 226 | **Closed issues:** 227 | 228 | - Hooks cannot remove inherited properties [\#89](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/89) 229 | - Pop wrong hooks in bundled.test [\#87](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/87) 230 | 231 | **Merged pull requests:** 232 | 233 | - Fix \#89 Hooks cannot remove inherited properties [\#90](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/90) ([supasate](https://github.com/supasate)) 234 | - Fix \#87 pop wrong hooks in bundled test [\#88](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/88) ([supasate](https://github.com/supasate)) 235 | 236 | ## [v1.5.5](https://github.com/feathersjs-ecosystem/feathers-hooks/tree/v1.5.5) (2016-08-11) 237 | [Full Changelog](https://github.com/feathersjs-ecosystem/feathers-hooks/compare/v1.5.4...v1.5.5) 238 | 239 | **Closed issues:** 240 | 241 | - Multi populate with client control of which fields to populate [\#85](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/85) 242 | 243 | **Merged pull requests:** 244 | 245 | - Update mocha to version 3.0.0 🚀 [\#86](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/86) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) 246 | - Add ability to delete deeply nested attributes in bundled hooks [\#84](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/84) ([ryuken](https://github.com/ryuken)) 247 | 248 | ## [v1.5.4](https://github.com/feathersjs-ecosystem/feathers-hooks/tree/v1.5.4) (2016-05-30) 249 | [Full Changelog](https://github.com/feathersjs-ecosystem/feathers-hooks/compare/v1.5.3...v1.5.4) 250 | 251 | **Closed issues:** 252 | 253 | - Throw or log an error for catch block in populate method. [\#80](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/80) 254 | 255 | **Merged pull requests:** 256 | 257 | - Remove query in populate [\#81](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/81) ([t2t2](https://github.com/t2t2)) 258 | - mocha@2.5.0 breaks build 🚨 [\#78](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/78) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) 259 | 260 | ## [v1.5.3](https://github.com/feathersjs-ecosystem/feathers-hooks/tree/v1.5.3) (2016-05-09) 261 | [Full Changelog](https://github.com/feathersjs-ecosystem/feathers-hooks/compare/v1.5.2...v1.5.3) 262 | 263 | **Closed issues:** 264 | 265 | - Populate hook doesn't work with populating arrays [\#74](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/74) 266 | - hashPassword validation error [\#70](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/70) 267 | - Remove field hook not working. [\#69](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/69) 268 | - Remove field and haspassword hooks are not working properly. [\#67](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/67) 269 | 270 | **Merged pull requests:** 271 | 272 | - Handle array population [\#75](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/75) ([startupthekid](https://github.com/startupthekid)) 273 | - Update babel-plugin-add-module-exports to version 0.2.0 🚀 [\#73](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/73) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) 274 | - Removed garbage [\#71](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/71) ([harangue](https://github.com/harangue)) 275 | 276 | ## [v1.5.2](https://github.com/feathersjs-ecosystem/feathers-hooks/tree/v1.5.2) (2016-04-11) 277 | [Full Changelog](https://github.com/feathersjs-ecosystem/feathers-hooks/compare/v1.5.1...v1.5.2) 278 | 279 | **Fixed bugs:** 280 | 281 | - Do not throw an error when a field that is supposed to be lowercased is undefined [\#66](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/66) ([gurisko](https://github.com/gurisko)) 282 | 283 | **Merged pull requests:** 284 | 285 | - Update feathers-memory to version 0.7.0 🚀 [\#65](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/65) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) 286 | - Query bundled hooks [\#59](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/59) ([harangue](https://github.com/harangue)) 287 | 288 | ## [v1.5.1](https://github.com/feathersjs-ecosystem/feathers-hooks/tree/v1.5.1) (2016-03-30) 289 | [Full Changelog](https://github.com/feathersjs-ecosystem/feathers-hooks/compare/v1.5.0...v1.5.1) 290 | 291 | **Closed issues:** 292 | 293 | - Header or request information in hooks [\#61](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/61) 294 | - Create a `populate` hook [\#56](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/56) 295 | 296 | **Merged pull requests:** 297 | 298 | - adding ability to remove from result.data object [\#64](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/64) ([ekryski](https://github.com/ekryski)) 299 | 300 | ## [v1.5.0](https://github.com/feathersjs-ecosystem/feathers-hooks/tree/v1.5.0) (2016-03-15) 301 | [Full Changelog](https://github.com/feathersjs-ecosystem/feathers-hooks/compare/v1.4.0...v1.5.0) 302 | 303 | **Closed issues:** 304 | 305 | - Create a "select" or "pluck" hook for serialization [\#50](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/50) 306 | 307 | **Merged pull requests:** 308 | 309 | - Adding functionality and tests for the populate hook [\#60](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/60) ([daffl](https://github.com/daffl)) 310 | - Add "pluck" bundled hook [\#58](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/58) ([harangue](https://github.com/harangue)) 311 | 312 | ## [v1.4.0](https://github.com/feathersjs-ecosystem/feathers-hooks/tree/v1.4.0) (2016-03-06) 313 | [Full Changelog](https://github.com/feathersjs-ecosystem/feathers-hooks/compare/v1.3.0...v1.4.0) 314 | 315 | **Closed issues:** 316 | 317 | - The idea for a new hook [\#42](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/42) 318 | - Make sure hook promises propagate their errors [\#30](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/30) 319 | - Defining a service hook without an app/before app initialization? [\#25](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/25) 320 | 321 | **Merged pull requests:** 322 | 323 | - Refactoring hooks to run before event dispatching [\#57](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/57) ([daffl](https://github.com/daffl)) 324 | 325 | ## [v1.3.0](https://github.com/feathersjs-ecosystem/feathers-hooks/tree/v1.3.0) (2016-02-26) 326 | [Full Changelog](https://github.com/feathersjs-ecosystem/feathers-hooks/compare/v1.2.0...v1.3.0) 327 | 328 | **Closed issues:** 329 | 330 | - Add toLowerCase hook [\#49](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/49) 331 | 332 | **Merged pull requests:** 333 | 334 | - Params provider [\#55](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/55) ([ekryski](https://github.com/ekryski)) 335 | - add hooks.lowerCase\(\[field\]\) [\#54](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/54) ([kulakowka](https://github.com/kulakowka)) 336 | 337 | ## [v1.2.0](https://github.com/feathersjs-ecosystem/feathers-hooks/tree/v1.2.0) (2016-02-26) 338 | [Full Changelog](https://github.com/feathersjs-ecosystem/feathers-hooks/compare/v1.1.2...v1.2.0) 339 | 340 | **Fixed bugs:** 341 | 342 | - should be able to bypass remove hook [\#48](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/48) 343 | 344 | **Merged pull requests:** 345 | 346 | - Alllow to conditionally remove fields [\#51](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/51) ([daffl](https://github.com/daffl)) 347 | 348 | ## [v1.1.2](https://github.com/feathersjs-ecosystem/feathers-hooks/tree/v1.1.2) (2016-02-24) 349 | [Full Changelog](https://github.com/feathersjs-ecosystem/feathers-hooks/compare/v1.1.1...v1.1.2) 350 | 351 | **Merged pull requests:** 352 | 353 | - Update feathers-errors to version 2.0.1 🚀 [\#47](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/47) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) 354 | 355 | ## [v1.1.1](https://github.com/feathersjs-ecosystem/feathers-hooks/tree/v1.1.1) (2016-02-23) 356 | [Full Changelog](https://github.com/feathersjs-ecosystem/feathers-hooks/compare/v1.1.0...v1.1.1) 357 | 358 | **Closed issues:** 359 | 360 | - Ability to call the same service from a hook? [\#43](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/43) 361 | - more request param in hook? [\#40](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/40) 362 | - hook on non-service apps? [\#39](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/39) 363 | 364 | **Merged pull requests:** 365 | 366 | - Update feathers-memory to version 0.6.1 🚀 [\#45](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/45) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) 367 | - Update feathers to version 2.0.0 🚀 [\#44](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/44) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) 368 | - feathers-errors@1.2.2 breaks build 🚨 [\#41](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/41) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) 369 | 370 | ## [v1.1.0](https://github.com/feathersjs-ecosystem/feathers-hooks/tree/v1.1.0) (2016-02-14) 371 | [Full Changelog](https://github.com/feathersjs-ecosystem/feathers-hooks/compare/v1.0.0...v1.1.0) 372 | 373 | **Merged pull requests:** 374 | 375 | - Allow before hooks to set the result which will skip service method [\#38](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/38) ([daffl](https://github.com/daffl)) 376 | 377 | ## [v1.0.0](https://github.com/feathersjs-ecosystem/feathers-hooks/tree/v1.0.0) (2016-02-13) 378 | [Full Changelog](https://github.com/feathersjs-ecosystem/feathers-hooks/compare/v1.0.0-pre.4...v1.0.0) 379 | 380 | **Closed issues:** 381 | 382 | - Provide a collection of common hooks [\#31](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/31) 383 | 384 | **Merged pull requests:** 385 | 386 | - Update feathers-commons to version 0.7.0 🚀 [\#37](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/37) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) 387 | - updating docs to point to official feathers docs [\#36](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/36) ([ekryski](https://github.com/ekryski)) 388 | - Add docs for `hook.app` [\#35](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/35) ([marshallswain](https://github.com/marshallswain)) 389 | - Added remove and disable common hook [\#33](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/33) ([marshallswain](https://github.com/marshallswain)) 390 | 391 | ## [v1.0.0-pre.4](https://github.com/feathersjs-ecosystem/feathers-hooks/tree/v1.0.0-pre.4) (2016-01-23) 392 | [Full Changelog](https://github.com/feathersjs-ecosystem/feathers-hooks/compare/v1.0.0-pre.3...v1.0.0-pre.4) 393 | 394 | ## [v1.0.0-pre.3](https://github.com/feathersjs-ecosystem/feathers-hooks/tree/v1.0.0-pre.3) (2016-01-23) 395 | [Full Changelog](https://github.com/feathersjs-ecosystem/feathers-hooks/compare/v1.0.0-pre.2...v1.0.0-pre.3) 396 | 397 | **Merged pull requests:** 398 | 399 | - Adds the app object at hook.app. [\#34](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/34) ([marshallswain](https://github.com/marshallswain)) 400 | - Update feathers-commons to version 0.6.0 🚀 [\#32](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/32) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) 401 | 402 | ## [v1.0.0-pre.2](https://github.com/feathersjs-ecosystem/feathers-hooks/tree/v1.0.0-pre.2) (2016-01-12) 403 | [Full Changelog](https://github.com/feathersjs-ecosystem/feathers-hooks/compare/v1.0.0-pre.1...v1.0.0-pre.2) 404 | 405 | ## [v1.0.0-pre.1](https://github.com/feathersjs-ecosystem/feathers-hooks/tree/v1.0.0-pre.1) (2016-01-12) 406 | [Full Changelog](https://github.com/feathersjs-ecosystem/feathers-hooks/compare/v0.6.0...v1.0.0-pre.1) 407 | 408 | **Closed issues:** 409 | 410 | - Should support promises returned from service methods [\#28](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/28) 411 | 412 | **Merged pull requests:** 413 | 414 | - Refactoring for hooks to use promises and promise chains [\#29](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/29) ([daffl](https://github.com/daffl)) 415 | 416 | ## [v0.6.0](https://github.com/feathersjs-ecosystem/feathers-hooks/tree/v0.6.0) (2016-01-10) 417 | [Full Changelog](https://github.com/feathersjs-ecosystem/feathers-hooks/compare/v0.5.1...v0.6.0) 418 | 419 | **Fixed bugs:** 420 | 421 | - After hooks fail if params was missing on the original service method [\#19](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/19) 422 | 423 | **Closed issues:** 424 | 425 | - examples not working [\#24](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/24) 426 | - Unable to modify result object in after hook [\#23](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/23) 427 | 428 | **Merged pull requests:** 429 | 430 | - Prevent next from being called multiple times [\#27](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/27) ([daffl](https://github.com/daffl)) 431 | - Migrate to ES6 and new plugin infrastructure [\#26](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/26) ([daffl](https://github.com/daffl)) 432 | 433 | ## [v0.5.1](https://github.com/feathersjs-ecosystem/feathers-hooks/tree/v0.5.1) (2015-10-06) 434 | [Full Changelog](https://github.com/feathersjs-ecosystem/feathers-hooks/compare/0.5.0...v0.5.1) 435 | 436 | **Implemented enhancements:** 437 | 438 | - Be able to apply hooks to custom service methods [\#9](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/9) 439 | 440 | **Merged pull requests:** 441 | 442 | - Use feathers-commons hook functionality [\#22](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/22) ([daffl](https://github.com/daffl)) 443 | 444 | ## [0.5.0](https://github.com/feathersjs-ecosystem/feathers-hooks/tree/0.5.0) (2015-02-05) 445 | [Full Changelog](https://github.com/feathersjs-ecosystem/feathers-hooks/compare/0.4.0...0.5.0) 446 | 447 | **Closed issues:** 448 | 449 | - feathers-hooks is changing the scope of makeWrapper\(\) [\#17](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/17) 450 | - Arrays of Hooks are running in reverse order. [\#13](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/13) 451 | - Remove peer dependency [\#12](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/12) 452 | - Before all and after all hooks [\#11](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/11) 453 | - Hooks are always called even if you don't want them to be [\#8](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/8) 454 | 455 | **Merged pull requests:** 456 | 457 | - Make sure hooks and service methods keep their context [\#18](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/18) ([daffl](https://github.com/daffl)) 458 | - Refactoring to fix hook execution order and all-hooks [\#16](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/16) ([daffl](https://github.com/daffl)) 459 | - Better check for .makeArguments id [\#15](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/15) ([daffl](https://github.com/daffl)) 460 | 461 | ## [0.4.0](https://github.com/feathersjs-ecosystem/feathers-hooks/tree/0.4.0) (2014-07-20) 462 | [Full Changelog](https://github.com/feathersjs-ecosystem/feathers-hooks/compare/0.3.2...0.4.0) 463 | 464 | **Closed issues:** 465 | 466 | - multiple hooks / array of hooks [\#2](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/2) 467 | 468 | **Merged pull requests:** 469 | 470 | - Adding the ability to define multiple hooks in an array. [\#7](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/7) ([ekryski](https://github.com/ekryski)) 471 | 472 | ## [0.3.2](https://github.com/feathersjs-ecosystem/feathers-hooks/tree/0.3.2) (2014-06-18) 473 | [Full Changelog](https://github.com/feathersjs-ecosystem/feathers-hooks/compare/0.3.1...0.3.2) 474 | 475 | **Closed issues:** 476 | 477 | - Update repository link on npm [\#5](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/5) 478 | 479 | ## [0.3.1](https://github.com/feathersjs-ecosystem/feathers-hooks/tree/0.3.1) (2014-06-05) 480 | [Full Changelog](https://github.com/feathersjs-ecosystem/feathers-hooks/compare/0.3.0...0.3.1) 481 | 482 | ## [0.3.0](https://github.com/feathersjs-ecosystem/feathers-hooks/tree/0.3.0) (2014-06-05) 483 | [Full Changelog](https://github.com/feathersjs-ecosystem/feathers-hooks/compare/0.2.0...0.3.0) 484 | 485 | **Closed issues:** 486 | 487 | - Allow hooks to return a promise [\#3](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/3) 488 | - normalize parameters [\#1](https://github.com/feathersjs-ecosystem/feathers-hooks/issues/1) 489 | 490 | **Merged pull requests:** 491 | 492 | - Allow hooks to return a promise [\#4](https://github.com/feathersjs-ecosystem/feathers-hooks/pull/4) ([daffl](https://github.com/daffl)) 493 | 494 | ## [0.2.0](https://github.com/feathersjs-ecosystem/feathers-hooks/tree/0.2.0) (2014-06-02) 495 | [Full Changelog](https://github.com/feathersjs-ecosystem/feathers-hooks/compare/0.1.1...0.2.0) 496 | 497 | ## [0.1.1](https://github.com/feathersjs-ecosystem/feathers-hooks/tree/0.1.1) (2014-05-29) 498 | [Full Changelog](https://github.com/feathersjs-ecosystem/feathers-hooks/compare/0.1.0...0.1.1) 499 | 500 | ## [0.1.0](https://github.com/feathersjs-ecosystem/feathers-hooks/tree/0.1.0) (2014-05-28) 501 | 502 | 503 | \* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)* -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright 2015 David Luecke 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Feathers Hooks 2 | 3 | > _Important:_ `feathers-hooks` is included in Feathers (`@feathersjs/feathers`) v3 and later and does not have to be loaded and configured separately anymore. 4 | 5 | [![Greenkeeper badge](https://badges.greenkeeper.io/feathersjs-ecosystem/feathers-hooks.svg)](https://greenkeeper.io/) 6 | 7 | [![Build Status](https://travis-ci.org/feathersjs-ecosystem/feathers-hooks.png?branch=master)](https://travis-ci.org/feathersjs-ecosystem/feathers-hooks) 8 | [![Code Climate](https://codeclimate.com/github/feathersjs-ecosystem/feathers-hooks/badges/gpa.svg)](https://codeclimate.com/github/feathersjs-ecosystem/feathers-hooks) 9 | [![Test Coverage](https://codeclimate.com/github/feathersjs-ecosystem/feathers-hooks/badges/coverage.svg)](https://codeclimate.com/github/feathersjs-ecosystem/feathers-hooks/coverage) 10 | [![Dependency Status](https://img.shields.io/david/feathersjs-ecosystem/feathers-hooks.svg?style=flat-square)](https://david-dm.org/feathersjs-ecosystem/feathers-hooks) 11 | [![Download Status](https://img.shields.io/npm/dm/feathers-hooks.svg?style=flat-square)](https://www.npmjs.com/package/feathers-hooks) 12 | [![Slack Status](http://slack.feathersjs.com/badge.svg)](http://slack.feathersjs.com) 13 | 14 | > Middleware for Feathers service methods 15 | 16 | ## Documentation 17 | 18 | Please refer to the [Feathers hooks documentation](https://docs.feathersjs.com/api/hooks.html) for more details on: 19 | 20 | - The philosophy behind hooks 21 | - How you can use hooks 22 | - How you can chain hooks using Promises 23 | - Params that are available in hooks 24 | - Hooks that are bundled with feathers and feathers plugins 25 | 26 | ## Quick start 27 | 28 | `feathers-hooks` allows to register composable middleware functions when a Feathers service method executes. This makes it easy to decouple things like authorization and pre- or post processing and error handling from your service logic. 29 | 30 | To install from [npm](https://www.npmjs.com/package/feathers-hooks), run: 31 | 32 | ```bash 33 | $ npm install feathers-hooks --save 34 | ``` 35 | 36 | Then, to use the plugin in your Feathers app: 37 | 38 | ```javascript 39 | const feathers = require('feathers'); 40 | const hooks = require('feathers-hooks'); 41 | 42 | const app = feathers().configure(hooks()); 43 | ``` 44 | 45 | Then, you can register a hook for a service: 46 | 47 | ```javascript 48 | // User service 49 | const service = require('feathers-memory'); 50 | 51 | module.exports = function(){ 52 | const app = this; 53 | 54 | let myHook = function(options) { 55 | return 56 | } 57 | 58 | // Initialize our service 59 | app.use('/users', service()); 60 | 61 | // Get our initialize service to that we can bind hooks 62 | const userService = app.service('/users'); 63 | 64 | // Set up our before hook 65 | userService.hooks({ 66 | before(hook){ 67 | console.log('My custom before hook ran!'); 68 | } 69 | }); 70 | } 71 | ``` 72 | 73 | ## License 74 | 75 | Copyright (c) 2016 [Feathers contributors](https://github.com/feathersjs-ecosystem/feathers-hooks/graphs/contributors) 76 | 77 | Licensed under the [MIT license](LICENSE). 78 | -------------------------------------------------------------------------------- /hooks.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./lib/bundled'); 2 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | import * as feathers from "feathers"; 2 | 3 | declare function hooks(): () => void; 4 | 5 | declare module "feathers" { 6 | interface Service { 7 | before(hooks: hooks.HookMap): Application; 8 | after(hooks: hooks.HookMap): Application; 9 | hooks(hooks: hooks.HooksObject): Application; 10 | } 11 | interface Application { 12 | hooks(hooks: hooks.HooksObject): Application; 13 | } 14 | } 15 | 16 | declare namespace hooks { 17 | interface Hook { 18 | (hook: HookProps): Promise | void; 19 | } 20 | 21 | interface HookProps { 22 | app?: feathers.Application; 23 | data?: T; 24 | error?: any; 25 | id?: string | number; 26 | method?: string; 27 | params?: any; 28 | path?: string; 29 | result?: T; 30 | service: feathers.Service; 31 | type: "before" | "after" | "error"; 32 | } 33 | 34 | interface HookMap { 35 | all?: Hook | Hook[]; 36 | find?: Hook | Hook[]; 37 | get?: Hook | Hook[]; 38 | create?: Hook | Hook[]; 39 | update?: Hook | Hook[]; 40 | patch?: Hook | Hook[]; 41 | remove?: Hook | Hook[]; 42 | } 43 | 44 | interface HooksObject { 45 | before?: HookMap; 46 | after?: HookMap; 47 | error?: HookMap; 48 | } 49 | } 50 | 51 | export = hooks; 52 | -------------------------------------------------------------------------------- /mocha.opts: -------------------------------------------------------------------------------- 1 | --recursive test/ 2 | --compilers js:babel-core/register -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "feathers-hooks", 3 | "version": "2.1.2", 4 | "description": "Before and after service method call hooks for easy authorization and processing.", 5 | "homepage": "https://github.com/feathersjs-ecosystem/feathers-hooks", 6 | "keywords": [ 7 | "feathers-plugin", 8 | "feathers" 9 | ], 10 | "license": "MIT", 11 | "repository": { 12 | "type": "git", 13 | "url": "git://github.com/feathersjs-ecosystem/feathers-hooks.git" 14 | }, 15 | "author": { 16 | "name": "David Luecke", 17 | "email": "daff@neyeon.com", 18 | "url": "https://feathersjs.com" 19 | }, 20 | "contributors": [], 21 | "bugs": { 22 | "url": "https://github.com/feathersjs-ecosystem/feathers-hooks/issues" 23 | }, 24 | "engines": { 25 | "node": ">= 4.0" 26 | }, 27 | "main": "lib/hooks.js", 28 | "types": [ 29 | "./index.d.ts" 30 | ], 31 | "scripts": { 32 | "prepare": "npm run compile", 33 | "prepublish": "npm run compile", 34 | "publish": "git push origin --tags && npm run changelog && git push origin", 35 | "release:prerelease": "npm version prerelease && npm publish", 36 | "release:patch": "npm version patch && npm publish", 37 | "release:minor": "npm version minor && npm publish", 38 | "release:major": "npm version major && npm publish", 39 | "changelog": "github_changelog_generator && git add CHANGELOG.md && git commit -am \"Updating changelog\"", 40 | "compile": "rimraf lib/ && babel -d lib/ src/", 41 | "watch": "babel --watch -d lib/ src/", 42 | "lint": "eslint-if-supported semistandard --fix", 43 | "mocha": "mocha --opts mocha.opts", 44 | "coverage": "istanbul cover node_modules/mocha/bin/_mocha -- --opts mocha.opts", 45 | "test": "npm run compile && npm run lint && npm run coverage" 46 | }, 47 | "semistandard": { 48 | "env": [ 49 | "mocha" 50 | ], 51 | "ignore": [ 52 | "/lib" 53 | ] 54 | }, 55 | "directories": { 56 | "lib": "lib" 57 | }, 58 | "dependencies": { 59 | "feathers-commons": "^0.8.6", 60 | "uberproto": "^1.2.0" 61 | }, 62 | "greenkeeper": { 63 | "ignore": [ 64 | "feathers-commons", 65 | "uberproto", 66 | "@feathersjs/feathers", 67 | "babel-cli", 68 | "babel-core", 69 | "babel-plugin-add-module-exports", 70 | "babel-preset-es2015", 71 | "eslint-if-supported", 72 | "eslint-if-supported", 73 | "feathers", 74 | "feathers-errors", 75 | "feathers-memory", 76 | "feathers-mongoose", 77 | "feathers-rest", 78 | "istanbul", 79 | "mocha", 80 | "mongoose", 81 | "rimraf", 82 | "semistandard" 83 | ] 84 | }, 85 | "devDependencies": { 86 | "@feathersjs/feathers": "^3.0.0-pre.3", 87 | "babel-cli": "^6.4.0", 88 | "babel-core": "^6.18.1", 89 | "babel-plugin-add-module-exports": "^0.2.0", 90 | "babel-preset-es2015": "^6.3.13", 91 | "eslint-if-supported": "^1.0.1", 92 | "feathers": "^2.0.0", 93 | "feathers-errors": "^2.5.0", 94 | "feathers-memory": "^1.0.0", 95 | "feathers-mongoose": "^6.0.0", 96 | "feathers-rest": "^1.2.2", 97 | "istanbul": "^1.1.0-alpha.1", 98 | "mocha": "^4.0.0", 99 | "mongoose": "^4.5.9", 100 | "rimraf": "^2.5.2", 101 | "semistandard": "^11.0.0" 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/commons.js: -------------------------------------------------------------------------------- 1 | import { each, hooks as utils } from 'feathers-commons'; 2 | 3 | export function isHookObject (hookObject) { 4 | return typeof hookObject === 'object' && 5 | typeof hookObject.method === 'string' && 6 | typeof hookObject.type === 'string'; 7 | } 8 | 9 | export function processHooks (hooks, initialHookObject) { 10 | let hookObject = initialHookObject; 11 | let updateCurrentHook = current => { 12 | if (current) { 13 | if (!isHookObject(current)) { 14 | throw new Error(`${hookObject.type} hook for '${hookObject.method}' method returned invalid hook object`); 15 | } 16 | 17 | hookObject = current; 18 | } 19 | 20 | return hookObject; 21 | }; 22 | let promise = Promise.resolve(hookObject); 23 | 24 | // Go through all hooks and chain them into our promise 25 | hooks.forEach(fn => { 26 | const hook = fn.bind(this); 27 | 28 | if (hook.length === 2) { // function(hook, next) 29 | promise = promise.then(hookObject => { 30 | return new Promise((resolve, reject) => { 31 | hook(hookObject, (error, result) => 32 | error ? reject(error) : resolve(result)); 33 | }); 34 | }); 35 | } else { // function(hook) 36 | promise = promise.then(hook); 37 | } 38 | 39 | // Use the returned hook object or the old one 40 | promise = promise.then(updateCurrentHook); 41 | }); 42 | 43 | return promise.catch(error => { 44 | // Add the hook information to any errors 45 | error.hook = hookObject; 46 | throw error; 47 | }); 48 | } 49 | 50 | export function addHookTypes (target, types = ['before', 'after', 'error']) { 51 | Object.defineProperty(target, '__hooks', { 52 | value: {} 53 | }); 54 | 55 | types.forEach(type => { 56 | // Initialize properties where hook functions are stored 57 | target.__hooks[type] = {}; 58 | }); 59 | } 60 | 61 | export function getHooks (app, service, type, method, appLast = false) { 62 | const appHooks = app.__hooks[type][method] || []; 63 | const serviceHooks = service.__hooks[type][method] || []; 64 | 65 | if (appLast) { 66 | return serviceHooks.concat(appHooks); 67 | } 68 | 69 | return appHooks.concat(serviceHooks); 70 | } 71 | 72 | export function baseMixin (methods, ...objs) { 73 | const mixin = { 74 | hooks (allHooks) { 75 | each(allHooks, (obj, type) => { 76 | if (!this.__hooks[type]) { 77 | throw new Error(`'${type}' is not a valid hook type`); 78 | } 79 | 80 | const hooks = utils.convertHookData(obj); 81 | 82 | each(hooks, (value, method) => { 83 | if (method !== 'all' && methods.indexOf(method) === -1) { 84 | throw new Error(`'${method}' is not a valid hook method`); 85 | } 86 | }); 87 | 88 | methods.forEach(method => { 89 | if (!(hooks[method] || hooks.all)) { 90 | return; 91 | } 92 | 93 | const myHooks = this.__hooks[type][method] || 94 | (this.__hooks[type][method] = []); 95 | 96 | if (hooks.all) { 97 | myHooks.push.apply(myHooks, hooks.all); 98 | } 99 | 100 | if (hooks[method]) { 101 | myHooks.push.apply(myHooks, hooks[method]); 102 | } 103 | }); 104 | }); 105 | 106 | return this; 107 | } 108 | }; 109 | 110 | return Object.assign(mixin, ...objs); 111 | } 112 | -------------------------------------------------------------------------------- /src/hooks.js: -------------------------------------------------------------------------------- 1 | import Proto from 'uberproto'; 2 | import { hooks as utils } from 'feathers-commons'; 3 | import { addHookTypes, processHooks, baseMixin, getHooks } from './commons'; 4 | 5 | function isPromise (result) { 6 | return typeof result !== 'undefined' && 7 | typeof result.then === 'function'; 8 | } 9 | 10 | function hookMixin (service) { 11 | if (typeof service.hooks === 'function') { 12 | return; 13 | } 14 | 15 | const app = this; 16 | const methods = app.methods; 17 | const old = { 18 | before: service.before, 19 | after: service.after 20 | }; 21 | const mixin = baseMixin(methods, { 22 | before (before) { 23 | return this.hooks({ before }); 24 | }, 25 | 26 | after (after) { 27 | return this.hooks({ after }); 28 | } 29 | }); 30 | 31 | addHookTypes(service); 32 | 33 | methods.forEach(method => { 34 | if (typeof service[method] !== 'function') { 35 | return; 36 | } 37 | 38 | mixin[method] = function () { 39 | const service = this; 40 | // A reference to the original method 41 | const _super = this._super.bind(this); 42 | // Additional data to add to the hook object 43 | const hookData = { 44 | app, 45 | service, 46 | get path () { 47 | return Object.keys(app.services) 48 | .find(path => app.services[path] === service); 49 | } 50 | }; 51 | // Create the hook object that gets passed through 52 | const hookObject = utils.hookObject(method, 'before', arguments, hookData); 53 | // Get all hooks 54 | const hooks = { 55 | // For before hooks the app hooks will run first 56 | before: getHooks(app, this, 'before', method), 57 | // For after and error hooks the app hooks will run last 58 | after: getHooks(app, this, 'after', method, true), 59 | error: getHooks(app, this, 'error', method, true) 60 | }; 61 | 62 | // Process all before hooks 63 | return processHooks.call(this, hooks.before, hookObject) 64 | // Use the hook object to call the original method 65 | .then(hookObject => { 66 | if (typeof hookObject.result !== 'undefined') { 67 | return Promise.resolve(hookObject); 68 | } 69 | 70 | return new Promise((resolve, reject) => { 71 | const args = utils.makeArguments(hookObject); 72 | // The method may not be normalized yet so we have to handle both 73 | // ways, either by callback or by Promise 74 | const callback = function (error, result) { 75 | if (error) { 76 | reject(error); 77 | } else { 78 | hookObject.result = result; 79 | resolve(hookObject); 80 | } 81 | }; 82 | 83 | // We replace the callback with resolving the promise 84 | args.splice(args.length - 1, 1, callback); 85 | 86 | const result = _super(...args); 87 | 88 | if (isPromise(result)) { 89 | result.then(data => callback(null, data), callback); 90 | } 91 | }); 92 | }) 93 | // Make a copy of hookObject from `before` hooks and update type 94 | .then(hookObject => Object.assign({}, hookObject, { type: 'after' })) 95 | // Run through all `after` hooks 96 | .then(processHooks.bind(this, hooks.after)) 97 | // Finally, return the result 98 | .then(hookObject => hookObject.result) 99 | // Handle errors 100 | .catch(error => { 101 | const errorHook = Object.assign({}, error.hook || hookObject, { 102 | type: 'error', 103 | result: null, 104 | original: error.hook, 105 | error 106 | }); 107 | 108 | return processHooks 109 | .call(this, hooks.error, errorHook) 110 | .then(hook => hook.result || Promise.reject(hook.error)); 111 | }); 112 | }; 113 | }); 114 | 115 | service.mixin(mixin); 116 | 117 | // Before hooks that were registered in the service 118 | if (old.before) { 119 | service.before(old.before); 120 | } 121 | 122 | // After hooks that were registered in the service 123 | if (old.after) { 124 | service.after(old.after); 125 | } 126 | } 127 | 128 | function configure () { 129 | return function () { 130 | const app = this; 131 | 132 | if (app.version && app.version >= '3.0.0') { 133 | throw new Error(`You are using Feathers v${app.version} which already includes feathers-hooks. You can remove this module from your application.`); 134 | } 135 | 136 | addHookTypes(app); 137 | 138 | Proto.mixin(baseMixin(app.methods), app); 139 | 140 | this.mixins.unshift(hookMixin); 141 | }; 142 | } 143 | 144 | export default configure; 145 | -------------------------------------------------------------------------------- /test/after.test.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable handle-callback-err */ 2 | import assert from 'assert'; 3 | import feathers from 'feathers'; 4 | 5 | import hooks from '../src/hooks'; 6 | 7 | describe('.after hooks', () => { 8 | describe('function(hook)', () => { 9 | it('returning a non-hook object throws error', () => { 10 | const app = feathers().configure(hooks()).use('/dummy', { 11 | get (id) { 12 | return Promise.resolve({ id }); 13 | }, 14 | 15 | after: { 16 | get () { 17 | return {}; 18 | } 19 | } 20 | }); 21 | const service = app.service('dummy'); 22 | 23 | return service.get(10).catch(e => { 24 | assert.equal(e.message, 'after hook for \'get\' method returned invalid hook object'); 25 | }); 26 | }); 27 | 28 | it('.after hooks can return a promise', done => { 29 | const app = feathers().configure(hooks()).use('/dummy', { 30 | get (id) { 31 | return Promise.resolve({ 32 | id: id, 33 | description: `You have to do ${id}` 34 | }); 35 | }, 36 | 37 | find () { 38 | return Promise.resolve([]); 39 | } 40 | }); 41 | const service = app.service('dummy'); 42 | 43 | service.after({ 44 | get (hook) { 45 | hook.result.ran = true; 46 | return Promise.resolve(hook); 47 | }, 48 | 49 | find () { 50 | return Promise.reject(new Error('You can not see this')); 51 | } 52 | }); 53 | 54 | service.get('laundry', {}, (error, data) => { 55 | assert.deepEqual(data, { 56 | id: 'laundry', 57 | description: 'You have to do laundry', 58 | ran: true 59 | }); 60 | service.find({}, error => { 61 | assert.equal(error.message, 'You can not see this'); 62 | done(); 63 | }); 64 | }); 65 | }); 66 | 67 | it('.after hooks do not need to return anything', () => { 68 | const app = feathers().configure(hooks()).use('/dummy', { 69 | get (id) { 70 | return Promise.resolve({ 71 | id, description: `You have to do ${id}` 72 | }); 73 | }, 74 | 75 | find () { 76 | return Promise.resolve([]); 77 | } 78 | }); 79 | const service = app.service('dummy'); 80 | 81 | service.after({ 82 | get (hook) { 83 | hook.result.ran = true; 84 | }, 85 | 86 | find () { 87 | throw new Error('You can not see this'); 88 | } 89 | }); 90 | 91 | return service.get('laundry').then(data => { 92 | assert.deepEqual(data, { 93 | id: 'laundry', 94 | description: 'You have to do laundry', 95 | ran: true 96 | }); 97 | 98 | return service.find().catch(error => { 99 | assert.equal(error.message, 'You can not see this'); 100 | }); 101 | }); 102 | }); 103 | }); 104 | 105 | describe('function(hook, next)', () => { 106 | it('gets mixed into a service and modifies data', done => { 107 | const dummyService = { 108 | after: { 109 | create (hook, next) { 110 | assert.equal(hook.type, 'after'); 111 | 112 | hook.result.some = 'thing'; 113 | 114 | next(null, hook); 115 | } 116 | }, 117 | 118 | create (data, params, callback) { 119 | callback(null, data); 120 | } 121 | }; 122 | 123 | const app = feathers().configure(hooks()).use('/dummy', dummyService); 124 | const service = app.service('dummy'); 125 | 126 | service.create({ my: 'data' }, {}, (error, data) => { 127 | assert.deepEqual({ my: 'data', some: 'thing' }, data, 'Got modified data'); 128 | done(); 129 | }); 130 | }); 131 | 132 | it('also makes the app available at hook.app', done => { 133 | const dummyService = { 134 | after: { 135 | create (hook, next) { 136 | hook.result.appPresent = typeof hook.app === 'function'; 137 | assert.equal(hook.result.appPresent, true); 138 | 139 | next(null, hook); 140 | } 141 | }, 142 | 143 | create (data, params, callback) { 144 | callback(null, data); 145 | } 146 | }; 147 | 148 | const app = feathers().configure(hooks()).use('/dummy', dummyService); 149 | const service = app.service('dummy'); 150 | 151 | service.create({ my: 'data' }, {}, (error, data) => { 152 | assert.deepEqual({ my: 'data', appPresent: true }, data, 'The app was present in the hook.'); 153 | done(); 154 | }); 155 | }); 156 | 157 | it('returns errors', done => { 158 | const dummyService = { 159 | after: { 160 | update (hook, next) { 161 | next(new Error('This did not work')); 162 | } 163 | }, 164 | 165 | update (id, data, params, callback) { 166 | callback(null, data); 167 | } 168 | }; 169 | 170 | const app = feathers().configure(hooks()).use('/dummy', dummyService); 171 | const service = app.service('dummy'); 172 | 173 | service.update(1, { my: 'data' }, {}, error => { 174 | assert.ok(error, 'Got an error'); 175 | assert.equal(error.message, 'This did not work', 'Got expected error message from hook'); 176 | done(); 177 | }); 178 | }); 179 | 180 | it('does not run after hook when there is an error', done => { 181 | const dummyService = { 182 | after: { 183 | remove () { 184 | assert.ok(false, 'This should never get called'); 185 | } 186 | }, 187 | 188 | remove (id, params, callback) { 189 | callback(new Error('Error removing item')); 190 | } 191 | }; 192 | 193 | const app = feathers().configure(hooks()).use('/dummy', dummyService); 194 | const service = app.service('dummy'); 195 | 196 | service.remove(1, {}, error => { 197 | assert.ok(error, 'Got error'); 198 | assert.equal(error.message, 'Error removing item', 'Got error message from service'); 199 | done(); 200 | }); 201 | }); 202 | 203 | it('adds .after() and chains multiple hooks for the same method', done => { 204 | const dummyService = { 205 | create (data, params, callback) { 206 | callback(null, data); 207 | } 208 | }; 209 | 210 | const app = feathers().configure(hooks()).use('/dummy', dummyService); 211 | const service = app.service('dummy'); 212 | 213 | service.after({ 214 | create (hook, next) { 215 | hook.result.some = 'thing'; 216 | next(); 217 | } 218 | }); 219 | 220 | service.after({ 221 | create (hook, next) { 222 | hook.result.other = 'stuff'; 223 | 224 | next(); 225 | } 226 | }); 227 | 228 | service.create({ my: 'data' }, {}, (error, data) => { 229 | assert.deepEqual({ 230 | my: 'data', 231 | some: 'thing', 232 | other: 'stuff' 233 | }, data, 'Got modified data'); 234 | done(); 235 | }); 236 | }); 237 | 238 | it('chains multiple after hooks using array syntax', done => { 239 | const dummyService = { 240 | create (data, params, callback) { 241 | callback(null, data); 242 | } 243 | }; 244 | 245 | const app = feathers().configure(hooks()).use('/dummy', dummyService); 246 | const service = app.service('dummy'); 247 | 248 | service.after({ 249 | create: [ 250 | function (hook, next) { 251 | hook.result.some = 'thing'; 252 | next(); 253 | }, 254 | function (hook, next) { 255 | hook.result.other = 'stuff'; 256 | 257 | next(); 258 | } 259 | ] 260 | }); 261 | 262 | service.create({ my: 'data' }, {}, (error, data) => { 263 | assert.deepEqual({ 264 | my: 'data', 265 | some: 'thing', 266 | other: 'stuff' 267 | }, data, 'Got modified data'); 268 | done(); 269 | }); 270 | }); 271 | 272 | it('.after hooks run in the correct order (#13)', done => { 273 | const app = feathers().configure(hooks()).use('/dummy', { 274 | get (id, params, callback) { 275 | callback(null, { id }); 276 | } 277 | }); 278 | 279 | const service = app.service('dummy'); 280 | 281 | service.after({ 282 | get (hook, next) { 283 | hook.result.items = ['first']; 284 | next(); 285 | } 286 | }); 287 | 288 | service.after({ 289 | get: [ 290 | function (hook, next) { 291 | hook.result.items.push('second'); 292 | next(); 293 | }, 294 | function (hook, next) { 295 | hook.result.items.push('third'); 296 | next(); 297 | } 298 | ] 299 | }); 300 | 301 | service.get(10, {}, (error, data) => { 302 | assert.deepEqual(data.items, ['first', 'second', 'third']); 303 | done(error); 304 | }); 305 | }); 306 | 307 | it('after all hooks (#11)', done => { 308 | const app = feathers().configure(hooks()).use('/dummy', { 309 | after: { 310 | all (hook, next) { 311 | hook.result.afterAllObject = true; 312 | next(); 313 | } 314 | }, 315 | 316 | get (id, params, callback) { 317 | callback(null, { 318 | id: id, 319 | items: [] 320 | }); 321 | }, 322 | 323 | find (params, callback) { 324 | callback(null, []); 325 | } 326 | }); 327 | 328 | const service = app.service('dummy'); 329 | 330 | service.after([ 331 | function (hook, next) { 332 | hook.result.afterAllMethodArray = true; 333 | next(); 334 | } 335 | ]); 336 | 337 | service.find({}, (error, data) => { 338 | assert.ok(data.afterAllObject); 339 | assert.ok(data.afterAllMethodArray); 340 | 341 | service.get(1, {}, (error, data) => { 342 | assert.ok(data.afterAllObject); 343 | assert.ok(data.afterAllMethodArray); 344 | done(); 345 | }); 346 | }); 347 | }); 348 | 349 | it('after hooks have service as context and keep it in service method (#17)', done => { 350 | const app = feathers().configure(hooks()).use('/dummy', { 351 | number: 42, 352 | get (id, params, callback) { 353 | callback(null, { 354 | id: id, 355 | number: this.number 356 | }); 357 | } 358 | }); 359 | 360 | const service = app.service('dummy'); 361 | 362 | service.after({ 363 | get (hook, next) { 364 | hook.result.test = this.number + 1; 365 | next(); 366 | } 367 | }); 368 | 369 | service.get(10, {}, (error, data) => { 370 | assert.deepEqual(data, { 371 | id: 10, 372 | number: 42, 373 | test: 43 374 | }); 375 | done(); 376 | }); 377 | }); 378 | 379 | it('can not call next() multiple times', () => { 380 | const app = feathers().configure(hooks()).use('/dummy', { 381 | get (id, params, callback) { 382 | callback(null, { id }); 383 | } 384 | }); 385 | 386 | const service = app.service('dummy'); 387 | 388 | service.after({ 389 | get: [ 390 | function (hook, next) { 391 | next(); 392 | }, 393 | 394 | function (hook, next) { 395 | next(); 396 | next(); 397 | } 398 | ] 399 | }); 400 | 401 | try { 402 | service.get(10); 403 | } catch (e) { 404 | assert.deepEqual(e.message, `next() called multiple times for hook on 'get' method`); 405 | } 406 | }); 407 | }); 408 | }); 409 | -------------------------------------------------------------------------------- /test/app.test.js: -------------------------------------------------------------------------------- 1 | import assert from 'assert'; 2 | import feathers from 'feathers'; 3 | 4 | import hooks from '../src/hooks'; 5 | 6 | describe('app.hooks', () => { 7 | let app; 8 | 9 | beforeEach(() => { 10 | app = feathers() 11 | .configure(hooks()) 12 | .use('/todos', { 13 | get (id, params) { 14 | if (id === 'error') { 15 | return Promise.reject(new Error('Something went wrong')); 16 | } 17 | 18 | return Promise.resolve({ id, params }); 19 | }, 20 | 21 | create (data, params) { 22 | return Promise.resolve({ data, params }); 23 | } 24 | }); 25 | }); 26 | 27 | it('app has the .hooks method', () => { 28 | assert.equal(typeof app.hooks, 'function'); 29 | }); 30 | 31 | describe('app.hooks({ before })', () => { 32 | it('basic app before hook', () => { 33 | const service = app.service('todos'); 34 | 35 | app.hooks({ 36 | before (hook) { 37 | assert.equal(hook.app, app); 38 | hook.params.ran = true; 39 | } 40 | }); 41 | 42 | return service.get('test').then(result => { 43 | assert.deepEqual(result, { 44 | id: 'test', 45 | params: { ran: true } 46 | }); 47 | 48 | const data = { test: 'hi' }; 49 | 50 | return service.create(data).then(result => { 51 | assert.deepEqual(result, { 52 | data, params: { ran: true } 53 | }); 54 | }); 55 | }); 56 | }); 57 | 58 | it('app before hooks always run first', () => { 59 | app.service('todos').hooks({ 60 | before (hook) { 61 | assert.equal(hook.app, app); 62 | hook.params.order.push('service.before'); 63 | } 64 | }); 65 | 66 | app.service('todos').hooks({ 67 | before (hook) { 68 | assert.equal(hook.app, app); 69 | hook.params.order.push('service.before 1'); 70 | } 71 | }); 72 | 73 | app.hooks({ 74 | before (hook) { 75 | assert.equal(hook.app, app); 76 | hook.params.order = []; 77 | hook.params.order.push('app.before'); 78 | } 79 | }); 80 | 81 | return app.service('todos').get('test').then(result => { 82 | assert.deepEqual(result, { 83 | id: 'test', 84 | params: { 85 | order: [ 'app.before', 'service.before', 'service.before 1' ] 86 | } 87 | }); 88 | }); 89 | }); 90 | }); 91 | 92 | describe('app.hooks({ after })', () => { 93 | it('basic app after hook', () => { 94 | app.hooks({ 95 | after (hook) { 96 | assert.equal(hook.app, app); 97 | hook.result.ran = true; 98 | } 99 | }); 100 | 101 | return app.service('todos').get('test').then(result => { 102 | assert.deepEqual(result, { 103 | id: 'test', 104 | params: {}, 105 | ran: true 106 | }); 107 | }); 108 | }); 109 | 110 | it('app after hooks always run last', () => { 111 | app.hooks({ 112 | after (hook) { 113 | assert.equal(hook.app, app); 114 | hook.result.order.push('app.after'); 115 | } 116 | }); 117 | 118 | app.service('todos').hooks({ 119 | after (hook) { 120 | assert.equal(hook.app, app); 121 | hook.result.order = []; 122 | hook.result.order.push('service.after'); 123 | } 124 | }); 125 | 126 | app.service('todos').hooks({ 127 | after (hook) { 128 | assert.equal(hook.app, app); 129 | hook.result.order.push('service.after 1'); 130 | } 131 | }); 132 | 133 | return app.service('todos').get('test').then(result => { 134 | assert.deepEqual(result, { 135 | id: 'test', 136 | params: {}, 137 | order: [ 'service.after', 'service.after 1', 'app.after' ] 138 | }); 139 | }); 140 | }); 141 | }); 142 | 143 | describe('app.hooks({ error })', () => { 144 | it('basic app error hook', () => { 145 | app.hooks({ 146 | error (hook) { 147 | assert.equal(hook.app, app); 148 | hook.error = new Error('App hook ran'); 149 | } 150 | }); 151 | 152 | return app.service('todos').get('error').catch(error => { 153 | assert.equal(error.message, 'App hook ran'); 154 | }); 155 | }); 156 | 157 | it('app error hooks always run last', () => { 158 | app.hooks({ 159 | error (hook) { 160 | assert.equal(hook.app, app); 161 | hook.error = new Error(`${hook.error.message} app.after`); 162 | } 163 | }); 164 | 165 | app.service('todos').hooks({ 166 | error (hook) { 167 | assert.equal(hook.app, app); 168 | hook.error = new Error(`${hook.error.message} service.after`); 169 | } 170 | }); 171 | 172 | app.service('todos').hooks({ 173 | error (hook) { 174 | assert.equal(hook.app, app); 175 | hook.error = new Error(`${hook.error.message} service.after 1`); 176 | } 177 | }); 178 | 179 | return app.service('todos').get('error').catch(error => { 180 | assert.equal(error.message, 'Something went wrong service.after service.after 1 app.after'); 181 | }); 182 | }); 183 | }); 184 | }); 185 | -------------------------------------------------------------------------------- /test/before.test.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable handle-callback-err */ 2 | import assert from 'assert'; 3 | import feathers from 'feathers'; 4 | 5 | import hooks from '../src/hooks'; 6 | 7 | describe('.before hooks', () => { 8 | describe('function([hook])', () => { 9 | it('returning a non-hook object throws error', () => { 10 | const app = feathers().configure(hooks()).use('/dummy', { 11 | get (id) { 12 | return Promise.resolve({ id }); 13 | }, 14 | 15 | before: { 16 | get () { 17 | return {}; 18 | } 19 | } 20 | }); 21 | const service = app.service('dummy'); 22 | 23 | return service.get(10).catch(e => { 24 | assert.equal(e.message, 'before hook for \'get\' method returned invalid hook object'); 25 | }); 26 | }); 27 | 28 | it('hooks in chain can be replaced', () => { 29 | const app = feathers().configure(hooks()).use('/dummy', { 30 | get (id) { 31 | return Promise.resolve({ 32 | id, description: `You have to do ${id}` 33 | }); 34 | } 35 | }); 36 | 37 | const service = app.service('dummy').before({ 38 | get: [ 39 | function (hook) { 40 | return Object.assign({}, hook, { 41 | modified: true 42 | }); 43 | }, 44 | function (hook) { 45 | assert.ok(hook.modified); 46 | } 47 | ] 48 | }); 49 | 50 | return service.get('laundry'); 51 | }); 52 | 53 | it('.before hooks can return a promise', () => { 54 | const app = feathers().configure(hooks()).use('/dummy', { 55 | get (id, params) { 56 | assert.ok(params.ran, 'Ran through promise hook'); 57 | 58 | return Promise.resolve({ 59 | id: id, 60 | description: `You have to do ${id}` 61 | }); 62 | }, 63 | 64 | remove () { 65 | assert.ok(false, 'Should never get here'); 66 | } 67 | }); 68 | 69 | const service = app.service('dummy').before({ 70 | get: function (hook) { 71 | return new Promise(resolve => { 72 | hook.params.ran = true; 73 | resolve(); 74 | }); 75 | }, 76 | 77 | remove () { 78 | return new Promise((resolve, reject) => { 79 | reject(new Error('This did not work')); 80 | }); 81 | } 82 | }); 83 | 84 | return service.get('dishes').then(() => service.remove(10)) 85 | .catch(error => { 86 | assert.equal(error.message, 'This did not work'); 87 | }); 88 | }); 89 | 90 | it('.before hooks do not need to return anything', () => { 91 | const app = feathers().configure(hooks()).use('/dummy', { 92 | get (id, params) { 93 | assert.ok(params.ran, 'Ran through promise hook'); 94 | 95 | return Promise.resolve({ 96 | id: id, 97 | description: `You have to do ${id}` 98 | }); 99 | }, 100 | 101 | remove () { 102 | assert.ok(false, 'Should never get here'); 103 | } 104 | }); 105 | 106 | const service = app.service('dummy').before({ 107 | get: function (hook) { 108 | hook.params.ran = true; 109 | }, 110 | 111 | remove () { 112 | throw new Error('This did not work'); 113 | } 114 | }); 115 | 116 | return service.get('dishes').then(() => service.remove(10)) 117 | .catch(error => { 118 | assert.equal(error.message, 'This did not work'); 119 | }); 120 | }); 121 | 122 | it('.before hooks can set hook.result which will skip service method', () => { 123 | const app = feathers().configure(hooks()).use('/dummy', { 124 | get (id) { 125 | assert.ok(false, 'This should never run'); 126 | return Promise.resolve({ id }); 127 | } 128 | }); 129 | 130 | const service = app.service('dummy'); 131 | 132 | service.before({ 133 | get (hook) { 134 | hook.result = { 135 | id: hook.id, 136 | message: 'Set from hook' 137 | }; 138 | } 139 | }); 140 | 141 | return service.get(10, {}).then(data => { 142 | assert.deepEqual(data, { 143 | id: 10, 144 | message: 'Set from hook' 145 | }); 146 | }); 147 | }); 148 | }); 149 | 150 | describe('function(hook, next)', () => { 151 | it('gets mixed into a service and modifies data', done => { 152 | const dummyService = { 153 | before: { 154 | create (hook, next) { 155 | assert.equal(hook.type, 'before'); 156 | 157 | hook.data.modified = 'data'; 158 | 159 | Object.assign(hook.params, { 160 | modified: 'params' 161 | }); 162 | 163 | next(null, hook); 164 | } 165 | }, 166 | 167 | create (data, params, callback) { 168 | assert.deepEqual(data, { 169 | some: 'thing', 170 | modified: 'data' 171 | }, 'Data modified'); 172 | 173 | assert.deepEqual(params, { 174 | modified: 'params' 175 | }, 'Params modified'); 176 | 177 | callback(null, data); 178 | } 179 | }; 180 | 181 | const app = feathers().configure(hooks()).use('/dummy', dummyService); 182 | const service = app.service('dummy'); 183 | 184 | service.create({ some: 'thing' }, {}, (error, data) => { 185 | assert.ok(!error, 'No error'); 186 | 187 | assert.deepEqual(data, { 188 | some: 'thing', 189 | modified: 'data' 190 | }, 'Data got modified'); 191 | 192 | done(); 193 | }); 194 | }); 195 | 196 | it('contains the app object at hook.app', done => { 197 | const someServiceConfig = { 198 | before: { 199 | create (hook, next) { 200 | hook.data.appPresent = typeof hook.app === 'function'; 201 | assert.equal(hook.data.appPresent, true); 202 | next(null, hook); 203 | } 204 | }, 205 | 206 | create (data, params, callback) { 207 | callback(null, data); 208 | } 209 | }; 210 | 211 | const app = feathers().configure(hooks()).use('/some-service', someServiceConfig); 212 | const someService = app.service('some-service'); 213 | 214 | someService.create({ some: 'thing' }, {}, (error, data) => { 215 | assert.deepEqual(data, { 216 | some: 'thing', 217 | appPresent: true 218 | }, 'App object was present'); 219 | 220 | done(); 221 | }); 222 | }); 223 | 224 | it('passes errors', done => { 225 | const dummyService = { 226 | before: { 227 | update (hook, next) { 228 | next(new Error('You are not allowed to update')); 229 | } 230 | }, 231 | 232 | update () { 233 | assert.ok(false, 'Never should be called'); 234 | } 235 | }; 236 | 237 | const app = feathers().configure(hooks()).use('/dummy', dummyService); 238 | const service = app.service('dummy'); 239 | 240 | service.update(1, {}, {}, error => { 241 | assert.ok(error, 'Got an error'); 242 | assert.equal(error.message, 'You are not allowed to update', 'Got error message'); 243 | done(); 244 | }); 245 | }); 246 | 247 | it('calling back with no arguments uses the old ones', done => { 248 | const dummyService = { 249 | before: { 250 | remove (hook, next) { 251 | next(); 252 | } 253 | }, 254 | 255 | remove (id, params, callback) { 256 | assert.equal(id, 1, 'Got id'); 257 | assert.deepEqual(params, { my: 'param' }); 258 | callback(); 259 | } 260 | }; 261 | 262 | const app = feathers().configure(hooks()).use('/dummy', dummyService); 263 | const service = app.service('dummy'); 264 | 265 | service.remove(1, { my: 'param' }, done); 266 | }); 267 | 268 | it('adds .before() and chains multiple hooks for the same method', done => { 269 | const dummyService = { 270 | create (data, params, callback) { 271 | assert.deepEqual(data, { 272 | some: 'thing', 273 | modified: 'second data' 274 | }, 'Data modified'); 275 | 276 | assert.deepEqual(params, { 277 | modified: 'params' 278 | }, 'Params modified'); 279 | 280 | callback(null, data); 281 | } 282 | }; 283 | 284 | const app = feathers().configure(hooks()).use('/dummy', dummyService); 285 | const service = app.service('dummy'); 286 | 287 | service.before({ 288 | create (hook, next) { 289 | hook.params.modified = 'params'; 290 | 291 | next(); 292 | } 293 | }); 294 | 295 | service.before({ 296 | create (hook, next) { 297 | hook.data.modified = 'second data'; 298 | 299 | next(); 300 | } 301 | }); 302 | 303 | service.create({ some: 'thing' }, {}, error => { 304 | assert.ok(!error, 'No error'); 305 | done(); 306 | }); 307 | }); 308 | 309 | it('chains multiple before hooks using array syntax', done => { 310 | const dummyService = { 311 | create (data, params, callback) { 312 | assert.deepEqual(data, { 313 | some: 'thing', 314 | modified: 'second data' 315 | }, 'Data modified'); 316 | 317 | assert.deepEqual(params, { 318 | modified: 'params' 319 | }, 'Params modified'); 320 | 321 | callback(null, data); 322 | } 323 | }; 324 | 325 | const app = feathers().configure(hooks()).use('/dummy', dummyService); 326 | const service = app.service('dummy'); 327 | 328 | service.before({ 329 | create: [ 330 | function (hook, next) { 331 | hook.params.modified = 'params'; 332 | 333 | next(); 334 | }, 335 | function (hook, next) { 336 | hook.data.modified = 'second data'; 337 | 338 | next(); 339 | } 340 | ] 341 | }); 342 | 343 | service.create({ some: 'thing' }, {}, error => { 344 | assert.ok(!error, 'No error'); 345 | done(); 346 | }); 347 | }); 348 | 349 | it('.before hooks run in the correct order (#13)', done => { 350 | const app = feathers().configure(hooks()).use('/dummy', { 351 | get (id, params, callback) { 352 | assert.deepEqual(params.items, ['first', 'second', 'third']); 353 | callback(null, { 354 | id: id, 355 | items: [] 356 | }); 357 | } 358 | }); 359 | 360 | const service = app.service('dummy'); 361 | 362 | service.before({ 363 | get (hook, next) { 364 | hook.params.items = ['first']; 365 | next(); 366 | } 367 | }); 368 | 369 | service.before({ 370 | get: [ 371 | function (hook, next) { 372 | hook.params.items.push('second'); 373 | next(); 374 | }, 375 | function (hook, next) { 376 | hook.params.items.push('third'); 377 | next(); 378 | } 379 | ] 380 | }); 381 | 382 | service.get(10, {}, done); 383 | }); 384 | 385 | it('before all hooks (#11)', done => { 386 | const app = feathers().configure(hooks()).use('/dummy', { 387 | before: { 388 | all (hook, next) { 389 | hook.params.beforeAllObject = true; 390 | next(); 391 | } 392 | }, 393 | 394 | get (id, params, callback) { 395 | assert.ok(params.beforeAllObject); 396 | assert.ok(params.beforeAllMethodArray); 397 | callback(null, { 398 | id: id, 399 | items: [] 400 | }); 401 | }, 402 | 403 | find (params, callback) { 404 | assert.ok(params.beforeAllObject); 405 | assert.ok(params.beforeAllMethodArray); 406 | callback(null, []); 407 | } 408 | }); 409 | 410 | const service = app.service('dummy'); 411 | 412 | service.before([ 413 | function (hook, next) { 414 | hook.params.beforeAllMethodArray = true; 415 | next(); 416 | } 417 | ]); 418 | 419 | service.find({}, () => { 420 | service.get(1, {}, done); 421 | }); 422 | }); 423 | 424 | it('before hooks have service as context and keep it in service method (#17)', done => { 425 | const app = feathers().configure(hooks()).use('/dummy', { 426 | number: 42, 427 | get (id, params, callback) { 428 | callback(null, { 429 | id: id, 430 | number: this.number, 431 | test: params.test 432 | }); 433 | } 434 | }); 435 | 436 | const service = app.service('dummy'); 437 | 438 | service.before({ 439 | get (hook, next) { 440 | hook.params.test = this.number + 2; 441 | next(); 442 | } 443 | }); 444 | 445 | service.get(10, {}, (error, data) => { 446 | assert.deepEqual(data, { 447 | id: 10, 448 | number: 42, 449 | test: 44 450 | }); 451 | done(); 452 | }); 453 | }); 454 | 455 | it('calling next() multiple times does not do anything', () => { 456 | const app = feathers().configure(hooks()).use('/dummy', { 457 | get (id, params, callback) { 458 | callback(null, { id }); 459 | } 460 | }); 461 | 462 | const service = app.service('dummy'); 463 | 464 | service.before({ 465 | get: [ 466 | function (hook, next) { 467 | next(); 468 | }, 469 | 470 | function (hook, next) { 471 | next(); 472 | next(); 473 | } 474 | ] 475 | }); 476 | 477 | return service.get(10).then(data => { 478 | assert.deepEqual(data, { id: 10 }); 479 | }); 480 | }); 481 | }); 482 | }); 483 | -------------------------------------------------------------------------------- /test/error.test.js: -------------------------------------------------------------------------------- 1 | import assert from 'assert'; 2 | import feathers from 'feathers'; 3 | 4 | import hooks from '../src/hooks'; 5 | 6 | describe('error hooks', () => { 7 | describe('on direct service method errors', () => { 8 | const errorMessage = 'Something else went wrong'; 9 | const app = feathers().configure(hooks()).use('/dummy', { 10 | get () { 11 | return Promise.reject(new Error('Something went wrong')); 12 | } 13 | }); 14 | const service = app.service('dummy'); 15 | 16 | afterEach(() => service.__hooks.error.get.pop()); 17 | 18 | it('basic error hook', done => { 19 | service.hooks({ 20 | error: { 21 | get (hook) { 22 | assert.equal(hook.type, 'error'); 23 | assert.equal(hook.id, 'test'); 24 | assert.equal(hook.method, 'get'); 25 | assert.equal(hook.app, app); 26 | assert.equal(hook.error.message, 'Something went wrong'); 27 | } 28 | } 29 | }); 30 | 31 | service.get('test').then(() => { 32 | done(new Error('Should never get here')); 33 | }).catch(() => done()); 34 | }); 35 | 36 | it('can change the error', () => { 37 | service.hooks({ 38 | error: { 39 | get (hook) { 40 | hook.error = new Error(errorMessage); 41 | } 42 | } 43 | }); 44 | 45 | return service.get('test').catch(error => { 46 | assert.equal(error.message, errorMessage); 47 | }); 48 | }); 49 | 50 | it('throwing an error', () => { 51 | service.hooks({ 52 | error: { 53 | get () { 54 | throw new Error(errorMessage); 55 | } 56 | } 57 | }); 58 | 59 | return service.get('test').catch(error => { 60 | assert.equal(error.message, errorMessage); 61 | }); 62 | }); 63 | 64 | it('rejecting a promise', () => { 65 | service.hooks({ 66 | error: { 67 | get () { 68 | return Promise.reject(new Error(errorMessage)); 69 | } 70 | } 71 | }); 72 | 73 | return service.get('test').catch(error => { 74 | assert.equal(error.message, errorMessage); 75 | }); 76 | }); 77 | 78 | it('calling `next` with error', () => { 79 | service.hooks({ 80 | error: { 81 | get (hook, next) { 82 | next(new Error(errorMessage)); 83 | } 84 | } 85 | }); 86 | 87 | return service.get('test').catch(error => { 88 | assert.equal(error.message, errorMessage); 89 | }); 90 | }); 91 | 92 | it('can chain multiple hooks', () => { 93 | service.hooks({ 94 | error: { 95 | get: [ 96 | function (hook) { 97 | hook.error = new Error(errorMessage); 98 | hook.error.first = true; 99 | }, 100 | 101 | function (hook) { 102 | hook.error.second = true; 103 | 104 | return Promise.resolve(hook); 105 | }, 106 | 107 | function (hook, next) { 108 | hook.error.third = true; 109 | 110 | next(); 111 | } 112 | ] 113 | } 114 | }); 115 | 116 | return service.get('test').catch(error => { 117 | assert.equal(error.message, errorMessage); 118 | assert.ok(error.first); 119 | assert.ok(error.second); 120 | assert.ok(error.third); 121 | }); 122 | }); 123 | 124 | it('setting `hook.result` will return result', () => { 125 | const data = { 126 | message: 'It worked' 127 | }; 128 | 129 | service.hooks({ 130 | error (hook) { 131 | hook.result = data; 132 | return Promise.resolve(hook); 133 | } 134 | }); 135 | 136 | return service.get(10) 137 | .then(result => assert.deepEqual(result, data)); 138 | }); 139 | }); 140 | 141 | describe('error in hooks', () => { 142 | const errorMessage = 'before hook broke'; 143 | 144 | let app, service; 145 | 146 | beforeEach(() => { 147 | app = feathers().configure(hooks()).use('/dummy', { 148 | get (id) { 149 | return Promise.resolve({ 150 | id, text: `You have to do ${id}` 151 | }); 152 | } 153 | }); 154 | 155 | service = app.service('dummy'); 156 | }); 157 | 158 | it('error in before hook', done => { 159 | service.before(function () { 160 | throw new Error(errorMessage); 161 | }).hooks({ 162 | error (hook) { 163 | assert.equal(hook.error.hook.type, 'before', 164 | 'Original hook still set' 165 | ); 166 | assert.equal(hook.id, 'dishes'); 167 | assert.equal(hook.error.message, errorMessage); 168 | done(); 169 | } 170 | }); 171 | 172 | service.get('dishes').then(done); 173 | }); 174 | 175 | it('error in after hook', done => { 176 | service.hooks({ 177 | after () { 178 | throw new Error(errorMessage); 179 | }, 180 | 181 | error (hook) { 182 | assert.equal(hook.error.hook.type, 'after', 183 | 'Original hook still set' 184 | ); 185 | assert.equal(hook.id, 'dishes'); 186 | assert.deepEqual(hook.original.result, { 187 | id: 'dishes', 188 | text: 'You have to do dishes' 189 | }); 190 | assert.equal(hook.error.message, errorMessage); 191 | done(); 192 | } 193 | }); 194 | 195 | service.get('dishes').then(done); 196 | }); 197 | 198 | it('uses the current hook object if thrown in a hook and sets hook.original', done => { 199 | service.hooks({ 200 | after (hook) { 201 | hook.modified = true; 202 | 203 | throw new Error(errorMessage); 204 | }, 205 | 206 | error (hook) { 207 | assert.ok(hook.modified); 208 | assert.equal(hook.original.type, 'after'); 209 | 210 | done(); 211 | } 212 | }); 213 | 214 | service.get('laundry').then(done); 215 | }); 216 | }); 217 | }); 218 | -------------------------------------------------------------------------------- /test/hooks.test.js: -------------------------------------------------------------------------------- 1 | import assert from 'assert'; 2 | import feathers from 'feathers'; 3 | 4 | import hooks from '../src/hooks'; 5 | 6 | describe('feathers-hooks', () => { 7 | it('always turns service call into a promise (#28)', () => { 8 | const app = feathers().configure(hooks()).use('/dummy', { 9 | get (id, params, callback) { 10 | callback(null, { id }); 11 | } 12 | }); 13 | 14 | const service = app.service('dummy'); 15 | 16 | return service.get(10).then(data => { 17 | assert.deepEqual(data, { id: 10 }); 18 | }); 19 | }); 20 | 21 | it('works with services that return a promise (#28)', () => { 22 | const app = feathers().configure(hooks()).use('/dummy', { 23 | get (id, params) { 24 | return Promise.resolve({ id, user: params.user }); 25 | } 26 | }); 27 | 28 | const service = app.service('dummy'); 29 | 30 | service.before({ 31 | get (hook) { 32 | hook.params.user = 'David'; 33 | } 34 | }).after({ 35 | get (hook) { 36 | hook.result.after = true; 37 | } 38 | }); 39 | 40 | return service.get(10).then(data => { 41 | assert.deepEqual(data, { id: 10, user: 'David', after: true }); 42 | }); 43 | }); 44 | 45 | it('dispatches events with data modified by hook', done => { 46 | const app = feathers().configure(hooks()).use('/dummy', { 47 | create (data) { 48 | return Promise.resolve(data); 49 | } 50 | }); 51 | 52 | const service = app.service('dummy'); 53 | 54 | service.before({ 55 | create (hook) { 56 | hook.data.user = 'David'; 57 | } 58 | }).after({ 59 | create (hook) { 60 | hook.result.after = true; 61 | } 62 | }); 63 | 64 | service.once('created', function (data) { 65 | try { 66 | assert.deepEqual(data, { 67 | test: true, 68 | user: 'David', 69 | after: true 70 | }); 71 | done(); 72 | } catch (e) { 73 | done(e); 74 | } 75 | }); 76 | 77 | service.create({ test: true }); 78 | }); 79 | 80 | it('has hook.app, hook.service and hook.path', done => { 81 | const app = feathers().configure(hooks()).use('/dummy', { 82 | get (id) { 83 | return Promise.resolve({ id }); 84 | } 85 | }); 86 | 87 | const service = app.service('dummy'); 88 | 89 | service.hooks({ 90 | before (hook) { 91 | try { 92 | assert.equal(this, service); 93 | assert.equal(hook.service, service); 94 | assert.equal(hook.app, app); 95 | assert.equal(hook.path, 'dummy'); 96 | done(); 97 | } catch (e) { 98 | done(e); 99 | } 100 | } 101 | }); 102 | 103 | service.get('test'); 104 | }); 105 | 106 | it('does not error when result is null', () => { 107 | const app = feathers().configure(hooks()).use('/dummy', { 108 | get (id, params, callback) { 109 | callback(null, { id }); 110 | } 111 | }); 112 | 113 | const service = app.service('dummy'); 114 | 115 | service.after({ 116 | get: [ 117 | function (hook) { 118 | hook.result = null; 119 | return hook; 120 | } 121 | ] 122 | }); 123 | 124 | return service.get(1) 125 | .then(result => assert.equal(result, null)); 126 | }); 127 | 128 | it('invalid type in .hooks throws error', () => { 129 | const app = feathers().configure(hooks()).use('/dummy', { 130 | get (id, params, callback) { 131 | callback(null, { id, params }); 132 | } 133 | }); 134 | 135 | try { 136 | app.service('dummy').hooks({ 137 | invalid: {} 138 | }); 139 | assert.ok(false); 140 | } catch (e) { 141 | assert.equal(e.message, `'invalid' is not a valid hook type`); 142 | } 143 | }); 144 | 145 | it('invalid hook method throws error', () => { 146 | const app = feathers().configure(hooks()).use('/dummy', { 147 | get (id, params, callback) { 148 | callback(null, { id, params }); 149 | } 150 | }); 151 | 152 | try { 153 | app.service('dummy').hooks({ 154 | before: { 155 | invalid () {} 156 | } 157 | }); 158 | assert.ok(false); 159 | } catch (e) { 160 | assert.equal(e.message, `'invalid' is not a valid hook method`); 161 | } 162 | }); 163 | 164 | it('.hooks and backwards compatibility methods chain their hooks', () => { 165 | const app = feathers().configure(hooks()).use('/dummy', { 166 | get (id, params, callback) { 167 | callback(null, { id, params }); 168 | } 169 | }); 170 | const makeHooks = name => { 171 | return { 172 | all (hook) { 173 | hook.params.items.push(`${name}_all`); 174 | }, 175 | 176 | get (hook) { 177 | hook.params.items.push(`${name}_get`); 178 | } 179 | }; 180 | }; 181 | 182 | const service = app.service('dummy'); 183 | 184 | service.hooks({ before: makeHooks('hooks_before') }); 185 | service.hooks({ before: makeHooks('hooks_before_1') }); 186 | service.before(makeHooks('before')); 187 | service.before(makeHooks('before_1')); 188 | 189 | return service.get('testing', { items: [] }) 190 | .then(data => assert.deepEqual(data.params.items, [ 191 | 'hooks_before_all', 192 | 'hooks_before_get', 193 | 'hooks_before_1_all', 194 | 'hooks_before_1_get', 195 | 'before_all', 196 | 'before_get', 197 | 'before_1_all', 198 | 'before_1_get' 199 | ])); 200 | }); 201 | 202 | it('registering an already hooked service works (#154)', () => { 203 | const app = feathers().configure(hooks()).use('/dummy', { 204 | get (id, params, callback) { 205 | callback(null, { id, params }); 206 | } 207 | }); 208 | 209 | app.use('/dummy2', app.service('dummy')); 210 | }); 211 | 212 | if(process.version > 'v6.0.0') { 213 | it('throws an error when using Feathers v3 or later', () => { 214 | const feathers3 = require('@feathersjs/feathers'); 215 | 216 | try { 217 | feathers3().configure(hooks()); 218 | assert.ok(false, 'Should never get here'); 219 | } catch (e) { 220 | assert.equal(e.message, `You are using Feathers v${feathers3.version} which already includes feathers-hooks. You can remove this module from your application.`); 221 | } 222 | }); 223 | } 224 | }); 225 | --------------------------------------------------------------------------------