Skip to content

Commit

Permalink
fix(core): get rid of array registration
Browse files Browse the repository at this point in the history
  • Loading branch information
shimks committed Feb 13, 2018
1 parent 0101887 commit 8d33bec
Show file tree
Hide file tree
Showing 13 changed files with 65 additions and 112 deletions.
3 changes: 2 additions & 1 deletion packages/authentication/test/acceptance/basic-auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,8 @@ describe('Basic Authentication', () => {

async function givenAServer() {
app = new Application();
app.components([AuthenticationComponent, RestComponent]);
app.component(AuthenticationComponent);
app.component(RestComponent);
server = await app.getServer(RestServer);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,6 @@ class LoggingComponent implements Component{
loggers: [ColorLogger];
}

const app = new LoggingApplication({
components: [LoggingComponent] // Logger from MyComponent will be bound to loggers.ColorLogger
});
const app = new LoggingApplication();
app.component(LoggingComponent); // Logger from MyComponent will be bound to loggers.ColorLogger
```
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,18 @@ The logger will log the URL, the parsed request parameters, and the result. The

TimerProvider is automatically bound to your Application's [Context](http://loopback.io/doc/en/lb4/Context.html) using the LogComponent which exports this provider with a binding key of `extension-starter.timer`. You can learn more about components in the [related resources section](#related-resources).

This provider makes availble to your application a timer function which given a start time _(given as an array [seconds, nanoseconds])_ can give you a total time elapsed since the start in milliseconds. The timer can also start timing if no start time is given. This is used by LogComponent to allow a user to time a Sequence.
This provider makes availble to your application a timer function which given a start time _(given as an array [seconds, nanoseconds])_ can give you a total time elapsed since the start in milliseconds. The timer can also start timing if no start time is given. This is used by LogComponent to allow a user to time a Sequence.

*NOTE:* _You can get the start time in the required format by using `this.logger.startTimer()`._

You can provide your own implementation of the elapsed time function by binding it to the binding key (accessible via `ExtensionStarterBindings`) as follows:
```ts
app.bind(ExtensionStarterBindings.TIMER).to(timerFn);
```
```

### LogProvider

LogProvider can automatically be bound to your Application's Context using the LogComponent which exports the provider with a binding key of `extension-starter.actions.log`.
LogProvider can automatically be bound to your Application's Context using the LogComponent which exports the provider with a binding key of `extension-starter.actions.log`.

The key can be accessed by importing `ExtensionStarterBindings` as follows:

Expand All @@ -38,7 +38,7 @@ import {ExtensionStarterBindings} from 'HelloExtensions';
const key = ExtensionStarterBindings.LOG_ACTION;
```

LogProvider gives us a seuqence action and a `startTimer` function. In order to use the sequence action, you must define your own sequence as shown below.
LogProvider gives us a seuqence action and a `startTimer` function. In order to use the sequence action, you must define your own sequence as shown below.

**Example: Sequence**
```ts
Expand Down Expand Up @@ -85,9 +85,9 @@ Once a sequence has been written, we can just use that in our Application as fol
**Example: Application**
```ts
const app = new Application({
sequence: LogSequence,
components: [LogComponent]
sequence: LogSequence
});
app.component(LogComponent);

// Now all requests handled by our sequence will be logged.
```
Expand Down
6 changes: 2 additions & 4 deletions packages/core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,8 @@ const app = new Application({
port: 3001,
},
});
app.components([
RestComponent, // REST Server
GrpcComponent, // GRPC Server
]);
app.component(RestComponent) // REST Server
app.component(GrpcComponent) // GRPC Server

(async function start() {
// Let's retrieve the bound instances of our servers.
Expand Down
47 changes: 0 additions & 47 deletions packages/core/src/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,30 +48,6 @@ export class Application extends Context {
.tag('controller');
}

/**
* Bind an array of controllers to the Application's master
* context.
* Each controller added in this way will automatically be named based on
* the class constructor name with the "controllers." prefix.
*
* If you wish to control the binding keys for particular controller
* instances, use the app.controller function instead.
* ```ts
* app.controllers([
* FooController,
* BarController,
* ]);
* // Creates a binding for "controllers.FooController" and a binding for
* // "controllers.BarController";
* ```
*
* @param ctors Array of controller classes.
* @returns {Binding[]} An array of bindings for the registered controllers.
*/
controllers(ctors: ControllerClass[]): Binding[] {
return ctors.map(ctor => this.controller(ctor));
}

/**
* Bind a Server constructor to the Application's master context.
* Each server constructor added in this way must provide a unique prefix
Expand Down Expand Up @@ -218,29 +194,6 @@ export class Application extends Context {
const instance = this.getSync(componentKey);
mountComponent(this, instance);
}

/**
* Add multiple components to this application and register their
* extensions.
*
* Each component added in this way will automatically be named based on the
* class constructor name with the "components." prefix.
*
* If you wish to control the binding keys for particular instances,
* use the app.component function instead.
* ```ts
* app.components([
* RestComponent,
* GRPCComponent,
* ]);
* // Creates a binding for "components.RestComponent" and a binding for
* // "components.GRPCComponent";
* ```
* @param ctors Array of components to add.
*/
public components(ctors: Constructor<Component>[]) {
ctors.map(ctor => this.component(ctor));
}
}

/**
Expand Down
3 changes: 2 additions & 1 deletion packages/core/test/acceptance/application.acceptance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,8 @@ describe('Bootstrapping the application', () => {
}
}
const app = new Application();
app.components([ConfigComponent, GreetingComponent]);
app.component(ConfigComponent);
app.component(GreetingComponent);

expect(app.getSync('greeting')).to.equal('Hi!');
});
Expand Down
16 changes: 7 additions & 9 deletions packages/core/test/acceptance/application.feature.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,24 @@

- In order to initialize my application
- As an app developer
- I want to register components and sequences when initalizing the application
- I want to register components when initalizing the application
- So that I can use them throughout the application lifecycle

## Scenario: Basic usage (config provided)

- Given an importable `Application` class
- When I create an application with user-defined configurations
- Then the application should register the given components and sequences
- Then the application should register the given components

```ts
import {Application} from '@loopback/core';
import {Authentication} from '@loopback/authentication';
import {Authorization} from '@loopback/authorization';
import {Rejection} from '@loopback/rejection';

const app = new Application({
components: [Todo, Authentication, Authorization, Rejection],
sequence: [TodoSequence]
});

// get metadata about the registered components
console.log(app.find('sequence.*')); // [Bindings] should match the 1 sequence registered above
const app = new Application();
app.component(Todo);
app.component(Authentication);
app.component(Authorization);
app.component(Rejection);
```
66 changes: 36 additions & 30 deletions packages/core/test/unit/application.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,12 @@

import {expect} from '@loopback/testlab';
import {Application, Server, Component} from '../../index';
import {Context} from '@loopback/context';
import {Context, Constructor} from '@loopback/context';

describe('Application', () => {
describe('controller binding', () => {
let app: Application;
class MyController {}
class MySecondController {}

beforeEach(givenApp);

Expand All @@ -29,21 +28,6 @@ describe('Application', () => {
expect(findKeysByTag(app, 'controller')).to.containEql(binding.key);
});

it('binds multiple controllers passed in as an array', () => {
const controllers = [MyController, MySecondController];
const bindings = app.controllers(controllers);

const bindA = bindings[0];
expect(Array.from(bindA.tags)).to.containEql('controller');
expect(bindA.key).to.equal('controllers.MyController');
expect(findKeysByTag(app, 'controller')).to.containEql(bindA.key);

const bindB = bindings[1];
expect(Array.from(bindB.tags)).to.containEql('controller');
expect(bindB.key).to.equal('controllers.MySecondController');
expect(findKeysByTag(app, 'controller')).to.containEql(bindB.key);
});

function givenApp() {
app = new Application();
}
Expand All @@ -55,9 +39,6 @@ describe('Application', () => {
class MyComponent implements Component {
controllers = [MyController];
}
class MySecondComponent implements Component {
controllers = [MyController];
}

beforeEach(givenApp);

Expand All @@ -75,16 +56,6 @@ describe('Application', () => {
);
});

it('binds multiple components passed in as an array', () => {
app.components([MyComponent, MySecondComponent]);
expect(findKeysByTag(app, 'component')).to.containEql(
'components.MyComponent',
);
expect(findKeysByTag(app, 'component')).to.containEql(
'components.MySecondComponent',
);
});

function givenApp() {
app = new Application();
}
Expand Down Expand Up @@ -119,11 +90,46 @@ describe('Application', () => {
});
});

describe('start', () => {
it('starts all injected servers', async () => {
const app = new Application();
app.component(FakeComponent);

await app.start();
const server = await app.getServer(FakeServer);
expect(server).to.not.be.null();
expect(server.running).to.equal(true);
await app.stop();
});

it('does not attempt to start poorly named bindings', async () => {
const app = new Application();
app.component(FakeComponent);

// The app.start should not attempt to start this binding.
app.bind('controllers.servers').to({});
await app.start();
await app.stop();
});
});

function findKeysByTag(ctx: Context, tag: string | RegExp) {
return ctx.findByTag(tag).map(binding => binding.key);
}
});

class FakeComponent implements Component {
servers: {
[name: string]: Constructor<Server>;
};
constructor() {
this.servers = {
FakeServer,
FakeServer2: FakeServer,
};
}
}

class FakeServer extends Context implements Server {
running: boolean = false;
constructor() {
Expand Down
2 changes: 1 addition & 1 deletion packages/example-log-extension/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class LogApp extends LogLevelMixin(RestApplication) {
super({
logLevel: LOG_LEVEL.ERROR,
});
this.components(LogComponent);
this.component(LogComponent);
this.controller(MyController);
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

import {Application} from '@loopback/core';
import {
RestComponent,
RestApplication,
RestServer,
get,
param,
Expand Down Expand Up @@ -46,7 +45,7 @@ describe('log extension acceptance test', () => {
let server: RestServer;
let spy: SinonSpy;

class LogApp extends LogLevelMixin(Application) {}
class LogApp extends LogLevelMixin(RestApplication) {}

const debugMatch: string = chalk.white(
'DEBUG: /debug :: MyController.debug() => debug called',
Expand Down Expand Up @@ -239,7 +238,7 @@ describe('log extension acceptance test', () => {

async function createApp() {
app = new LogApp();
app.components([RestComponent, LogComponent]);
app.component(LogComponent);

app.bind(EXAMPLE_LOG_BINDINGS.TIMER).to(timer);
server = await app.getServer(RestServer);
Expand Down
2 changes: 1 addition & 1 deletion packages/rest/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ Application options.
port: 3001
}
});
app.components(RestComponent);
app.component(RestComponent);
```

### `rest` options
Expand Down
5 changes: 1 addition & 4 deletions packages/rest/src/rest-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,7 @@ const OPENAPI_SPEC_MAPPING: {[key: string]: OpenApiSpecOptions} = {
* A REST API server for use with Loopback.
* Add this server to your application by importing the RestComponent.
* ```ts
* const app = new MyApplication({
* components: [RestComponent]
* });
* // OR
* const app = new MyApplication();
* app.component(RestComponent);
* ```
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ describe('RestApplication', () => {
() => {
// tslint:disable-next-line:no-unused-variable
const app = new RestApplication();
app.components([RestComponent, OtherRestComponent]);
app.component(RestComponent);
app.component(OtherRestComponent);
},
Error,
ERR_NO_MULTI_SERVER,
Expand Down

0 comments on commit 8d33bec

Please sign in to comment.