feat(threads): thread and forum API, admin, schemas and docs (#3251)

This commit is contained in:
Hampus authored and GitHub committed 2026-10-07 15:25:12 +02:00
1 parent 5f4295e399
commit 3f4160b138
495 files changed
+39232 -1215

No files matched your search

@@ -16,16 +16,54 @@ describe('OpenAPI generation from API controllers', () => {
required: false,
content: {'application/json': {schema: {$ref: '#/components/schemas/ChannelUpdateRequestBody'}}},
});
const options = document.components.schemas.ChannelUpdateRequestBody.anyOf;
expect(options).toHaveLength(6);
const withType = (options ?? []).filter((option) => option.properties && 'type' in option.properties);
const options = document.components.schemas.ChannelUpdateRequestBody.anyOf ?? [];
expect(options).toHaveLength(11);
const withType = options.filter((option) => option.properties && 'type' in option.properties);
expect(withType).toHaveLength(2);
for (const option of withType) {
expect(option.properties?.type).toMatchObject({anyOf: [{const: 0}, {const: 5}]});
}
for (const option of options ?? []) {
for (const option of options.slice(0, 6)) {
expect(option.required).toBeUndefined();
}
expect(options.slice(6)).toEqual([
{$ref: '#/components/schemas/ChannelUpdatePublicThreadRequestBody'},
{$ref: '#/components/schemas/ChannelUpdatePrivateThreadRequestBody'},
{$ref: '#/components/schemas/ChannelUpdateThreadParentRequestBody'},
{$ref: '#/components/schemas/ChannelUpdateForumRequestBody'},
{$ref: '#/components/schemas/ChannelUpdateMediaRequestBody'},
]);
for (const name of [
'ChannelUpdateThreadParentRequestBody',
'ChannelUpdateForumRequestBody',
'ChannelUpdateMediaRequestBody',
]) {
const schema = document.components.schemas[name];
expect(schema['x-fluxer-experiment']).toBe('channel_threads');
expect(schema.properties).not.toHaveProperty('type');
expect(schema.properties).toHaveProperty('default_auto_archive_duration');
}
expect(document.components.schemas.ChannelUpdateForumRequestBody.properties).toHaveProperty('available_tags');
expect(document.components.schemas.ChannelCreateRequest.oneOf).toEqual(
expect.arrayContaining([
{$ref: '#/components/schemas/GuildForumChannelCreateRequest'},
{$ref: '#/components/schemas/GuildMediaChannelCreateRequest'},
]),
);
for (const name of ['ChannelUpdatePublicThreadRequestBody', 'ChannelUpdatePrivateThreadRequestBody']) {
const schema = document.components.schemas[name];
expect(schema['x-fluxer-experiment']).toBe('channel_threads');
expect(schema.properties).not.toHaveProperty('type');
expect(schema.properties).toHaveProperty('archived');
expect(schema.required).toBeUndefined();
}
expect(document.components.schemas.ChannelUpdatePrivateThreadRequestBody.properties).toHaveProperty('invitable');
});
it('documents the bodyless response of thread member settings', () => {
expect(document.paths['/channels/{channel_id}/thread-members/@me/settings'].patch.responses['204']).toEqual({
description: 'No Content',
});
});
it('keeps path-supplied voice IDs out of JSON request bodies', () => {
@@ -45,6 +83,39 @@ describe('OpenAPI generation from API controllers', () => {
]);
});
it('tags every channel thread operation with its experiment', () => {
const tagged = Object.values(document.paths)
.flatMap((pathItem) => Object.values(pathItem))
.filter((operation) => operation['x-fluxer-experiment'] === 'channel_threads')
.map((operation) => operation.operationId)
.sort();
expect(tagged).toEqual(
[
'start_thread_from_message',
'start_thread',
'list_guild_active_threads',
'list_public_archived_threads',
'list_private_archived_threads',
'list_joined_private_archived_threads',
'list_thread_members',
'get_thread_member',
'join_thread',
'add_thread_member',
'leave_thread',
'remove_thread_member',
'update_thread_member_settings',
'search_threads',
'get_channel_post_data',
'create_forum_tag',
'update_forum_tag',
'delete_forum_tag',
'list_admin_guild_threads',
'delete_admin_thread_channel',
].sort(),
);
expect(document.paths['/channels/{channel_id}'].get).not.toHaveProperty('x-fluxer-experiment');
});
it('retains the explicit optional sudo body on channel deletion', () => {
expect(document.paths['/channels/{channel_id}'].delete.requestBody).toMatchObject({
required: false,