phodal/chapi

View on GitHub
docs/usecases/code-linter.md

Summary

Maintainability
Test Coverage
---
layout: default
title: Code Linter
nav_order: 1
parent: Usecases
permalink: /usecases/code-linter
---

Case example: [ArchGuard Rule Linter](https://archguard.org/governance)

- Code Smell, like [SonarQube](https://www.sonarqube.org/)
- Test Code Smell
- Web API Smell
- SQL Smell, like [SQLFluff](https://sqlfluff.com)

```kotlin
class WebApiRuleVisitor(services: List<ContainerService>) : RuleVisitor(services) {
    private var resources: List<ContainerSupply>

    init {
        resources = services.flatMap {
            it.resources
        }
    }

    override fun visitor(ruleSets: Iterable<RuleSet>): List<Issue> {
        val results: MutableList<Issue> = mutableListOf()
        val context = RuleContext()

        ruleSets.forEach { ruleSet ->
            ruleSet.rules.forEach { rule ->
                val apiRule = rule as WebApiRule
                resources.map {
                    apiRule.visitResource(it, context, fun(rule: Rule, position: IssuePosition) {
                        results += Issue(
                            position,
                            ruleId = rule.key,
                            name = rule.name,
                            detail = rule.description,
                            ruleType = RuleType.HTTP_API_SMELL,
                            fullName = "${it.packageName}:${it.className}:${it.methodName}",
                            source = "${it.sourceHttpMethod} ${it.sourceUrl}"
                        )
                    })
                }
            }
        }

        return results
    }
}
```

Web API Rule Example:

```kotlin
class StartWithoutCrudRule : WebApiRule() {
    init {
        this.id = "start-without-crud"
        this.name = "StartWithoutCrudRule"
        this.key = this.javaClass.name
        this.description = "URL 不应该以 CRUD 开头。错误示例: `/api/getbook`, 正确示例: `GET /api/book`"
        this.severity = Severity.WARN
    }

    override fun visitResource(resource: ContainerSupply, context: RuleContext, callback: IssueEmit) {
        val split = resource.sourceUrl.split("/")
        split.forEach { node ->
            CRUD.forEach {
                if (node != it && node.lowercase().startsWith(it)) {
                    callback(this, IssuePosition())
                }
            }
        }
    }
}
```