Compare commits

...

58 Commits

Author SHA1 Message Date
Miroslav Bajtoš 6cb6fc879b
3.5.2
* chore: update LTS status to End-of-Life (Rifa Achrinza)
2021-01-19 15:31:27 +01:00
Miroslav Bajtoš 74016f5f90
Merge pull request #56 from achrinzafork/chore/update-lts
chore: update LTS status to End-of-Life
2021-01-19 15:30:53 +01:00
Rifa Achrinza 3ead6521e0 chore: update LTS status to End-of-Life
see https://github.com/strongloop/loopback-next/issues/6957

Co-authored-by: Miroslav Bajtoš <mbajtoss@gmail.com>
2021-01-16 20:45:49 +08:00
Miroslav Bajtoš e7584e23d7
3.5.1
* Update LTS status in README (Miroslav Bajtoš)
2020-03-06 09:58:20 +01:00
Miroslav Bajtoš 5c4376a54e
Merge pull request #55 from strongloop/feat/maintenance-lts
Update LTS status in README
2020-03-06 09:57:39 +01:00
Miroslav Bajtoš 23b14733a6
Update LTS status in README 2020-03-05 13:42:27 +01:00
Miroslav Bajtoš 1dd8282826
3.5.0
* Add support for Node.js 12 (Ahmet Cetin)
 * chore: enable stalebot (Diana Lau)
 * chore: improve issue and PR templates (Nora)
 * Drop support for Node.js 6.x (Miroslav Bajtoš)
 * chore: update copyrights years (Agnes Lin)
