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

PoC showing how to mount an LB3 app and include its REST API in OpenAPI spec [DO NOT MERGE] #2318

Closed
wants to merge 29 commits into from
Closed
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
518312d
feat: add lb3app example
bajtos Jan 31, 2019
0534f4a
feat(example-lb3app): add loopback-getting-started app
bajtos Jan 31, 2019
c33613d
feat(example-lb3app): boot the legacy app
bajtos Jan 31, 2019
450e637
feat(example-lb3app): disable LB3 error handling
bajtos Jan 31, 2019
c8433d3
feat(example-lb3app): disable static assets in LB3 app
bajtos Jan 31, 2019
720991c
feat(example-lb3app): expose API spec of LB3 app
bajtos Jan 31, 2019
9217594
feat(example-lb3app): first draft of request handling
bajtos Jan 31, 2019
71d731d
chore: update greenkeeper
bajtos Feb 7, 2019
8c07d48
build: move tests to `src/__tests__`
bajtos Feb 7, 2019
4cdcd02
build: ignore legacy LB3 files in lb3app example
bajtos Feb 7, 2019
d65dff0
feat: introduce `app.mountExpressRouter()` API
bajtos Feb 7, 2019
dc8d3b9
fix(rest): make rebaseOpenApiSpec work
bajtos Feb 11, 2019
1ffa7ba
fix(example-lb3app): add callback to seed script, log to debug
bajtos Feb 11, 2019
0f2a1c7
feat(example-lb3app): add debug logs
bajtos Feb 11, 2019
4c80bef
test(example-lb3app): add basic tests for CoffeeShop API
bajtos Feb 11, 2019
eb7942f
fix(example-lb3app): print status to debuglog
bajtos Feb 11, 2019
3eb7f2c
refactor(example-lb3app): rename the app to CoffeeShopsApplication
bajtos Feb 11, 2019
ce9e311
fix(example-lb3app): rework LB3 booting to correctly wait for boot to…
bajtos Feb 11, 2019
ad37e5c
feat(rest): add support for external Express routes with OpenAPI spec
bajtos Feb 11, 2019
5dd84b7
docs(example-lb3app): update README and other docs
bajtos Feb 11, 2019
d53f67e
feat: start new package `@loopback/booter-lb3app`
bajtos Feb 11, 2019
0ecf71b
feat: implement lb3app booter + update the example
bajtos Feb 11, 2019
f9d9fe5
docs: add SPIKE-NOTES.md
bajtos Feb 11, 2019
b5bed33
fixup! SPIKE-NOTES
bajtos Feb 11, 2019
07cc734
fix(booter-lb3app): fix detection of boot-in-progress
bajtos Feb 12, 2019
54579dd
build: add new packages to greenkeeper
bajtos Feb 12, 2019
8e95629
refactor(booter-lb3app): move boot/core/rest to dev-dependencies
bajtos Feb 12, 2019
8933f63
fix: linting
bajtos Feb 12, 2019
379e0c0
feat(example-lb3app): use in-memory database to avoid race conditions
bajtos Feb 12, 2019
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
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ sandbox/**/*
*.json
CHANGELOG.md
*.yml
examples/lb3app/legacy
107 changes: 107 additions & 0 deletions SPIKE-NOTES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# Spike notes

In this spike pull request, I am showing how we can allow LB3 developers to
expose their legacy applications in an LB4 project and thus allow them to run
both old LB3 and new LB4 endpoints in the same process.

The solution has two major parts:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these going to be permanent part of LB4? Or are we getting rid of them after some time, when?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

app.mountExpressRouter(basePath, router, spec) will become a permanent part of @loopback/rest. I see it as a generic extension point that can be useful beyond LB3 migration.

Lb3AppBooter will live in its own package, we can decide how long we want to support it and when to deprecate it, independently on the rest of the framework.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

app.mountExpressRouter is a good addition for extensibility and customizability.


- `app.mountExpressRouter(basePath, router, spec)`
- `Lb3AppBooter`

## mountExpressRouter

```ts
mountExpressRouter(
basePath: string,
router: ExpressRequestHandler,
spec?: RouterSpec,
): void;
```

This new API is building on top of StaticAssetsRoute and allows LB4 app
developers to add arbitrary set of Express routes and provide OpenAPI spec.

The new request-handling pipeline has the following steps now:

1. Request-preprocessing middleware like CORS, this is not customizable yet.
2. Native LoopBack 4 routes (controller methods or route handlers).
3. External Express routers (if the request was not handled yet).
4. Static assets (if the request was not handled yet).

