-
-
Notifications
You must be signed in to change notification settings - Fork 7.6k
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
refactor(core): extract instance wrapper merge logic #9915
Changes from all commits
1da899f
ad15406
6b9ed77
7b759d5
57cd765
d1ec1e2
a052e0c
75b4577
aa2ee95
62f1fa3
2a7fc84
7696f43
b0d735d
a2f4b34
4c16d20
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import { | ||
ClassProvider, | ||
FactoryProvider, | ||
Provider, | ||
ValueProvider, | ||
} from '@nestjs/common'; | ||
import { isUndefined } from '@nestjs/common/utils/shared.utils'; | ||
|
||
export function isClassProvider<T = any>( | ||
provider: Provider, | ||
): provider is ClassProvider<T> { | ||
return Boolean((provider as ClassProvider<T>)?.useClass); | ||
} | ||
|
||
export function isValueProvider<T = any>( | ||
provider: Provider, | ||
): provider is ValueProvider<T> { | ||
const providerValue = (provider as ValueProvider)?.useValue; | ||
return !isUndefined(providerValue); | ||
} | ||
|
||
export function isFactoryProvider<T = any>( | ||
provider: Provider, | ||
): provider is FactoryProvider<T> { | ||
return Boolean((provider as FactoryProvider).useFactory); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
import { ClassProvider, FactoryProvider, ValueProvider } from '@nestjs/common'; | ||
import { expect } from 'chai'; | ||
import { | ||
isClassProvider, | ||
isFactoryProvider, | ||
isValueProvider, | ||
} from '../../../injector/helpers/provider-classifier'; | ||
|
||
describe('provider classifier', () => { | ||
describe('isClassProvider', () => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For each of these utility functions, it may be useful to have some runtime checks to guarantee we're dealing with correct providers. For instance, in the case of a const wrongFactoryProvider = {
provide: 'token',
useFactory: {}
} as FactoryProvider The object itself is not a real function. Should this really be recognized as a |
||
it('should return true if useClass is present', () => { | ||
const classProvider: ClassProvider = { | ||
useClass: class TestClass {}, | ||
provide: 'token', | ||
}; | ||
|
||
expect(isClassProvider(classProvider)).to.be.true; | ||
}); | ||
|
||
it('should return false if useClass is undefined', () => { | ||
const classProvider: ClassProvider = { | ||
useClass: undefined, | ||
provide: 'token', | ||
}; | ||
|
||
expect(isClassProvider(classProvider)).to.be.false; | ||
}); | ||
|
||
it('should return false if useClass is not present', () => { | ||
const classProvider = { | ||
provide: 'token', | ||
}; | ||
|
||
expect(isClassProvider(classProvider as ClassProvider)).to.be.false; | ||
}); | ||
|
||
it('should return false if provider is undefined', () => { | ||
const classProvider = undefined; | ||
|
||
expect(isClassProvider(classProvider as ClassProvider)).to.be.false; | ||
}); | ||
}); | ||
|
||
describe('isValueProvider', () => { | ||
it('should return true if useValue is not undefined', () => { | ||
const valueProvider: ValueProvider = { | ||
useValue: 'value', | ||
provide: 'token', | ||
}; | ||
|
||
expect(isValueProvider(valueProvider)).to.be.true; | ||
}); | ||
|
||
it('should return true if useValue is "false"', () => { | ||
const valueProvider: ValueProvider = { | ||
useValue: false, | ||
provide: 'token', | ||
}; | ||
|
||
expect(isValueProvider(valueProvider)).to.be.true; | ||
}); | ||
|
||
it('should return true if useValue is "null"', () => { | ||
const valueProvider: ValueProvider = { | ||
useValue: null, | ||
provide: 'token', | ||
}; | ||
|
||
expect(isValueProvider(valueProvider)).to.be.true; | ||
}); | ||
|
||
it('should return true if useValue is an empty string', () => { | ||
const valueProvider: ValueProvider = { | ||
useValue: null, | ||
provide: '', | ||
}; | ||
|
||
expect(isValueProvider(valueProvider)).to.be.true; | ||
}); | ||
|
||
it('should return false if useValue is undefined', () => { | ||
const valueProvider: ValueProvider = { | ||
useValue: undefined, | ||
provide: 'token', | ||
}; | ||
|
||
expect(isValueProvider(valueProvider)).to.be.false; | ||
}); | ||
|
||
it('should return false if useValue is not present', () => { | ||
const valueProvider = { | ||
provide: 'token', | ||
}; | ||
|
||
expect(isValueProvider(valueProvider as ValueProvider)).to.be.false; | ||
}); | ||
|
||
it('should return false if provider is undefined', () => { | ||
const valueProvider = undefined; | ||
|
||
expect(isValueProvider(valueProvider as ValueProvider)).to.be.false; | ||
}); | ||
}); | ||
|
||
describe('isFactoryProvider', () => { | ||
it('should return true if useFactory is present', () => { | ||
const factoryProvider: FactoryProvider = { | ||
provide: 'token', | ||
useFactory: () => {}, | ||
}; | ||
|
||
expect(isFactoryProvider(factoryProvider)).to.be.true; | ||
}); | ||
|
||
it('should return false if useFactory is not present', () => { | ||
const factoryProvider = { | ||
provide: 'token', | ||
}; | ||
|
||
expect(isFactoryProvider(factoryProvider as FactoryProvider)).to.be.false; | ||
}); | ||
|
||
it('should return false if useFactory is undefined', () => { | ||
const factoryProvider: FactoryProvider = { | ||
provide: 'token', | ||
useFactory: undefined, | ||
}; | ||
|
||
expect(isFactoryProvider(factoryProvider as FactoryProvider)).to.be.false; | ||
}); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -434,4 +434,68 @@ describe('InstanceWrapper', () => { | |
}); | ||
}); | ||
}); | ||
|
||
describe('mergeWith', () => { | ||
describe('when provider is a ValueProvider', () => { | ||
it('should provide the given value in the STATIC_CONTEXT', () => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I appreciate a careful review of these tests to guarantee I tested the correct outcome for each case |
||
const wrapper = new InstanceWrapper(); | ||
wrapper.mergeWith({ | ||
useValue: 'value', | ||
provide: 'token', | ||
}); | ||
|
||
expect( | ||
wrapper.getInstanceByContextId(STATIC_CONTEXT).instance, | ||
).to.be.equal('value'); | ||
}); | ||
}); | ||
|
||
describe('when provider is a ClassProvider', () => { | ||
it('should alter the instance wrapper metatype with the given class', () => { | ||
const wrapper = new InstanceWrapper(); | ||
|
||
wrapper.mergeWith({ | ||
useClass: TestClass, | ||
provide: 'token', | ||
}); | ||
|
||
expect(wrapper.metatype).to.be.eql(TestClass); | ||
}); | ||
}); | ||
|
||
describe('when provider is a FactoryProvider', () => { | ||
describe('and it has injected dependencies', () => { | ||
it('should alter the instance wrapper metatype and inject attributes with the given values', () => { | ||
const wrapper = new InstanceWrapper(); | ||
|
||
const factory = (_dependency1: any, _dependency2: any) => {}; | ||
const injectedDependencies = ['dependency1', 'dependency2']; | ||
|
||
wrapper.mergeWith({ | ||
provide: 'token', | ||
useFactory: factory, | ||
inject: injectedDependencies, | ||
}); | ||
|
||
expect(wrapper.metatype).to.be.eql(factory); | ||
expect(wrapper.inject).to.be.eq(injectedDependencies); | ||
}); | ||
}); | ||
|
||
describe('and it has no injected dependencies', () => { | ||
it('should alter the instance wrapper metatype with the given values', () => { | ||
const wrapper = new InstanceWrapper(); | ||
const factory = (_dependency1: any, _dependency2: any) => {}; | ||
|
||
wrapper.mergeWith({ | ||
provide: 'token', | ||
useFactory: factory, | ||
}); | ||
|
||
expect(wrapper.metatype).to.be.eql(factory); | ||
expect(wrapper.inject).to.be.eql([]); | ||
}); | ||
}); | ||
}); | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we have to set the scope to DEFAULT in this case? @kamilmysliwiec
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@thiagomini static values are always singletons
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense, thanks for the reply!