2020-02-17 11:48:17 +01:00
Miroslav Bajtoš 824ebec5e0
Merge pull request #54 from ahmetcetin/patch-2
Add support for Node.js 12
2020-02-17 11:47:34 +01:00
Ahmet Cetin 06f0d16701
Add support for Node.js 12
Setting node version the same as in loopback-datasource-juggler
to support new versions of node.
2020-02-17 11:42:05 +01:00
Diana Lau a575d934a7
Merge pull request #53 from strongloop/stalebot
chore: enable stalebot
2020-02-10 11:39:36 -05:00
Diana Lau 5490cbfe18 chore: enable stalebot 2020-02-08 21:37:17 -05:00
Nora 3376b1ef74
Merge pull request #51 from strongloop/chore/improve-issue-templates
chore: improve issue and PR templates
2019-11-19 15:41:31 -05:00
Nora 9631185843 chore: improve issue and PR templates 2019-11-17 14:35:11 -05:00
Miroslav Bajtoš 91fa368457
Merge pull request #49 from strongloop/drop-node6
Drop support for Node.js 6.x
2019-10-04 09:08:44 +02:00
Miroslav Bajtoš d4622392d3
Drop support for Node.js 6.x
Signed-off-by: Miroslav Bajtoš <mbajtoss@gmail.com>
2019-10-03 14:41:23 +02:00
Agnes Lin d54b970161
Merge pull request #46 from strongloop/copyrights
chore: update copyrights years
2019-05-07 14:30:26 -04:00
Agnes Lin 391d9eb2ad chore: update copyrights years 2019-05-07 14:03:34 -04:00
Miroslav Bajtoš a41a55b3aa
3.4.0
* Travis: enable Node.js 10.x (Miroslav Bajtoš)
 * Node version ^10.14 added to engines list (Ahmet Cetin)
 * add lts annoucement (jannyHou)
 * Upgrade dependecies, fix new linter errors (Miroslav Bajtoš)
 * Drop support for Node.js 4.x, 5.x and 7.x (Miroslav Bajtoš)
 * Create Issue and PR Templates (#36) (Sakib Hasan)
 * Add CODEOWNERS file (Diana Lau)
2019-01-02 16:20:58 +01:00
Miroslav Bajtoš eef46ef9bb
Merge pull request #44 from strongloop/travis-node10
Travis: enable Node.js 10.x
2019-01-02 16:20:35 +01:00
Miroslav Bajtoš 5931f5351f
Travis: enable Node.js 10.x
This is a follow-up for #43 which added Node.js 10 to supported engines.
2019-01-02 16:01:37 +01:00
Janny f561dc5296
Merge pull request #43 from ahmetcetin/master
Node version ^10.14 added to engines list
2018-12-30 14:53:56 -05:00
Ahmet Cetin 8ddd939c54 Node version ^10.14 added to engines list 2018-12-27 12:34:51 +01:00
Janny 22d95df742
Merge pull request #40 from strongloop/lts
add lts annoucement
2018-11-26 11:53:33 -05:00
jannyHou c15c89d735 add lts annoucement 2018-11-22 13:42:53 -05:00
Miroslav Bajtoš c5b303fe5b
Merge pull request #39 from strongloop/upgrade-deps
Drop support for Node.js 4.x/5.x/7.x + upgrade dependencies
2018-06-29 17:27:45 +02:00
Miroslav Bajtoš ea61dfdb3a
Upgrade dependecies, fix new linter errors 2018-06-29 17:17:16 +02:00
Miroslav Bajtoš fcaa7adc61
Drop support for Node.js 4.x, 5.x and 7.x
These version are no longer supported by the Node.js project.
2018-06-29 17:17:16 +02:00
Sakib Hasan d7b74e293e Create Issue and PR Templates (#36)
* create issue template

* create pr template
2017-08-16 14:37:10 -04:00
Miroslav Bajtoš bcfbc2a163 Merge pull request #35 from strongloop/add-codeowner
Add CODEOWNERS file
2017-07-31 14:44:26 +02:00
Diana Lau eafbc7f6fd
Add CODEOWNERS file 2017-07-31 14:28:14 +02:00
Miroslav Bajtoš 63a4c80678
3.3.0
* add nodejs v8.2.1 support (Oleg Kubrakov)
2017-07-21 13:25:09 +02:00
Miroslav Bajtoš 66c1954441 Merge pull request #34 from yellowred/add-node8
Add NodeJS v8.2.1 support
2017-07-21 13:24:40 +02:00
Oleg Kubrakov b9fa114ad6 add nodejs v8.2.1 support 2017-07-21 17:40:54 +08:00
Miroslav Bajtoš 3c10d219c7
3.2.0
* Add Node.js 7 to package.json and .travis.yml (Edgars Zagorskis)
2017-07-10 15:56:46 +02:00
Miroslav Bajtoš 4d4920c491 Merge pull request #31 from xnf/master
Add Node.js 7 to package.json
2017-07-10 15:56:12 +02:00
Edgars Zagorskis 6d5fedb85e
Add Node.js 7 to package.json and .travis.yml
Added Node.js 7 to package.json. This way yarn will not complain when
Node.js 7 is used. Tests still pass and the code itself also works
on Node.js 7.

Configure Travis CI to run the build on Node.js 7 too, in order to
verify correctness.
2017-07-10 15:51:01 +02:00
Miroslav Bajtoš f49752780b
3.1.0
* fix typo in readme (biniam)
 * Add bind option to getCurrentContext() (Emiliano Daddario)
 * Upgrade eslint & config to latest (Miroslav Bajtoš)
2017-03-17 16:36:28 +01:00
Miroslav Bajtoš ebdbb7bfe7 Merge pull request #26 from strongloop/fix/readme-typo
fix typo in readme
2017-03-17 16:13:34 +01:00
biniam 3a3b2fd676 fix typo in readme
Fix typo in readme and
fix the module name in
contributing file.

apply changes
2017-03-17 09:58:34 -04:00
Miroslav Bajtoš 2f236b884e Merge pull request #25 from strongloop/upgrade/eslint-config
Upgrade eslint & config to latest
2017-02-24 14:52:44 +01:00
Emiliano Daddario d5f977fc4a Add bind option to getCurrentContext()
Add bind:true option to getCurrentContext

Workaround issue #17 with cujojs/when v3.7.7 due to the fact
that it internally uses a mechanism of user-land queuing for
async-related operations, and possibly issues with similar
packages using queuing (mostly Promise libraries)
2017-02-24 11:54:34 +01:00
Miroslav Bajtoš 06b99c26df
Upgrade eslint & config to latest
This upgrade allows us to start using ES6 features like arrow functions.
2017-02-24 10:26:20 +01:00
Miroslav Bajtoš 750d8bb5c9 3.0.0
* Rework README, add info from docs site (Miroslav Bajtoš)
 * Drop continuation-local-storage, use cls-hooked (josieusa)
 * Update paid support URL (Siddhi Pai)
 * Add app using loopback-context (Amir Jafarian)
 * Drop support for Node v0.10 and v0.12 (Siddhi Pai)
 * Start the development of the next major version (Siddhi Pai)
 * Update deps to loopback 3.0.0 RC (Miroslav Bajtoš)
 * make warning less ambigious (Carl Fürstenberg)
2017-01-06 10:35:11 +01:00
Miroslav Bajtoš b0e0b350d4 Merge pull request #18 from strongloop/improve-readme
Rework README, add info from docs site
2017-01-05 15:10:57 +01:00
Miroslav Bajtoš bfed31f2dc Rework README, add info from docs site
Rework the initial section, simplify warnings and installation
instructions.

Update the "Usage" section with content from docs site, as was found in
[Using-current-context.md](http://bit.ly/2hNJSBN).
2017-01-05 14:59:13 +01:00
Miroslav Bajtoš db9387e56b Merge pull request #11 from josieusa/feature/cls-hooked-3
[SEMVER-MAJOR] Replace continuation-local-storage with cls-hooked
2017-01-03 13:49:11 +01:00
josieusa 996a49f7da Drop continuation-local-storage, use cls-hooked
Rework the module to use the new cls-hooked module (which uses AsyncWrap
available since Node v4.5) instead of old continuation-local-storage
(based on very unreliable async-listener).
2017-01-03 13:12:28 +01:00
Simon Ho 7cef4817c4 Merge pull request #16 from strongloop/update-support-URL
Replicate .github from loopback repo
2016-12-06 22:48:06 -08:00
Siddhi Pai 0bc1ac0886 Update paid support URL 2016-12-06 03:08:19 -08:00
Amirali Jafarian 15eb91df7d Merge pull request #15 from strongloop/add_app
Add app using loopback-context
2016-11-25 14:35:09 -05:00
Amir Jafarian 30218be9e5 Add app using loopback-context 2016-11-25 14:28:01 -05:00
Miroslav Bajtoš 3aeee7dbe4 Merge pull request #14 from strongloop/next-major-drop-old-node
Start 3.x + drop support for Node v0.10/v0.12
2016-11-22 09:01:07 +01:00
Siddhi Pai 89a1e4ae23 Drop support for Node v0.10 and v0.12 2016-11-19 02:27:54 -08:00
Siddhi Pai 1844bfcd22 Start the development of the next major version 2016-11-19 02:27:27 -08:00
Miroslav Bajtoš 814171d2b3 Merge pull request #12 from strongloop/update-lb-3-rc
Update deps to loopback 3.0.0 RC
2016-09-22 14:10:33 +02:00
Miroslav Bajtoš 46cfc17600 Update deps to loopback 3.0.0 RC 2016-09-22 13:57:02 +02:00
Simon Ho 268716f69e Merge pull request #5 from azatoth/disambig_readme
make warning less ambigious
2016-08-10 16:20:25 -07:00
Carl Fürstenberg 7d0f6afaef make warning less ambigious 2016-08-10 14:16:21 +02:00
23 changed files with 755 additions and 58 deletions

50
.github/ISSUE_TEMPLATE/Bug_report.md vendored Normal file
View File

@ -0,0 +1,50 @@
---
name: Bug report
about: Create a report to help us improve
labels: bug
---
<!-- 🚨 STOP 🚨 STOP 🚨 STOP 🚨
HELP US HELP YOU, PLEASE
- Do a quick search to avoid duplicate issues
- Provide as much information as possible (reproduction sandbox, use case for features, etc.)
- Consider using a more suitable venue for questions such as Stack Overflow, Gitter, etc.
Please fill in the *entire* template below.
-->
## Steps to reproduce
<!-- Describe how to reproduce the issue -->
## Current Behavior
<!-- Describe the observed result -->
## Expected Behavior
<!-- Describe what did you expect instead, what is the desired outcome? -->
## Link to reproduction sandbox
<!--
See https://loopback.io/doc/en/contrib/Reporting-issues.html#loopback-3x-bugs
Note: Failure to provide a sandbox application for reproduction purposes will result in the issue being closed.
-->
## Additional information
<!--
Copy+paste the output of these two commands:
node -e 'console.log(process.platform, process.arch, process.versions.node)'
npm ls --prod --depth 0 | grep loopback
-->
## Related Issues
<!-- Did you find other bugs that looked similar? -->
_See [Reporting Issues](http://loopback.io/doc/en/contrib/Reporting-issues.html) for more tips on writing good issues_

View File

@ -0,0 +1,25 @@
---
name: Feature request
about: Suggest an idea for this project
labels: feature
---
## Suggestion
<!-- A summary of what you'd like to see added or changed -->
## Use Cases
<!--
What do you want to use this for?
What shortcomings exist with current approaches?
-->
## Examples
<!-- Show how this would be used and what the behavior would be -->
## Acceptance criteria
TBD - will be filled by the team.

27
.github/ISSUE_TEMPLATE/Question.md vendored Normal file
View File

@ -0,0 +1,27 @@
---
name: Question
about: The issue tracker is not for questions. Please use Stack Overflow or other resources for help.
labels: question
---
<!-- 🚨 STOP 🚨 STOP 🚨 STOP 🚨
THE ISSUE TRACKER IS NOT FOR QUESTIONS.
DO NOT CREATE A NEW ISSUE TO ASK A QUESTION.
Please use one of the following resources for help:
**Questions**
- https://stackoverflow.com/tags/loopbackjs
- https://groups.google.com/forum/#!forum/loopbackjs
- https://gitter.im/strongloop/loopback
**Immediate support**
- https://strongloop.com/api-connect-faqs/
- https://strongloop.com/node-js/subscription-plans/
-->

11
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@ -0,0 +1,11 @@
blank_issues_enabled: false
contact_links:
- name: Report a security vulnerability
url: https://loopback.io/doc/en/contrib/Reporting-issues.html#security-issues
about: Do not report security vulnerabilities using GitHub issues. Please send an email to `reachsl@us.ibm.com` instead.
- name: Get help on StackOverflow
url: https://stackoverflow.com/tags/loopbackjs
about: Please ask and answer questions on StackOverflow.
- name: Join our mailing list
url: https://groups.google.com/forum/#!forum/loopbackjs
about: You can also post your question to our mailing list.

18
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@ -0,0 +1,18 @@
<!--
Please provide a high-level description of the changes made by your pull request.
Include references to all related GitHub issues and other pull requests, for example:
Fixes #123
Implements #254
See also #23
-->
## Checklist
👉 [Read and sign the CLA (Contributor License Agreement)](https://cla.strongloop.com/agreements/strongloop/loopback-context) 👈
- [ ] `npm test` passes on your machine
- [ ] New tests added or existing tests modified to cover all changes
- [ ] Code conforms with the [style guide](https://loopback.io/doc/en/contrib/style-guide-es6.html)
- [ ] Commit messages are following our [guidelines](https://loopback.io/doc/en/contrib/git-commit-messages.html)

24
.github/stale.yml vendored Normal file
View File

@ -0,0 +1,24 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 60
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 14
# Issues with these labels will never be considered stale
exemptLabels:
- pinned
- security
- critical
- p1
- major
- good first issue
# Label to use when marking an issue as stale
staleLabel: stale
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: >
This issue has been closed due to continued inactivity. Thank you for your understanding.
If you believe this to be in error, please contact one of the code owners,
listed in the `CODEOWNERS` file at the top-level of this repository.

View File

@ -1,7 +1,6 @@
sudo: false sudo: false
language: node_js language: node_js
node_js: node_js:
- "0.10" - "8"
- "0.12" - "10"
- "4" - "12"
- "6"

View File

@ -1,3 +1,89 @@
2021-01-19, Version 3.5.2
=========================
* chore: update LTS status to End-of-Life (Rifa Achrinza)
2020-03-06, Version 3.5.1
=========================
* Update LTS status in README (Miroslav Bajtoš)
2020-02-17, Version 3.5.0
=========================
* Add support for Node.js 12 (Ahmet Cetin)
* chore: enable stalebot (Diana Lau)
* chore: improve issue and PR templates (Nora)
* Drop support for Node.js 6.x (Miroslav Bajtoš)
* chore: update copyrights years (Agnes Lin)
2019-01-02, Version 3.4.0
=========================
* Travis: enable Node.js 10.x (Miroslav Bajtoš)
* Node version ^10.14 added to engines list (Ahmet Cetin)
* add lts annoucement (jannyHou)
* Upgrade dependecies, fix new linter errors (Miroslav Bajtoš)
* Drop support for Node.js 4.x, 5.x and 7.x (Miroslav Bajtoš)
* Create Issue and PR Templates (#36) (Sakib Hasan)
* Add CODEOWNERS file (Diana Lau)
2017-07-21, Version 3.3.0
=========================
* add nodejs v8.2.1 support (Oleg Kubrakov)
2017-07-10, Version 3.2.0
=========================
* Add Node.js 7 to package.json and .travis.yml (Edgars Zagorskis)
2017-03-17, Version 3.1.0
=========================
* fix typo in readme (biniam)
* Add bind option to getCurrentContext() (Emiliano Daddario)
* Upgrade eslint & config to latest (Miroslav Bajtoš)
2017-01-06, Version 3.0.0
=========================
* Rework README, add info from docs site (Miroslav Bajtoš)
* Drop continuation-local-storage, use cls-hooked (josieusa)
* Update paid support URL (Siddhi Pai)
* Add app using loopback-context (Amir Jafarian)
* Drop support for Node v0.10 and v0.12 (Siddhi Pai)
* Start the development of the next major version (Siddhi Pai)
* Update deps to loopback 3.0.0 RC (Miroslav Bajtoš)
* make warning less ambigious (Carl Fürstenberg)
2016-08-10, Version 1.0.0 2016-08-10, Version 1.0.0
========================= =========================

6
CODEOWNERS Normal file
View File

@ -0,0 +1,6 @@
# Lines starting with '#' are comments.
# Each line is a file pattern followed by one or more owners,
# the last matching pattern has the most precendence.
# Core team members from IBM
* @bajtos @josieusa

View File

@ -1,9 +1,9 @@
### Contributing ### ### Contributing ###
Thank you for your interest in `loopback-context-cls`, an open source project Thank you for your interest in `loopback-context`, an open source project
administered by StrongLoop. administered by StrongLoop.
Contributing to `loopback-context-cls` is easy. In a few simple steps: Contributing to `loopback-context` is easy. In a few simple steps:
* Ensure that your effort is aligned with the project's roadmap by * Ensure that your effort is aligned with the project's roadmap by
talking to the maintainers, especially if you are going to spend a talking to the maintainers, especially if you are going to spend a

238
README.md
View File

@ -1,25 +1,90 @@
# loopback-context # loopback-context
Current context for LoopBack applications, based on **⚠️ LoopBack 3 has reached end of life. We are no longer accepting pull requests or providing
node-continuation-local-storage. support for community users. The only exception is fixes for critical bugs and security
vulnerabilities provided as part of support for IBM API Connect customers. (See
[Module Long Term Support Policy](#module-long-term-support-policy) below.)**
We urge all LoopBack 3 users to migrate their applications to LoopBack 4 as
soon as possible. Refer to our
[Migration Guide](https://loopback.io/doc/en/lb4/migration-overview.html)
for more information on how to upgrade.
## Overview
Current context for LoopBack applications, based on cls-hooked.
## WARNING ## WARNING
The module node-continuation-local-storage is known to have many problems, **`cls-hooked` module uses undocumented `AsyncWrap` API that was introduced to Node.js relatively recently. While this new API seems to be more reliable than the old `async-listener` used by `continuation-local-storage`, there are still cases where the context (local storage) is not preserved correctly. Please consider this risk before using loopback-context.**
see e.g. [issue #59](https://github.com/othiym23/node-continuation-local-storage/issues/59).
As a result, loopback-context does not work in many situations, as can be
seen from issues reported in LoopBack's
[issue tracker](https://github.com/strongloop/loopback/issues?utf8=%E2%9C%93&q=is%3Aissue%20getCurrentcontext).
**We recommend AGAINST using this module.** ### Known issues
If you are running on Node v6, you can try the new alternative - [when](https://www.npmjs.com/package/when), a popular Promise
[cls-hooked](https://github.com/Jeff-Lewis/cls-hooked). implementation, breaks context propagation. Please consider using the
built-in `Promise` implementation provided by Node.js or
[Bluebird](https://www.npmjs.com/package/bluebird) instead.
- Express middleware chains which contain a "bad" middleware (i.e. one which
breaks context propagation inside its function body, in a way mentioned in
this doc) especially if called before other "good" ones needs refactoring,
in order to prevent the context from getting mixed up among HTTP requests.
See usage below for details.
Discussion: https://github.com/strongloop/loopback-context/issues/17
In general, any module that implements a custom task queue or a connection pool
is prone to break context storage. This is an inherent problem of continuation
local storage that needs to be fixed at lower level - first in Node.js core
and then in modules implementing task queues and connection pools.
## Installation
```
$ npm install --save loopback-context cls-hooked
```
Make sure you are running on a Node.js version supported by this module
(`^4.5`, `^5.10`, `^6.0`, `^7.0`, `^8.2.1` or `^10.14`). When installing, check the output of `npm install`
and make sure there are no `engine` related warnings.
## Usage ## Usage
1) Add `per-request` middleware to your ### Setup cls-hooked
`server/middleware-config.json`:
To minimize the likelihood of loosing context in your application, you should
ensure that `cls-hooked` is loaded as the first module of your application, so
that it can wrap certain Node.js APIs before any other modules start using these
APIs.
Our recommended approach is to add `-r cls-hooked` to node's list of
arguments when starting your LoopBack application.
```
$ node -r cls-hooked .
```
If you are using a process manager like `strong-pm` or `pm2`, then consult
their documentation whether it's possible to configure the arguments used to
spawn worker processes. Note that `slc run` does not support this feature yet,
see [strong-supervisor#56](https://github.com/strongloop/strong-supervisor/issues/56).
Alternatively, you can add the following line as the first line of your main
application file:
```js
require('cls-hooked');
```
This approach should be compatible with all process managers, including
`strong-pm`. However, we feel that relying on the order of `require` statements
is error-prone.
### Configure context propagation
To setup your LoopBack application to create a new context for each incoming
HTTP request, configure `per-context` middleware in your
`server/middleware.json` as follows:
```json ```json
{ {
@ -30,7 +95,28 @@ If you are running on Node v6, you can try the new alternative
} }
``` ```
2) Then you can access the context from your code: **IMPORTANT: By default, the HTTP req/res objects are not set onto the current context. You
need to set `enableHttpContext` to true to enable automatic population
of req/res objects.**
```json
{
"initial": {
"loopback-context#per-request": {
"params": {
"enableHttpContext": true
}
}
}
}
```
### Use the current context
Once youve enabled context propagation, you can access the current context
object using `LoopBackContext.getCurrentContext()`. The context will be
available in middleware (if it is loaded after the context middleware),
remoting hooks, model hooks, and custom methods.
```js ```js
var LoopBackContext = require('loopback-context'); var LoopBackContext = require('loopback-context');
@ -44,6 +130,126 @@ MyModel.myMethod = function(cb) {
}); });
``` ```
See the official LoopBack ### Bind for concurrency
[documentation](https://docs.strongloop.com/display/APIC/Using+current+context)
for more details. In order to workaround the aforementioned concurrency issue with `when` (and
similar `Promise`-like and other libraries implementing custom queues and/or
connection pools), it's recommended to activate context binding inside each
HTTP request or concurrent `runInContext()` call, by using the `bind` option, as
in this example:
var ctx = LoopBackContext.getCurrentContext({ bind: true });
With the option enabled, this both creates the context, and binds the access
methods of the context (i.e. `get` and `set`), at once.
**Warning**: this only works if it's **the first expression evaluated** in every
middleware/operation hook/`runInContext()` call etc. that uses
`getCurrentContext`. (It must be the first expression; it may not be enough if
it's at the first line). Explanation: you must bind the context while it's still
correct, i.e. before it gets mixed up between concurrent operations affected by
bugs. Therefore, to be sure, you must bind it before *any* operation.
Also, with respect to the "bad", context-breaking middleware use case mentioned in "Known issues"
before, the following 2 lines need to be present at the beginning of the middleware
body. At least the "bad" one; but, as a preventive measure, they can be present
in every other middleware of every chain as well, being backward-compatible:
var badMiddleware = function(req, res, next) {
// these 2 lines below are needed
var ctx = LoopBackContext.getCurrentContext({bind: true});
next = ctx.bind(next);
...
The `bind` option defaults to `false`. This is only in order to prevent breaking
legacy apps; but if your app doesn't have such issue, then you can safely use
`bind: true` everywhere in your app (e.g. with a
[codemod](https://github.com/facebook/jscodeshift), or by monkey-patching
`getCurrentContext()` globally, if you prefer an automated fashion).
**Warning**: this only applies to application modules. In fact, if the module
affected by the concurrency issue is of this kind, you can easily refactor/write
your own code so to enable `bind`. Not if it's a 3rd-party module, nor a
Loopback non-core module, unless you fork and fix it.
### Use current authenticated user in remote methods
In advanced use cases, for example when you want to add custom middleware, you
have to add the context middleware at the right position in the middleware
chain (before the middleware that depends on
`LoopBackContext.getCurrentContext`).
**IMPORTANT: `LoopBackContext.perRequest()` detects the situation when it is
invoked multiple times on the same request and returns immediately in
subsequent runs.**
Here is a snippet using a middleware function to place the currently
authenticated user into the context so that remote methods may use it:
**server/middleware/store-current-user.js**
```js
module.exports = function(options) {
return function storeCurrentUser(req, res, next) {
if (!req.accessToken) {
return next();
}
app.models.UserModel.findById(req.accessToken.userId, function(err, user) {
if (err) {
return next(err);
}
if (!user) {
return next(new Error('No user with this access token was found.'));
}
var loopbackContext = LoopBackContext.getCurrentContext();
if (loopbackContext) {
loopbackContext.set('currentUser', user);
}
next();
});
};
};
```
**server/middleware.json**
```json
{
"initial": {
"loopback-context#per-request": {}
},
"auth": {
"loopback#token": {}
},
"auth:after": {
"./middleware/store-current-user": {}
}
}
```
**common/models/YourModel.json**
```js
var LoopBackContext = require('loopback-context');
module.exports = function(YourModel) {
...
//remote method
YourModel.someRemoteMethod = function(arg1, arg2, cb) {
var ctx = LoopBackContext.getCurrentContext();
var currentUser = ctx && ctx.get('currentUser');
console.log('currentUser.username: ', currentUser.username); // voila!
...
cb(null);
};
...
};
```
## Module Long Term Support Policy
This module adopts the [Module Long Term Support (LTS)](http://github.com/CloudNativeJS/ModuleLTS) policy, with the following End Of Life (EOL) dates:
| Version | Status | Published | EOL |
| ------- | --------------- | --------- | -------- |
| 3.x | End-of-Life | Jan 2017 | Dec 2020 |
| 1.x | End-of-Life | Aug 2016 | Apr 2019 |
Learn more about our LTS plan in the [docs](https://loopback.io/doc/en/contrib/Long-term-support.html).

View File

@ -1,5 +1,5 @@
// Copyright IBM Corp. 2015. All Rights Reserved. // Copyright IBM Corp. 2016. All Rights Reserved.
// Node module: loopback-context-cls // Node module: loopback-context
// This file is licensed under the MIT License. // This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT // License text available at https://opensource.org/licenses/MIT

43
example/app.js Normal file
View File

@ -0,0 +1,43 @@
// Copyright IBM Corp. 2016. All Rights Reserved.
// Node module: loopback-context
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
var contextPerRequest = require('../server/middleware/per-request.js');
// Use `var lbContext = require('loopback-context');` in your app
var lbContext = require('../');
var loopback = require('loopback');
var app = loopback();
// Configure the context middleware
app.middleware('initial', contextPerRequest());
// Store a request property in the context
app.use(function saveHostToContext(req, res, next) {
var currentContext = lbContext.getCurrentContext();
if (currentContext)
currentContext.set('host', req.hostname);
next();
});
app.use(loopback.rest());
var Color = loopback.createModel('color', {name: String});
Color.beforeRemote('**', function(ctx, unused, next) {
// Inside LoopBack code, you can read the property from the context
var currentContext = lbContext.getCurrentContext();
if (currentContext)
console.log('Request to host %s',
currentContext && currentContext.get('host'));
next();
});
app.dataSource('db', {connector: 'memory'});
app.model(Color, {dataSource: 'db'});
app.listen(3000, function() {
console.log('A list of colors is available at http://localhost:3000/colors');
});

View File

@ -1,7 +1,10 @@
{ {
"name": "loopback-context", "name": "loopback-context",
"version": "1.0.0", "version": "3.5.2",
"description": "Current context for LoopBack applications, based on node-continuation-local-storage", "description": "Current context for LoopBack applications, based on cls-hooked",
"engines": {
"node": "^8.2.1 || ^10.14 || ^12.15"
},
"keywords": [ "keywords": [
"StrongLoop", "StrongLoop",
"LoopBack", "LoopBack",
@ -20,15 +23,18 @@
}, },
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"continuation-local-storage": "^3.1.7" "cls-hooked": "^4.2.0"
}, },
"devDependencies": { "devDependencies": {
"chai": "^3.5.0", "async-1.5.2": "file:./test/stub-modules/async-1.5.2",
"dirty-chai": "^1.2.2", "chai": "^4.1.2",
"eslint": "^2.13.1", "dirty-chai": "^2.0.1",
"eslint-config-loopback": "^4.0.0", "eslint": "^5.0.1",
"loopback": "^3.0.0-alpha.1", "eslint-config-loopback": "^10.0.0",
"mocha": "^2.5.3", "loopback": "^3.0.0",
"supertest": "^1.2.0" "mocha": "^5.2.0",
} "supertest": "^3.1.0",
"when-3.7.7": "file:./test/stub-modules/when-3.7.7"
},
"author": "IBM Corp."
} }

View File

@ -1,33 +1,26 @@
// Copyright IBM Corp. 2015,2016. All Rights Reserved. // Copyright IBM Corp. 2016,2017. All Rights Reserved.
// Node module: loopback-context-cls // Node module: loopback-context
// This file is licensed under the MIT License. // This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT // License text available at https://opensource.org/licenses/MIT
'use strict'; 'use strict';
var cls = require('cls-hooked');
var domain = require('domain'); var domain = require('domain');
// Require CLS only when using the current context feature.
// As soon as this require is done, all the instrumentation/patching
// of async-listener is fired which is not ideal.
//
// Some users observed stack overflows due to promise instrumentation
// and other people have seen similar things:
// https://github.com/othiym23/async-listener/issues/57
// It all goes away when instrumentation is disabled.
var cls = function() {
return require('continuation-local-storage');
};
var LoopBackContext = module.exports; var LoopBackContext = module.exports;
/** /**
* Get the current context object. The context is preserved * Get the current context object. The context is preserved
* across async calls, it behaves like a thread-local storage. * across async calls, it behaves like a thread-local storage.
* *
* @options {Object} [options]
* @property {Boolean} bind Bind get/set/bind methods of the context to the
* context that's current at the time getCurrentContext() is invoked. This
* can be used to work around 3rd party code breaking CLS context propagation.
* @returns {Namespace} The context object or null. * @returns {Namespace} The context object or null.
*/ */
LoopBackContext.getCurrentContext = function() { LoopBackContext.getCurrentContext = function(options) {
// A placeholder method, see LoopBackContext.createContext() for the real version // A placeholder method, see LoopBackContext.createContext() for the real version
return null; return null;
}; };
@ -85,11 +78,30 @@ LoopBackContext.createContext = function(scopeName) {
process.context = process.context || {}; process.context = process.context || {};
var ns = process.context[scopeName]; var ns = process.context[scopeName];
if (!ns) { if (!ns) {
ns = cls().createNamespace(scopeName); ns = cls.createNamespace(scopeName);
process.context[scopeName] = ns; process.context[scopeName] = ns;
// Set up LoopBackContext.getCurrentContext() // Set up LoopBackContext.getCurrentContext()
LoopBackContext.getCurrentContext = function() { LoopBackContext.getCurrentContext = function(options) {
return ns && ns.active ? ns : null; if (!ns || !ns.active) {
return null;
}
if (!options || !options.bind) {
return ns;
}
/**
* **NOTE**
* This only re-binds get, set and bind methods, the most used.
* If you use other methods of the context, e.g. runInContext(), etc.,
* you may run into unexpected issues that are fixed only for get & set.
*/
var boundContext = Object.create(ns);
boundContext.get = boundContext.bind(ns.get);
boundContext.set = boundContext.bind(ns.set);
// Call to Function.prototype.bind(), not ns.bind()
boundContext.bind = ns.bind.bind(ns);
return boundContext;
}; };
} }
return ns; return ns;

View File

@ -1,5 +1,5 @@
// Copyright IBM Corp. 2014,2016. All Rights Reserved. // Copyright IBM Corp. 2016. All Rights Reserved.
// Node module: loopback-context-cls // Node module: loopback-context
// This file is licensed under the MIT License. // This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT // License text available at https://opensource.org/licenses/MIT

View File

@ -1,3 +1,8 @@
// Copyright IBM Corp. 2016. All Rights Reserved.
// Node module: loopback-context
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict'; 'use strict';
var chai = require('chai'); var chai = require('chai');

View File

@ -1,10 +1,12 @@
// Copyright IBM Corp. 2013,2016. All Rights Reserved. // Copyright IBM Corp. 2016,2018. All Rights Reserved.
// Node module: loopback-context-cls // Node module: loopback-context
// This file is licensed under the MIT License. // This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT // License text available at https://opensource.org/licenses/MIT
'use strict'; 'use strict';
var asyncV152 = require('async-1.5.2');
var whenV377 = require('when-3.7.7');
var LoopBackContext = require('..'); var LoopBackContext = require('..');
var Domain = require('domain'); var Domain = require('domain');
var EventEmitter = require('events').EventEmitter; var EventEmitter = require('events').EventEmitter;
@ -50,7 +52,7 @@ describe('LoopBack Context', function() {
TestModel.test = function(inst, cb) { TestModel.test = function(inst, cb) {
var tmpCtx = LoopBackContext.getCurrentContext(); var tmpCtx = LoopBackContext.getCurrentContext();
if (tmpCtx) tmpCtx.set('data', 'a value stored in context'); if (tmpCtx) tmpCtx.set('data', 'a value stored in context');
if (process.domain) cb = process.domain.bind(cb); // IMPORTANT if (process.domain) cb = process.domain.bind(cb); // IMPORTANT
runInOtherDomain(cb); runInOtherDomain(cb);
}; };
@ -98,4 +100,150 @@ describe('LoopBack Context', function() {
}); });
}); });
}); });
// Credits for the original idea for this test case to @marlonkjoseph
// Original source of the POC gist of the idea:
// https://gist.github.com/marlonkjoseph/f42f3c71f746896a0d4b7279a34ea753
// Heavily edited by others
it('keeps context when using waterfall() from async 1.5.2',
function(done) {
LoopBackContext.runInContext(function() {
// Trigger async waterfall callbacks
asyncV152.waterfall([
function pushToContext(next) {
var ctx = LoopBackContext.getCurrentContext();
expect(ctx).is.an('object');
ctx.set('test-key', 'test-value');
next();
},
function pullFromContext(next) {
var ctx = LoopBackContext.getCurrentContext();
expect(ctx).is.an('object');
var testValue = ctx && ctx.get('test-key', 'test-value');
next(null, testValue);
},
function verify(testValue, next) {
expect(testValue).to.equal('test-value');
next();
},
], done);
});
});
it('handles concurrent then() calls with when v3.7.7 promises & bind option',
function() {
return Promise.all([
runWithPushedValue('test-value-1', {bind: true}),
runWithPushedValue('test-value-2', {bind: true}),
])
.then(function verify(values) {
var failureCount = getFailureCount(values);
expect(failureCount).to.equal(0);
});
});
it('fails once without bind option and when v3.7.7 promises',
function() {
return Promise.all([
runWithPushedValue('test-value-3'),
runWithPushedValue('test-value-4'),
])
.then(function verify(values) {
var failureCount = getFailureCount(values);
expect(failureCount).to.equal(1);
});
});
var timeout = 100;
function runWithPushedValue(pushedValue, options) {
return new Promise(function concurrentExecutor(outerResolve, reject) {
LoopBackContext.runInContext(function pushToContext() {
var ctx = LoopBackContext.getCurrentContext(options);
expect(ctx).is.an('object');
ctx.set('test-key', pushedValue);
var whenPromise = whenV377().delay(timeout);
whenPromise.then(function pullFromContextAndReturn() {
var pulledValue = ctx && ctx.get('test-key');
outerResolve({
pulledValue: pulledValue,
pushedValue: pushedValue,
});
}).catch(reject);
});
});
}
function getFailureCount(values) {
var failureCount = 0;
values.forEach(function(v) {
if (v.pulledValue !== v.pushedValue) {
failureCount++;
}
});
return failureCount;
}
it('doesn\'t mix up req\'s in chains of ' +
'Express-middleware-like func\'s if next() cb is bound',
function() {
return Promise.all([
runWithRequestId('test-value-5', true),
runWithRequestId('test-value-6', true),
])
.then(function verify(values) {
var failureCount = getFailureCount(values);
expect(failureCount).to.equal(0);
});
});
it('fails & mixes up ctx among requests in mw chains if next() cb is unbound',
function() {
return Promise.all([
runWithRequestId('test-value-7'),
runWithRequestId('test-value-8'),
])
.then(function verify(values) {
var failureCount = getFailureCount(values);
expect(failureCount).to.equal(1);
});
});
function runWithRequestId(pushedValue, bindNextCb) {
return new Promise(function chainExecutor(outerResolve, reject) {
LoopBackContext.runInContext(function concurrentChain() {
function middlewareBreakingCls(req, res, next) {
var ctx = LoopBackContext.getCurrentContext({bind: true});
if (bindNextCb) {
next = ctx.bind(next);
}
ctx.set('test-key', pushedValue);
var whenPromise = whenV377().delay(timeout);
whenPromise.then(next).catch(reject);
};
function middlewareReadingContext(req, res, next) {
var ctx = LoopBackContext.getCurrentContext({bind: true});
var pulledValue = ctx && ctx.get('test-key');
next(null, pulledValue);
};
// Run the chain
var req = null;
var res = null;
middlewareBreakingCls(req, res, function(err) {
if (err) return reject(err);
middlewareReadingContext(req, res, function(err, result) {
if (err) return reject(err);
outerResolve({
pulledValue: result,
pushedValue: pushedValue,
});
});
});
});
});
}
}); });

1
test/mocha.opts Normal file
View File

@ -0,0 +1 @@
-r cls-hooked

View File

@ -0,0 +1,7 @@
// Copyright IBM Corp. 2017. All Rights Reserved.
// Node module: loopback-context
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
module.exports = require('async');

View File

@ -0,0 +1,8 @@
{
"name": "async-1.5.2",
"version": "1.5.2",
"description":"async version 1.5.2",
"dependencies": {
"async":"1.5.2"
}
}

View File

@ -0,0 +1,7 @@
// Copyright IBM Corp. 2017. All Rights Reserved.
// Node module: loopback-context
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
module.exports = require('when');

View File

@ -0,0 +1,8 @@
{
"name": "when-3.7.7",
"version": "3.7.7",
"description":"when version 3.7.7",
"dependencies": {
"when":"3.7.7"
}
}