From bd73716bb6ae99a1379cedafab0b559e6ecbb871 Mon Sep 17 00:00:00 2001 From: Shahed Nasser Date: Wed, 8 Jan 2025 21:40:46 +0200 Subject: [PATCH] docs-util: add warning on request body change (#10881) --- .../packages/docs-generator/package.json | 1 + .../docs-generator/src/classes/kinds/oas.ts | 212 +++++++++++++----- www/utils/yarn.lock | 8 + 3 files changed, 161 insertions(+), 60 deletions(-) diff --git a/www/utils/packages/docs-generator/package.json b/www/utils/packages/docs-generator/package.json index 184339e9fc7ce..3ce3a2a271c93 100644 --- a/www/utils/packages/docs-generator/package.json +++ b/www/utils/packages/docs-generator/package.json @@ -19,6 +19,7 @@ "dependencies": { "@faker-js/faker": "^8.4.0", "@octokit/core": "^5.0.2", + "chalk": "^5.4.1", "commander": "^11.1.0", "dotenv": "^16.3.1", "eslint": "8.56.0", diff --git a/www/utils/packages/docs-generator/src/classes/kinds/oas.ts b/www/utils/packages/docs-generator/src/classes/kinds/oas.ts index 7978e0d00555c..0b5dac43ea9a1 100644 --- a/www/utils/packages/docs-generator/src/classes/kinds/oas.ts +++ b/www/utils/packages/docs-generator/src/classes/kinds/oas.ts @@ -1,3 +1,4 @@ +import chalk from "chalk" import { readFileSync, writeFileSync } from "fs" import { OpenAPIV3 } from "openapi-types" import { basename, join } from "path" @@ -473,6 +474,8 @@ class OasKindGenerator extends FunctionKindGenerator { oas["x-authenticated"] = isAuthenticated oas.security = this.getSecurity({ isAdminAuthenticated, isAuthenticated }) + let parametersUpdated = false + // update path parameters const newPathParameters = this.getPathParameters({ oasPath, tagName }) @@ -480,12 +483,15 @@ class OasKindGenerator extends FunctionKindGenerator { const headerParams = this.getHeaderParameters(oasPath) newPathParameters.push(...headerParams) - oas.parameters = this.updateParameters({ + let updateParameterResult = this.updateParameters({ oldParameters: oas.parameters as OpenAPIV3.ParameterObject[], newParameters: newPathParameters, types: ["path", "header"], }) + oas.parameters = updateParameterResult.parameters + parametersUpdated = updateParameterResult.wasUpdated + // retrieve updated query and request schemas const { queryParameters, requestSchema } = this.getRequestParameters({ node, @@ -495,12 +501,15 @@ class OasKindGenerator extends FunctionKindGenerator { }) // update query parameters - oas.parameters = this.updateParameters({ + updateParameterResult = this.updateParameters({ oldParameters: oas.parameters as OpenAPIV3.ParameterObject[], newParameters: queryParameters, types: ["query"], }) + oas.parameters = updateParameterResult.parameters + parametersUpdated = updateParameterResult.wasUpdated || parametersUpdated + if (!oas.parameters.length) { oas.parameters = undefined } @@ -514,9 +523,11 @@ class OasKindGenerator extends FunctionKindGenerator { newSchema: requestSchema, }) + parametersUpdated = updatedRequestSchema?.wasUpdated || parametersUpdated + if ( - !updatedRequestSchema || - Object.keys(updatedRequestSchema).length === 0 + !updatedRequestSchema?.schema || + Object.keys(updatedRequestSchema.schema).length === 0 ) { // if there's no request schema, remove it from the OAS delete oas.requestBody @@ -527,9 +538,11 @@ class OasKindGenerator extends FunctionKindGenerator { "application/json": { schema: this.oasSchemaHelper.namedSchemaToReference( - updatedRequestSchema + updatedRequestSchema.schema ) || - this.oasSchemaHelper.schemaChildrenToRefs(updatedRequestSchema), + this.oasSchemaHelper.schemaChildrenToRefs( + updatedRequestSchema.schema + ), }, }, } @@ -581,7 +594,7 @@ class OasKindGenerator extends FunctionKindGenerator { updatedResponseSchema = this.updateSchema({ oldSchema: oldResponseSchema, newSchema: newResponseSchema, - }) + })?.schema if (oldResponseStatus && oldResponseSchema !== newStatus) { // delete the old response schema if its status is different @@ -633,7 +646,7 @@ class OasKindGenerator extends FunctionKindGenerator { parameters: (oas.parameters as OpenAPIV3.ParameterObject[])?.filter( (parameter) => parameter.in === "path" ), - requestBody: updatedRequestSchema, + requestBody: updatedRequestSchema?.schema, responseBody: updatedResponseSchema, }) @@ -673,6 +686,13 @@ class OasKindGenerator extends FunctionKindGenerator { source: newCurlExample, }, ] + } else if (parametersUpdated) { + // show a warning if the request parameters have changed + console.warn( + chalk.yellow( + `[WARNING] The request parameters of ${methodName} ${oasPath} have changed. Please update the cURL example.` + ) + ) } // push new tags to the tags property @@ -1927,9 +1947,16 @@ class OasKindGenerator extends FunctionKindGenerator { * The type of parameters. */ types: ParameterType[] - }): OpenAPIV3.ParameterObject[] { + }): { + parameters: OpenAPIV3.ParameterObject[] + wasUpdated: boolean + } { + let wasUpdated = false if (!oldParameters) { - return newParameters || [] + return { + parameters: newParameters || [], + wasUpdated: !!newParameters?.length, + } } const oppositeParamType = ["path", "query", "header"].filter( (item) => !types.includes(item as ParameterType) @@ -1952,6 +1979,7 @@ class OasKindGenerator extends FunctionKindGenerator { if (!updatedParameter) { // remove the parameter paramsToRemove.add(parameter.name) + wasUpdated = true return } @@ -1964,6 +1992,7 @@ class OasKindGenerator extends FunctionKindGenerator { if (updatedParameter.required !== parameter.required) { parameter.required = updatedParameter.required + wasUpdated = true } if ( @@ -1972,19 +2001,24 @@ class OasKindGenerator extends FunctionKindGenerator { ) { // the entire schema should be updated if the type changes. parameter.schema = updatedParameter.schema + wasUpdated = true } else if ((updatedParameter.schema as OpenApiSchema).type === "array") { + const updateResult = this.updateSchema({ + oldSchema: (parameter.schema as OpenAPIV3.ArraySchemaObject).items, + newSchema: (updatedParameter.schema as OpenAPIV3.ArraySchemaObject) + .items, + }) ;(parameter.schema as OpenAPIV3.ArraySchemaObject).items = - this.updateSchema({ - oldSchema: (parameter.schema as OpenAPIV3.ArraySchemaObject).items, - newSchema: (updatedParameter.schema as OpenAPIV3.ArraySchemaObject) - .items, - }) || (updatedParameter.schema as OpenAPIV3.ArraySchemaObject).items + updateResult?.schema || + (updatedParameter.schema as OpenAPIV3.ArraySchemaObject).items + wasUpdated = updateResult?.wasUpdated || wasUpdated } else if ((updatedParameter.schema as OpenApiSchema).type === "object") { - parameter.schema = - this.updateSchema({ - oldSchema: parameter.schema, - newSchema: updatedParameter.schema, - }) || updatedParameter.schema + const updateResult = this.updateSchema({ + oldSchema: parameter.schema, + newSchema: updatedParameter.schema, + }) + parameter.schema = updateResult?.schema || updatedParameter.schema + wasUpdated = updateResult?.wasUpdated || wasUpdated } if ( @@ -1994,6 +2028,7 @@ class OasKindGenerator extends FunctionKindGenerator { ;(parameter.schema as OpenApiSchema).title = ( updatedParameter.schema as OpenApiSchema ).title + wasUpdated = true } if ( @@ -2014,19 +2049,23 @@ class OasKindGenerator extends FunctionKindGenerator { } existingParams?.push(parameter) + wasUpdated = true }) // remove parameters no longer existing - return [ - ...oppositeParams, - ...(existingParams?.filter( - (parameter) => - oppositeParamType.includes( - (parameter as OpenAPIV3.ParameterObject).in as ParameterType - ) || - !paramsToRemove.has((parameter as OpenAPIV3.ParameterObject).name) - ) || []), - ] + return { + parameters: [ + ...oppositeParams, + ...(existingParams?.filter( + (parameter) => + oppositeParamType.includes( + (parameter as OpenAPIV3.ParameterObject).in as ParameterType + ) || + !paramsToRemove.has((parameter as OpenAPIV3.ParameterObject).name) + ) || []), + ], + wasUpdated, + } } /** @@ -2053,7 +2092,13 @@ class OasKindGenerator extends FunctionKindGenerator { * maximum call stack size exceeded error */ level?: number - }): OpenApiSchema | undefined { + }): + | { + schema: OpenApiSchema | undefined + wasUpdated: boolean + } + | undefined { + let wasUpdated = false if (isLevelExceeded(level, this.MAX_LEVEL)) { return } @@ -2070,10 +2115,28 @@ class OasKindGenerator extends FunctionKindGenerator { : newSchema ) as OpenApiSchema | undefined - if (!oldSchemaObj && newSchemaObj) { - return newSchemaObj - } else if (!newSchemaObj || !Object.keys(newSchemaObj).length) { - return undefined + const oldSchemaKeys = oldSchemaObj ? Object.keys(oldSchemaObj) : [] + const hasOldSchemaObj = + oldSchemaObj !== undefined && oldSchemaKeys.length > 0 + const hasNewSchemaObj = + newSchemaObj !== undefined && Object.keys(newSchemaObj).length > 0 + + if (!hasOldSchemaObj || !hasNewSchemaObj) { + // if old schema is just made up of description, return it. + const useOldSchema = + oldSchemaKeys.length === 1 && + newSchemaObj !== undefined && + oldSchemaKeys[0] === "description" + return { + schema: hasNewSchemaObj + ? newSchemaObj + : useOldSchema + ? oldSchemaObj + : undefined, + wasUpdated: !hasNewSchemaObj + ? !useOldSchema && hasOldSchemaObj + : hasOldSchemaObj !== hasNewSchemaObj, + } } const oldSchemaType = this.inferOasSchemaType(oldSchemaObj) @@ -2085,28 +2148,36 @@ class OasKindGenerator extends FunctionKindGenerator { description: oldSchemaObj?.description, example: oldSchemaObj?.example || newSchemaObj.example, } + wasUpdated = true } else if ( oldSchemaObj?.allOf && newSchemaObj.allOf && oldSchemaObj.allOf.length !== newSchemaObj.allOf.length ) { oldSchemaObj.allOf = newSchemaObj.allOf + wasUpdated = true } else if ( oldSchemaObj?.oneOf && newSchemaObj.oneOf && oldSchemaObj.oneOf.length !== newSchemaObj.oneOf.length ) { oldSchemaObj.oneOf = newSchemaObj.oneOf + wasUpdated = true } else if ( oldSchemaObj?.anyOf && newSchemaObj.anyOf && oldSchemaObj.anyOf.length !== newSchemaObj.anyOf.length ) { oldSchemaObj.anyOf = newSchemaObj.anyOf + wasUpdated = true } else if (oldSchemaType === "object") { if (!oldSchemaObj?.properties && newSchemaObj?.properties) { oldSchemaObj!.properties = newSchemaObj.properties + wasUpdated = true } else if (!newSchemaObj?.properties) { + if (oldSchemaObj!.properties) { + wasUpdated = true + } delete oldSchemaObj!.properties // check if additionalProperties should be updated @@ -2121,12 +2192,13 @@ class OasKindGenerator extends FunctionKindGenerator { typeof oldSchemaObj!.additionalProperties !== "boolean" && typeof newSchemaObj!.additionalProperties !== "boolean" ) { + const updateResult = this.updateSchema({ + oldSchema: oldSchemaObj!.additionalProperties, + newSchema: newSchemaObj.additionalProperties, + level: maybeIncrementLevel(level, "object"), + }) oldSchemaObj!.additionalProperties = - this.updateSchema({ - oldSchema: oldSchemaObj!.additionalProperties, - newSchema: newSchemaObj.additionalProperties, - level: maybeIncrementLevel(level, "object"), - }) || oldSchemaObj!.additionalProperties + updateResult?.schema || oldSchemaObj!.additionalProperties } } else { // update existing properties @@ -2138,17 +2210,20 @@ class OasKindGenerator extends FunctionKindGenerator { if (!newPropertySchemaKey) { // remove property delete oldSchemaObj!.properties![propertyName] + wasUpdated = true return } + const updateResult = this.updateSchema({ + oldSchema: propertySchema as OpenApiSchema, + newSchema: newSchemaObj!.properties![ + propertyName + ] as OpenApiSchema, + level: maybeIncrementLevel(level, "object"), + }) oldSchemaObj!.properties![propertyName] = - this.updateSchema({ - oldSchema: propertySchema as OpenApiSchema, - newSchema: newSchemaObj!.properties![ - propertyName - ] as OpenApiSchema, - level: maybeIncrementLevel(level, "object"), - }) || propertySchema + updateResult?.schema || propertySchema + wasUpdated = updateResult?.wasUpdated || wasUpdated } ) // add new properties @@ -2177,12 +2252,13 @@ class OasKindGenerator extends FunctionKindGenerator { ) if (schemaToUpdate) { - updatedSchema = - this.updateSchema({ - oldSchema: schemaToUpdate.schema, - newSchema: schema, - level: maybeIncrementLevel(level, "object"), - }) || newProperty + const updateResult = this.updateSchema({ + oldSchema: schemaToUpdate.schema, + newSchema: schema, + level: maybeIncrementLevel(level, "object"), + }) + updatedSchema = updateResult?.schema || newProperty + wasUpdated = updateResult?.wasUpdated || wasUpdated } } @@ -2207,12 +2283,13 @@ class OasKindGenerator extends FunctionKindGenerator { oldSchemaObj?.type === "array" && newSchemaObj?.type === "array" ) { - oldSchemaObj.items = - this.updateSchema({ - oldSchema: oldSchemaObj.items as OpenApiSchema, - newSchema: newSchemaObj!.items as OpenApiSchema, - level: maybeIncrementLevel(level, "array"), - }) || oldSchemaObj.items + const updateResult = this.updateSchema({ + oldSchema: oldSchemaObj.items as OpenApiSchema, + newSchema: newSchemaObj!.items as OpenApiSchema, + level: maybeIncrementLevel(level, "array"), + }) + oldSchemaObj.items = updateResult?.schema || oldSchemaObj.items + wasUpdated = updateResult?.wasUpdated || wasUpdated } if ( @@ -2223,10 +2300,25 @@ class OasKindGenerator extends FunctionKindGenerator { newSchemaObj?.description || SUMMARY_PLACEHOLDER } + if (!wasUpdated) { + const requiredChanged = + oldSchemaObj!.required?.length !== newSchemaObj?.required?.length || + oldSchemaObj!.required?.some( + (item, index) => item !== newSchemaObj!.required![index] + ) || + false + + const schemaNameChanged = + oldSchemaObj!["x-schemaName"] !== newSchemaObj?.["x-schemaName"] + wasUpdated = requiredChanged || schemaNameChanged + } oldSchemaObj!.required = newSchemaObj?.required oldSchemaObj!["x-schemaName"] = newSchemaObj?.["x-schemaName"] - return oldSchemaObj + return { + schema: oldSchemaObj, + wasUpdated, + } } /** diff --git a/www/utils/yarn.lock b/www/utils/yarn.lock index dacf49531b9f5..9c6df4b85939e 100644 --- a/www/utils/yarn.lock +++ b/www/utils/yarn.lock @@ -2242,6 +2242,13 @@ __metadata: languageName: node linkType: hard +"chalk@npm:^5.4.1": + version: 5.4.1 + resolution: "chalk@npm:5.4.1" + checksum: b23e88132c702f4855ca6d25cb5538b1114343e41472d5263ee8a37cccfccd9c4216d111e1097c6a27830407a1dc81fecdf2a56f2c63033d4dbbd88c10b0dcef + languageName: node + linkType: hard + "charenc@npm:0.0.2": version: 0.0.2 resolution: "charenc@npm:0.0.2" @@ -2605,6 +2612,7 @@ __metadata: "@types/eslint": 8.56.6 "@types/node": ^20.12.10 "@types/pluralize": ^0.0.33 + chalk: ^5.4.1 commander: ^11.1.0 dotenv: ^16.3.1 eslint: 8.56.0