fix api mismatch on rest metadata (#13680)
## Context This PR fixes a REST API metadata schema mismatch that occurred after the webhook and apiKey migration from workspace entities to metadata level. ## The Issue After PR #13576 deleted the webhook and apiKey workspace entities, the metadata OpenAPI specification ended up in a broken state: - The `/metadata` endpoints still had paths for `/webhooks` and `/apiKeys` - But the schema definitions were missing (they were previously pulled from workspace entities) - This created dangling references in the OpenAPI document ## The Fix Added proper schema definitions for webhook and apiKey directly in `computeMetadataSchemaComponents`: - `Webhook`, `WebhookForUpdate`, `WebhookForResponse` - `ApiKey`, `ApiKeyForUpdate`, `ApiKeyForResponse` ## Why the CI is Failing The CI breaking changes detection is comparing: - **Main branch**: Has a broken OpenAPI document with endpoints that reference non-existent schemas - **This branch**: Has the fixed OpenAPI document with proper schemas The OpenAPI diff tool can't even parse the main branch's document due to the missing schema references, hence the error -- tested locally
This commit is contained in:
@@ -37,12 +37,13 @@ import {
|
||||
getUpdateOneResponse200,
|
||||
} from 'src/engine/core-modules/open-api/utils/responses.utils';
|
||||
import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service';
|
||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { workspaceValidator } from 'src/engine/core-modules/workspace/workspace.validate';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { ObjectMetadataService } from 'src/engine/metadata-modules/object-metadata/object-metadata.service';
|
||||
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 {
|
||||
@@ -264,18 +265,9 @@ 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),
|
||||
...computeSchemaComponents(webhookAndApiKeyObjectMetadataItems),
|
||||
},
|
||||
schemas: computeMetadataSchemaComponents(metadata),
|
||||
parameters: computeParameterComponents(true),
|
||||
responses: {
|
||||
'400': get400ErrorResponses(),
|
||||
@@ -283,7 +275,12 @@ export class OpenApiService {
|
||||
},
|
||||
};
|
||||
|
||||
schema.tags = computeSchemaTags(webhookAndApiKeyObjectMetadataItems);
|
||||
schema.tags = computeSchemaTags(
|
||||
metadata.map((item) => ({
|
||||
nameSingular: item.nameSingular,
|
||||
namePlural: item.namePlural,
|
||||
})) as ObjectMetadataEntity[],
|
||||
);
|
||||
|
||||
return schema;
|
||||
}
|
||||
|
||||
@@ -382,6 +382,117 @@ export const computeMetadataSchemaComponents = (
|
||||
},
|
||||
};
|
||||
|
||||
return schemas;
|
||||
}
|
||||
case 'webhook': {
|
||||
schemas[`${capitalize(item.nameSingular)}`] = {
|
||||
type: 'object',
|
||||
description: `A webhook`,
|
||||
properties: {
|
||||
targetUrl: { type: 'string' },
|
||||
operations: {
|
||||
type: 'array',
|
||||
items: { type: 'string' },
|
||||
default: ['*.*'],
|
||||
},
|
||||
description: { type: 'string' },
|
||||
secret: { type: 'string' },
|
||||
},
|
||||
required: ['targetUrl'],
|
||||
};
|
||||
schemas[`${capitalize(item.namePlural)}`] = {
|
||||
type: 'array',
|
||||
description: `A list of ${item.namePlural}`,
|
||||
items: {
|
||||
$ref: `#/components/schemas/${capitalize(item.nameSingular)}`,
|
||||
},
|
||||
};
|
||||
schemas[`${capitalize(item.nameSingular)}ForUpdate`] = {
|
||||
type: 'object',
|
||||
description: `A webhook for update`,
|
||||
properties: {
|
||||
targetUrl: { type: 'string' },
|
||||
operations: {
|
||||
type: 'array',
|
||||
items: { type: 'string' },
|
||||
},
|
||||
description: { type: 'string' },
|
||||
},
|
||||
};
|
||||
schemas[`${capitalize(item.nameSingular)}ForResponse`] = {
|
||||
type: 'object',
|
||||
description: `A webhook`,
|
||||
properties: {
|
||||
id: { type: 'string', format: 'uuid' },
|
||||
targetUrl: { type: 'string' },
|
||||
operations: {
|
||||
type: 'array',
|
||||
items: { type: 'string' },
|
||||
},
|
||||
description: { type: 'string' },
|
||||
workspaceId: { type: 'string', format: 'uuid' },
|
||||
createdAt: { type: 'string', format: 'date-time' },
|
||||
updatedAt: { type: 'string', format: 'date-time' },
|
||||
deletedAt: { type: 'string', format: 'date-time' },
|
||||
},
|
||||
};
|
||||
schemas[`${capitalize(item.namePlural)}ForResponse`] = {
|
||||
type: 'array',
|
||||
description: `A list of ${item.namePlural}`,
|
||||
items: {
|
||||
$ref: `#/components/schemas/${capitalize(item.nameSingular)}ForResponse`,
|
||||
},
|
||||
};
|
||||
|
||||
return schemas;
|
||||
}
|
||||
case 'apiKey': {
|
||||
schemas[`${capitalize(item.nameSingular)}`] = {
|
||||
type: 'object',
|
||||
description: `An API key`,
|
||||
properties: {
|
||||
name: { type: 'string' },
|
||||
expiresAt: { type: 'string', format: 'date-time' },
|
||||
},
|
||||
required: ['name', 'expiresAt'],
|
||||
};
|
||||
schemas[`${capitalize(item.namePlural)}`] = {
|
||||
type: 'array',
|
||||
description: `A list of ${item.namePlural}`,
|
||||
items: {
|
||||
$ref: `#/components/schemas/${capitalize(item.nameSingular)}`,
|
||||
},
|
||||
};
|
||||
schemas[`${capitalize(item.nameSingular)}ForUpdate`] = {
|
||||
type: 'object',
|
||||
description: `An API key for update`,
|
||||
properties: {
|
||||
name: { type: 'string' },
|
||||
expiresAt: { type: 'string', format: 'date-time' },
|
||||
revokedAt: { type: 'string', format: 'date-time' },
|
||||
},
|
||||
};
|
||||
schemas[`${capitalize(item.nameSingular)}ForResponse`] = {
|
||||
type: 'object',
|
||||
description: `An API key`,
|
||||
properties: {
|
||||
id: { type: 'string', format: 'uuid' },
|
||||
name: { type: 'string' },
|
||||
expiresAt: { type: 'string', format: 'date-time' },
|
||||
revokedAt: { type: 'string', format: 'date-time' },
|
||||
workspaceId: { type: 'string', format: 'uuid' },
|
||||
createdAt: { type: 'string', format: 'date-time' },
|
||||
updatedAt: { type: 'string', format: 'date-time' },
|
||||
},
|
||||
};
|
||||
schemas[`${capitalize(item.namePlural)}ForResponse`] = {
|
||||
type: 'array',
|
||||
description: `A list of ${item.namePlural}`,
|
||||
items: {
|
||||
$ref: `#/components/schemas/${capitalize(item.nameSingular)}ForResponse`,
|
||||
},
|
||||
};
|
||||
|
||||
return schemas;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user