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..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,10 +33,8 @@ 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_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 +43,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 +59,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 +81,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 +161,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 +178,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 +195,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() @@ -207,11 +205,10 @@ class DocumentService { .setRow( 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") ) @@ -276,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 @@ -315,8 +314,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,10 +323,9 @@ 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, + rows = tableRows.sortedBy { it.columns[0] }, key = "model_$key" ) }, key) @@ -352,10 +349,9 @@ 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, + rows = tableRows.sortedBy { it.columns[0] }, key = "model_$it" ) }, it).apply { @@ -363,9 +359,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 +412,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 +439,7 @@ class DocumentService { ) ) val jsonString = try { - JsonUtil.toJson(jsonSimple) + JsonUtil.toSortJson(jsonSimple) } catch (e: Throwable) { jsonSimple.toString() } @@ -493,7 +488,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 +497,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" } @@ -515,7 +510,7 @@ class DocumentService { TableRow(httpStatus, loadModelType(schema), response.description) ) } - return tableRow + return tableRow.sortedBy { it.columns[0] } } private fun parseRequestBody(requestBody: RequestBody?): List { @@ -541,9 +536,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,14 +545,13 @@ 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 ) ) } } - return tableRow + return tableRow.sortedBy { it.columns[0] } } private fun loadSwagger(): OpenAPI { @@ -618,10 +611,9 @@ 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, + rows = table.sortedBy { it.columns[0] }, key = "model_$key" ) }, key @@ -641,9 +633,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 +730,7 @@ class DocumentService { is StringSchema -> { if (property.enum == null) { - property.type + "" } else { "enum" } @@ -883,19 +874,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 +900,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 +926,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 +945,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 -> {