From ddd805315353250c0079ff4f0faeecd47fbf3b2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?yongyiduan=28=E6=AE=B5=E6=B0=B8=E5=84=84=29?= Date: Mon, 23 Dec 2024 15:36:36 +0800 Subject: [PATCH 1/4] =?UTF-8?q?API=E8=87=AA=E5=8A=A8=E5=8C=96=E6=96=87?= =?UTF-8?q?=E6=A1=A3=E4=BC=98=E5=8C=96=20#11339?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/resources/schema/V3_0/ci.json | 16 +- .../openapi/service/doc/DocumentService.kt | 140 +++++++++--------- 2 files changed, 83 insertions(+), 73 deletions(-) diff --git a/src/backend/ci/core/common/common-pipeline-yaml/src/main/resources/schema/V3_0/ci.json b/src/backend/ci/core/common/common-pipeline-yaml/src/main/resources/schema/V3_0/ci.json index e20917b0ef8..83662096e8a 100644 --- a/src/backend/ci/core/common/common-pipeline-yaml/src/main/resources/schema/V3_0/ci.json +++ b/src/backend/ci/core/common/common-pipeline-yaml/src/main/resources/schema/V3_0/ci.json @@ -1479,7 +1479,13 @@ } ] }, "label" : { - "type" : "string" + "anyOf" : [ { + "type" : "string" + }, { + "type" : "number" + }, { + "type" : "boolean" + } ] }, "description" : { "type" : "string" @@ -1648,7 +1654,13 @@ } ] }, "label" : { - "type" : "string" + "anyOf" : [ { + "type" : "string" + }, { + "type" : "number" + }, { + "type" : "boolean" + } ] }, "description" : { "type" : "string" diff --git a/src/backend/ci/core/openapi/biz-openapi/src/main/kotlin/com/tencent/devops/openapi/service/doc/DocumentService.kt b/src/backend/ci/core/openapi/biz-openapi/src/main/kotlin/com/tencent/devops/openapi/service/doc/DocumentService.kt index 315f93a161f..63d04d84096 100644 --- a/src/backend/ci/core/openapi/biz-openapi/src/main/kotlin/com/tencent/devops/openapi/service/doc/DocumentService.kt +++ b/src/backend/ci/core/openapi/biz-openapi/src/main/kotlin/com/tencent/devops/openapi/service/doc/DocumentService.kt @@ -36,7 +36,6 @@ import com.tencent.devops.openapi.constant.OpenAPIMessageCode.BK_ALL_MODEL_DATA import com.tencent.devops.openapi.constant.OpenAPIMessageCode.BK_APPLICATION_STATE_REQUIRED import com.tencent.devops.openapi.constant.OpenAPIMessageCode.BK_BODY_PARAMETER import com.tencent.devops.openapi.constant.OpenAPIMessageCode.BK_CURL_PROMPT -import com.tencent.devops.openapi.constant.OpenAPIMessageCode.BK_DEFAULT_VALUE import com.tencent.devops.openapi.constant.OpenAPIMessageCode.BK_DISCRIMINATOR_ILLUSTRATE import com.tencent.devops.openapi.constant.OpenAPIMessageCode.BK_ERROR_PROMPT import com.tencent.devops.openapi.constant.OpenAPIMessageCode.BK_HAVE_TO @@ -45,7 +44,6 @@ import com.tencent.devops.openapi.constant.OpenAPIMessageCode.BK_HTTP_CODE import com.tencent.devops.openapi.constant.OpenAPIMessageCode.BK_ILLUSTRATE import com.tencent.devops.openapi.constant.OpenAPIMessageCode.BK_INPUT_PARAMETER_DESCRIPTION import com.tencent.devops.openapi.constant.OpenAPIMessageCode.BK_MUST_BE -import com.tencent.devops.openapi.constant.OpenAPIMessageCode.BK_NO import com.tencent.devops.openapi.constant.OpenAPIMessageCode.BK_OBJECT_PROPERTY_ILLUSTRATE import com.tencent.devops.openapi.constant.OpenAPIMessageCode.BK_PARAM_ILLUSTRATE import com.tencent.devops.openapi.constant.OpenAPIMessageCode.BK_PARAM_NAME @@ -62,7 +60,6 @@ import com.tencent.devops.openapi.constant.OpenAPIMessageCode.BK_RESPONSE_PARAME import com.tencent.devops.openapi.constant.OpenAPIMessageCode.BK_RETURNS_THE_SAMPLE import com.tencent.devops.openapi.constant.OpenAPIMessageCode.BK_THE_FIELD_IS_READ_ONLY import com.tencent.devops.openapi.constant.OpenAPIMessageCode.BK_USER_NAME -import com.tencent.devops.openapi.constant.OpenAPIMessageCode.BK_YES import com.tencent.devops.openapi.pojo.SwaggerDocParameterInfo import com.tencent.devops.openapi.pojo.SwaggerDocResponse import com.tencent.devops.openapi.utils.markdown.Code @@ -85,13 +82,18 @@ import io.swagger.v3.oas.models.media.StringSchema import io.swagger.v3.oas.models.parameters.Parameter import io.swagger.v3.oas.models.parameters.RequestBody import io.swagger.v3.oas.models.responses.ApiResponse +import java.lang.reflect.Modifier +import java.math.BigDecimal +import java.time.LocalDateTime import kotlin.jvm.internal.DefaultConstructorMarker import kotlin.reflect.KFunction import kotlin.reflect.KType +import kotlin.reflect.full.isSubtypeOf import kotlin.reflect.full.memberProperties import kotlin.reflect.jvm.isAccessible import kotlin.reflect.jvm.javaConstructor import kotlin.reflect.jvm.javaType +import kotlin.reflect.typeOf import org.apache.commons.lang3.StringUtils import org.reflections.Reflections import org.springframework.beans.factory.annotation.Value @@ -160,8 +162,7 @@ class DocumentService { getI18n(BK_PARAM_NAME), getI18n(BK_PARAM_TYPE), getI18n(BK_HAVE_TO), - getI18n(BK_PARAM_ILLUSTRATE), - getI18n(BK_DEFAULT_VALUE) + getI18n(BK_PARAM_ILLUSTRATE) ), rows = parseParameters( operation.parameters?.filter { it.`in` == PATH_PARAM } ?: emptyList() @@ -178,8 +179,7 @@ class DocumentService { getI18n(BK_PARAM_NAME), getI18n(BK_PARAM_TYPE), getI18n(BK_HAVE_TO), - getI18n(BK_PARAM_ILLUSTRATE), - getI18n(BK_DEFAULT_VALUE) + getI18n(BK_PARAM_ILLUSTRATE) ), rows = parseParameters( operation.parameters?.filter { it.`in` == QUERY_PARAM } ?: emptyList() @@ -196,8 +196,7 @@ class DocumentService { getI18n(BK_PARAM_NAME), getI18n(BK_PARAM_TYPE), getI18n(BK_HAVE_TO), - getI18n(BK_PARAM_ILLUSTRATE), - getI18n(BK_DEFAULT_VALUE) + getI18n(BK_PARAM_ILLUSTRATE) ), rows = parseParameters( operation.parameters?.filter { it.`in` == HEADER_PARAM } ?: emptyList() @@ -208,10 +207,9 @@ class DocumentService { AUTH_HEADER_USER_ID, "string", getI18n(BK_APPLICATION_STATE_REQUIRED), - getI18n(BK_USER_NAME), - "{X-DEVOPS-UID}" + getI18n(BK_USER_NAME) ) - .setRow("Content-Type", "string", getI18n(BK_YES), "", "application/json") + .setRow("Content-Type", "string", "√", "application/json") .removeRow(AUTH_HEADER_DEVOPS_APP_CODE) }, path + httpMethod + "header") ) @@ -315,8 +313,7 @@ class DocumentService { val reflectInfo = parametersInfo?.get("${model.title}@${model.name}")?.get(table.columns[0]) if (reflectInfo != null) { val column = table.columns.toMutableList() - column[2] = if (reflectInfo.markedNullable.not()) getI18n(BK_YES) else getI18n(BK_NO) - column[4] = if (reflectInfo.markedNullable) reflectInfo.defaultValue ?: "" else column[4] + column[2] = if (reflectInfo.markedNullable.not()) "√" else "" table.columns = column } } @@ -325,8 +322,7 @@ class DocumentService { getI18n(BK_PARAM_NAME), getI18n(BK_PARAM_TYPE), getI18n(BK_HAVE_TO), - getI18n(BK_PARAM_ILLUSTRATE), - getI18n(BK_DEFAULT_VALUE) + getI18n(BK_PARAM_ILLUSTRATE) ), rows = tableRows, key = "model_$key" @@ -352,8 +348,7 @@ class DocumentService { getI18n(BK_PARAM_NAME), getI18n(BK_PARAM_TYPE), getI18n(BK_HAVE_TO), - getI18n(BK_PARAM_ILLUSTRATE), - getI18n(BK_DEFAULT_VALUE) + getI18n(BK_PARAM_ILLUSTRATE) ), rows = tableRows, key = "model_$it" @@ -363,9 +358,8 @@ class DocumentService { this.setRow( (definitions[it] as Schema).discriminator.propertyName, "string", - getI18n(BK_YES), - getI18n(BK_DISCRIMINATOR_ILLUSTRATE, arrayOf("${polymorphismMap[it]?.keys}")), - "" + "√", + getI18n(BK_DISCRIMINATOR_ILLUSTRATE, arrayOf("${polymorphismMap[it]?.keys}")) ) } }.checkLoadModel(onLoadModel) @@ -417,7 +411,7 @@ class DocumentService { ) ) markdownElement.add( - Code(language = "Json", body = JsonUtil.toJson(loadJson), key = "${httpStatus}_return_example") + Code(language = "Json", body = JsonUtil.toSortJson(loadJson), key = "${httpStatus}_return_example") ) } return markdownElement @@ -444,7 +438,7 @@ class DocumentService { ) ) val jsonString = try { - JsonUtil.toJson(jsonSimple) + JsonUtil.toSortJson(jsonSimple) } catch (e: Throwable) { jsonSimple.toString() } @@ -493,7 +487,7 @@ class DocumentService { body = "$httpMethod ${getI18n(BK_REQUEST_SAMPLE)}", key = "${httpMethod}_request_sample_title" ), - Code(language = "Json", body = JsonUtil.toJson(outJson), key = "${httpMethod}_request_sample") + Code(language = "Json", body = JsonUtil.toSortJson(outJson), key = "${httpMethod}_request_sample") ) } @@ -502,7 +496,7 @@ class DocumentService { "${it.columns[0]}={${it.columns[0]}}" } ?: "" val headerString = header.rows.takeIf { it.isNotEmpty() }?.joinToString(prefix = "\\\n", separator = "\\\n") { - "-H '${it.columns[0]}: ${it.columns[4]}' " + "-H '${it.columns[0]}: ${it.columns[3]}' " } ?: "" return "curl -X ${httpMethod.toUpperCase()} '${getI18n(BK_CURL_PROMPT, arrayOf(queryString))}' $headerString" } @@ -541,9 +535,8 @@ class DocumentService { TableRow( it.name, Link(key, "#$key").toString(), - if (it.required == true) getI18n(BK_YES) else getI18n(BK_NO), - it.description, - "" + if (it.required == true) "√" else "", + it.description ) ) } else { @@ -551,9 +544,8 @@ class DocumentService { TableRow( it.name, loadSerializableParameter(it.schema), - if (it.required == true) getI18n(BK_YES) else getI18n(BK_NO), - it.description, - it.schema.default?.toString() ?: "" + if (it.required == true) "√" else "", + it.description ) ) } @@ -618,8 +610,7 @@ class DocumentService { getI18n(BK_PARAM_NAME), getI18n(BK_PARAM_TYPE), getI18n(BK_HAVE_TO), - getI18n(BK_PARAM_ILLUSTRATE), - getI18n(BK_DEFAULT_VALUE) + getI18n(BK_PARAM_ILLUSTRATE) ), rows = table, key = "model_$key" @@ -641,9 +632,8 @@ class DocumentService { TableRow( key, loadPropertyType(property), - if (model.required != null && key in model.required) getI18n(BK_YES) else getI18n(BK_NO), - loadDescriptionInfo(property), - loadPropertyDefault(property) + if (model.required != null && key in model.required) "√" else "", + loadDescriptionInfo(property) ) ) } @@ -739,7 +729,7 @@ class DocumentService { is StringSchema -> { if (property.enum == null) { - property.type + "" } else { "enum" } @@ -883,19 +873,25 @@ class DocumentService { val nullable = mutableMapOf() val kClazz = clazz.kotlin val mock = mockModel(clazz, nullable) -// val mock = try { val res = mutableMapOf() kClazz.memberProperties.forEach { // 编译后,属性默认是private,需要设置isAccessible 才可以读取到值 it.isAccessible = true res[it.name] = SwaggerDocParameterInfo( markedNullable = nullable[it.name] ?: false, - defaultValue = checkDefaultValue(it.call(mock).toString()) + defaultValue = null ) } return res } + private val initClazz: Lazy, Any>> = lazy { + mapOf( + LocalDateTime::class.java to LocalDateTime.now(), + BigDecimal::class.java to BigDecimal.ONE + ) + } + private fun mockModel(clazz: Class<*>, nullable: MutableMap = mutableMapOf()): Any? { if (clazz.simpleName == "Object") { return "" @@ -903,13 +899,13 @@ class DocumentService { if (clazz.isEnum) { return clazz.enumConstants.first() } - if (clazz.simpleName == "GithubRepository") { - println() + if (Modifier.isAbstract(clazz.modifiers)) { + return null } println(clazz.name) val kClazz = clazz.kotlin - if (!kClazz.isData) { - return null + if (clazz in initClazz.value.keys) { + return initClazz.value[clazz] } val constructor = kClazz.constructors.maxByOrNull { it.parameters.size }!! val parameters = constructor.parameters @@ -929,7 +925,9 @@ class DocumentService { arguments[i] = 0 } if (syntheticInit != null) { - arguments[argumentsSize - 2] = offset + if (syntheticInit.parameterTypes.size - parameters.size >= 2) { + arguments[argumentsSize - 2] = offset + } arguments[argumentsSize - 1] = null as DefaultConstructorMarker? } val javaConstructor = constructor.javaConstructor @@ -946,43 +944,43 @@ class DocumentService { @Suppress("ComplexMethod") private fun makeStandardArgument(type: KType, debug: KFunction<*>): Any? { if (type.isMarkedNullable) return null - return when (type.classifier) { - Boolean::class -> false - Byte::class -> 0.toByte() - Short::class -> 0.toShort() - Char::class -> 0.toChar() - Int::class -> 0 - Long::class -> 0L - Float::class -> 0f - Double::class -> 0.0 - String::class -> "" - Enum::class -> { - null + return when { + type.isSubtypeOf(typeOf()) -> false + type.isSubtypeOf(typeOf()) -> 0.toByte() + type.isSubtypeOf(typeOf()) -> 0.toShort() + type.isSubtypeOf(typeOf()) -> 0.toChar() + type.isSubtypeOf(typeOf()) -> 0 + type.isSubtypeOf(typeOf()) -> 0L + type.isSubtypeOf(typeOf()) -> 0f + type.isSubtypeOf(typeOf()) -> 0.0 + type.isSubtypeOf(typeOf()) -> "" + type.isSubtypeOf(typeOf>()) -> { + (type.javaType as Class<*>).enumConstants.firstOrNull() } - Set::class -> { - type.arguments.firstOrNull()?.let { setOf(makeStandardArgument(it.type!!, debug)) } ?: "" + type.isSubtypeOf(typeOf>()) -> { + emptySet() } - List::class -> { - type.arguments.firstOrNull()?.let { listOf(makeStandardArgument(it.type!!, debug)) } ?: "" + type.isSubtypeOf(typeOf>()) -> { + ArrayList() } - ArrayList::class -> { - type.arguments.firstOrNull()?.let { arrayListOf(makeStandardArgument(it.type!!, debug)) } ?: "" + type.isSubtypeOf(typeOf>()) -> { + emptyList() } - Array::class -> { - type.arguments.firstOrNull()?.let { arrayOf(makeStandardArgument(it.type!!, debug)) } ?: "" + type.isSubtypeOf(typeOf>()) -> { + val arrayType = type.arguments.firstOrNull()?.type ?: return emptyArray() + java.lang.reflect.Array.newInstance(arrayType.javaType as Class<*>, 0) } - Map::class -> { - mapOf( - makeStandardArgument( - type.arguments[0].type!!, - debug - ) to makeStandardArgument(type.arguments[1].type!!, debug) - ) + type.isSubtypeOf(typeOf>()) -> { + emptyList() + } + + type.isSubtypeOf(typeOf>()) -> { + emptyMap() } else -> { From 0e90f2261e9034698c9e5a63a20c56b547db0722 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?yongyiduan=28=E6=AE=B5=E6=B0=B8=E5=84=84=29?= Date: Mon, 23 Dec 2024 17:02:50 +0800 Subject: [PATCH 2/4] =?UTF-8?q?API=E8=87=AA=E5=8A=A8=E5=8C=96=E6=96=87?= =?UTF-8?q?=E6=A1=A3=E4=BC=98=E5=8C=96=20#11339?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/tencent/devops/openapi/service/doc/DocumentService.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/ci/core/openapi/biz-openapi/src/main/kotlin/com/tencent/devops/openapi/service/doc/DocumentService.kt b/src/backend/ci/core/openapi/biz-openapi/src/main/kotlin/com/tencent/devops/openapi/service/doc/DocumentService.kt index 63d04d84096..1fac1c654a0 100644 --- a/src/backend/ci/core/openapi/biz-openapi/src/main/kotlin/com/tencent/devops/openapi/service/doc/DocumentService.kt +++ b/src/backend/ci/core/openapi/biz-openapi/src/main/kotlin/com/tencent/devops/openapi/service/doc/DocumentService.kt @@ -206,7 +206,7 @@ class DocumentService { .setRow( AUTH_HEADER_USER_ID, "string", - getI18n(BK_APPLICATION_STATE_REQUIRED), + "√", getI18n(BK_USER_NAME) ) .setRow("Content-Type", "string", "√", "application/json") From 76304f419933f952d600be908e14f9192052781b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?yongyiduan=28=E6=AE=B5=E6=B0=B8=E5=84=84=29?= Date: Mon, 23 Dec 2024 21:01:02 +0800 Subject: [PATCH 3/4] =?UTF-8?q?API=E8=87=AA=E5=8A=A8=E5=8C=96=E6=96=87?= =?UTF-8?q?=E6=A1=A3=E4=BC=98=E5=8C=96=20#11339?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../devops/openapi/service/doc/DocumentService.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/backend/ci/core/openapi/biz-openapi/src/main/kotlin/com/tencent/devops/openapi/service/doc/DocumentService.kt b/src/backend/ci/core/openapi/biz-openapi/src/main/kotlin/com/tencent/devops/openapi/service/doc/DocumentService.kt index 1fac1c654a0..472b265691d 100644 --- a/src/backend/ci/core/openapi/biz-openapi/src/main/kotlin/com/tencent/devops/openapi/service/doc/DocumentService.kt +++ b/src/backend/ci/core/openapi/biz-openapi/src/main/kotlin/com/tencent/devops/openapi/service/doc/DocumentService.kt @@ -324,7 +324,7 @@ class DocumentService { getI18n(BK_HAVE_TO), getI18n(BK_PARAM_ILLUSTRATE) ), - rows = tableRows, + rows = tableRows.sortedBy { it.columns[0] }, key = "model_$key" ) }, key) @@ -350,7 +350,7 @@ class DocumentService { getI18n(BK_HAVE_TO), getI18n(BK_PARAM_ILLUSTRATE) ), - rows = tableRows, + rows = tableRows.sortedBy { it.columns[0] }, key = "model_$it" ) }, it).apply { @@ -509,7 +509,7 @@ class DocumentService { TableRow(httpStatus, loadModelType(schema), response.description) ) } - return tableRow + return tableRow.sortedBy { it.columns[0] } } private fun parseRequestBody(requestBody: RequestBody?): List { @@ -550,7 +550,7 @@ class DocumentService { ) } } - return tableRow + return tableRow.sortedBy { it.columns[0] } } private fun loadSwagger(): OpenAPI { @@ -612,7 +612,7 @@ class DocumentService { getI18n(BK_HAVE_TO), getI18n(BK_PARAM_ILLUSTRATE) ), - rows = table, + rows = table.sortedBy { it.columns[0] }, key = "model_$key" ) }, key From af11738adaf229bdac1a4a8dd6958250727d1efe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?yongyiduan=28=E6=AE=B5=E6=B0=B8=E5=84=84=29?= Date: Tue, 24 Dec 2024 11:02:40 +0800 Subject: [PATCH 4/4] =?UTF-8?q?API=E8=87=AA=E5=8A=A8=E5=8C=96=E6=96=87?= =?UTF-8?q?=E6=A1=A3=E4=BC=98=E5=8C=96=20#11339?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../devops/openapi/service/doc/DocumentService.kt | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/backend/ci/core/openapi/biz-openapi/src/main/kotlin/com/tencent/devops/openapi/service/doc/DocumentService.kt b/src/backend/ci/core/openapi/biz-openapi/src/main/kotlin/com/tencent/devops/openapi/service/doc/DocumentService.kt index 472b265691d..3bb0fa29250 100644 --- a/src/backend/ci/core/openapi/biz-openapi/src/main/kotlin/com/tencent/devops/openapi/service/doc/DocumentService.kt +++ b/src/backend/ci/core/openapi/biz-openapi/src/main/kotlin/com/tencent/devops/openapi/service/doc/DocumentService.kt @@ -33,7 +33,6 @@ import com.tencent.devops.common.api.util.FileUtil import com.tencent.devops.common.api.util.JsonUtil import com.tencent.devops.common.api.util.MessageUtil import com.tencent.devops.openapi.constant.OpenAPIMessageCode.BK_ALL_MODEL_DATA -import com.tencent.devops.openapi.constant.OpenAPIMessageCode.BK_APPLICATION_STATE_REQUIRED import com.tencent.devops.openapi.constant.OpenAPIMessageCode.BK_BODY_PARAMETER import com.tencent.devops.openapi.constant.OpenAPIMessageCode.BK_CURL_PROMPT import com.tencent.devops.openapi.constant.OpenAPIMessageCode.BK_DISCRIMINATOR_ILLUSTRATE @@ -274,21 +273,23 @@ class DocumentService { // 组装所有已使用的模型 loadMarkdown.addAll(parseAllModel(onLoadModel, loadedModel)) operation.tags.forEach { tag -> - response[tag] = SwaggerDocResponse( + val res = SwaggerDocResponse( path = path, httpMethod = httpMethod.name, markdown = if (checkMDData) loadMarkdown.joinToString(separator = "") else null, metaData = if (checkMetaData) loadMarkdown else null ) + response[tag] = res if (!outputPath.isNullOrBlank()) { FileUtil.outFile(outputPath, "$tag.md", loadMarkdown.joinToString(separator = "")) } + + if (!outputPath.isNullOrBlank()) { + FileUtil.outFile("$outputPath/json", "$tag.json", JsonUtil.toJson(res)) + } } } } - if (!outputPath.isNullOrBlank()) { - FileUtil.outFile(outputPath, "all.json", JsonUtil.toJson(response)) - } onLoadTable.clear() definitions.clear() return response