Skip to content

Commit

Permalink
feat(example-context): add sync-async example to resolve bindings
Browse files Browse the repository at this point in the history
  • Loading branch information
raymondfeng committed Jun 3, 2019
1 parent a1d88a9 commit 01383b7
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 22 deletions.
29 changes: 15 additions & 14 deletions examples/context/README.md
Expand Up @@ -6,20 +6,21 @@ Control (IoC) and Dependency Injection (DI) capabilities provided by

## Examples

| Example | Description |
| :------------------------------------------------------------------------------------------------------------------------------------------------ | :-------------------------------------------------------------- |
| [binding-types.ts](https://github.com/strongloop/loopback-next/blob/master/examples/context/src/binding-types.ts) | Various ways to provide values for a binding |
| [configuration-injection.ts](https://github.com/strongloop/loopback-next/blob/master/examples/context/src/configuration-injection.ts) | Configuration for bindings and injection of configurations |
| [context-chain.ts](https://github.com/strongloop/loopback-next/blob/master/examples/context/src/context-chain.ts) | Contexts are chained to create a hierarchy of registries |
| [context-observation.ts](https://github.com/strongloop/loopback-next/blob/master/examples/context/src/context-observation.ts) | Observe context (bind/unbind) and context view (refresh) events |
| [custom-configuration-resolver.ts](https://github.com/strongloop/loopback-next/blob/master/examples/context/src/custom-configuration-resolver.ts) | Override how configuration is resolved from a given binding |
| [custom-inject-decorator.ts](https://github.com/strongloop/loopback-next/blob/master/examples/context/src/custom-inject-decorator.ts) | How to create a new decorator for custom injections |
| [custom-inject-resolve.ts](https://github.com/strongloop/loopback-next/blob/master/examples/context/src/custom-inject-resolve.ts) | How to specify a custom resolve function for bindings |
| [dependency-injection.ts](https://github.com/strongloop/loopback-next/blob/master/examples/context/src/dependency-injection.ts) | Different styles of dependency injection |
| [find-bindings.ts](https://github.com/strongloop/loopback-next/blob/master/examples/context/src/find-bindings.ts) | Different flavors of finding bindings in a context |
| [injection-without-binding.ts](https://github.com/strongloop/loopback-next/blob/master/examples/context/src/injection-without-binding.ts) | Perform dependency injection without binding a class |
| [interceptor-proxy.ts](https://github.com/strongloop/loopback-next/blob/master/examples/context/src/interceptor-proxy.ts) | Get proxies to intercept method invocations |
| [parameterized-decoration.ts](https://github.com/strongloop/loopback-next/blob/master/examples/context/src/parameterized-decoration.ts) | Apply decorators that require parameters as arguments |
| Example | Description |
| :------------------------------------------------------------------------------------------------------------------------------------------------ | :----------------------------------------------------------------- |
| [binding-types.ts](https://github.com/strongloop/loopback-next/blob/master/examples/context/src/binding-types.ts) | Various ways to provide values for a binding |
| [configuration-injection.ts](https://github.com/strongloop/loopback-next/blob/master/examples/context/src/configuration-injection.ts) | Configuration for bindings and injection of configurations |
| [context-chain.ts](https://github.com/strongloop/loopback-next/blob/master/examples/context/src/context-chain.ts) | Contexts are chained to create a hierarchy of registries |
| [context-observation.ts](https://github.com/strongloop/loopback-next/blob/master/examples/context/src/context-observation.ts) | Observe context (bind/unbind) and context view (refresh) events |
| [custom-configuration-resolver.ts](https://github.com/strongloop/loopback-next/blob/master/examples/context/src/custom-configuration-resolver.ts) | Override how configuration is resolved from a given binding |
| [custom-inject-decorator.ts](https://github.com/strongloop/loopback-next/blob/master/examples/context/src/custom-inject-decorator.ts) | How to create a new decorator for custom injections |
| [custom-inject-resolve.ts](https://github.com/strongloop/loopback-next/blob/master/examples/context/src/custom-inject-resolve.ts) | How to specify a custom resolve function for bindings |
| [dependency-injection.ts](https://github.com/strongloop/loopback-next/blob/master/examples/context/src/dependency-injection.ts) | Different styles of dependency injection |
| [find-bindings.ts](https://github.com/strongloop/loopback-next/blob/master/examples/context/src/find-bindings.ts) | Different flavors of finding bindings in a context |
| [injection-without-binding.ts](https://github.com/strongloop/loopback-next/blob/master/examples/context/src/injection-without-binding.ts) | Perform dependency injection without binding a class |
| [interceptor-proxy.ts](https://github.com/strongloop/loopback-next/blob/master/examples/context/src/interceptor-proxy.ts) | Get proxies to intercept method invocations |
| [parameterized-decoration.ts](https://github.com/strongloop/loopback-next/blob/master/examples/context/src/parameterized-decoration.ts) | Apply decorators that require parameters as arguments |
| [sync-async.ts](https://github.com/strongloop/loopback-next/blob/master/examples/context/src/sync-async.ts) | Resolve bindings with dependencies synchronously or asynchronously |

## Use

Expand Down
22 changes: 14 additions & 8 deletions examples/context/fixtures/examples-output.txt
@@ -1,9 +1,9 @@
> binding-types.js
[2019-05-31T17:52:20.865Z] (/greet#1) Hello, John
[2019-05-31T17:52:20.865Z] (/greet#2) Hello, John
[2019-06-01T22:49:33.602Z] (/greet#1) Hello, John
[2019-06-01T22:49:33.621Z] (/greet#2) Hello, John

> configuration-injection.js
[2019-05-31T17:52:20.867Z] >>>: Hello, Ray
[2019-06-01T22:49:33.641Z] >>>: Hello, Ray

> context-chain.js
[app] Hello, John!
Expand Down Expand Up @@ -35,13 +35,13 @@ Context: invocation-context Binding: greeter
Injection: Greeter.constructor[0]
Context: invocation-context Binding: greeter
Injection: Greeter.prototype.prefix
[2019-05-31T17:52:20.877Z] Hello, John
[2019-06-01T22:49:33.650Z] Hello, John

> dependency-injection.js
[2019-05-31T17:52:20.879Z] (en) Hello, Jane!
[2019-05-31T17:52:20.880Z] Hello, John!
[2019-05-31T17:52:20.880Z] (zh) 你好,John!
[2019-05-31T17:52:20.881Z] (en) Hello, Jane!
[2019-06-01T22:49:33.652Z] (en) Hello, Jane!
[2019-06-01T22:49:33.653Z] Hello, John!
[2019-06-01T22:49:33.653Z] (zh) 你好,John!
[2019-06-01T22:49:33.653Z] (en) Hello, Jane!

> find-bindings.js
greeters.EnglishGreeter
Expand All @@ -66,3 +66,9 @@ Hello, JOHN
1: Hello, John
2: Hello, Jane

> sync-async.js
Hello, John (sync)
Hello, John (sync)
Hello, Jane (async)
Expect to fail with error: Cannot get greeter synchronously: the value is a promise

80 changes: 80 additions & 0 deletions examples/context/src/sync-async.ts
@@ -0,0 +1,80 @@
// Copyright IBM Corp. 2019. All Rights Reserved.
// Node module: @loopback/example-context
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

import {BindingKey, Context, inject} from '@loopback/context';

const CURRENT_USER = BindingKey.create<string>('currentUser');

/**
* A class with dependency injection
*/
class Greeter {
/**
* The dependency of current user can be sync or async depending on the
* value for `CURRENT_USER` binding.
* @param userName - User name
*/
constructor(@inject(CURRENT_USER) private userName: string) {}

hello() {
return `Hello, ${this.userName}`;
}
}

/**
* A strongly-typed binding key for `Greeter`
*/
const GREETER = BindingKey.create<Greeter>('greeter');

async function greetWithSyncUser(ctx: Context) {
// Set the current user to `John` (a constant value)
// As a result, CURRENT_USER can be resolved synchronously
ctx.bind(CURRENT_USER).to('John (sync)');

// Greeter has a dependency on current user which can be resolved
// synchronously. This allows GREETER to be resolved either synchronously or
// asynchronously.
// Get an instance of Greeter synchronously
let greeter = ctx.getSync(GREETER);
console.log('%s', greeter.hello());

// Get an instance of Greeter asynchronously
greeter = await ctx.get(GREETER);
console.log('%s', greeter.hello());
return greeter;
}

async function greetWithAsyncUser(ctx: Context) {
// Now set the current user to an async factory
// As a result, CURRENT_USER can only be resolved asynchronously

ctx.bind(CURRENT_USER).toDynamicValue(() => Promise.resolve('Jane (async)'));
// Get an instance of Greeter asynchronously
let greeter = await ctx.get(GREETER);
console.log('%s', greeter.hello());
try {
// Get an instance of Greeter synchronously - THIS WILL FAIL
greeter = ctx.getSync(GREETER);
console.log(greeter.hello());
} catch (err) {
// Error: Cannot get greeter synchronously: the value is a promise
console.log('Expect to fail with error: %s', err.message);
}
}

export async function main() {
const ctx = new Context('request');
// Bind `GREETER` to a class from which the value is instantiated
ctx.bind(GREETER).toClass(Greeter);

// Now try to invoke a greeter with synchronous resolution of the current user
await greetWithSyncUser(ctx);

// Then try to invoke a greeter with asynchronous resolution of the current user
await greetWithAsyncUser(ctx);
}

// tslint:disable-next-line:no-floating-promises
if (require.main === module) main();

0 comments on commit 01383b7

Please sign in to comment.