From ded442bc8a2aa7b5afaee55e223c013476cad9cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Bajto=C5=A1?= Date: Thu, 30 Apr 2020 15:48:14 +0200 Subject: [PATCH] docs: first draft of Component Migration - general aspects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Miroslav Bajtoš --- .../migration/components/current-context.md | 47 +++++++ docs/site/migration/components/mixins.md | 48 ++++++++ docs/site/migration/components/overview.md | 79 ++++++++++++ .../migration/components/project-layout.md | 116 ++++++++++++++++++ docs/site/migration/extensions.md | 17 --- docs/site/sidebars/lb4_sidebar.yml | 17 ++- .../2020-04-how-to-migrate-lb3-components.md | 116 +++++------------- 7 files changed, 338 insertions(+), 102 deletions(-) create mode 100644 docs/site/migration/components/current-context.md create mode 100644 docs/site/migration/components/mixins.md create mode 100644 docs/site/migration/components/overview.md create mode 100644 docs/site/migration/components/project-layout.md delete mode 100644 docs/site/migration/extensions.md diff --git a/docs/site/migration/components/current-context.md b/docs/site/migration/components/current-context.md new file mode 100644 index 000000000000..928f7cac885b --- /dev/null +++ b/docs/site/migration/components/current-context.md @@ -0,0 +1,47 @@ +--- +lang: en +title: 'Migrating access to current context' +keywords: LoopBack 4, LoopBack 3, Migration, Extensions, Components +sidebar: lb4_sidebar +permalink: /doc/en/lb4/migration-extensions-current-context.html +--- + +{% include tip.html content=" +Missing instructions for your LoopBack 3 use case? Please report a [Migration docs issue](https://github.com/strongloop/loopback-next/issues/new?labels=question,Migration,Docs&template=Migration_docs.md) on GitHub to let us know. +" %} + +It's often desirable to share contextual data between different parts of an +application. For example, a REST connector calling a backend web service may +want to forward transaction (correlation) id provided in a HTTP header of the +incoming request, or perhaps an auditing component wants to access the +information about the user making the request to store it in the log. + +LoopBack 3 offers three approaches: + +1. The recommended solution is to explicitly pass any contextual information via + `options` argument. Most LoopBack APIs accept (and forward) this argument and + there are means how to initialize the `options` value based on the incoming + request. + +2. Code working at REST layer can access and store contextual information on the + HTTP request object. + +3. An experimental component + [`loopback-context`](https://github.com/strongloop/loopback-context) uses + continuation-local-storage to provide static per-request storage that can be + accessed from anywhere inside a LoopBack application (an Express middleware, + a model method, a connector, etc.). + +In LoopBack 4, extensions should use `@inject` decorators to access contextual +information. For example: + +- `@inject(key)` and `@inject.getter(key)` to receive values from the context +- `@inject.setter(key)` to obtain a setter function for writing values to the + context + +To keep the contextual data per-request (as opposed to per-application), the +`TRANSIENT` binding scope should be used. + +Components can keep using the old `options`-based approach where it makes more +sense than Dependency Injection, typically when working with existing +`options`-based code like Repository APIs. diff --git a/docs/site/migration/components/mixins.md b/docs/site/migration/components/mixins.md new file mode 100644 index 000000000000..a888bacfd267 --- /dev/null +++ b/docs/site/migration/components/mixins.md @@ -0,0 +1,48 @@ +--- +lang: en +title: 'Migrating components contributing Model mixins' +keywords: LoopBack 4, LoopBack 3, Migration, Extensions, Components, Mixins +sidebar: lb4_sidebar +permalink: /doc/en/lb4/migration-extensions-mixins.html +--- + +{% include tip.html content=" +Missing instructions for your LoopBack 3 use case? Please report a [Migration docs issue](https://github.com/strongloop/loopback-next/issues/new?labels=question,Migration,Docs&template=Migration_docs.md) on GitHub to let us know. +" %} + +In LoopBack 3, a component contributes mixins by providing files exporting mixin +functions, for example: + +```js +// lib/my-mixin.js +module.exports = myMixin(Model, options) { + // modify the target model provided in `Model` + // apply configuration as specified in `options` +}; +``` + +In LoopBack 4, models are architecturally decoupled into 3 entities (a model, a +repository and a controller), as further explained in +[Migrating models](../models/overview.md) and +[Migrating custom model methods](../models/methods.md). As a result, most +LoopBack 3 mixins become a set of multiple mixins in LoopBack 4. + +To migrate a mixin from a LoopBack 3 component to a LoopBack 4 component: + +1. Follow the steps described in + [Migrating model mixins ](https://loopback.io/doc/en/lb4/migration-models-mixins.html) + to convert your mixin implementation to LB4 style. We recommend to put the + new files to `src/mixins` directory in your LB4 component project. + +2. Modify your LB4 component to export the mixins - add `export` statements to + components `src/index.ts` file. For example: + + ```ts + // src/index.ts + export * from './mixins/my-mixin'; + ``` + +3. Update your documentation to show how to apply the new mixins in LoopBack 4 + applications, use the content provided in + [Migrating model mixins ](https://loopback.io/doc/en/lb4/migration-models-mixins.html) + for inspiration. diff --git a/docs/site/migration/components/overview.md b/docs/site/migration/components/overview.md new file mode 100644 index 000000000000..2e934cd8dfa1 --- /dev/null +++ b/docs/site/migration/components/overview.md @@ -0,0 +1,79 @@ +--- +lang: en +title: 'Migrating components and extensions' +keywords: LoopBack 4, LoopBack 3, Migration, Extensions, Components +sidebar: lb4_sidebar +permalink: /doc/en/lb4/migration-extensions-overview.html +redirect_from: /doc/en/lb4/migration-extensions.html +--- + +{% include tip.html content=" +Missing instructions for your LoopBack 3 use case? Please report a [Migration docs issue](https://github.com/strongloop/loopback-next/issues/new?labels=question,Migration,Docs&template=Migration_docs.md) on GitHub to let us know. +" %} + +{% include note.html content=" +This is a placeholder page, the task of adding content is tracked by the +following GitHub issue: +[loopback-next#3955](https://github.com/strongloop/loopback-next/issues/3955) +" %} + +In LoopBack 3, a component is essentially a function that extends and/or patches +the target application. + +LoopBack 3 components can contribute: + +- additional models +- new REST API endpoints (Express routes) +- mixin to be used by application models + +They can also modify application's models to + +- register new + [Operation Hooks](https://loopback.io/doc/en/lb3/Operation-hooks.html) +- register new [Remote Hooks](https://loopback.io/doc/en/lb3/Remote-hooks.html) +- define new relations + +In LoopBack 4, a component is typically a class providing artifacts it wants to +contribute to the application. It is responsibility of the target application to +import those artifacts. + +LoopBack 4 components can contribute: + +- [Model and Entity classes](../../Model.md) +- [Mixins](../../Mixin.md) +- [Decorators](../../Creating-decorators.md) +- [Sequence Actions](../../Sequence.md#actions) +- [Controllers](../../Controllers.md) +- [Life cycle observers](../../Extension-life-cycle.md) +- [Repositories](../../Repositories.md) +- [Service proxies](../../Calling-other-APIs-and-Web-Services.md) +- [Servers](../../Creating-servers.md) +- [HTTP request parsers](../../Extending-request-body-parsing.md) +- Extensions for [Extension Points](../../Extension-point-and-extensions.md) + - [Booters](../../Booting-an-Application.md#custom-booters) + - [Model API builders](../../Extending-Model-API-builder.md) + - Extensions for Extension Points defined by 3rd-party component +- Any other values to be bound in `Application`'s context: + - [Classes](../../Binding.md#a-class) + - [Providers](../../Binding.md#a-provider) + - [Arbitrary bindings](../../Binding.md) (instances of `Binding` class) + +As the last resort, LoopBack 4 components can also modify the target application +directly by calling `Application` APIs (this is similar to LoopBack 3 approach). + +To make the migration guide easier to navigate, we split component-related +instructions into several sub-sections. + +1. [Migrating component project layout](./project-layout.md) describes how to + migrate your LoopBack 3 extension project infrastructure to LoopBack 4 style + and how to update the instructions for using your component. + +1. [Migrating access to current context](./current-context.md) describes how to + migrate code accessing contextual information shared by different parts of a + LoopBack application. + +1. [Migrating components contributing Model mixins](./mixins) explains how to + migrate a component that's contributing Model mixins. + +1. _More sections will be created as part of + [loopback-next#3955](https://github.com/strongloop/loopback-next/issues/3955)._ diff --git a/docs/site/migration/components/project-layout.md b/docs/site/migration/components/project-layout.md new file mode 100644 index 000000000000..3480982a3058 --- /dev/null +++ b/docs/site/migration/components/project-layout.md @@ -0,0 +1,116 @@ +--- +lang: en +title: 'Migrating component project layout' +keywords: LoopBack 4, LoopBack 3, Migration, Extensions, Components +sidebar: lb4_sidebar +permalink: /doc/en/lb4/migration-extensions-project-layout.html +--- + +{% include tip.html content=" +Missing instructions for your LoopBack 3 use case? Please report a [Migration docs issue](https://github.com/strongloop/loopback-next/issues/new?labels=question,Migration,Docs&template=Migration_docs.md) on GitHub to let us know. +" %} + +The first step on the component migration journey is to reorganize the project +files, change component's entry point and documentation for adding the component +to the target application. + +## LoopBack 3 component layout & mounting + +A LB3 component is implemented as a function accepting the target app object and +the configuration options. + +A minimal component consists of a single `index.js` file with the following +content: + +```ts +module.exports = function initializeComponent(loopbackApplication, options) { + // implementation +}; +``` + +A component is typically added to a LB3 application by creating a new entry in +`server/component-config.json`, see +[LoopBack components](https://loopback.io/doc/en/lb3/LoopBack-components.html). + +For example: + +```json +{ + "loopback-explorer": { + "mountPath": "/explorer" + } +} +``` + +This allows the component to receive configuration. App developers can provide +environment-specific configuration by using `component-config.{env}.json` files. + +## LoopBack 4 layout + +As explained in [Creating components](../../Creating-Components.md) and +[Using components](../../Using-components.md), a typical LoopBack 4 component is +an npm package exporting a Component class. + +The component class is usually implemented inside +`src/{component-name}.component.ts` file, for example +[src/metrics.component.ts](https://github.com/strongloop/loopback-next/blob/38f10b240551227d2d030c2fe8ee206880c9e029/extensions/metrics/src/metrics.component.ts): + +```ts +import {Application, Component, CoreBindings} from '@loopback/core'; +import {bind, config, ContextTags, inject} from '@loopback/context'; +import {MetricsBindings} from './keys'; +import {DEFAULT_METRICS_OPTIONS, MetricsOptions} from './types'; + +@bind({tags: {[ContextTags.KEY]: MetricsBindings.COMPONENT}}) +export class MetricsComponent implements Component { + constructor( + @inject(CoreBindings.APPLICATION_INSTANCE) + private application: Application, + @config() + options: MetricsOptions = DEFAULT_METRICS_OPTIONS, + ) { + // ... + } + // ... +} +``` + +The code snippet above also shows how a LoopBack 4 component can receive +configuration and the target application object. + +## Usage instructions + +LoopBack 4 components are added to applications inside application constructor. + +First, the application file needs to import the component class: + +```ts +import {MetricsComponent} from '@loopback/extension-metrics'; +``` + +Then in the constructor, add the component to your application: + +```ts +this.component(MetricsComponent); +``` + +Finally, the application can configure the component by adding the following +code to its constructor: + +```ts +this.configure(MetricsBindings.COMPONENT).to({ + // the configuration +}); +``` + +## Migration steps + +It is not feasible to migrate a LoopBack 3 component project to a LoopBack 4 +component project in a series of incremental changes done within the same +repository. We recommend to create a new project using +[`lb4 extension`](../../Extension-generator.md) CLI command and then +incrementally migrate artifacts from the original LoopBack 3 component to the +new project. + +{% include note.html content="The extension project template used by `lb4 extension` is a bit outdated now, please refer to [loopback-next#5336](https://github.com/strongloop/loopback-next/issues/5336) for more details. +" %} diff --git a/docs/site/migration/extensions.md b/docs/site/migration/extensions.md deleted file mode 100644 index 59bb59108122..000000000000 --- a/docs/site/migration/extensions.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -lang: en -title: 'Migrating components and extensions' -keywords: LoopBack 4.0, LoopBack 4, LoopBack 3, Migration -sidebar: lb4_sidebar -permalink: /doc/en/lb4/migration-extensions.html ---- - -{% include tip.html content=" -Missing instructions for your LoopBack 3 use case? Please report a [Migration docs issue](https://github.com/strongloop/loopback-next/issues/new?labels=question,Migration,Docs&template=Migration_docs.md) on GitHub to let us know. -" %} - -{% include note.html content=" -This is a placeholder page, the task of adding content is tracked by the -following GitHub issue: -[loopback-next#3955](https://github.com/strongloop/loopback-next/issues/3955) -" %} diff --git a/docs/site/sidebars/lb4_sidebar.yml b/docs/site/sidebars/lb4_sidebar.yml index 5cd3fd1643d2..360081bdb83c 100644 --- a/docs/site/sidebars/lb4_sidebar.yml +++ b/docs/site/sidebars/lb4_sidebar.yml @@ -153,7 +153,7 @@ children: url: Access-databases.html output: 'web, pdf' children: - + - title: 'Working with data' url: Working-with-data.html output: 'web, pdf' @@ -864,8 +864,21 @@ children: output: 'web, pdf' - title: 'Components and extensions' - url: migration-extensions.html + url: migration-extensions-overview.html output: 'web, pdf' + children: + + - title: 'Project layout' + url: migration-extensions-project-layout.html + output: 'web, pdf' + + - title: 'Current context' + url: migration-extensions-current-context.html + output: 'web, pdf' + + - title: 'Mixins' + url: migration-extensions-mixins.html + output: 'web, pdf' - title: 'Clients (API consumers)' url: migration-clients.html diff --git a/docs/spikes/2020-04-how-to-migrate-lb3-components.md b/docs/spikes/2020-04-how-to-migrate-lb3-components.md index ead59894a01b..4d09e0295f07 100644 --- a/docs/spikes/2020-04-how-to-migrate-lb3-components.md +++ b/docs/spikes/2020-04-how-to-migrate-lb3-components.md @@ -24,9 +24,14 @@ Migration of the following component is out of scope of this spike: - [API transports](#api-transports) - [Authentication & authorization](#authentication--authorization) - [Introspection](#introspection) -- [Migration of component layout & instructions from mounting to an app](#migration-of-component-layout--instructions-from-mounting-to-an-app) - - [LB3 component layout & mounting](#lb3-component-layout--mounting) - - [Migration to LB4](#migration-to-lb4) +- [Migration](#migration) + - [Migrate general aspects](#migrate-general-aspects) + - [Migrate Models, Entities and Repositories](#migrate-models-entities-and-repositories) + - [Migrate REST API](#migrate-rest-api) + - [Migrate Services (local and remote)](#migrate-services-local-and-remote) + - [Migrate API transports](#migrate-api-transports) + - [Migrate Authentication & authorization](#migrate-authentication--authorization) + - [Migrate Introspection](#migrate-introspection) - [Overview of existing LB3 components](#overview-of-existing-lb3-components) - [Push notifications](#push-notifications) - [Storage component](#storage-component) @@ -52,16 +57,6 @@ coming from._ code to receive user-provided configuration & target app instance. What are the instructions for adding a LB4 component to a LB4 app. - TODOs for the migration guide based on @raymondfeng comments: - - - Describe what a LB3 component can contribute to the application when it's - mounted. - - Maybe a diagram would help if it shows the handshake between an application - and a component as well as typical artifacts exported from a component. - - To some extent, a LB3 application asks its components to extend/patch the - app. In contrast, a LB4 application imports bindings (representing the - component's contribution to the app) from its components. - 2. A context shared by all parts of the application, allowing different layers to store and retrieve values like "the current user". In LB3, we have `loopback-context` based on continuation-local-storage and `options`-based @@ -203,95 +198,50 @@ coming from._ information necessary to configure reverse-proxy routing rules (Kong, nginx, etc.). -## Migration of component layout & instructions from mounting to an app +## Migration -### LB3 component layout & mounting +See [Migrating components](../site/migration/components/overview.md) -A LB3 component is implemented as a function accepting the target app object and -the configuration options. +### Migrate general aspects -```ts -module.exports = function initializeComponent(loopbackApplication, options) { - // impl -}; -``` +> Component layout & instructions for mounting to an app -A component is typically added to a LB3 application by creating a new entry in -`server/component-config.json`, see -[LoopBack components](https://loopback.io/doc/en/lb3/LoopBack-components.html). +See +[Migrating component project layout](../site/migration/components/project-layout.md). -For example: +> Migrate context -```json -{ - "loopback-explorer": { - "mountPath": "/explorer" - } -} -``` +See +[Migrating access to current context](../site/migration/components/current-context.md). -This allows the component to receive configuration. App developers can provide -environment-specific configuration by using `component-config.{env}.json` files. +> Migrate mixins -### Migration to LB4 +See +[Migrating components contributing Model mixins](../site/migration/components/mixins.md) -- How to add a component to a LB4 application? -- How to configure the component? -- How can a component receive the configuration object and the target - application? +### Migrate Models, Entities and Repositories -See the existing solutions: +TBD -- https://github.com/strongloop/loopback-next/tree/master/extensions/logging#basic-use -- https://github.com/strongloop/loopback-next/tree/master/extensions/logging#configure-the-logging-component -- https://github.com/strongloop/loopback-next/tree/master/extensions/apiconnect#usage -- https://github.com/strongloop/loopback-next/tree/master/extensions/health#basic-use -- https://github.com/strongloop/loopback-next/tree/master/extensions/metrics#basic-use +### Migrate REST API -Here is the current convention for adding a component to a LB4 app & configuring -it: +TBD -Start by importing the component class: +### Migrate Services (local and remote) -```ts -import {MetricsComponent} from '@loopback/extension-metrics'; -``` +TBD -In the constructor, add the component to your application: +### Migrate API transports -```ts -this.component(MetricsComponent); -``` +TBD -Configure the component by adding the following code to the app: +### Migrate Authentication & authorization -```ts -this.configure(MetricsBindings.COMPONENT).to({ - // the configuration -}); -``` +TBD -Obtaining the target app & the custom configuration inside the component: +### Migrate Introspection -```ts -import {Application, Component, CoreBindings} from '@loopback/core'; -import {bind, config, ContextTags, inject} from '@loopback/context'; -import {MetricsBindings} from './keys'; -import {DEFAULT_METRICS_OPTIONS, MetricsOptions} from './types'; - -@bind({tags: {[ContextTags.KEY]: MetricsBindings.COMPONENT}}) -export class MetricsComponent implements Component { - constructor( - @inject(CoreBindings.APPLICATION_INSTANCE) - private application: Application, - @config() - options: MetricsOptions = DEFAULT_METRICS_OPTIONS, - ) { - // ... - } - // ... -} -``` +TBD ## Overview of existing LB3 components @@ -374,7 +324,7 @@ LB3 models to formally describe these entities. ### Logging components _NOTE: We already have a Winston-based logging extension, see -[@loopback/logging](./extensions/logging/README.md)._ +[@loopback/logging](../../extensions/logging/README.md)._ https://www.npmjs.com/package/loopback-component-bunyan