fork of hey-api/openapi-ts because I need some additional things
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request #3416 from hey-api/copilot/add-schema-key-rename-transformer

authored by

Lubos and committed by
GitHub
918ed5dd a8ae16c5

+737
+6
.changeset/quick-carrots-tie.md
··· 1 + --- 2 + "@hey-api/openapi-ts": patch 3 + "@hey-api/shared": patch 4 + --- 5 + 6 + **parser(transforms)**: add `schemaName` transform
+47
docs/openapi-ts/configuration/parser.md
··· 447 447 448 448 You can customize the naming and casing pattern for `requests` and `responses` schemas using the `.name` and `.case` options. 449 449 450 + ### Schema Name 451 + 452 + Sometimes your schema names are auto-generated or follow a naming convention that produces verbose or awkward type names. You can rename schema component keys throughout the specification, automatically updating all `$ref` pointers. For example, stripping version markers from schema names, removing vendor prefixes, converting naming conventions, or shortening deeply qualified names. 453 + 454 + ::: code-group 455 + 456 + <!-- prettier-ignore-start --> 457 + ```js [function] 458 + export default { 459 + input: 'hey-api/backend', // sign up at app.heyapi.dev 460 + output: 'src/client', 461 + parser: { 462 + transforms: { 463 + schemaName: (name) => { // [!code ++] 464 + // Strip version markers: ServiceRoot_v1_20_0_ServiceRoot → ServiceRoot // [!code ++] 465 + let clean = name.replace(/([A-Za-z\d]+)_v\d+_\d+_\d+_([A-Za-z\d]*)/g, (_, p1, p2) => // [!code ++] 466 + p2.startsWith(p1) ? p2 : p1 + p2, // [!code ++] 467 + ); // [!code ++] 468 + // Deduplicate prefixes: Foo_Foo → Foo // [!code ++] 469 + const m = clean.match(/^([A-Za-z\d]+)_\1([A-Za-z\d]*)$/); // [!code ++] 470 + if (m) clean = m[1] + m[2]; // [!code ++] 471 + return clean; // [!code ++] 472 + }, // [!code ++] 473 + }, 474 + }, 475 + }; 476 + ``` 477 + <!-- prettier-ignore-end --> 478 + 479 + ```js [template] 480 + export default { 481 + input: 'hey-api/backend', // sign up at app.heyapi.dev 482 + output: 'src/client', 483 + parser: { 484 + transforms: { 485 + schemaName: 'Api{{name}}', // [!code ++] 486 + }, 487 + }, 488 + }; 489 + ``` 490 + 491 + ::: 492 + 493 + ::: tip Name Collisions 494 + If a transformed schema name conflicts with an existing schema, the rename is skipped for that schema to prevent overwrites. The original name is preserved. 495 + ::: 496 + 450 497 ## Pagination 451 498 452 499 Paginated operations are detected by having a pagination keyword in its parameters or request body. By default, we consider the following to be pagination keywords: `after`, `before`, `cursor`, `offset`, `page`, and `start`.
+22
packages/openapi-ts-tests/main/test/2.0.x.test.ts
··· 289 289 }, 290 290 { 291 291 config: createConfig({ 292 + input: 'transforms-schemas-name.yaml', 293 + output: 'transforms-schemas-name', 294 + parser: { 295 + transforms: { 296 + schemaName: (name: string) => { 297 + // Strip version markers: User_v1_0_0_User → User 298 + let clean = name.replace(/([A-Za-z\d]+)_v\d+_\d+_\d+_([A-Za-z\d]*)/g, (_, p1, p2) => 299 + p2.startsWith(p1) ? p2 : p1 + p2, 300 + ); 301 + // Deduplicate prefixes: Foo_Foo → Foo 302 + const m = clean.match(/^([A-Za-z\d]+)_\1([A-Za-z\d]*)$/); 303 + if (m) clean = m[1]! + m[2]!; 304 + return clean; 305 + }, 306 + }, 307 + }, 308 + plugins: ['@hey-api/typescript'], 309 + }), 310 + description: 'handles schema name transforms', 311 + }, 312 + { 313 + config: createConfig({ 292 314 input: 'schema-unknown.yaml', 293 315 output: 'schema-unknown', 294 316 plugins: ['@hey-api/client-fetch', '@hey-api/sdk'],
+38
packages/openapi-ts-tests/main/test/3.0.x.test.ts
··· 592 592 }, 593 593 { 594 594 config: createConfig({ 595 + input: 'transforms-schemas-name.yaml', 596 + output: 'transforms-schemas-name', 597 + parser: { 598 + transforms: { 599 + schemaName: (name: string) => { 600 + // Strip version markers: User_v1_0_0_User → User 601 + let clean = name.replace(/([A-Za-z\d]+)_v\d+_\d+_\d+_([A-Za-z\d]*)/g, (_, p1, p2) => 602 + p2.startsWith(p1) ? p2 : p1 + p2, 603 + ); 604 + // Deduplicate prefixes: Foo_Foo → Foo 605 + const m = clean.match(/^([A-Za-z\d]+)_\1([A-Za-z\d]*)$/); 606 + if (m) clean = m[1]! + m[2]!; 607 + return clean; 608 + }, 609 + }, 610 + }, 611 + plugins: ['@hey-api/typescript'], 612 + }), 613 + description: 'handles schema name transforms', 614 + }, 615 + { 616 + config: createConfig({ 617 + input: 'transforms-schemas-name-collision.yaml', 618 + output: 'transforms-schemas-name-collision', 619 + parser: { 620 + transforms: { 621 + schemaName: (name: string) => 622 + // Try to rename all _vX_User schemas to "User" 623 + // This should cause collisions since "User" already exists 624 + name.replace(/_v\d+_User$/, ''), 625 + }, 626 + }, 627 + plugins: ['@hey-api/typescript'], 628 + }), 629 + description: 'handles schema name collision prevention', 630 + }, 631 + { 632 + config: createConfig({ 595 633 input: 'security-api-key.yaml', 596 634 output: 'security-api-key', 597 635 plugins: [
+22
packages/openapi-ts-tests/main/test/3.1.x.test.ts
··· 694 694 }, 695 695 { 696 696 config: createConfig({ 697 + input: 'transforms-schemas-name.yaml', 698 + output: 'transforms-schemas-name', 699 + parser: { 700 + transforms: { 701 + schemaName: (name: string) => { 702 + // Strip version markers: User_v1_0_0_User → User 703 + let clean = name.replace(/([A-Za-z\d]+)_v\d+_\d+_\d+_([A-Za-z\d]*)/g, (_, p1, p2) => 704 + p2.startsWith(p1) ? p2 : p1 + p2, 705 + ); 706 + // Deduplicate prefixes: Foo_Foo → Foo 707 + const m = clean.match(/^([A-Za-z\d]+)_\1([A-Za-z\d]*)$/); 708 + if (m) clean = m[1]! + m[2]!; 709 + return clean; 710 + }, 711 + }, 712 + }, 713 + plugins: ['@hey-api/typescript'], 714 + }), 715 + description: 'handles schema name transforms', 716 + }, 717 + { 718 + config: createConfig({ 697 719 input: 'transforms-read-write-nested.yaml', 698 720 output: 'transforms-read-write-nested', 699 721 plugins: ['@hey-api/typescript'],
+3
packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/transforms-schemas-name/index.ts
··· 1 + // This file is auto-generated by @hey-api/openapi-ts 2 + 3 + export type { ClientOptions, Comment, GetUsersData, GetUsersResponse, GetUsersResponses, Post, PostPostsData, PostPostsResponse, PostPostsResponses, User, UserProfile } from './types.gen';
+61
packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/transforms-schemas-name/types.gen.ts
··· 1 + // This file is auto-generated by @hey-api/openapi-ts 2 + 3 + export type ClientOptions = { 4 + baseUrl: string; 5 + }; 6 + 7 + export type User = { 8 + id?: string; 9 + name?: string; 10 + profile?: UserProfile; 11 + }; 12 + 13 + export type UserProfile = { 14 + bio?: string; 15 + avatar?: string; 16 + }; 17 + 18 + export type Post = { 19 + id?: string; 20 + title?: string; 21 + author?: User; 22 + comments?: Array<Comment>; 23 + }; 24 + 25 + export type Comment = { 26 + id?: string; 27 + text?: string; 28 + author?: User; 29 + }; 30 + 31 + export type GetUsersData = { 32 + body?: never; 33 + path?: never; 34 + query?: never; 35 + url: '/users'; 36 + }; 37 + 38 + export type GetUsersResponses = { 39 + /** 40 + * Success 41 + */ 42 + 200: User; 43 + }; 44 + 45 + export type GetUsersResponse = GetUsersResponses[keyof GetUsersResponses]; 46 + 47 + export type PostPostsData = { 48 + body?: Post; 49 + path?: never; 50 + query?: never; 51 + url: '/posts'; 52 + }; 53 + 54 + export type PostPostsResponses = { 55 + /** 56 + * Created 57 + */ 58 + 201: Post; 59 + }; 60 + 61 + export type PostPostsResponse = PostPostsResponses[keyof PostPostsResponses];
+3
packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/transforms-schemas-name-collision/index.ts
··· 1 + // This file is auto-generated by @hey-api/openapi-ts 2 + 3 + export type { ClientOptions, GetTestData, GetTestResponse, GetTestResponses, User, UserV1User, UserV2User } from './types.gen';
+39
packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/transforms-schemas-name-collision/types.gen.ts
··· 1 + // This file is auto-generated by @hey-api/openapi-ts 2 + 3 + export type ClientOptions = { 4 + baseUrl: `${string}://${string}` | (string & {}); 5 + }; 6 + 7 + export type UserV1User = { 8 + id?: string; 9 + version?: 'v1'; 10 + }; 11 + 12 + export type User = { 13 + name?: string; 14 + }; 15 + 16 + export type UserV2User = { 17 + email?: string; 18 + version?: 'v2'; 19 + }; 20 + 21 + export type GetTestData = { 22 + body?: never; 23 + path?: never; 24 + query?: never; 25 + url: '/test'; 26 + }; 27 + 28 + export type GetTestResponses = { 29 + /** 30 + * Success 31 + */ 32 + 200: { 33 + user1?: UserV1User; 34 + user2?: User; 35 + user3?: UserV2User; 36 + }; 37 + }; 38 + 39 + export type GetTestResponse = GetTestResponses[keyof GetTestResponses];
+3
packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/transforms-schemas-name/index.ts
··· 1 + // This file is auto-generated by @hey-api/openapi-ts 2 + 3 + export type { ClientOptions, Comment, GetUsersData, GetUsersResponse, GetUsersResponses, Post, PostPostsData, PostPostsResponse, PostPostsResponses, User, UserProfile } from './types.gen';
+61
packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/transforms-schemas-name/types.gen.ts
··· 1 + // This file is auto-generated by @hey-api/openapi-ts 2 + 3 + export type ClientOptions = { 4 + baseUrl: `${string}://${string}` | (string & {}); 5 + }; 6 + 7 + export type User = { 8 + id?: string; 9 + name?: string; 10 + profile?: UserProfile; 11 + }; 12 + 13 + export type UserProfile = { 14 + bio?: string; 15 + avatar?: string; 16 + }; 17 + 18 + export type Post = { 19 + id?: string; 20 + title?: string; 21 + author?: User; 22 + comments?: Array<Comment>; 23 + }; 24 + 25 + export type Comment = { 26 + id?: string; 27 + text?: string; 28 + author?: User; 29 + }; 30 + 31 + export type GetUsersData = { 32 + body?: never; 33 + path?: never; 34 + query?: never; 35 + url: '/users'; 36 + }; 37 + 38 + export type GetUsersResponses = { 39 + /** 40 + * Success 41 + */ 42 + 200: User; 43 + }; 44 + 45 + export type GetUsersResponse = GetUsersResponses[keyof GetUsersResponses]; 46 + 47 + export type PostPostsData = { 48 + body?: Post; 49 + path?: never; 50 + query?: never; 51 + url: '/posts'; 52 + }; 53 + 54 + export type PostPostsResponses = { 55 + /** 56 + * Created 57 + */ 58 + 201: Post; 59 + }; 60 + 61 + export type PostPostsResponse = PostPostsResponses[keyof PostPostsResponses];
+3
packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/transforms-schemas-name/index.ts
··· 1 + // This file is auto-generated by @hey-api/openapi-ts 2 + 3 + export type { ClientOptions, Comment, GetUsersData, GetUsersResponse, GetUsersResponses, Post, PostPostsData, PostPostsResponse, PostPostsResponses, User, UserProfile } from './types.gen';
+61
packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/transforms-schemas-name/types.gen.ts
··· 1 + // This file is auto-generated by @hey-api/openapi-ts 2 + 3 + export type ClientOptions = { 4 + baseUrl: `${string}://${string}` | (string & {}); 5 + }; 6 + 7 + export type User = { 8 + id?: string; 9 + name?: string; 10 + profile?: UserProfile; 11 + }; 12 + 13 + export type UserProfile = { 14 + bio?: string; 15 + avatar?: string; 16 + }; 17 + 18 + export type Post = { 19 + id?: string; 20 + title?: string; 21 + author?: User; 22 + comments?: Array<Comment>; 23 + }; 24 + 25 + export type Comment = { 26 + id?: string; 27 + text?: string; 28 + author?: User; 29 + }; 30 + 31 + export type GetUsersData = { 32 + body?: never; 33 + path?: never; 34 + query?: never; 35 + url: '/users'; 36 + }; 37 + 38 + export type GetUsersResponses = { 39 + /** 40 + * Success 41 + */ 42 + 200: User; 43 + }; 44 + 45 + export type GetUsersResponse = GetUsersResponses[keyof GetUsersResponses]; 46 + 47 + export type PostPostsData = { 48 + body?: Post; 49 + path?: never; 50 + query?: never; 51 + url: '/posts'; 52 + }; 53 + 54 + export type PostPostsResponses = { 55 + /** 56 + * Created 57 + */ 58 + 201: Post; 59 + }; 60 + 61 + export type PostPostsResponse = PostPostsResponses[keyof PostPostsResponses];
+3
packages/shared/src/config/parser/config.ts
··· 36 36 name: '{{name}}', 37 37 }, 38 38 }, 39 + schemaName: undefined, 39 40 }, 40 41 validate_EXPERIMENTAL: false, 41 42 }, ··· 137 138 }, 138 139 value: fields.readWrite, 139 140 }), 141 + schemaName: 142 + fields.schemaName !== undefined ? fields.schemaName : defaultValue.schemaName, 140 143 }), 141 144 }, 142 145 value: fields.transforms,
+22
packages/shared/src/config/parser/types.ts
··· 175 175 name?: NameTransformer; 176 176 }; 177 177 }; 178 + /** 179 + * Sometimes your schema names are auto-generated or follow a naming convention 180 + * that produces verbose or awkward type names. You can rename schema component 181 + * keys throughout the specification, automatically updating all `$ref` pointers. 182 + * 183 + * @example 184 + * ```ts 185 + * { 186 + * schemaName: (name) => name.replace(/_v\d+_\d+_\d+_/, '_') 187 + * } 188 + * ``` 189 + * 190 + * @default undefined 191 + */ 192 + schemaName?: NameTransformer; 178 193 }; 179 194 /** 180 195 * **This is an experimental feature.** ··· 280 295 */ 281 296 responses: NamingOptions; 282 297 }; 298 + /** 299 + * Rename schema component keys and automatically update all `$ref` pointers 300 + * throughout the specification. 301 + * 302 + * @default undefined 303 + */ 304 + schemaName?: NameTransformer; 283 305 }; 284 306 /** 285 307 * **This is an experimental feature.**
+9
packages/shared/src/openApi/shared/transforms/index.ts
··· 2 2 import { enumsTransform } from './enums'; 3 3 import { propertiesRequiredByDefaultTransform } from './propertiesRequiredByDefault'; 4 4 import { readWriteTransform } from './readWrite'; 5 + import { schemaNameTransform } from './schemas'; 5 6 6 7 export const transformOpenApiSpec = ({ context }: { context: Context }) => { 7 8 const { logger } = context; 8 9 const eventTransformOpenApiSpec = logger.timeEvent('transform-openapi-spec'); 10 + 11 + if (context.config.parser.transforms.schemaName) { 12 + schemaNameTransform({ 13 + config: context.config.parser.transforms.schemaName, 14 + spec: context.spec, 15 + }); 16 + } 17 + 9 18 if (context.config.parser.transforms.enums.enabled) { 10 19 enumsTransform({ 11 20 config: context.config.parser.transforms.enums,
+79
packages/shared/src/openApi/shared/transforms/schemas.ts
··· 1 + import type { Parser } from '../../../config/parser/types'; 2 + import { applyNaming } from '../../../utils/naming/naming'; 3 + import { getSchemasObject } from '../utils/transforms'; 4 + import { specToSchemasPointerNamespace } from './utils'; 5 + 6 + type SchemaNameConfig = Parser['transforms']['schemaName']; 7 + 8 + /** 9 + * Recursively walks the entire spec object and replaces all $ref strings 10 + * according to the provided rename mapping. 11 + */ 12 + const rewriteRefs = (node: unknown, renameMap: Record<string, string>) => { 13 + if (node instanceof Array) { 14 + node.forEach((item) => rewriteRefs(item, renameMap)); 15 + } else if (node && typeof node === 'object') { 16 + for (const [key, value] of Object.entries(node)) { 17 + if (key === '$ref' && typeof value === 'string' && value in renameMap) { 18 + (node as Record<string, unknown>)[key] = renameMap[value]; 19 + } else { 20 + rewriteRefs(value, renameMap); 21 + } 22 + } 23 + } 24 + }; 25 + 26 + /** 27 + * Renames schema component keys and updates all $ref pointers throughout 28 + * the spec. Handles collisions by skipping renames when the target name 29 + * already exists or conflicts with another rename. 30 + */ 31 + export const schemaNameTransform = ({ 32 + config, 33 + spec, 34 + }: { 35 + config: SchemaNameConfig; 36 + spec: unknown; 37 + }) => { 38 + if (!config) { 39 + return; 40 + } 41 + 42 + const schemasObj = getSchemasObject(spec); 43 + if (!schemasObj) { 44 + return; 45 + } 46 + 47 + const schemasPointerNamespace = specToSchemasPointerNamespace(spec); 48 + if (!schemasPointerNamespace) { 49 + return; 50 + } 51 + 52 + const renameMap: Record<string, string> = {}; 53 + const newNames = new Set<string>(); 54 + const namingConfig = { name: config }; 55 + 56 + for (const oldName of Object.keys(schemasObj)) { 57 + const newName = applyNaming(oldName, namingConfig); 58 + 59 + if (newName === oldName || newName in schemasObj || newNames.has(newName)) { 60 + continue; 61 + } 62 + 63 + renameMap[`${schemasPointerNamespace}${oldName}`] = `${schemasPointerNamespace}${newName}`; 64 + newNames.add(newName); 65 + } 66 + 67 + for (const [oldPointer, newPointer] of Object.entries(renameMap)) { 68 + const oldName = oldPointer.slice(schemasPointerNamespace.length); 69 + const newName = newPointer.slice(schemasPointerNamespace.length); 70 + const schema = schemasObj[oldName]; 71 + 72 + delete schemasObj[oldName]; 73 + schemasObj[newName] = schema; 74 + } 75 + 76 + if (Object.keys(renameMap).length > 0) { 77 + rewriteRefs(spec, renameMap); 78 + } 79 + };
+71
specs/2.0.x/transforms-schemas-name.yaml
··· 1 + swagger: '2.0' 2 + info: 3 + title: Schema Name Transform Test (Swagger 2.0) 4 + version: 1.0.0 5 + paths: 6 + /users: 7 + get: 8 + summary: Get users 9 + produces: 10 + - application/json 11 + responses: 12 + '200': 13 + description: Success 14 + schema: 15 + $ref: '#/definitions/User_v1_0_0_User' 16 + /posts: 17 + post: 18 + summary: Create post 19 + consumes: 20 + - application/json 21 + produces: 22 + - application/json 23 + parameters: 24 + - in: body 25 + name: body 26 + schema: 27 + $ref: '#/definitions/Post_v2_1_3_Post' 28 + responses: 29 + '201': 30 + description: Created 31 + schema: 32 + $ref: '#/definitions/Post_v2_1_3_Post' 33 + definitions: 34 + User_v1_0_0_User: 35 + type: object 36 + properties: 37 + id: 38 + type: string 39 + name: 40 + type: string 41 + profile: 42 + $ref: '#/definitions/UserProfile_v1_0_0_UserProfile' 43 + UserProfile_v1_0_0_UserProfile: 44 + type: object 45 + properties: 46 + bio: 47 + type: string 48 + avatar: 49 + type: string 50 + Post_v2_1_3_Post: 51 + type: object 52 + properties: 53 + id: 54 + type: string 55 + title: 56 + type: string 57 + author: 58 + $ref: '#/definitions/User_v1_0_0_User' 59 + comments: 60 + type: array 61 + items: 62 + $ref: '#/definitions/Comment_v1_5_2_Comment' 63 + Comment_v1_5_2_Comment: 64 + type: object 65 + properties: 66 + id: 67 + type: string 68 + text: 69 + type: string 70 + author: 71 + $ref: '#/definitions/User_v1_0_0_User'
+44
specs/3.0.x/transforms-schemas-name-collision.yaml
··· 1 + openapi: 3.0.3 2 + info: 3 + title: Schema Name Collision Test 4 + version: 1.0.0 5 + paths: 6 + /test: 7 + get: 8 + responses: 9 + '200': 10 + description: Success 11 + content: 12 + application/json: 13 + schema: 14 + type: object 15 + properties: 16 + user1: 17 + $ref: '#/components/schemas/User_v1_User' 18 + user2: 19 + $ref: '#/components/schemas/User' 20 + user3: 21 + $ref: '#/components/schemas/User_v2_User' 22 + components: 23 + schemas: 24 + User_v1_User: 25 + type: object 26 + properties: 27 + id: 28 + type: string 29 + version: 30 + type: string 31 + enum: ['v1'] 32 + User: 33 + type: object 34 + properties: 35 + name: 36 + type: string 37 + User_v2_User: 38 + type: object 39 + properties: 40 + email: 41 + type: string 42 + version: 43 + type: string 44 + enum: ['v2']
+70
specs/3.0.x/transforms-schemas-name.yaml
··· 1 + openapi: 3.0.3 2 + info: 3 + title: Schema Name Transform Test 4 + version: 1.0.0 5 + paths: 6 + /users: 7 + get: 8 + summary: Get users 9 + responses: 10 + '200': 11 + description: Success 12 + content: 13 + application/json: 14 + schema: 15 + $ref: '#/components/schemas/User_v1_0_0_User' 16 + /posts: 17 + post: 18 + summary: Create post 19 + requestBody: 20 + content: 21 + application/json: 22 + schema: 23 + $ref: '#/components/schemas/Post_v2_1_3_Post' 24 + responses: 25 + '201': 26 + description: Created 27 + content: 28 + application/json: 29 + schema: 30 + $ref: '#/components/schemas/Post_v2_1_3_Post' 31 + components: 32 + schemas: 33 + User_v1_0_0_User: 34 + type: object 35 + properties: 36 + id: 37 + type: string 38 + name: 39 + type: string 40 + profile: 41 + $ref: '#/components/schemas/UserProfile_v1_0_0_UserProfile' 42 + UserProfile_v1_0_0_UserProfile: 43 + type: object 44 + properties: 45 + bio: 46 + type: string 47 + avatar: 48 + type: string 49 + Post_v2_1_3_Post: 50 + type: object 51 + properties: 52 + id: 53 + type: string 54 + title: 55 + type: string 56 + author: 57 + $ref: '#/components/schemas/User_v1_0_0_User' 58 + comments: 59 + type: array 60 + items: 61 + $ref: '#/components/schemas/Comment_v1_5_2_Comment' 62 + Comment_v1_5_2_Comment: 63 + type: object 64 + properties: 65 + id: 66 + type: string 67 + text: 68 + type: string 69 + author: 70 + $ref: '#/components/schemas/User_v1_0_0_User'
+70
specs/3.1.x/transforms-schemas-name.yaml
··· 1 + openapi: 3.1.0 2 + info: 3 + title: Schema Name Transform Test 4 + version: 1.0.0 5 + paths: 6 + /users: 7 + get: 8 + summary: Get users 9 + responses: 10 + '200': 11 + description: Success 12 + content: 13 + application/json: 14 + schema: 15 + $ref: '#/components/schemas/User_v1_0_0_User' 16 + /posts: 17 + post: 18 + summary: Create post 19 + requestBody: 20 + content: 21 + application/json: 22 + schema: 23 + $ref: '#/components/schemas/Post_v2_1_3_Post' 24 + responses: 25 + '201': 26 + description: Created 27 + content: 28 + application/json: 29 + schema: 30 + $ref: '#/components/schemas/Post_v2_1_3_Post' 31 + components: 32 + schemas: 33 + User_v1_0_0_User: 34 + type: object 35 + properties: 36 + id: 37 + type: string 38 + name: 39 + type: string 40 + profile: 41 + $ref: '#/components/schemas/UserProfile_v1_0_0_UserProfile' 42 + UserProfile_v1_0_0_UserProfile: 43 + type: object 44 + properties: 45 + bio: 46 + type: string 47 + avatar: 48 + type: string 49 + Post_v2_1_3_Post: 50 + type: object 51 + properties: 52 + id: 53 + type: string 54 + title: 55 + type: string 56 + author: 57 + $ref: '#/components/schemas/User_v1_0_0_User' 58 + comments: 59 + type: array 60 + items: 61 + $ref: '#/components/schemas/Comment_v1_5_2_Comment' 62 + Comment_v1_5_2_Comment: 63 + type: object 64 + properties: 65 + id: 66 + type: string 67 + text: 68 + type: string 69 + author: 70 + $ref: '#/components/schemas/User_v1_0_0_User'