From 090cb365433e5cd437f8ddb54bcf96dc5bd9b09f Mon Sep 17 00:00:00 2001 From: Shahed Nasser Date: Wed, 4 Sep 2024 19:36:10 +0300 Subject: [PATCH] docs-utils: fix auth detection + added query params (#8997) --- .../docs-generator/src/classes/kinds/oas.ts | 124 +++++++++++++----- 1 file changed, 93 insertions(+), 31 deletions(-) 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 ce4e725344..278f485a61 100644 --- a/www/utils/packages/docs-generator/src/classes/kinds/oas.ts +++ b/www/utils/packages/docs-generator/src/classes/kinds/oas.ts @@ -42,6 +42,12 @@ export type OasArea = "admin" | "store" type ParameterType = "query" | "path" +type AuthRequests = { + exact?: string + startsWith?: string + requiresAuthentication: boolean +} + /** * OAS generator for API routes. It extends the {@link FunctionKindGenerator} * since API routes are functions. @@ -55,7 +61,21 @@ class OasKindGenerator extends FunctionKindGenerator { "RequestWithContext", "AuthenticatedMedusaRequest", ] + // as it's not always possible to detect authenticated request + // use this to override the default detection logic. + readonly AUTH_REQUESTS: AuthRequests[] = [ + { + exact: "store/orders", + requiresAuthentication: true, + }, + { + startsWith: "store/customers/me", + requiresAuthentication: true, + }, + ] readonly RESPONSE_TYPE_NAMES = ["MedusaResponse"] + readonly FIELD_QUERY_PARAMS = ["fields", "expand"] + readonly PAGINATION_QUERY_PARAMS = ["limit", "offset", "order"] /** * This map collects tags of all the generated OAS, then, once the generation process finishes, @@ -452,6 +472,10 @@ class OasKindGenerator extends FunctionKindGenerator { type: "query", }) + if (!oas.parameters.length) { + oas.parameters = undefined + } + // update request schema const existingRequestBodySchema = ( oas.requestBody as OpenAPIV3.RequestBodyObject @@ -461,7 +485,10 @@ class OasKindGenerator extends FunctionKindGenerator { newSchema: requestSchema, }) - if (!updatedRequestSchema) { + if ( + !updatedRequestSchema || + Object.keys(updatedRequestSchema).length === 0 + ) { // if there's no request schema, remove it from the OAS delete oas.requestBody } else { @@ -531,19 +558,26 @@ class OasKindGenerator extends FunctionKindGenerator { delete oas.responses![oldResponseStatus] } - // update the response schema - oas.responses![newStatus] = { - description: "OK", - content: { - "application/json": { - schema: updatedResponseSchema - ? this.oasSchemaHelper.namedSchemaToReference( - updatedResponseSchema - ) || - this.oasSchemaHelper.schemaChildrenToRefs(updatedResponseSchema) - : updatedResponseSchema, + if (updatedResponseSchema && Object.keys(updatedResponseSchema).length) { + // update the response schema + oas.responses![newStatus] = { + description: "OK", + content: { + "application/json": { + schema: updatedResponseSchema + ? this.oasSchemaHelper.namedSchemaToReference( + updatedResponseSchema + ) || + this.oasSchemaHelper.schemaChildrenToRefs( + updatedResponseSchema + ) + : updatedResponseSchema, + }, }, - }, + } + } else if (oldResponseStatus) { + // delete the old response schema + delete oas.responses![oldResponseStatus] } } @@ -733,11 +767,21 @@ class OasKindGenerator extends FunctionKindGenerator { .statements.some((statement) => statement.getText().includes("AUTHENTICATE = false") ) + const hasAuthenticationOverride = + this.AUTH_REQUESTS.find((authRequest) => { + return ( + authRequest.exact === oasPath || + (authRequest.startsWith && oasPath.startsWith(authRequest.startsWith)) + ) + })?.requiresAuthentication === true const isAdminAuthenticated = - !isAuthenticationDisabled && oasPath.startsWith("admin") - const isStoreAuthenticated = - !isAuthenticationDisabled && oasPath.startsWith("store/customers/me") - const isAuthenticated = isAdminAuthenticated || isStoreAuthenticated + (!isAuthenticationDisabled || hasAuthenticationOverride) && + oasPath.startsWith("admin") + const isStoreAuthenticated = hasAuthenticationOverride + ? oasPath.startsWith("store") + : !isAuthenticationDisabled && oasPath.startsWith("store") + const isAuthenticated = + isAdminAuthenticated || isStoreAuthenticated || hasAuthenticationOverride return { isAdminAuthenticated, @@ -958,13 +1002,26 @@ class OasKindGenerator extends FunctionKindGenerator { // TODO for now I'll use the type for validatedQuery until // we have an actual approach to infer query types const querySymbol = requestType.getProperty("validatedQuery") - if (querySymbol && this.shouldAddQueryParams(node)) { + if (querySymbol) { + const { shouldAddFields, shouldAddPagination } = + this.shouldAddQueryParams(node) const queryType = this.checker.getTypeOfSymbol(querySymbol) const queryTypeName = this.checker.typeToString(queryType) queryType.getProperties().forEach((property) => { + const propertyName = property.getName() + // if this is a field / pagination query parameter and + // they're not used in the route, don't add them. + if ( + (this.FIELD_QUERY_PARAMS.includes(propertyName) && + !shouldAddFields) || + (this.PAGINATION_QUERY_PARAMS.includes(propertyName) && + !shouldAddPagination) + ) { + return + } const propertyType = this.checker.getTypeOfSymbol(property) const descriptionOptions: SchemaDescriptionOptions = { - typeStr: property.getName(), + typeStr: propertyName, parentName: tagName, rawParentName: queryTypeName, node: property.valueDeclaration, @@ -973,13 +1030,13 @@ class OasKindGenerator extends FunctionKindGenerator { } parameters.push( this.getParameterObject({ - name: property.getName(), + name: propertyName, type: "query", description: this.getSchemaDescription(descriptionOptions), required: this.isRequired(property), schema: this.typeToSchema({ itemType: propertyType, - title: property.getName(), + title: propertyName, descriptionOptions, context: "request", }), @@ -2062,15 +2119,16 @@ class OasKindGenerator extends FunctionKindGenerator { } } - shouldAddQueryParams(node: FunctionNode): boolean { - const queryParamsUsageIndicators = [ - `req.filterableFields`, - `req.remoteQueryConfig`, - ] + shouldAddQueryParams(node: FunctionNode): { + shouldAddFields: boolean + shouldAddPagination: boolean + } { const fnText = node.getText() - return queryParamsUsageIndicators.some((indicator) => - fnText.includes(indicator) - ) + + return { + shouldAddFields: fnText.includes(`req.remoteQueryConfig.fields`), + shouldAddPagination: fnText.includes(`req.remoteQueryConfig.pagination`), + } } hasResponseType(node: FunctionNode, oas: OpenApiOperation): boolean { @@ -2081,14 +2139,18 @@ class OasKindGenerator extends FunctionKindGenerator { return false } - const responseContent = (oas.responses![oldResponseStatus] as OpenAPIV3.ResponseObject).content + const responseContent = ( + oas.responses![oldResponseStatus] as OpenAPIV3.ResponseObject + ).content if (!responseContent) { return false } const fnText = node.getText() - return Object.keys(responseContent).some((responseType) => fnText.includes(responseType)) + return Object.keys(responseContent).some((responseType) => + fnText.includes(responseType) + ) } private removeStringRegExpTypeOverlaps(types: ts.Type[]): ts.Type[] {