Skip to content

Commit

Permalink
feat: merge api tool parser from DevTi
Browse files Browse the repository at this point in the history
  • Loading branch information
phodal committed Dec 4, 2023
1 parent 151f9f7 commit 56b96ff
Show file tree
Hide file tree
Showing 49 changed files with 7,178 additions and 0 deletions.
1 change: 1 addition & 0 deletions code-modules/api-tool-parser/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.env
12 changes: 12 additions & 0 deletions code-modules/api-tool-parser/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# API Processor

Todos:

- [x] API Parser
- [x] Swagger
- [ ] Split swagger to grouping
- [x] Postman
- [x] CLI for prompting
- [x] API to Domains
- [ ] Domain Knowledge Tree
- [ ] Remove Version from API
38 changes: 38 additions & 0 deletions code-modules/api-tool-parser/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
@Suppress("DSL_SCOPE_VIOLATION")
plugins {
alias(libs.plugins.jvm)
alias(libs.plugins.shadow)
alias(libs.plugins.serialization)
application
}

repositories {
mavenCentral()
}

dependencies {
implementation(libs.clikt)
implementation(libs.serialization.json)

// Logging
implementation(libs.logging.slf4j.api)
implementation(libs.logging.logback.classic)

implementation(libs.bundles.markdown)
implementation(libs.dotenv)

// java parser
implementation(libs.swagger.parser.v3)

testImplementation(kotlin("test"))

testImplementation(libs.bundles.test)
}

tasks.test {
useJUnitPlatform()
}

kotlin {
jvmToolchain(11)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package cc.unitmesh.processor.api

import cc.unitmesh.processor.api.base.ApiProcessor
import cc.unitmesh.processor.api.parser.PostmanProcessor
import cc.unitmesh.processor.api.swagger.Swagger3Processor
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.io.File

object ApiProcessorDetector {
private val logger: Logger = LoggerFactory.getLogger(ApiProcessorDetector::class.java)

fun detectApiProcessor(file: File, withPostman: Boolean = false, postmanOnly: Boolean = false): ApiProcessor? {
val content = file.readText()

if (withPostman || postmanOnly) {
if (file.extension == "json") {
val isPostmanContent = content.contains("_postman_id") && content.contains("schema")
if (isPostmanContent) {
return PostmanProcessor(file)
}
}
}

if (postmanOnly) {
return null
}

// if not json, yaml, yml file, skip
if (!file.extension.matches(Regex("json|yaml|yml"))) {
return null
}

return getSwaggerProcessor(file)?.let {
return it
}
}

private fun getSwaggerProcessor(it: File): ApiProcessor? {
return try {
val openAPI = Swagger3Processor.fromFile(it) ?: return null
Swagger3Processor(openAPI)
} catch (e: Exception) {
logger.info("Failed to parse ${it.absolutePath}", e)
null
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package cc.unitmesh.processor.api.base

import kotlinx.serialization.Serializable

@Serializable
data class ApiCollection(
val name: String,
val description: String,
val items: List<ApiItem>,
) {
override fun toString(): String {
return "$name: ${items.joinToString(", ") { it.toString() }}"
}
}

@Serializable
data class ApiItem(
val path: String,
val method: String,
var description: String,
val operationId: String,
val tags: List<String>,
val request: Request? = null,
val response: List<Response> = listOf(),
) {
override fun toString(): String {
val request = request.toString()
val response = response.joinToString(", ") { it.toString() }
return "$method $path $description $request $response"
}
}

@Serializable
data class Parameter(
val name: String,
val type: String,
) {
override fun toString() = "$name: $type"
}

enum class BodyMode {
RAW_TEXT,
TYPED,
}

@Serializable
data class Request(
val parameters: List<Parameter> = listOf(),
val body: List<Parameter> = listOf(),
val bodyMode: BodyMode = BodyMode.TYPED,
var bodyString: String = "",
) {
override fun toString(): String {
val params = parameters.joinToString(", ") { it.toString() }
val body = body.joinToString(", ") { it.toString() }
if (params.isEmpty() && body.isEmpty()) return ""
if (params.isEmpty()) return body
if (body.isEmpty()) return params

return "$params, ($body)"
}
}

@Serializable
data class Response(
val code: Int,
val parameters: List<Parameter> = listOf(),
var bodyMode: BodyMode = BodyMode.TYPED,
var bodyString: String = "",
) {
override fun toString(): String = when (bodyMode) {
BodyMode.RAW_TEXT -> {
// TODO: 256 is a magic number
if (bodyString.length > 256) {
"$code: {}"
} else {
"$code: ${bodyString.replace("\r\n", "").replace("\n", "")}"
}
}

BodyMode.TYPED -> {
if (parameters.isEmpty()) {
"$code: {}"
} else {
"$code: {${parameters.joinToString(", ") { it.toString() }}}"
}
}
}
}

@Serializable
data class ApiTagOutput(val string: String) {
override fun toString() = string
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package cc.unitmesh.processor.api.base

interface ApiDetailRender {
fun render(apiCollections: List<ApiCollection>): String {
val apiDetailsByTag = apiCollections.map { renderCollection(it) }.filter {
it.isNotBlank()
}
return apiDetailsByTag.joinToString("\n\n") { it }
}

fun renderCollection(collection: ApiCollection): String
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package cc.unitmesh.processor.api.base

interface ApiProcessor {
fun convertApi(): List<ApiCollection>
}
Loading

0 comments on commit 56b96ff

Please sign in to comment.