-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
The example illustrates how to implement extension point/extenion pattern in LoopBack 4 to provide great extensibility.
- Loading branch information
1 parent
167668d
commit fc53201
Showing
23 changed files
with
585 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
package-lock=false |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
dist | ||
*.json |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
{ | ||
"bracketSpacing": false, | ||
"singleQuote": true, | ||
"printWidth": 80, | ||
"trailingComma": "all" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
{ | ||
"editor.rulers": [80], | ||
"editor.tabCompletion": "on", | ||
"editor.tabSize": 2, | ||
"editor.trimAutoWhitespace": true, | ||
"editor.formatOnSave": true, | ||
|
||
"files.exclude": { | ||
"**/.DS_Store": true, | ||
"**/.git": true, | ||
"**/.hg": true, | ||
"**/.svn": true, | ||
"**/CVS": true, | ||
"dist": true, | ||
}, | ||
"files.insertFinalNewline": true, | ||
"files.trimTrailingWhitespace": true, | ||
|
||
"tslint.ignoreDefinitionFiles": true, | ||
"typescript.tsdk": "./node_modules/typescript/lib" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
{ | ||
// See https://go.microsoft.com/fwlink/?LinkId=733558 | ||
// for the documentation about the tasks.json format | ||
"version": "2.0.0", | ||
"tasks": [ | ||
{ | ||
"label": "Watch and Compile Project", | ||
"type": "shell", | ||
"command": "npm", | ||
"args": ["--silent", "run", "build:watch"], | ||
"group": { | ||
"kind": "build", | ||
"isDefault": true | ||
}, | ||
"problemMatcher": "$tsc-watch" | ||
}, | ||
{ | ||
"label": "Build, Test and Lint", | ||
"type": "shell", | ||
"command": "npm", | ||
"args": ["--silent", "run", "test:dev"], | ||
"group": { | ||
"kind": "test", | ||
"isDefault": true | ||
}, | ||
"problemMatcher": ["$tsc", "$tslint5"] | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
Copyright (c) Author 2018. All Rights Reserved. | ||
Node module: @loopback/example-greeter-extension | ||
This project is licensed under the MIT License, full text below. | ||
|
||
-------- | ||
|
||
MIT license | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in | ||
all copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
THE SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
# @loopback/example-greeter-extension | ||
|
||
An example project showing how to extend LoopBack 4 using Extension Point/Extension | ||
pattern. | ||
|
||
![greeters](greeters.png) | ||
|
||
## Overview | ||
|
||
[Extension point/extension](https://wiki.eclipse.org/FAQ_What_are_extensions_and_extension_points%3F) is a powerful pattern to allow great extensibility/pluggability and avoid tight coupling. | ||
|
||
## Define an extension point | ||
|
||
In our scenario, we want to allow other modules to extend or customize how | ||
people are greeted in different languages. To achieve that, we declare the | ||
`greeter` extension point, which declares a contract as a TypeScript interface | ||
that extensions must conform to. | ||
|
||
### Define interface for extensions | ||
|
||
```ts | ||
/** | ||
* Typically an extension point defines an interface as the contract for | ||
* extensions to implement | ||
*/ | ||
export interface Greeter { | ||
language: string; | ||
greet(name: string): string; | ||
} | ||
``` | ||
|
||
### Define class for the extension point | ||
|
||
```ts | ||
/** | ||
* An extension point for greeters that can greet in different languages | ||
*/ | ||
export class GreeterExtensionPoint { | ||
constructor( | ||
/** | ||
* Inject a getter function to fetch greeters (bindings tagged with | ||
* 'greeter') | ||
*/ | ||
@inject.getter(bindingTagFilter({extensionPoint: 'greeter'})) | ||
private greeters: Getter<Greeter[]>, | ||
) {} | ||
``` | ||
#### Access extensions for a given extension point | ||
To simplify access to extensions for a given extension point, we use dependency | ||
injection to receive a `getter` function that gives us a list of greeters. | ||
#### Implement the delegation logic | ||
Typically, the extension point implementation will get a list of registered | ||
extensions. For example, when a person needs to be greeted in a specific | ||
language, the code iterates through all greeters to find an instance that | ||
matches the language. | ||
## Implement an extension | ||
Modules that want to connect to `greeter` extension point must implement | ||
`Greeter` interface in their extension. The key attribute is that the | ||
`GreeterExtensionPoint` being extended knows nothing about the module that is | ||
connecting to it beyond the scope of that contract. This allows `greeters` built | ||
by different individuals or companies to interact seamlessly, even without | ||
their knowing much about one another. | ||
## Register an extension point | ||
To register an extension point, we simply bind the implementation class to a | ||
`Context`. For example: | ||
```ts | ||
app | ||
.bind('greeter-extension-point') | ||
.toClass(GreeterExtensionPoint) | ||
.inScope(BindingScope.SINGLETON); | ||
``` | ||
The process can be automated with a component too: | ||
```ts | ||
import {createBindingFromClass} from '@loopback/context'; | ||
import {Component} from '@loopback/core'; | ||
import {GreeterExtensionPoint} from './greeter-extension-point'; | ||
|
||
export class GreeterComponent implements Component { | ||
bindings = [ | ||
createBindingFromClass(GreeterExtensionPoint, { | ||
key: 'greeter-extension-point', | ||
}), | ||
]; | ||
} | ||
``` | ||
## Register extensions | ||
To connect an extension to an extension point, we just have to bind the | ||
extension to the `Context` and tag the binding with `{extensionPoint: 'greeter'}`. | ||
```ts | ||
app | ||
.bind('greeters.FrenchGreeter') | ||
.toClass(FrenchGreeter) | ||
.apply(asGreeter); | ||
``` | ||
Please note `asGreeter` is a binding template function, which is equivalent as | ||
configuring a binding with `{extensionPoint: 'greeter'}` tag and in the | ||
`SINGLETON` scope. | ||
```ts | ||
/** | ||
* A binding template for greeter extensions | ||
* @param binding | ||
*/ | ||
export const asGreeter: BindingTemplate = binding => | ||
binding.inScope(BindingScope.SINGLETON).tag({extensionPoint: 'greeter'}); | ||
``` | ||
## Configure an extension point | ||
Sometimes it's desirable to make the extension point configurable. | ||
## Configure an extension | ||
Some extensions also support customization. | ||
## Contributions | ||
- [Guidelines](https://github.com/strongloop/loopback-next/blob/master/docs/CONTRIBUTING.md) | ||
- [Join the team](https://github.com/strongloop/loopback-next/issues/110) | ||
## Tests | ||
Run `npm test` from the root folder. | ||
## Contributors | ||
See | ||
[all contributors](https://github.com/strongloop/loopback-next/graphs/contributors). | ||
## License | ||
MIT |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
// Copyright IBM Corp. 2018. All Rights Reserved. | ||
// Node module: @loopback/example-greeter-extension | ||
// This file is licensed under the MIT License. | ||
// License text available at https://opensource.org/licenses/MIT | ||
|
||
export * from './dist'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
// Copyright IBM Corp. 2018. All Rights Reserved. | ||
// Node module: @loopback/example-greeter-extension | ||
// This file is licensed under the MIT License. | ||
// License text available at https://opensource.org/licenses/MIT | ||
|
||
module.exports = require('./dist'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
// Copyright IBM Corp. 2018. All Rights Reserved. | ||
// Node module: @loopback/example-log-extension | ||
// This file is licensed under the MIT License. | ||
// License text available at https://opensource.org/licenses/MIT | ||
|
||
export * from './src'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
{ | ||
"name": "@loopback/example-greeter-extension", | ||
"version": "1.0.0-1", | ||
"description": "An example extension point/extensions for LoopBack 4", | ||
"main": "index.js", | ||
"engines": { | ||
"node": ">=8.9" | ||
}, | ||
"scripts": { | ||
"build:apidocs": "lb-apidocs", | ||
"build": "lb-tsc es2017 --outDir dist", | ||
"build:watch": "lb-tsc es2017 --outDir dist --watch", | ||
"clean": "lb-clean *example-greeter-extension-*.tgz dist package api-docs", | ||
"lint": "npm run prettier:check && npm run tslint", | ||
"lint:fix": "npm run tslint:fix && npm run prettier:fix", | ||
"prettier:cli": "lb-prettier \"**/*.ts\" \"**/*.js\"", | ||
"prettier:check": "npm run prettier:cli -- -l", | ||
"prettier:fix": "npm run prettier:cli -- --write", | ||
"tslint": "lb-tslint", | ||
"tslint:fix": "npm run tslint -- --fix", | ||
"pretest": "npm run clean && npm run build", | ||
"test": "lb-mocha \"dist/test/unit/**/*.js\" \"dist/test/acceptance/**/*.js\"", | ||
"posttest": "npm run lint", | ||
"test:dev": "lb-mocha --allow-console-logs dist/test/**/*.js && npm run posttest", | ||
"verify": "npm pack && tar xf *example-greeter-extension*.tgz && tree package && npm run clean" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/strongloop/loopback-next.git" | ||
}, | ||
"publishConfig": { | ||
"access": "public" | ||
}, | ||
"keywords": [ | ||
"loopback", | ||
"loopback-extension" | ||
], | ||
"license": "MIT", | ||
"bugs": { | ||
"url": "https://github.com/strongloop/loopback-next/issues" | ||
}, | ||
"homepage": "https://github.com/strongloop/loopback-next/tree/master/examples/greeter-extension", | ||
"devDependencies": { | ||
"@loopback/build": "^1.1.0", | ||
"@loopback/testlab": "^1.0.3", | ||
"@loopback/tslint-config": "^1.0.0", | ||
"@types/debug": "0.0.30", | ||
"@types/node": "^10.11.2", | ||
"tslint": "^5.12.0", | ||
"typescript": "^3.2.2" | ||
}, | ||
"dependencies": { | ||
"@loopback/context": "^1.4.0", | ||
"@loopback/core": "^1.1.3", | ||
"@loopback/openapi-v3": "^1.1.5", | ||
"@loopback/rest": "^1.5.1", | ||
"chalk": "^2.4.2", | ||
"debug": "^4.0.1" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
// Copyright IBM Corp. 2018. All Rights Reserved. | ||
// Node module: @loopback/example-greeter-extension | ||
// This file is licensed under the MIT License. | ||
// License text available at https://opensource.org/licenses/MIT | ||
|
||
import {createBindingFromClass} from '@loopback/context'; | ||
import {Component} from '@loopback/core'; | ||
import {ChineseGreeter} from './greeters/greeter-cn'; | ||
import {EnglishGreeter} from './greeters/greeter-en'; | ||
import {GreeterExtensionPoint} from './greeter-extension-point'; | ||
|
||
export class GreeterComponent implements Component { | ||
bindings = [ | ||
createBindingFromClass(GreeterExtensionPoint, { | ||
key: 'greeter-extension-point', | ||
}), | ||
createBindingFromClass(EnglishGreeter, {namespace: 'greeters'}), | ||
createBindingFromClass(ChineseGreeter, {namespace: 'greeters'}), | ||
]; | ||
} |
Oops, something went wrong.