Skip to content

Commit

Permalink
feat: ✨ Ability to override services from autowire
Browse files Browse the repository at this point in the history
  • Loading branch information
zazoomauro committed Sep 27, 2024
1 parent 197446d commit 2017d1c
Show file tree
Hide file tree
Showing 15 changed files with 238 additions and 2 deletions.
20 changes: 20 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"standard.enable": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "always"
},
"editor.formatOnSave": true,
"[javascript]": {
"editor.defaultFormatter": null
},
"[typescript]": {
"editor.defaultFormatter": null
},
"standard.autoFixOnSave": true,
"standard.validate": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact"
],
}
6 changes: 6 additions & 0 deletions lib/Autowire.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import Reference from './Reference'
import ServiceFile from './ServiceFile'
import AutowireIdentifier from './AutowireIdentifier'
import ContainerDefaultDirMustBeSet from './Exception/ContainerDefaultDirMustBeSet'
import PassConfig from './PassConfig'
import AutowireOverridePass from './CompilerPass/AutowireOverridePass'

export default class Autowire {
/**
Expand Down Expand Up @@ -121,6 +123,10 @@ export default class Autowire {
if (this._serviceFile instanceof ServiceFile) {
await this._serviceFile.generateFromContainer(this._container)
}
this._container.addCompilerPass(
new AutowireOverridePass(),
PassConfig.TYPE_BEFORE_OPTIMIZATION
)
}

/**
Expand Down
48 changes: 48 additions & 0 deletions lib/CompilerPass/AutowireOverridePass.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import Reference from '../Reference'

export default class AutowireOverridePass {
/**
* @param {ContainerBuilder} container
*/
process (container) {
this._definitions = container.instanceManager.definitions
const overrideDefinitions = container.instanceManager.searchDefinitionsToOverrideArgs()
const toDelete = []

for (const [key, definitionToOverride] of overrideDefinitions) {
toDelete.push(key)
const definitionsToOverride = container.instanceManager.searchNotOverrideDefinitionsByObject(definitionToOverride.Object)

for (const overrideArg of definitionToOverride.overrideArgs) {
const argumentsToOverride = this._searchDefinitionsByClassName(overrideArg.id)
const references = argumentsToOverride.map(arg => new Reference(arg.key))

for (const [, definitionFromOverride] of definitionsToOverride) {
definitionFromOverride.args = [...references]
}
}
}

this._removeDefinitions(toDelete)
}

/**
* @param {string} className
* @returns {Array} - Retorna los resultados de la búsqueda
*/
_searchDefinitionsByClassName (className) {
const result = []
for (const [key, definition] of this._definitions) {
if (definition.Object?.name === className) {
result.push({ key, definition })
}
}
return result
}

_removeDefinitions (keysToDelete) {
for (const key of keysToDelete) {
this._definitions.delete(key)
}
}
}
17 changes: 16 additions & 1 deletion lib/Definition.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ class Definition {
* @param {*|null} Object
* @param {Array} args
*/
constructor (Object = null, args = []) {
constructor (Object = null, args = [], overrideArgs = []) {
this._Object = Object
this._args = args
this._overrideArgs = overrideArgs
this._calls = []
this._tags = []
this._properties = new Map()
Expand Down Expand Up @@ -221,6 +222,20 @@ class Definition {
this._parent = value
}

/**
* @param {Array} value
*/
set overrideArgs (value) {
this._overrideArgs = value
}

/**
* @returns {Array}
*/
get overrideArgs () {
return this._overrideArgs
}

/**
* @param {Object|Reference} Object
* @param {string} method
Expand Down
7 changes: 7 additions & 0 deletions lib/Exception/CannotAutowireOverrideSearch.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default class CannotAutowireOverrideSearch extends Error {
constructor (className = null) {
super(`Cannot Autowire Override ${className}`)
this.name = 'CannotAutowireOverrideSearch'
this.stack = (new Error()).stack
}
}
29 changes: 29 additions & 0 deletions lib/InstanceManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ export default class InstanceManager {
this._alias = alias
}

/**
* @returns {Map}
*/
get definitions () {
return this._definitions
}

/**
* @private
* @param {string} id
Expand Down Expand Up @@ -338,4 +345,26 @@ export default class InstanceManager {

service[call.method](...args)
}

/**
* @returns {Map}
*/
searchDefinitionsToOverrideArgs () {
return new Map(
[...this._definitions]
.filter(([key, definition]) => definition.overrideArgs.length > 0)
)
}

/**
* @param {Function} Object
*
* @returns {Map}
*/
searchNotOverrideDefinitionsByObject (Object) {
return new Map(
[...this._definitions]
.filter(([key, definition]) => definition.Object?.name === Object.name && definition.overrideArgs.length === 0)
)
}
}
11 changes: 11 additions & 0 deletions lib/Loader/FileLoader.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ class FileLoader {
definition.shared = service.shared

this._parseArguments(definition, service.arguments)
this._parseOverrideArguments(definition, service.override_arguments)
this._parseProperties(definition, service.properties)
this._parseCalls(definition, service.calls)
this._parseTags(definition, service.tags)
Expand Down Expand Up @@ -244,6 +245,16 @@ class FileLoader {
definition[argument] = this._getParsedArguments(args)
}

/**
* @param {Definition} definition
* @param {Array} args
*
* @private
*/
_parseOverrideArguments (definition, args = []) {
definition.overrideArgs = this._getParsedArguments(args)
}

/**
* @param {string} classObject
* @param {string} mainClassName
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
services:
_defaults:
autowire: true
rootDir: ../src

App.NoExists:
class: './FooBarAutowireOverride'
override_arguments:
- '@Caca'
9 changes: 9 additions & 0 deletions test/Resources-ts/Autowire-Override/config/services.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
services:
_defaults:
autowire: true
rootDir: ../src

App.FooBarAutowireOverride:
class: './FooBarAutowireOverride'
override_arguments:
- '@CiAdapter'
3 changes: 3 additions & 0 deletions test/Resources-ts/Autowire-Override/src/Adapter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default interface Adapter {
toString(): string;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import Adapter from "../Adapter";

export default class BarAdapter implements Adapter {
toString(): string {
return "bar";
}
}
7 changes: 7 additions & 0 deletions test/Resources-ts/Autowire-Override/src/Adapters/CiAdapter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import Adapter from "../Adapter";

export default class CiAdapter implements Adapter {
toString(): string {
return 'ci';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import Adapter from "../Adapter";

export default class FooAdapter implements Adapter {
toString(): string {
return "foo";
}
}
13 changes: 13 additions & 0 deletions test/Resources-ts/Autowire-Override/src/FooBarAutowireOverride.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import Adapter from "./Adapter";

export default class FooBarAutowireOverride {
constructor(private readonly _adapter: Adapter) {}

getString(): string {
return this._adapter.toString();
}

get adapter() {
return this._adapter
}
}
47 changes: 46 additions & 1 deletion test/node-dependency-injection/lib-ts/Autowire.spec.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, it } from 'mocha'
import chai from 'chai'
import chai, { config } from 'chai'
import chaiAsPromised from 'chai-as-promised';
chai.use(chaiAsPromised);
import path from 'path'
Expand All @@ -25,6 +25,7 @@ import ImplementsOnePath from '../../Resources-ts/AutowireModulePath/src/Service
import ImplementsTwoPath from '../../Resources-ts/AutowireModulePath/src/Service/ImplementsTwo'
import PathExcludedService from '../../Resources-ts/AutowireModulePath/src/ToExclude/ExcludedService'
import PathInFolderExcludedService from '../../Resources-ts/AutowireModulePath/src/ToExclude/InFolderExclude/InFolderExcludedService'
import FooBarAutowireOverride from '../../Resources-ts/Autowire-Override/src/FooBarAutowireOverride'
import ServiceFile from '../../../lib/ServiceFile';
import RootDirectoryNotFound from '../../../lib/Exception/RootDirectoryNotFound';

Expand All @@ -36,6 +37,50 @@ describe('AutowireTS', () => {
const excludedServiceMessage = 'The service ExcludedService is not registered'
const inFolderExcludedMessage = 'The service InFolderExcludedService is not registered'

it("should not override single class with autowiring if not exists", async () => {
const configFile = path.join(
__dirname,
'..',
'..',
resourcesTsFolder,
'Autowire-Override',
'config',
'services-not-exists.yaml'
)
const cb = new ContainerBuilder()
const loader = new YamlFileLoader(cb)
await loader.load(configFile)
await cb.compile()

// Act.
const actual = cb.get(FooBarAutowireOverride)

// Assert.
assert.isUndefined(actual.adapter)
});

it("should override single class with autowiring", async () => {
const configFile = path.join(
__dirname,
'..',
'..',
resourcesTsFolder,
'Autowire-Override',
'config',
'services.yaml'
)
const cb = new ContainerBuilder()
const loader = new YamlFileLoader(cb)
await loader.load(configFile)
await cb.compile()

// Act.
const actual = cb.get(FooBarAutowireOverride)

// Assert.
assert.equal(actual.getString(), "ci")
});

it('should get service file when was properly set', () => {
// Arrange.
const dir = path.join(__dirname, '..', '..', resourcesTsFolder, 'Autowire', 'src')
Expand Down

0 comments on commit 2017d1c

Please sign in to comment.