Skip to content

Commit

Permalink
Merge pull request #3 from unit-mesh/javaServiceAnalyser
Browse files Browse the repository at this point in the history
Java service analyser
  • Loading branch information
phodal authored Jan 9, 2024
2 parents c239191 + f50ceff commit 7013a99
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -1,14 +1,35 @@
package cc.unitmesh.quality.extension

import cc.unitmesh.quality.QualityAnalyser
import cc.unitmesh.quality.extension.rule.TooManyRepositoryDependenciesRule
import chapi.domain.core.CodeDataStruct
import org.archguard.linter.rule.webapi.WebApiRuleSetProvider
import org.archguard.rule.core.Issue
import org.archguard.rule.core.IssuePosition
import org.archguard.rule.core.Rule
import org.archguard.rule.core.RuleType

class JavaServiceAnalyser(thresholds: Map<String, Int> = mapOf()) : QualityAnalyser {
private val webApiRuleSetProvider = WebApiRuleSetProvider()

override fun analysis(nodes: List<CodeDataStruct>): List<Issue> {
val serviceNodes = nodes.filter { it.filterAnnotations("Service").isNotEmpty() }
if (serviceNodes.isNotEmpty()) {
val results: MutableList<Issue> = mutableListOf()
TooManyRepositoryDependenciesRule().visitRoot(serviceNodes, fun(rule: Rule, position: IssuePosition) {
results += Issue(
position,
ruleId = rule.key,
name = rule.name,
detail = rule.description,
ruleType = RuleType.SERVICE_SMELL
)
})
return results;
}
//todo rule3: 调用的repository之间的关联度 -- ER关系上,关联度越强越好
//todo rule4: 不依赖controller层相关概念,如:request/response -- 层与层之间依赖关系清晰
//todo rule5: public filed数量 -- service应该都是private filed
//pending rule6: 不被外界访问的public method -- 应归属到bad smell
//pending rule1: service 长度小于x00行 -- bad smell large class

// 检查 Service 长度,调用的 repository 数量,
return listOf()
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package cc.unitmesh.quality.extension.rule

import chapi.domain.core.CodeDataStruct
import org.archguard.rule.core.IssueEmit
import org.archguard.rule.core.Rule

open class ServiceRule : Rule() {
open fun visitRoot(rootNode: List<CodeDataStruct>, callback: IssueEmit) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package cc.unitmesh.quality.extension.rule

import chapi.domain.core.CodeDataStruct
import org.archguard.rule.core.IssueEmit
import org.archguard.rule.core.IssuePosition
import org.archguard.rule.core.Severity

/**
* Service should not dependent more than 5 repositories.
*/
const val LIMIT = 5

class TooManyRepositoryDependenciesRule : ServiceRule() {
init {
this.id = "too-many-repository-dependencies"
this.name = "TooManyRepositoryDependencies"
this.key = this.javaClass.name
this.severity = Severity.WARN
this.description = "Service should not dependent more than 5 repositories."
}

override fun visitRoot(rootNodes: List<CodeDataStruct>, callback: IssueEmit) {
rootNodes.forEach {
val repositoryCount = it.Fields.filter { it.TypeType.contains("Repository", true) }.count()
if (repositoryCount > LIMIT) {
callback(this, IssuePosition())
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package cc.unitmesh.quality;

import cc.unitmesh.quality.extension.JavaServiceAnalyser
import chapi.ast.javaast.JavaAnalyser
import chapi.domain.core.CodeDataStruct
import kotlinx.serialization.json.Json
import org.archguard.rule.core.RuleType
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test
import java.io.File
import java.nio.file.Paths
import kotlin.test.assertEquals

class JavaServiceAnalyserTest {
private fun loadNodes(source: String): List<CodeDataStruct> {
return Json { ignoreUnknownKeys = true }.decodeFromString(
File(this.javaClass.classLoader.getResource(source)!!.file).readText()
)
}

@Test
fun `should return empty list of issues when node is not a service`() {
val nodes = loadNodes("java/structs_HelloController.json")
val issues = JavaServiceAnalyser().analysis(nodes)

assertEquals(0, issues.size)
}

@Test
fun `should identify too many repository dependencies`() {
val path = getAbsolutePath("java/ServiceWithSixRepositories.java")
val data = JavaAnalyser().analysis(File(path).readText(), "ServiceWithSixRepositories.java").DataStructures
val issues = JavaServiceAnalyser().analysis(data)

Assertions.assertEquals(1, issues.size)
Assertions.assertEquals("TooManyRepositoryDependencies", issues[0].name)
Assertions.assertEquals("Service should not dependent more than 5 repositories.", issues[0].detail)
Assertions.assertEquals(RuleType.SERVICE_SMELL, issues[0].ruleType)
}

private fun getAbsolutePath(path: String): String {
val resource = this.javaClass.classLoader.getResource(path)
return Paths.get(resource!!.toURI()).toFile().absolutePath
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.afs.restapi.service;

import com.afs.restapi.repository.CompanyRepository;
import com.afs.restapi.repository.EmployeeRepository;
import com.afs.restapi.repository.DepartmentRepository;
import com.afs.restapi.repository.TeamRepository;
import com.afs.restapi.repository.GroupRepository;
import com.afs.restapi.repository.CommunitRepository;
import org.springframework.stereotype.Service;

@Service
public class Example {
private CompanyRepository companyRepository;
private EmployeeRepository employeeRepository;
private DepartmentRepository departmentRepository;
private TeamRepository teamRepository;
private GroupRepository groupRepository;
private CommunitRepository communitRepository;

public Example(CompanyRepository companyRepository, EmployeeRepository employeeRepository,
DepartmentRepository departmentRepository, TeamRepository teamRepository,
GroupRepository groupRepository, CommunitRepository communitRepository) {
this.companyRepository = companyRepository;
this.employeeRepository = employeeRepository;
this.departmentRepository = departmentRepository;
this.teamRepository = teamRepository;
this.groupRepository = groupRepository;
this.communitRepository = communitRepository;
}
}

0 comments on commit 7013a99

Please sign in to comment.