See
[external-express-routes.ts](./packages/rest/src/router/external-express-routes.ts)
for most of the implementation details.

## Lb3AppBooter

This booter is responsible for setting up LoopBack 3 application, obtaining
OpenAPI spec describing app's REST API and mounting the app on LB4.

I am proposing to put this booter into a standalone npm package to allow us to
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am proposing to put this booter into a standalone npm package ...

Loose coupling FTW. Getting rid of it from the framework (in future) will also be simpler and neater.

get usage statistics from npm download numbers.

See [booter-lb3app/README.md](./packages/booter-lb3app) for documentation on how
to use this new booter. The implementation can be found in
[lb3app.booter.ts](.//packages/booter-lb3app/src/lb3app.booter.ts),

## Example app

The last part is an example application exposing
[loopback-getting-started](https://github.com/strongloop/loopback-getting-started)
via LoopBack 4.

- [lb3app/README](./examples/lb3app/README.md) describes the steps needed to add
LB3 app into an LB4 project
- [lb3app/legacy/](./examples/lb3app/legacy) contains the imported
getting-started app
- [lb3app/src/application.js](./examples/lb3app/src/application.ts) contains the
modified Application class.

## Next steps

I am proposing to create the following user stories to turn this PoC into a
production-ready implementation.

1. Implement `app.mountExpressRouter()`, including test coverage and
documentation.

2. Describe request handling steps. Explain the order in which different
middleware and route handlers are invoked in LB4, what extension points are
provided. This content can go into a new section to [Advanced topics in
Sequence](https://loopback.io/doc/en/lb4/Sequence.html#advanced-topics] or we
can create an entirely new page.

3. Implement `@loopback/booter-lb3app`, including test coverage and
documentation. As part of this effort, create a new package to hold types
needed by Booter implementations. The idea is to decouple API contract
between Bootstrapper and Booters from the actual Bootstrapper implementation.
`@loopback/booter-lb3app` should not have any runtime dependency on
`@loopback/boot` (a dev-dependency for tests is ok).
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking about this point little bit more and came to the conclusion that we should use a different approach. Instead of moving booter-related types to a new package that booter-lb3app will depend on, we should leverage peer dependencies to allow booter-lb3app to specify which versions of @loopback/boot it works with. This is the same approach as other pluggable tools use, e.g. tslint.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in refactor(booter-lb3app): move boot/core/rest to dev-dependencies 8e95629


4. Create an example app based on `loopback-getting-started`
(`@loopback/example-lb3app`) to showcase LB3+LB4 integration. Add acceptance
tests to verify few REST endpoints, also a test to verify that LB3 endpoints
are included in the OpenAPI spec produced by the LB4 app.

5. Migration guide.

- Start by reorganizing the
[docs for LoopBack 3.x users](https://loopback.io/doc/en/lb4/LoopBack-3.x.html):
keep the current page as a top-level overview.
- Move most of the content into a new page with a name like "Understanding
the differences".
- Create a new page with a name like "Migrating from v3". Describe how to use
`Lb3AppBooter`. Explain the differences (breaking changes) introduced by
the migration. I believe the only difference is in the spec format: LB3
produces Swagger (OpenApi v2), LB4 produces OpenApi v3.

6. Spike: dynamically updated LB3 API (routes + spec). In LB3, it's possible to
change the shape of REST API at runtime by adding or removing shared methods
or even entire models. The PoC version of LB3+LB4 does not support spec
updates, we need to do a spike to find the best solution. (For example,
`mountExpressRouter` can accept a spec getter instead of the spec object.) As
for the actual request handling, the PoC version reloads route handlers in
"full" mode but does not reload in "router" mode. As part of the stories
created from the spike, we should fix "router" mode to reload the LB3 handler
when needed.
1 change: 1 addition & 0 deletions examples/lb3app/.npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package-lock=false
2 changes: 2 additions & 0 deletions examples/lb3app/.prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
dist
*.json
6 changes: 6 additions & 0 deletions examples/lb3app/.prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"bracketSpacing": false,
"singleQuote": true,
"printWidth": 80,
"trailingComma": "all"
}
21 changes: 21 additions & 0 deletions examples/lb3app/.vscode/settings.json
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"
}
29 changes: 29 additions & 0 deletions examples/lb3app/.vscode/tasks.json
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"]
}
]
}
25 changes: 25 additions & 0 deletions examples/lb3app/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
Copyright (c) IBM Corp. 2017,2018. All Rights Reserved.
Node module: @loopback/example-lb3app
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.
118 changes: 118 additions & 0 deletions examples/lb3app/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
# @loopback/example-lb3app

