From 09e1b7775669bc64350b3b4fc2f1dec84ae52c75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Bajto=C5=A1?= Date: Fri, 6 Sep 2019 10:10:32 +0200 Subject: [PATCH] feat(context): add more logs to simplify troubleshooting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Miroslav Bajtoš --- .../__tests__/unit/binding-inspector.unit.ts | 12 ++++++ packages/context/src/binding-inspector.ts | 39 +++++++++++++------ 2 files changed, 40 insertions(+), 11 deletions(-) diff --git a/packages/context/src/__tests__/unit/binding-inspector.unit.ts b/packages/context/src/__tests__/unit/binding-inspector.unit.ts index 67f21dee6843..aac0bbed3bf2 100644 --- a/packages/context/src/__tests__/unit/binding-inspector.unit.ts +++ b/packages/context/src/__tests__/unit/binding-inspector.unit.ts @@ -206,6 +206,18 @@ describe('createBindingFromClass()', () => { expect(binding.key).to.eql('services.MyService'); }); + it('includes class name in error messages', () => { + expect(() => { + // Reproduce a problem that @bajtos encountered when the project + // was not built correctly and somehow `@bind` was called with `undefined` + // eslint-disable-next-line @typescript-eslint/no-explicit-any + @bind(undefined as any) + class MyClass {} + + return createBindingFromClass(MyClass); + }).to.throw(/(while building binding for class MyClass)/); + }); + function givenBindingFromClass( cls: Constructor, ctx: Context = new Context(), diff --git a/packages/context/src/binding-inspector.ts b/packages/context/src/binding-inspector.ts index 0080bf972f7c..dbc6b265fdcc 100644 --- a/packages/context/src/binding-inspector.ts +++ b/packages/context/src/binding-inspector.ts @@ -4,12 +4,15 @@ // License text available at https://opensource.org/licenses/MIT import {MetadataAccessor, MetadataInspector} from '@loopback/metadata'; +import * as debugFactory from 'debug'; import {Binding, BindingScope, BindingTag, BindingTemplate} from './binding'; import {BindingAddress} from './binding-key'; import {ContextTags} from './keys'; import {Provider} from './provider'; import {Constructor} from './value-promise'; +const debug = debugFactory('loopback:context:binding-inspector'); + /** * Binding metadata from `@bind` */ @@ -63,10 +66,11 @@ export function isProviderClass( export function asProvider( target: Constructor>, ): BindingTemplate { - return binding => + return function bindAsProvider(binding) { binding.toProvider(target).tag(ContextTags.PROVIDER, { [ContextTags.TYPE]: ContextTags.PROVIDER, }); + }; } /** @@ -78,7 +82,7 @@ export function asClassOrProvider( target: Constructor, ): BindingTemplate { // Add a template to bind to a class or provider - return binding => { + return function bindAsClassOrProvider(binding) { if (isProviderClass(target)) { asProvider(target)(binding); } else { @@ -94,7 +98,7 @@ export function asClassOrProvider( export function asBindingTemplate( scopeAndTags: BindingScopeAndTags, ): BindingTemplate { - return binding => { + return function applyBindingScopeAndTag(binding) { if (scopeAndTags.scope) { binding.inScope(scopeAndTags.scope); } @@ -140,10 +144,11 @@ export function bindingTemplateFor( cls: Constructor>, ): BindingTemplate { const spec = getBindingMetadata(cls); + debug('class %s has binding metadata', cls.name, spec); const templateFunctions = (spec && spec.templates) || [ asClassOrProvider(cls), ]; - return binding => { + return function applyBindingTemplatesFromMetadata(binding) { for (const t of templateFunctions) { binding.apply(t); } @@ -215,12 +220,23 @@ export function createBindingFromClass( cls: Constructor>, options: BindingFromClassOptions = {}, ): Binding { - const templateFn = bindingTemplateFor(cls); - let key = options.key; - if (!key) { - key = buildBindingKey(cls, options); + debug('create binding from class %s with options', cls.name, options); + try { + const templateFn = bindingTemplateFor(cls); + const key = buildBindingKey(cls, options); + const binding = Binding.bind(key).apply(templateFn); + applyClassBindingOptions(binding, options); + return binding; + } catch (err) { + err.message += ` (while building binding for class ${cls.name})`; + throw err; } - const binding = Binding.bind(key).apply(templateFn); +} + +function applyClassBindingOptions( + binding: Binding, + options: BindingFromClassOptions, +) { if (options.name) { binding.tag({name: options.name}); } @@ -230,7 +246,6 @@ export function createBindingFromClass( if (options.defaultScope) { binding.applyDefaultScope(options.defaultScope); } - return binding; } /** @@ -272,11 +287,13 @@ function buildBindingKey( cls: Constructor, options: BindingFromClassOptions = {}, ) { + if (options.key) return options.key; + const templateFn = bindingTemplateFor(cls); // Create a temporary binding const bindingTemplate = new Binding('template').apply(templateFn); // Is there a `key` tag? - let key: string = options.key || bindingTemplate.tagMap[ContextTags.KEY]; + let key: string = bindingTemplate.tagMap[ContextTags.KEY]; if (key) return key; let namespace =