Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(core): introduce basic life cycle support for LoopBack applications #1928

Merged
merged 4 commits into from
Apr 9, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 17 additions & 8 deletions docs/site/Creating-components.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,20 @@ import {MyValueProvider} from './providers/my-value.provider';
import {Component} from '@loopback/core';

export class MyComponent implements Component {
constructor() {
this.controllers = [MyController];
this.providers = {
'my-value': MyValueProvider,
};
this.classes = {
'my-validator': MyValidator,
};
servers = {
'my-server': MyServer,
};
lifeCycleObservers = [MyObserver];
controllers = [MyController];
providers = {
'my-value': MyValueProvider,
};
classes = {
'my-validator': MyValidator,
};

constructor() {
// Set up `bindings`
const bindingX = Binding.bind('x').to('Value X');
const bindingY = Binding.bind('y').toClass(ClassY);
this.bindings = [bindingX, bindingY];
Expand Down Expand Up @@ -57,6 +62,8 @@ class is created and then:
- Each Class is bound to its key in `classes` object via
`app.bind(key).toClass(cls)`
- Each Binding is added via `app.add(binding)`
- Each Server class is registered via `app.server()`
- Each LifeCycleObserver class is registered via `app.lifeCycleObserver()`

Please note that `providers` and `classes` are shortcuts for provider and class
`bindings`.
Expand All @@ -68,6 +75,8 @@ create the following bindings in the application context:
- `my-validator` -> `MyValidator` (class)
- `x` -> `'Value X'` (value)
- `y` -> `ClassY` (class)
- `my-server` -> `MyServer` (server)
- `lifeCycleObservers.MyObserver` -> `MyObserver` (life cycle observer)

## Providers

Expand Down
80 changes: 80 additions & 0 deletions docs/site/Extension-life-cycle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
---
lang: en
title: 'Extension life cycle'
keywords: LoopBack 4.0, LoopBack 4
sidebar: lb4_sidebar
permalink: /doc/en/lb4/Extension-life-cycle.html
---

## Extension life cycle

As described in [Life cycle](Life-cycle.md), a LoopBack
[Application](Application.md) has its own life cycles at runtime. Corresponding
events such as `start` and `stop` are emitted upon the state change. Please note
that LoopBack only support `start` and `stop` events for an application's life
cycles at this moment.

Extension modules for LoopBack often contribute artifacts such as servers,
datasources, and connectors to the application. They typically provide a
component to bind such artifacts to the context together. Being able to listen
on life cycle events is important for extension modules to collaborate with the
application.

An extension module follows the same way as applications to implement and
register life cycle observers.

### Implement a life cycle observer

A life cycle observer class optionally implements `start` and `stop` methods to
be invoked upon `start` and `stop` events emitted by an application's life cycle
respectively.

```ts
import {LifeCycleObserver} from '@loopback/core';

export class MyLifeCycleObserver implements LifeCycleObserver {
start() {
// It can return `void` or `Promise<void>`
}
stop() {
// It can return `void` or `Promise<void>`
}
}
```

A life cycle observer can be tagged with `CoreTags.LIFE_CYCLE_OBSERVER_GROUP` to
indicate its group to be invoked for ordering. We can decorate the observer
class with `@lifeCycleObserver` to provide more metadata for the binding.

```ts
import {lifeCycleObserver} from '@loopback/core';

@lifeCycleObserver('g1')
export class MyLifeCycleObserver {
// ...
}
```

### Register a life cycle observer

A life cycle observer can be registered by calling `lifeCycleObserver()` of the
application. It binds the observer to the application context with a special
tag - `CoreTags.LIFE_CYCLE_OBSERVER`.

```ts
app.lifeCycleObserver(MyObserver);
```

Life cycle observers can be declared via a component class too. when the
component is mounted to an application, the observers are automatically
registered.

```ts
export class MyComponentWithObservers implements Component {
lifeCycleObservers = [XObserver, YObserver];
raymondfeng marked this conversation as resolved.
Show resolved Hide resolved
}

// Mount the component
app.mount(MyComponentWithObservers);
// Now `XObserver` and `YObserver` are registered in the application.
```
83 changes: 83 additions & 0 deletions docs/site/Life-cycle-observer-generator.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
---
lang: en
title: 'Life cycle observer generator'
keywords: LoopBack 4.0, LoopBack 4
sidebar: lb4_sidebar
permalink: /doc/en/lb4/Life-cycle-observer-generator.html
---

{% include content/generator-create-app.html lang=page.lang %}

### Synopsis

Adds a new [LifeCycleObserver](Life-cycle.md) class to a LoopBack application.

```sh
lb4 observer [--group <group>] [<name>]
raymondfeng marked this conversation as resolved.
Show resolved Hide resolved
```

### Arguments and options

`<name>` - Required name of the observer to create as an argument to the
command. If provided, the tool will use that as the default when it prompts for
the name.

`--group <group>` - Optional name of the observer group to sort the execution of
observers by group.

### Interactive Prompts

The tool will prompt you for:

- **Name of the observer.** _(observerName)_ If the name had been supplied from
the command line, the prompt is skipped.

- **Group of the observer.** _(groupName)_ If the group had been supplied from
the command line, the prompt is skipped.

### Output

Once all the prompts have been answered, the CLI will do the following:

- Create a LifeCycleObserver class as follows:
`/src/observers/${observerName}.observer.ts`
- Update `/src/observers/index.ts` to export the newly created LifeCycleObserver
class.

The generated class looks like:

```ts
import {
/* inject, Application, CoreBindings, */
lifeCycleObserver, // The decorator
CoreTags,
LifeCycleObserver, // The interface
} from '@loopback/core';

/**
* This class will be bound to the application as a `LifeCycleObserver` during
* `boot`
*/
@lifeCycleObserver('observer-group-name')
export class HelloObserver implements LifeCycleObserver {
/*
constructor(
@inject(CoreBindings.APPLICATION_INSTANCE) private app: Application,
) {}
*/

/**
* This method will be invoked when the application starts
*/
async start(): Promise<void> {
// Add your logic for start
}

/**
* This method will be invoked when the application stops
*/
async stop(): Promise<void> {
// Add your logic for start
}
}
```
Loading