Skip to content

Commit

Permalink
feat(packages/eslint-plugin-sui): [ADR] Decorators: Use @AsyncInlineE…
Browse files Browse the repository at this point in the history
…rror() on services and reposito
  • Loading branch information
oriolpuig committed Jun 6, 2024
1 parent b9ec4eb commit 4870875
Showing 1 changed file with 92 additions and 39 deletions.
131 changes: 92 additions & 39 deletions packages/eslint-plugin-sui/src/rules/decorators-adr.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* @fileoverview Ensure that at least all your UseCases are using @AsyncInlineError decorator from sui
* @fileoverview Ensure that at least all your UseCases, Services and Repositories are using @AsyncInlineError decorator from sui
*/
'use strict'

Expand All @@ -22,57 +22,110 @@ module.exports = {
schema: [],
messages: {
notFoundAsyncInlineErrorDecorator: dedent`
You have to use the @AsyncInlineError() decorator in your UseCase execute method
The execute method of an UseCase or Service and the public Repository methods should use the @AsyncInlineError() decorator in order to follow the Adevinta domain code guidelines.
`,
asyncInlineErrorDecoratorIsNotFirst: dedent`
The @AsyncInlineError() decorator should be the first one
The @AsyncInlineError() decorator must be the first one assigned into the method to avoid inconsistence with other decorators.
`
}
},
create: function (context) {
// Estamos en una clase UseCase

return {
ClassDeclaration(node) {
const executeFn = node?.body?.body?.find(
methodDefinition => methodDefinition.type === 'MethodDefinition' && methodDefinition.key.name === 'execute'
)

// Pepe
// AsyncInlineErrror
// function()

if (executeFn) {
const decorators = executeFn?.decorators
const isAsyncInlineErrorLastDecorator = decorators?.at(-1)?.expression?.callee?.name === 'AsyncInlineError'
const asyncInlineErrorDecoratorNode = decorators?.find(
decorator => decorator.expression.callee.name === 'AsyncInlineError'
const className = node.id.name
const superClassName = node.superClass?.name

console.log('ClassName: ', className)

// UseCase
const containUseCase = className.endsWith('UseCase')
const extendsUseCase = superClassName === 'UseCase'
const isUsecase = containUseCase || extendsUseCase

// Service
const containService = className.endsWith('Service')
const extendsService = superClassName === 'Service'
const isService = containService || extendsService

// Repository
const containRepository = className.endsWith('Repository')
const extendsRepository = superClassName === 'Repository'
const isRepository = containRepository || extendsRepository

// Skip if it's not a UseCase, Service or Repository
if (!isUsecase && !isService && !isRepository) return

// UseCases and Services always have a execute method
if (isUsecase || isService) {
const executeFn = node?.body?.body?.find(
methodDefinition => methodDefinition.type === 'MethodDefinition' && methodDefinition.key.name === 'execute'
)

// TODO validar en services
// TODO validar en repositories
// TODO quickFix implementar
if (!asyncInlineErrorDecoratorNode) {
context.report({
node: executeFn.key,
messageId: 'notFoundAsyncInlineErrorDecorator',
fix: fixer => {
return fixer.insertTextBefore(executeFn, '@AsyncInlineError()\n')
}
})
}
if (executeFn) {
const decorators = executeFn?.decorators
const isAsyncInlineErrorLastDecorator = decorators?.at(-1)?.expression?.callee?.name === 'AsyncInlineError'
const asyncInlineErrorDecoratorNode = decorators?.find(
decorator => decorator?.expression?.callee?.name === 'AsyncInlineError'
)

if (asyncInlineErrorDecoratorNode && !isAsyncInlineErrorLastDecorator) {
context.report({
node: asyncInlineErrorDecoratorNode,
messageId: 'asyncInlineErrorDecoratorIsNotFirst',
*fix(fixer) {
yield fixer.remove(asyncInlineErrorDecoratorNode)
yield fixer.insertTextAfter(decorators.at(-1), '\n@AsyncInlineError()')
}
})
if (!asyncInlineErrorDecoratorNode) {
context.report({
node: executeFn.key,
messageId: 'notFoundAsyncInlineErrorDecorator'
})
}

if (asyncInlineErrorDecoratorNode && !isAsyncInlineErrorLastDecorator) {
context.report({
node: asyncInlineErrorDecoratorNode,
messageId: 'asyncInlineErrorDecoratorIsNotFirst',
*fix(fixer) {
yield fixer.remove(asyncInlineErrorDecoratorNode)
yield fixer.insertTextAfter(decorators.at(-1), '\n@AsyncInlineError()')
}
})
}
}
}

if (isRepository) {
// Get all public methods from the repository
const publicMethods = node?.body?.body?.filter(
methodDefinition =>
methodDefinition.type === 'MethodDefinition' &&
methodDefinition.key.name !== 'constructor' && // skip constructor method
!methodDefinition.key.name.startsWith('_') && // Remove methods with _ prefix
methodDefinition.key.type !== 'PrivateIdentifier' // Remove methods with # decorator
)

publicMethods?.forEach(method => {
const methodDecorators = method.decorators
const decorators = method?.decorators
const isAsyncInlineErrorLastDecorator =
methodDecorators?.at(-1)?.expression?.callee?.name === 'AsyncInlineError'
const asyncInlineErrorDecoratorNode = methodDecorators?.find(
decorator => decorator?.expression?.callee?.name === 'AsyncInlineError'
)

if (!asyncInlineErrorDecoratorNode) {
context.report({
node: method.key,
messageId: 'notFoundAsyncInlineErrorDecorator'
})
}

if (asyncInlineErrorDecoratorNode && !isAsyncInlineErrorLastDecorator) {
context.report({
node: asyncInlineErrorDecoratorNode,
messageId: 'asyncInlineErrorDecoratorIsNotFirst',
*fix(fixer) {
yield fixer.remove(asyncInlineErrorDecoratorNode)
yield fixer.insertTextAfter(decorators.at(-1), '\n@AsyncInlineError()')
}
})
}
})
}
}
}
}
Expand Down

0 comments on commit 4870875

Please sign in to comment.