@athenna/ioc
Version:
Global Ioc helper for Athenna ecosystem. Built on top of awilix.
136 lines (135 loc) • 3.77 kB
JavaScript
/**
* @athenna/ioc
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { debug } from '#src/debug';
import { Is, Module, Macroable } from '@athenna/common';
import { PROTECTED_FACADE_METHODS } from '#src/constants/ProtectedFacadeMethods';
const athennaTest = await Module.safeImport('@athenna/test');
const Mock = athennaTest?.Mock;
export class FacadeProxyHandler extends Macroable {
/**
* Creates a new instance of FacadeProxyHandler.
*/
constructor(facadeAccessor) {
super();
/**
* The service instance.
*/
this.provider = null;
this.facadeAccessor = facadeAccessor;
}
/**
* Resolve and return the service instance
* of the facade.
*/
getProvider() {
return ioc.safeUse(this.facadeAccessor);
}
/**
* Returns the service instance registered
* inside the facade if exists.
*/
getFreezedProvider() {
return this.provider;
}
/**
* Freezes the service instance of the
* facade until calling `unfreeze()` method.
*/
freeze() {
this.provider = this.getProvider();
}
/**
* Release the service instance of the
* facade so new instances can be created.
*/
unfreeze() {
this.provider = null;
}
/**
* Resolves a service instance of the
* facade and save it to be used as stub.
*
* The stub will be used instead of resolving
* the service.
*/
stub() {
this.freeze();
return Mock.stub(this.provider);
}
/**
* Resolve a service instance of the facade
* and save it to be
*/
spy() {
this.freeze();
return Mock.spy(this.provider);
}
/**
* Create a mock builder instance for the given method
* of the facade.
*/
when(method) {
this.freeze();
return Mock.when(this.provider, method);
}
/**
* Restore the mocked facade to the original state.
*/
restore() {
if (!this.provider) {
return;
}
Mock.restore(this.provider);
this.unfreeze();
}
/**
* Method called by Proxy every time that a value is changed.
* Returns the provider method with a Proxy applied in apply.
* This way we guarantee that we are working with
* the same instance when a Facade method returns this.
*/
set(_, key, value) {
if (this.provider) {
this.provider[key] = value;
return true;
}
const provider = this.getProvider();
provider[key] = value;
return true;
}
/**
* Method called by Proxy every time a new property is called.
* Returns the provider method with a Proxy applied in apply.
* This way we guarantee that we are working with
* the same instance when a Facade method returns this.
*/
get(_, key) {
if (PROTECTED_FACADE_METHODS.includes(key)) {
debug('restricted facade method executed: %s', key);
const method = this[key];
if (!Is.Function(method)) {
return method;
}
return this[key].bind(this);
}
if (this.provider) {
return this.provider[key];
}
const provider = this.getProvider();
if (provider[key] === undefined) {
return undefined;
}
if (!Is.Function(provider[key])) {
return provider[key];
}
return new Proxy(provider[key], {
apply: (method, _this, args) => method.bind(provider)(...args)
});
}
}