This example demonstrates how to mount your existing LoopBack 3 application in a
new LoopBack 4 project.

## Tutorial

1. Create a new LoopBack 4 project using `lb4 app`.

```
$ lb4 app
```

2. Create a new directory `legacy` and copy your existing LoopBack 3 application
there. You should end up with the following directory layout

```
legacy/
# LoopBack 3 application in JavaScript
common/
models/
# LB3 model files
server/
boot/
# LB3 boot scripts
public/
# front-end assets (LB4 way)
src/
# LoopBack 4 application in TypeScript
```

3. Move LB3 dependencies to the main package.json file and remove
`legacy/package.json`. Typically you will need to add the following entries,
plus any connectors or components you are using in your LB3 application.

```json
{
"compression": "^1.0.3",
"cors": "^2.5.2",
"helmet": "^3.15.0",
"loopback": "^3.0.0",
"loopback-boot": "^3.1.1",
"loopback-connector-mysql": "^5.3.1",
"serve-favicon": "^2.0.1",
"strong-error-handler": "^3.2.0"
}
```

4. Disable error handling in your LB3 app, leave it for the new LB4 app.

- Remove `legacy/server/middleware.development.json`
- Edit `legacy/server/middleware.json` and remove the following two entries:
- `final` >> `loopback#urlNotFound`
- `final:after` >> `strong-error-handler`
- Remove `strong-error-handler` from `package.json` dependencies.

5. Move your front-end files from `legacy/client` to `public/` directory and
disable static assets in your LB3 app by removing the following entry in
`legacy/server/middleware.json`:

- `files` >> `loopback#static`

6. Install and configure `@loopback/booter-lb3app` to boot and mount the LB3 application:

1. `npm install --save @loopback/booter-lb3app`

2. Import the booter at the top of your `src/application.ts` file.

```ts
import {Lb3AppBooter} from '@loopback/booter-lb3app';
```

3. Register the booter in Application's constructor:

```ts
this.booters(Lb3AppBooter);
```

## Try it out

Start the new LB4 application

```sh
$ npm start

Server is running at http://127.0.0.1:3000
```

Open the URL printed to console to view your front-end served from `public`
directory or go to `http://127.0.0.1:3000/explorer` to explore the REST API.

### Need help?

Check out our [Gitter channel](https://gitter.im/strongloop/loopback) and ask
for help with this tutorial.

### Bugs/Feedback

Open an issue in [loopback-next](https://github.com/strongloop/loopback-next)
and we'll take a look.

## 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
6 changes: 6 additions & 0 deletions examples/lb3app/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Copyright IBM Corp. 2017,2018. All Rights Reserved.
// Node module: @loopback/example-lb3app
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

export * from './dist';
26 changes: 26 additions & 0 deletions examples/lb3app/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright IBM Corp. 2017,2018. All Rights Reserved.
// Node module: @loopback/example-lb3app
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

const application = require('./dist');

module.exports = application;

if (require.main === module) {
// Run the application
const config = {
rest: {
port: +process.env.PORT || 3000,
host: process.env.HOST || 'localhost',
openApiSpec: {
// useful when used with OASGraph to locate your application
setServersFromRequest: true,
},
},
};
application.main(config).catch(err => {
console.error('Cannot start the application.', err);
process.exit(1);
});
}
8 changes: 8 additions & 0 deletions examples/lb3app/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Copyright IBM Corp. 2017,2018. All Rights Reserved.
// Node module: @loopback/example-lb3app
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

// DO NOT EDIT THIS FILE
// Add any additional (re)exports to src/index.ts instead.
export * from './src';
11 changes: 11 additions & 0 deletions examples/lb3app/legacy/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# loopback-getting-started

This is the example application that accompanies the [Getting started with LoopBack](http://loopback.io/doc/en/lb3/Getting-started-with-LoopBack.html) tutorial. Follow the steps in the tutorial to create this application from scratch.

NOTE: This repository has commits for each step of the tutorial, so you can pick up the tutorial at any point along the way:

See [Getting started with LoopBack](http://loopback.io/doc/en/lb3/Getting-started-with-LoopBack.html) for step-by-step instructions to create this application.

---

[More LoopBack examples](https://loopback.io/doc/en/lb3/Tutorials-and-examples.html)
Loading