Skip to content

Commit

Permalink
chore: honor binding metadata for class registrations
Browse files Browse the repository at this point in the history
  • Loading branch information
raymondfeng committed Dec 12, 2018
1 parent b3bf854 commit 2a44d96
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 66 deletions.
51 changes: 31 additions & 20 deletions packages/core/src/application.ts
Expand Up @@ -3,10 +3,16 @@
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

import {Context, Binding, BindingScope, Constructor} from '@loopback/context';
import {Server} from './server';
import {
Binding,
BindingScope,
Constructor,
Context,
createBindingFromClass,
} from '@loopback/context';
import {Component, mountComponent} from './component';
import {CoreBindings} from './keys';
import {Server} from './server';

/**
* Application is the container for various types of artifacts, such as
Expand Down Expand Up @@ -40,10 +46,13 @@ export class Application extends Context {
* ```
*/
controller(controllerCtor: ControllerClass, name?: string): Binding {
name = name || controllerCtor.name;
return this.bind(`controllers.${name}`)
.toClass(controllerCtor)
.tag('controller');
const binding = createBindingFromClass(controllerCtor, {
name,
namespace: 'controllers',
type: 'controller',
});
this.add(binding);
return binding;
}

/**
Expand All @@ -66,13 +75,14 @@ export class Application extends Context {
public server<T extends Server>(
ctor: Constructor<T>,
name?: string,
): Binding {
const suffix = name || ctor.name;
const key = `${CoreBindings.SERVERS}.${suffix}`;
return this.bind(key)
.toClass(ctor)
.tag('server')
.inScope(BindingScope.SINGLETON);
): Binding<T> {
const binding = createBindingFromClass(ctor, {
name,
namespace: CoreBindings.SERVERS,
type: 'server',
}).inScope(BindingScope.SINGLETON);
this.add(binding);
return binding;
}

/**
Expand Down Expand Up @@ -182,15 +192,16 @@ export class Application extends Context {
* ```
*/
public component(componentCtor: Constructor<Component>, name?: string) {
name = name || componentCtor.name;
const componentKey = `components.${name}`;
this.bind(componentKey)
.toClass(componentCtor)
.inScope(BindingScope.SINGLETON)
.tag('component');
const binding = createBindingFromClass(componentCtor, {
name,
namespace: 'components',
type: 'component',
}).inScope(BindingScope.SINGLETON);
this.add(binding);
// Assuming components can be synchronously instantiated
const instance = this.getSync<Component>(componentKey);
const instance = this.getSync<Component>(binding.key);
mountComponent(this, instance);
return binding;
}

