From 9380a1386a6528f36c41be6468508b7b868d3bda Mon Sep 17 00:00:00 2001 From: martmull Date: Fri, 25 Jul 2025 16:36:11 +0200 Subject: [PATCH] Fix missing components.schema for webhooks (#13433) Webhooks are still documented in core playground to detail Webhooks of core objects. We moved webhooks to metadata playground and forgot to keep computation of WebhookForResponse This PR adds it back --- .../playground/components/RestPlayground.tsx | 1 + .../core-modules/open-api/open-api.service.ts | 70 +++++++++++++------ .../open-api/utils/responses.utils.ts | 7 +- 3 files changed, 53 insertions(+), 25 deletions(-) diff --git a/packages/twenty-front/src/modules/settings/playground/components/RestPlayground.tsx b/packages/twenty-front/src/modules/settings/playground/components/RestPlayground.tsx index 3f9b04aea..4103ea348 100644 --- a/packages/twenty-front/src/modules/settings/playground/components/RestPlayground.tsx +++ b/packages/twenty-front/src/modules/settings/playground/components/RestPlayground.tsx @@ -81,6 +81,7 @@ export const RestPlayground = ({ onError, schema }: RestPlaygroundProps) => { forceDarkModeState: theme.name === 'dark' ? 'dark' : 'light', hideClientButton: true, hideDarkModeToggle: true, + hideModels: schema === 'metadata', pathRouting: { basePath: getSettingsPath(SettingsPath.RestPlayground, { schema, diff --git a/packages/twenty-server/src/engine/core-modules/open-api/open-api.service.ts b/packages/twenty-server/src/engine/core-modules/open-api/open-api.service.ts index 442b498a4..71f2c9c4e 100644 --- a/packages/twenty-server/src/engine/core-modules/open-api/open-api.service.ts +++ b/packages/twenty-server/src/engine/core-modules/open-api/open-api.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common'; import { Request } from 'express'; import { OpenAPIV3_1 } from 'openapi-types'; -import { capitalize } from 'twenty-shared/utils'; +import { capitalize, isDefined } from 'twenty-shared/utils'; import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action'; import { AccessTokenService } from 'src/engine/core-modules/auth/token/services/access-token.service'; @@ -42,6 +42,7 @@ import { ObjectMetadataService } from 'src/engine/metadata-modules/object-metada import { standardObjectMetadataDefinitions } from 'src/engine/workspace-manager/workspace-sync-metadata/standard-objects'; import { shouldExcludeFromWorkspaceApi } from 'src/engine/workspace-manager/workspace-sync-metadata/utils/should-exclude-from-workspace-api.util'; import { getServerUrl } from 'src/utils/get-server-url'; +import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; @Injectable() export class OpenApiService { @@ -52,6 +53,30 @@ export class OpenApiService { private readonly featureFlagService: FeatureFlagService, ) {} + private async getWorkspaceFromRequest(request: Request) { + try { + const { workspace } = + await this.accessTokenService.validateTokenByRequest(request); + + workspaceValidator.assertIsDefinedOrThrow(workspace); + + return workspace; + } catch (e) { + return null; + } + } + + private async getObjectMetadataItems(workspace: Workspace) { + return await this.objectMetadataService.findManyWithinWorkspace( + workspace.id, + { + order: { + namePlural: 'ASC', + }, + }, + ); + } + async generateCoreSchema(request: Request): Promise { const baseUrl = getServerUrl( this.twentyConfigService.get('SERVER_URL'), @@ -60,26 +85,14 @@ export class OpenApiService { const schema = baseSchema('core', baseUrl); - let objectMetadataItems; - let workspace; + const workspace = await this.getWorkspaceFromRequest(request); - try { - const authResult = - await this.accessTokenService.validateTokenByRequest(request); - - workspace = authResult.workspace; - workspaceValidator.assertIsDefinedOrThrow(workspace); - - objectMetadataItems = - await this.objectMetadataService.findManyWithinWorkspace(workspace.id, { - order: { - namePlural: 'ASC', - }, - }); - } catch (err) { + if (!isDefined(workspace)) { return schema; } + const objectMetadataItems = await this.getObjectMetadataItems(workspace); + if (!objectMetadataItems.length) { return schema; } @@ -105,7 +118,7 @@ export class OpenApiService { return paths; }, schema.paths as OpenAPIV3_1.PathsObject); - schema.webhooks = objectMetadataItems.reduce( + schema.webhooks = filteredObjectMetadataItems.reduce( (paths, item) => { paths[ this.createWebhookEventName( @@ -134,8 +147,6 @@ export class OpenApiService { >, ); - schema.tags = computeSchemaTags(objectMetadataItems); - schema.components = { ...schema.components, // components.securitySchemes is defined in base Schema schemas: computeSchemaComponents(filteredObjectMetadataItems), @@ -161,7 +172,11 @@ export class OpenApiService { const schema = baseSchema('metadata', baseUrl); - schema.tags = [{ name: 'placeholder' }]; + const workspace = await this.getWorkspaceFromRequest(request); + + if (!isDefined(workspace)) { + return schema; + } const metadata = [ { @@ -177,7 +192,7 @@ export class OpenApiService { namePlural: 'webhooks', }, { - nameSingular: 'apikey', + nameSingular: 'apiKey', namePlural: 'apiKeys', }, ]; @@ -249,9 +264,18 @@ export class OpenApiService { return path; }, schema.paths as OpenAPIV3_1.PathsObject); + const objectMetadataItems = await this.getObjectMetadataItems(workspace); + + const webhookAndApiKeyObjectMetadataItems = objectMetadataItems.filter( + ({ nameSingular }) => ['webhook', 'apiKey'].includes(nameSingular), + ); + schema.components = { ...schema.components, // components.securitySchemes is defined in base Schema - schemas: computeMetadataSchemaComponents(metadata), + schemas: { + ...computeMetadataSchemaComponents(metadata), + ...computeSchemaComponents(webhookAndApiKeyObjectMetadataItems), + }, parameters: computeParameterComponents(true), responses: { '400': get400ErrorResponses(), diff --git a/packages/twenty-server/src/engine/core-modules/open-api/utils/responses.utils.ts b/packages/twenty-server/src/engine/core-modules/open-api/utils/responses.utils.ts index 2da8fbcf2..28e1bcd47 100644 --- a/packages/twenty-server/src/engine/core-modules/open-api/utils/responses.utils.ts +++ b/packages/twenty-server/src/engine/core-modules/open-api/utils/responses.utils.ts @@ -239,8 +239,11 @@ export const getJsonResponse = () => { servers: { type: 'array', items: { - url: { type: 'string' }, - description: { type: 'string' }, + type: 'object', + properties: { + url: { type: 'string' }, + description: { type: 'string' }, + }, }, }, components: {