/**
Expand Down
75 changes: 45 additions & 30 deletions packages/repository/src/mixins/repository.mixin.ts
Expand Up @@ -3,7 +3,7 @@
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

import {BindingScope, Binding} from '@loopback/context';
import {BindingScope, Binding, createBindingFromClass} from '@loopback/context';
import {Application} from '@loopback/core';
import * as debugFactory from 'debug';
import {Class} from '../common-types';
Expand Down Expand Up @@ -38,7 +38,7 @@ export function RepositoryMixin<T extends Class<any>>(superClass: T) {
/**
* Add a repository to this application.
*
* @param repo The repository to add.
* @param repoClass The repository to add.
*
* ```ts
*
Expand All @@ -63,11 +63,17 @@ export function RepositoryMixin<T extends Class<any>>(superClass: T) {
* ```
*/
// tslint:disable-next-line:no-any
repository(repo: Class<Repository<any>>): void {
const repoKey = `repositories.${repo.name}`;
this.bind(repoKey)
.toClass(repo)
.tag('repository');
repository<R extends Repository<any>>(
repoClass: Class<R>,
name?: string,
): Binding<R> {
const binding = createBindingFromClass(repoClass, {
name,
namespace: 'repositories',
type: 'repository',
}).inScope(BindingScope.SINGLETON);
this.add(binding);
return binding;
}

/**
Expand Down Expand Up @@ -101,24 +107,24 @@ export function RepositoryMixin<T extends Class<any>>(superClass: T) {
* }
* ```
*/
dataSource(
dataSource: Class<juggler.DataSource> | juggler.DataSource,
dataSource<D extends juggler.DataSource>(
dataSource: Class<D> | D,
name?: string,
) {
): Binding<D> {
// We have an instance of
if (dataSource instanceof juggler.DataSource) {
const key = `datasources.${name || dataSource.name}`;
this.bind(key)
return this.bind(key)
.to(dataSource)
.tag('datasource');
} else if (typeof dataSource === 'function') {
const key = `datasources.${name ||
dataSource.dataSourceName ||
dataSource.name}`;
this.bind(key)
.toClass(dataSource)
.tag('datasource')
.inScope(BindingScope.SINGLETON);
const binding = createBindingFromClass(dataSource, {
name: name || dataSource.dataSourceName,
namespace: 'datasources',
type: 'datasource',
}).inScope(BindingScope.SINGLETON);
this.add(binding);
return binding;
} else {
throw new Error('not a valid DataSource.');
}
Expand All @@ -144,8 +150,8 @@ export function RepositoryMixin<T extends Class<any>>(superClass: T) {
* app.component(ProductComponent);
* ```
*/
public component(component: Class<{}>) {
super.component(component);
public component(component: Class<unknown>, name?: string) {
super.component(component, name);
this.mountComponentRepositories(component);
}

Expand All @@ -156,7 +162,7 @@ export function RepositoryMixin<T extends Class<any>>(superClass: T) {
*
* @param component The component to mount repositories of
*/
mountComponentRepositories(component: Class<{}>) {
mountComponentRepositories(component: Class<unknown>) {
const componentKey = `components.${component.name}`;
const compInstance = this.getSync(componentKey);

Expand Down Expand Up @@ -214,15 +220,18 @@ export function RepositoryMixin<T extends Class<any>>(superClass: T) {
*/
export interface ApplicationWithRepositories extends Application {
// tslint:disable-next-line:no-any
repository(repo: Class<any>): void;
repository<R extends Repository<any>>(
repo: Class<R>,
name?: string,
): Binding<R>;
// tslint:disable-next-line:no-any
getRepository<R extends Repository<any>>(repo: Class<R>): Promise<R>;
dataSource(
dataSource: Class<juggler.DataSource> | juggler.DataSource,
dataSource<D extends juggler.DataSource>(
dataSource: Class<D> | D,
name?: string,
): void;
component(component: Class<{}>): void;
mountComponentRepositories(component: Class<{}>): void;
): Binding<D>;
component(component: Class<unknown>, name?: string): Binding;
mountComponentRepositories(component: Class<unknown>): void;
migrateSchema(options?: SchemaMigrationOptions): Promise<void>;
}

Expand Down Expand Up @@ -269,7 +278,9 @@ export class RepositoryMixinDoc {
* ```
*/
// tslint:disable-next-line:no-any
repository(repo: Class<Repository<any>>): void {}
repository(repo: Class<Repository<any>>): Binding {
throw new Error();
}

/**
* Retrieve the repository instance from the given Repository class
Expand Down Expand Up @@ -305,7 +316,9 @@ export class RepositoryMixinDoc {
dataSource(
dataSource: Class<juggler.DataSource> | juggler.DataSource,
name?: string,
) {}
): Binding {
throw new Error();
}

/**
* Add a component to this application. Also mounts
Expand All @@ -327,7 +340,9 @@ export class RepositoryMixinDoc {
* app.component(ProductComponent);
* ```
*/
public component(component: Class<{}>) {}
public component(component: Class<{}>): Binding {
throw new Error();
}

/**
* Get an instance of a component and mount all it's
Expand Down
47 changes: 31 additions & 16 deletions packages/service-proxy/src/mixins/service.mixin.ts
Expand Up @@ -3,7 +3,12 @@
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

import {Provider} from '@loopback/context';
import {
Provider,
createBindingFromClass,
BindingScope,
Binding,
} from '@loopback/context';
import {Application} from '@loopback/core';

/**
Expand Down Expand Up @@ -49,7 +54,7 @@ export function ServiceMixin<T extends Class<any>>(superClass: T) {
*
* export class GeocoderServiceProvider implements Provider<GeocoderService> {
* constructor(
* @inject('datasources.geocoder')
* @inject('services.geocoder')
* protected dataSource: juggler.DataSource = new GeocoderDataSource(),
* ) {}
*
Expand All @@ -61,12 +66,18 @@ export function ServiceMixin<T extends Class<any>>(superClass: T) {
* app.serviceProvider(GeocoderServiceProvider);
* ```
*/
serviceProvider<S>(provider: Class<Provider<S>>): void {
const serviceName = provider.name.replace(/Provider$/, '');
const repoKey = `services.${serviceName}`;
this.bind(repoKey)
.toProvider(provider)
.tag('service');
serviceProvider<S>(
provider: Class<Provider<S>>,
name?: string,
): Binding<S> {
const serviceName = name || provider.name.replace(/Provider$/, '');
const binding = createBindingFromClass(provider, {
name: serviceName,
namespace: 'services',
type: 'service',
}).inScope(BindingScope.SINGLETON);
this.add(binding);
return binding;
}

/**
Expand All @@ -89,8 +100,8 @@ export function ServiceMixin<T extends Class<any>>(superClass: T) {
* app.component(ProductComponent);
* ```
*/
public component(component: Class<{}>) {
super.component(component);
public component(component: Class<unknown>, name?: string) {
super.component(component, name);
this.mountComponentServices(component);
}

Expand All @@ -101,7 +112,7 @@ export function ServiceMixin<T extends Class<any>>(superClass: T) {
*
* @param component The component to mount services of
*/
mountComponentServices(component: Class<{}>) {
mountComponentServices(component: Class<unknown>) {
const componentKey = `components.${component.name}`;
const compInstance = this.getSync(componentKey);

Expand All @@ -119,8 +130,8 @@ export function ServiceMixin<T extends Class<any>>(superClass: T) {
*/
export interface ApplicationWithServices extends Application {
// tslint:disable-next-line:no-any
serviceProvider<S>(provider: Class<Provider<S>>): void;
component(component: Class<{}>): void;
serviceProvider<S>(provider: Class<Provider<S>>, name?: string): Binding<S>;
component(component: Class<{}>, name?: string): Binding;
mountComponentServices(component: Class<{}>): void;
}

Expand Down Expand Up @@ -163,7 +174,9 @@ export class ServiceMixinDoc {
* app.serviceProvider(GeocoderServiceProvider);
* ```
*/
serviceProvider<S>(provider: Class<Provider<S>>): void {}
serviceProvider<S>(provider: Class<Provider<S>>): Binding<S> {
throw new Error();
}

/**
* Add a component to this application. Also mounts
Expand All @@ -185,7 +198,9 @@ export class ServiceMixinDoc {
* app.component(ProductComponent);
* ```
*/
public component(component: Class<{}>) {}
public component(component: Class<unknown>): Binding {
throw new Error();
}

/**
* Get an instance of a component and mount all it's
Expand All @@ -194,5 +209,5 @@ export class ServiceMixinDoc {
*
* @param component The component to mount services of
*/
mountComponentServices(component: Class<{}>) {}
mountComponentServices(component: Class<unknown>) {}
}

0 comments on commit 2a44d96

Please sign in to comment.