From 2dfe886c79142d334fbd48fe1e63517f37ca490b Mon Sep 17 00:00:00 2001 From: Emily Kuo Date: Mon, 24 Feb 2025 18:09:36 -0700 Subject: [PATCH 1/7] chore: add safeguard for docs upload if projects have bidi enabled --- src/lib/syncPagePath.ts | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/lib/syncPagePath.ts b/src/lib/syncPagePath.ts index 042e47ab0..f8025ac76 100644 --- a/src/lib/syncPagePath.ts +++ b/src/lib/syncPagePath.ts @@ -197,7 +197,7 @@ function sortFiles(files: PageMetadata[]): PageMetadata { + return this.handleAPIRes(res); + }); + + const biDiConnection = projectMetadata.git?.connection; + if (biDiConnection && !skipValidation) { + throw new Error( + `Bi-directional syncing is enabled for this project. Uploading these docs will overwrite what's currently synced from Git. To proceed with uploading via \`rdme\`, please use the \`--skip-validation\` flag.`, + ); + } + if (skipValidation) { this.warn('Skipping pre-upload validation of the Markdown file(s). This is not recommended.'); } From 04730e1fbcb281bbfeee43cd46d4760adcde8dda Mon Sep 17 00:00:00 2001 From: Emily Kuo Date: Wed, 26 Feb 2025 14:51:27 -0800 Subject: [PATCH 2/7] chore: add typing to project metadata and fix bidi check --- src/lib/syncPagePath.ts | 16 +- src/lib/types.ts | 4904 ++++++++++++++++++++------------------- 2 files changed, 2578 insertions(+), 2342 deletions(-) diff --git a/src/lib/syncPagePath.ts b/src/lib/syncPagePath.ts index f8025ac76..58e06a5ad 100644 --- a/src/lib/syncPagePath.ts +++ b/src/lib/syncPagePath.ts @@ -1,5 +1,5 @@ import type { PageMetadata } from './readPage.js'; -import type { GuidesRequestRepresentation } from './types.js'; +import type { GuidesRequestRepresentation, ProjectRepresentation } from './types.js'; import type DocsUploadCommand from '../commands/docs/upload.js'; import fs from 'node:fs/promises'; @@ -208,17 +208,17 @@ export default async function syncPagePath(this: CommandsThatSyncMarkdown) { throw err; }); - // get the project metadata via apiv2 https://api.readme.com/v2/projects/me - // look for project.git?.connection - // if it exists, AND the skipValidation flag is not on, throw an error - // if the it doesn't exist or the skipValidation flag is on, proceed - + // check whether or not the project has bidirection syncing enabled + // if it is, validations must be skipped to prevent frontmatter from being overwritten const headers = new Headers({ authorization: `Bearer ${key}` }); - const projectMetadata = await this.readmeAPIFetch('/projects/me', { method: 'GET', headers }).then(res => { + const projectMetadata: ProjectRepresentation = await this.readmeAPIFetch('/projects/me', { + method: 'GET', + headers, + }).then(res => { return this.handleAPIRes(res); }); - const biDiConnection = projectMetadata.git?.connection; + const biDiConnection = projectMetadata.data.git?.connection; if (biDiConnection && !skipValidation) { throw new Error( `Bi-directional syncing is enabled for this project. Uploading these docs will overwrite what's currently synced from Git. To proceed with uploading via \`rdme\`, please use the \`--skip-validation\` flag.`, diff --git a/src/lib/types.ts b/src/lib/types.ts index 23fb65733..41059cd50 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -8,7 +8,7 @@ export const readmeAPIv2Oas = { version: '2.0.0-beta', title: 'ReadMe API v2 πŸ¦‰ (BETA)', // @ts-expect-error custom extension - 'x-readme-deploy': '5.271.0', + 'x-readme-deploy': '5.287.0', termsOfService: 'https://readme.com/tos', contact: { name: 'API Support', @@ -2544,1038 +2544,1345 @@ export const readmeAPIv2Oas = { }, }, }, - '/versions/{version}/reference': { - post: { - operationId: 'createReference', - summary: 'Create a reference', - tags: ['API Reference'], + '/projects/me': { + get: { + operationId: 'getProject', + summary: 'Get project metadata', + tags: ['Projects'], description: - "Creates a reference page in the API Reference section of your developer hub. \n\n>πŸ“˜\n> This route is only available to projects that are using [ReadMe Refactored](https://docs.readme.com/main/docs/welcome-to-readme-refactored).\n\n>🚧 ReadMe's API v2 is currently in beta.\n> This API and its docs are a work in progress. While we don’t expect any major breaking changes, you may encounter occasional issues as we work toward a stable release. Make sure to [check out our API migration guide](https://docs.readme.com/main/reference/api-migration-guide), and [feel free to reach out](mailto:support@readme.io) if you have any questions or feedback!", - requestBody: { - content: { - 'application/json': { - schema: { - type: 'object', - properties: { - allow_crawlers: { - type: 'string', - enum: ['enabled', 'disabled'], - default: 'enabled', - description: 'Allow indexing by robots.', - }, - category: { - type: 'object', - properties: { - uri: { - type: 'string', - pattern: - '\\/versions\\/(stable|([0-9]+)(?:\\.([0-9]+))?(?:\\.([0-9]+))?(-.*)?)\\/categories\\/(guides|reference)\\/((.*))', - description: 'A URI to the category resource.', - }, - }, - required: ['uri'], - additionalProperties: false, - }, - content: { - type: 'object', - properties: { - body: { type: 'string', nullable: true }, - excerpt: { type: 'string', nullable: true }, - link: { - type: 'object', - properties: { - url: { type: 'string', nullable: true }, - new_tab: { type: 'boolean', nullable: true }, + "Returns data about your project.\n\n>🚧 ReadMe's API v2 is currently in beta.\n> This API and its docs are a work in progress. While we don’t expect any major breaking changes, you may encounter occasional issues as we work toward a stable release. Make sure to [check out our API migration guide](https://docs.readme.com/main/reference/api-migration-guide), and [feel free to reach out](mailto:support@readme.io) if you have any questions or feedback!", + responses: { + '200': { + description: 'OK', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + data: { + type: 'object', + properties: { + allow_crawlers: { + type: 'string', + enum: ['enabled', 'disabled'], + default: 'enabled', + description: 'Allow indexing by robots.', }, - additionalProperties: false, - description: - 'Information about where this page should redirect to; only available when `type` is `link`.', - }, - next: { - type: 'object', - properties: { - description: { type: 'string', nullable: true }, - pages: { - type: 'array', - items: { - anyOf: [ - { - type: 'object', - properties: { - slug: { type: 'string' }, - title: { type: 'string', nullable: true }, - type: { type: 'string', enum: ['basic', 'endpoint'] }, - }, - required: ['slug', 'title', 'type'], - additionalProperties: false, + api_designer: { + type: 'object', + properties: { + allow_editing: { + type: 'string', + enum: ['enabled', 'disabled'], + default: 'enabled', + description: 'API Designer is enabled for this project.', + }, + }, + additionalProperties: false, + }, + appearance: { + type: 'object', + properties: { + brand: { + type: 'object', + properties: { + primary_color: { type: 'string', nullable: true }, + link_color: { type: 'string', nullable: true }, + theme: { type: 'string', enum: ['system', 'light', 'dark'], default: 'light' }, + }, + required: ['primary_color', 'link_color'], + additionalProperties: false, + }, + changelog: { + type: 'object', + properties: { + layout: { type: 'string', enum: ['collapsed', 'continuous'], default: 'collapsed' }, + show_author: { + type: 'boolean', + default: true, + description: 'Should the changelog author be shown?', }, - { + show_exact_date: { + type: 'boolean', + default: false, + description: + 'Should the exact date of the changelog entry be shown, or should it be relative?', + }, + }, + additionalProperties: false, + }, + custom_code: { + type: 'object', + properties: { + css: { + type: 'string', + nullable: true, + description: + 'A chunk of custom CSS that you can use to override default CSS that we provide.', + }, + js: { + type: 'string', + nullable: true, + description: + 'A chunk of custom JS that you can use to override or add new behaviors to your documentation. Please note that we do not do any validation on the code that goes in here so you have the potential to negatively impact your users with broken code.', + }, + html: { type: 'object', properties: { - title: { type: 'string', nullable: true }, - type: { type: 'string', enum: ['link'] }, - url: { type: 'string' }, + header: { + type: 'string', + nullable: true, + description: + 'A block of custom HTML that will be added to your `` tag. Good for things like `` tags or loading external CSS.', + }, + home_footer: { + type: 'string', + nullable: true, + description: + 'A block of custom HTML that will be added before the closing `` tag of your **home page**.', + }, + page_footer: { + type: 'string', + nullable: true, + description: + 'A block of custom HTML that will be added before the closing `` tag of your pages.', + }, }, - required: ['title', 'type', 'url'], + required: ['header', 'home_footer', 'page_footer'], additionalProperties: false, }, - ], + }, + required: ['css', 'js', 'html'], + additionalProperties: false, }, - }, - }, - additionalProperties: false, - }, - }, - additionalProperties: false, - }, - href: { - type: 'object', - properties: { - dash: { type: 'string', format: 'uri', description: 'A URL to this page in your ReadMe Dash.' }, - }, - additionalProperties: false, - }, - metadata: { - type: 'object', - properties: { - description: { type: 'string', nullable: true }, - keywords: { type: 'string', nullable: true }, - title: { type: 'string', nullable: true }, - image: { - type: 'object', - properties: { uri: { type: 'string', pattern: '\\/images\\/([a-f\\d]{24})', nullable: true } }, - additionalProperties: false, - }, - }, - additionalProperties: false, - }, - parent: { - type: 'object', - properties: { - uri: { - type: 'string', - pattern: - '\\/versions\\/(stable|([0-9]+)(?:\\.([0-9]+))?(?:\\.([0-9]+))?(-.*)?)\\/(guides|reference)\\/(([a-z0-9-_ ]|[^\\\\x00-\\\\x7F])+)', - nullable: true, - }, - }, - additionalProperties: false, - }, - privacy: { - type: 'object', - properties: { - view: { type: 'string', enum: ['public', 'anyone_with_link'], default: 'anyone_with_link' }, - }, - additionalProperties: false, - }, - renderable: { - type: 'object', - properties: { - status: { - type: 'boolean', - default: true, - description: 'A flag for if the page is renderable or not.', - }, - error: { type: 'string', nullable: true }, - message: { type: 'string', nullable: true }, - }, - additionalProperties: false, - }, - slug: { - allOf: [{ type: 'string' }, { type: 'string', minLength: 1 }], - description: 'The accessible URL slug for the page.', - }, - state: { type: 'string', enum: ['current', 'deprecated'], default: 'current' }, - title: { type: 'string' }, - type: { type: 'string', enum: ['api_config', 'basic', 'endpoint', 'link'], default: 'basic' }, - connections: { - type: 'object', - properties: { - recipes: { - type: 'array', - items: { - type: 'object', - properties: { - uri: { - type: 'string', - pattern: - '\\/versions\\/(stable|([0-9]+)(?:\\.([0-9]+))?(?:\\.([0-9]+))?(-.*)?)\\/recipes\\/(([a-z0-9-_ ]|[^\\\\x00-\\\\x7F])+)', - description: - 'URI of the recipe that this API reference is connected to. The recipe and API reference must exist within the same version.', + footer: { + type: 'object', + properties: { readme_logo: { type: 'string', enum: ['hide', 'show'], default: 'show' } }, + additionalProperties: false, }, - }, - additionalProperties: false, - }, - nullable: true, - }, - }, - additionalProperties: false, - }, - position: { type: 'number' }, - api_config: { type: 'string', enum: ['authentication', 'getting-started', 'my-requests'] }, - api: { - type: 'object', - properties: { - method: { - type: 'string', - enum: ['get', 'put', 'post', 'delete', 'options', 'head', 'patch', 'trace'], - description: 'The endpoint HTTP method.', - }, - path: { type: 'string', description: 'The endpoint path.' }, - schema: { nullable: true }, - stats: { - type: 'object', - properties: { - additional_properties: { - type: 'boolean', - default: false, - description: - 'This API operation uses `additionalProperties` for handling extra schema properties.', - }, - callbacks: { - type: 'boolean', - default: false, - description: 'This API operation has `callbacks` documented.', - }, - circular_references: { - type: 'boolean', - default: false, - description: 'This API operation contains `$ref` schema pointers that resolve to itself.', - }, - common_parameters: { - type: 'boolean', - default: false, - description: 'This API operation utilizes common parameters set at the path level.', - }, - discriminators: { - type: 'boolean', - default: false, - description: - 'This API operation utilizes `discriminator` for discriminating between different parts in a polymorphic schema.', - }, - links: { - type: 'boolean', - default: false, - description: 'This API operation has `links` documented.', - }, - polymorphism: { - type: 'boolean', - default: false, - description: 'This API operation contains polymorphic schemas.', - }, - server_variables: { - type: 'boolean', - default: false, - description: - 'This API operation has composable variables configured for its server definition.', - }, - style: { - type: 'boolean', - default: false, - description: 'This API operation has parameters that have specific `style` serializations.', - }, - webhooks: { - type: 'boolean', - default: false, - description: 'This API definition has `webhooks` documented.', - }, - xml: { - type: 'boolean', - default: false, - description: 'This API operation has parameters or schemas that serialize to XML.', - }, - references: { - type: 'boolean', - description: - 'This API operation, after being dereferenced, has `x-readme-ref-name` entries defining what the original `$ref` schema pointers were named.', - }, - }, - additionalProperties: false, - description: 'OpenAPI features that are utilized within this API operation.', - }, - source: { - type: 'string', - enum: ['api', 'apidesigner', 'apieditor', 'bidi', 'form', 'rdme', 'rdme_github', 'url'], - nullable: true, - }, - uri: { - type: 'string', - pattern: - '\\/versions\\/(stable|([0-9]+)(?:\\.([0-9]+))?(?:\\.([0-9]+))?(-.*)?)\\/apis\\/((([a-z0-9-_ ]|[^\\\\x00-\\\\x7F])+.(json|yaml|yml)))', - nullable: true, - }, - }, - additionalProperties: false, - }, - }, - required: ['category', 'title'], - additionalProperties: false, - }, - }, - }, - required: true, - }, - parameters: [ - { - schema: { type: 'string', pattern: 'stable|([0-9]+)(?:\\.([0-9]+))?(?:\\.([0-9]+))?(-.*)?' }, - in: 'path', - name: 'version', - required: true, - description: 'Project version number or stable.', - }, - ], - responses: { - '201': { - description: 'Created', - content: { - 'application/json': { - schema: { - type: 'object', - properties: { - data: { - type: 'object', - properties: { - allow_crawlers: { - type: 'string', - enum: ['enabled', 'disabled'], - default: 'enabled', - description: 'Allow indexing by robots.', - }, - category: { - type: 'object', - properties: { - uri: { - type: 'string', - pattern: - '\\/versions\\/(stable|([0-9]+)(?:\\.([0-9]+))?(?:\\.([0-9]+))?(-.*)?)\\/categories\\/(guides|reference)\\/((.*))', - description: 'A URI to the category resource.', + header: { + type: 'object', + properties: { + type: { + type: 'string', + enum: ['solid', 'gradient', 'line', 'overlay'], + default: 'solid', + }, + gradient_color: { type: 'string', nullable: true }, + overlay: { + type: 'object', + properties: { + image: { + type: 'object', + properties: { + name: { type: 'string', nullable: true }, + width: { + type: 'number', + nullable: true, + description: 'The pixel width of the image. This is not present for SVGs.', + }, + height: { + type: 'number', + nullable: true, + description: 'The pixel height of the image. This is not present for SVGs.', + }, + color: { + type: 'string', + pattern: + '^(?:#[0-9a-fA-F]{3}|#[0-9a-fA-F]{4}|#[0-9a-fA-F]{6}|#[0-9a-fA-F]{8})$', + nullable: true, + description: 'The primary color contained within your image.', + }, + links: { + type: 'object', + properties: { + original_url: { + type: 'string', + format: 'uri', + nullable: true, + description: + 'If your image was resized upon upload this will be a URL to the original file.', + }, + }, + required: ['original_url'], + additionalProperties: false, + }, + uri: { + type: 'string', + pattern: '\\/images\\/([a-f\\d]{24})', + nullable: true, + description: + 'A URI to the `getImages` endpoint for this image. If the is a legacy image then this `uri` will be `null`. And if you wish to delete this image then you should set this to `null`.', + }, + url: { type: 'string', format: 'uri', nullable: true }, + }, + required: ['name', 'width', 'height', 'color', 'links', 'uri', 'url'], + additionalProperties: false, + }, + type: { + type: 'string', + enum: ['triangles', 'blueprint', 'grain', 'map', 'circuits', 'custom'], + default: 'triangles', + description: + 'The header overlay type. This value is only used if `appearance.header.type` is `overlay`.', + }, + fill: { + type: 'string', + enum: ['auto', 'tile', 'tile-x', 'tile-y', 'cover', 'contain'], + default: 'auto', + description: + 'The header fill type. This is only used if `appearance.header.overlay.type` is `custom`.', + }, + position: { + type: 'string', + enum: [ + 'top-left', + 'top-center', + 'top-right', + 'center-left', + 'center-center', + 'center-right', + 'bottom-left', + 'bottom-center', + 'bottom-right', + ], + default: 'top-left', + description: + 'The positioning of the header. This is only used if `appearance.header.overlay.type` is `custom`.', + }, + }, + required: ['image'], + additionalProperties: false, + }, + }, + required: ['gradient_color', 'overlay'], + additionalProperties: false, }, - }, - required: ['uri'], - additionalProperties: false, - }, - content: { - type: 'object', - properties: { - body: { type: 'string', nullable: true }, - excerpt: { type: 'string', nullable: true }, - link: { + logo: { type: 'object', properties: { - url: { type: 'string', nullable: true }, - new_tab: { - type: 'boolean', - nullable: true, - description: 'Should this URL be opened up in a new tab?', + dark_mode: { + type: 'object', + properties: { + name: { type: 'string', nullable: true }, + width: { + type: 'number', + nullable: true, + description: 'The pixel width of the image. This is not present for SVGs.', + }, + height: { + type: 'number', + nullable: true, + description: 'The pixel height of the image. This is not present for SVGs.', + }, + color: { + type: 'string', + pattern: '^(?:#[0-9a-fA-F]{3}|#[0-9a-fA-F]{4}|#[0-9a-fA-F]{6}|#[0-9a-fA-F]{8})$', + nullable: true, + description: 'The primary color contained within your image.', + }, + links: { + type: 'object', + properties: { + original_url: { + type: 'string', + format: 'uri', + nullable: true, + description: + 'If your image was resized upon upload this will be a URL to the original file.', + }, + }, + required: ['original_url'], + additionalProperties: false, + }, + uri: { + type: 'string', + pattern: '\\/images\\/([a-f\\d]{24})', + nullable: true, + description: + 'A URI to the `getImages` endpoint for this image. If the is a legacy image then this `uri` will be `null`. And if you wish to delete this image then you should set this to `null`.', + }, + url: { type: 'string', format: 'uri', nullable: true }, + }, + required: ['name', 'width', 'height', 'color', 'links', 'uri', 'url'], + additionalProperties: false, + }, + main: { + type: 'object', + properties: { + name: { type: 'string', nullable: true }, + width: { + type: 'number', + nullable: true, + description: 'The pixel width of the image. This is not present for SVGs.', + }, + height: { + type: 'number', + nullable: true, + description: 'The pixel height of the image. This is not present for SVGs.', + }, + color: { + type: 'string', + pattern: '^(?:#[0-9a-fA-F]{3}|#[0-9a-fA-F]{4}|#[0-9a-fA-F]{6}|#[0-9a-fA-F]{8})$', + nullable: true, + description: 'The primary color contained within your image.', + }, + links: { + type: 'object', + properties: { + original_url: { + type: 'string', + format: 'uri', + nullable: true, + description: + 'If your image was resized upon upload this will be a URL to the original file.', + }, + }, + required: ['original_url'], + additionalProperties: false, + }, + uri: { + type: 'string', + pattern: '\\/images\\/([a-f\\d]{24})', + nullable: true, + description: + 'A URI to the `getImages` endpoint for this image. If the is a legacy image then this `uri` will be `null`. And if you wish to delete this image then you should set this to `null`.', + }, + url: { type: 'string', format: 'uri', nullable: true }, + }, + required: ['name', 'width', 'height', 'color', 'links', 'uri', 'url'], + additionalProperties: false, + }, + favicon: { + type: 'object', + properties: { + name: { type: 'string', nullable: true }, + width: { + type: 'number', + nullable: true, + description: 'The pixel width of the image. This is not present for SVGs.', + }, + height: { + type: 'number', + nullable: true, + description: 'The pixel height of the image. This is not present for SVGs.', + }, + color: { + type: 'string', + pattern: '^(?:#[0-9a-fA-F]{3}|#[0-9a-fA-F]{4}|#[0-9a-fA-F]{6}|#[0-9a-fA-F]{8})$', + nullable: true, + description: 'The primary color contained within your image.', + }, + links: { + type: 'object', + properties: { + original_url: { + type: 'string', + format: 'uri', + nullable: true, + description: + 'If your image was resized upon upload this will be a URL to the original file.', + }, + }, + required: ['original_url'], + additionalProperties: false, + }, + uri: { + type: 'string', + pattern: '\\/images\\/([a-f\\d]{24})', + nullable: true, + description: + 'A URI to the `getImages` endpoint for this image. If the is a legacy image then this `uri` will be `null`. And if you wish to delete this image then you should set this to `null`.', + }, + url: { type: 'string', format: 'uri', nullable: true }, + }, + required: ['name', 'width', 'height', 'color', 'links', 'uri', 'url'], + additionalProperties: false, + }, + size: { type: 'string', enum: ['default', 'large'], default: 'default' }, + }, + required: ['dark_mode', 'main', 'favicon'], + additionalProperties: false, + }, + markdown: { + type: 'object', + properties: { + callouts: { + type: 'object', + properties: { + icon_font: { + type: 'string', + enum: ['emojis', 'fontawesome'], + default: 'emojis', + description: 'Handles the types of icons that are shown in Markdown callouts.', + }, + }, + additionalProperties: false, + }, + }, + required: ['callouts'], + additionalProperties: false, + }, + navigation: { + type: 'object', + properties: { + first_page: { + type: 'string', + enum: ['documentation', 'reference', 'landing_page'], + default: 'landing_page', + description: + 'The page that users will first see when they access your documentation hub.', + }, + left: { + type: 'array', + items: { + type: 'object', + properties: { + type: { + type: 'string', + enum: [ + 'home', + 'guides', + 'discussions', + 'changelog', + 'search_box', + 'link_url', + 'custom_page', + 'user_controls', + 'reference', + 'recipes', + ], + }, + title: { type: 'string', nullable: true }, + url: { type: 'string', nullable: true }, + custom_page: { type: 'string', nullable: true }, + }, + required: ['type', 'title', 'url', 'custom_page'], + additionalProperties: false, + }, + description: + 'The navigation settings for the left side of your projects navigation bar.', + }, + links: { + type: 'object', + properties: { + changelog: { + type: 'object', + properties: { + label: { type: 'string', enum: ['Changelog'] }, + alias: { type: 'string', nullable: true }, + visibility: { + type: 'string', + enum: ['enabled', 'disabled'], + default: 'enabled', + }, + }, + required: ['label', 'alias'], + additionalProperties: false, + }, + discussions: { + type: 'object', + properties: { + label: { type: 'string', enum: ['Discussions'] }, + alias: { type: 'string', nullable: true }, + visibility: { + type: 'string', + enum: ['enabled', 'disabled'], + default: 'enabled', + }, + }, + required: ['label', 'alias'], + additionalProperties: false, + }, + home: { + type: 'object', + properties: { + label: { type: 'string', enum: ['Home'] }, + visibility: { + type: 'string', + enum: ['enabled', 'disabled'], + default: 'enabled', + }, + }, + required: ['label'], + additionalProperties: false, + }, + graphql: { + type: 'object', + properties: { + label: { type: 'string', enum: ['GraphQL'] }, + visibility: { + type: 'string', + enum: ['enabled', 'disabled'], + default: 'disabled', + nullable: true, + }, + }, + required: ['label'], + additionalProperties: false, + }, + guides: { + type: 'object', + properties: { + label: { type: 'string', enum: ['Guides'] }, + alias: { type: 'string', nullable: true }, + visibility: { + type: 'string', + enum: ['enabled', 'disabled'], + default: 'enabled', + }, + }, + required: ['label', 'alias'], + additionalProperties: false, + }, + recipes: { + type: 'object', + properties: { + label: { type: 'string', enum: ['Recipes'] }, + alias: { type: 'string', nullable: true }, + visibility: { + type: 'string', + enum: ['enabled', 'disabled'], + default: 'disabled', + }, + }, + required: ['label', 'alias'], + additionalProperties: false, + }, + reference: { + type: 'object', + properties: { + label: { type: 'string', enum: ['API Reference'] }, + alias: { type: 'string', nullable: true }, + visibility: { + type: 'string', + enum: ['enabled', 'disabled'], + default: 'enabled', + }, + }, + required: ['label', 'alias'], + additionalProperties: false, + }, + }, + required: [ + 'changelog', + 'discussions', + 'home', + 'graphql', + 'guides', + 'recipes', + 'reference', + ], + additionalProperties: false, }, - }, - required: ['url', 'new_tab'], - additionalProperties: false, - description: - 'Information about where this page should redirect to; only available when `type` is `link`.', - }, - next: { - type: 'object', - properties: { - description: { type: 'string', nullable: true }, - pages: { + logo_link: { + type: 'string', + enum: ['landing_page', 'homepage'], + default: 'homepage', + description: + 'Where users will be directed to when they click on your logo in the navigation bar.', + }, + right: { type: 'array', items: { - anyOf: [ - { - type: 'object', - properties: { - slug: { type: 'string' }, - title: { type: 'string', nullable: true }, - type: { type: 'string', enum: ['basic', 'endpoint'] }, - }, - required: ['slug', 'title', 'type'], - additionalProperties: false, + type: 'object', + properties: { + type: { + type: 'string', + enum: [ + 'home', + 'guides', + 'discussions', + 'changelog', + 'search_box', + 'link_url', + 'custom_page', + 'user_controls', + 'reference', + 'recipes', + ], }, - { - type: 'object', - properties: { - title: { type: 'string', nullable: true }, - type: { type: 'string', enum: ['link'] }, - url: { type: 'string' }, - }, - required: ['title', 'type', 'url'], - additionalProperties: false, + title: { type: 'string', nullable: true }, + url: { type: 'string', nullable: true }, + custom_page: { type: 'string', nullable: true }, + }, + required: ['type', 'title', 'url', 'custom_page'], + additionalProperties: false, + }, + description: + 'The navigation settings for the right side of your projects navigation bar.', + }, + sub_nav: { + type: 'array', + items: { + type: 'object', + properties: { + type: { + type: 'string', + enum: [ + 'home', + 'guides', + 'discussions', + 'changelog', + 'search_box', + 'link_url', + 'custom_page', + 'user_controls', + 'reference', + 'recipes', + ], }, - ], + title: { type: 'string', nullable: true }, + url: { type: 'string', nullable: true }, + custom_page: { type: 'string', nullable: true }, + }, + required: ['type', 'title', 'url', 'custom_page'], + additionalProperties: false, }, + description: 'The navigation settings for your projects subnavigation bar.', }, - }, - required: ['description', 'pages'], - additionalProperties: false, - }, - }, - required: ['body', 'excerpt', 'link', 'next'], - additionalProperties: false, - }, - href: { - type: 'object', - properties: { - dash: { - type: 'string', - format: 'uri', - description: 'A URL to this page in your ReadMe Dash.', - }, - }, - required: ['dash'], - additionalProperties: false, - }, - metadata: { - type: 'object', - properties: { - description: { type: 'string', nullable: true }, - image: { - type: 'object', - properties: { - uri: { + subheader_layout: { type: 'string', enum: ['links', 'dropdown'], default: 'links' }, + version: { type: 'string', - pattern: '\\/images\\/([a-f\\d]{24})', - nullable: true, + enum: ['enabled', 'disabled'], + default: 'enabled', description: - 'A URI to the `getImages` endpoint for this image. If the is a legacy image then this `uri` will be `null`. And if you wish to delete this image then you should set this to `null`.', + 'Should your current documentation version be shown in the navigation bar?', }, - url: { type: 'string', format: 'uri', nullable: true }, }, - required: ['uri', 'url'], + required: ['left', 'links', 'right', 'sub_nav'], additionalProperties: false, }, - keywords: { + table_of_contents: { type: 'string', - nullable: true, - description: 'A comma-separated list of keywords to place into your page metadata.', + enum: ['enabled', 'disabled'], + default: 'enabled', + description: 'Should your guides show a table of contents?', }, - title: { type: 'string', nullable: true }, - }, - required: ['description', 'image', 'keywords', 'title'], - additionalProperties: false, - }, - parent: { - type: 'object', - properties: { - uri: { + whats_next_label: { type: 'string', - pattern: - '\\/versions\\/(stable|([0-9]+)(?:\\.([0-9]+))?(?:\\.([0-9]+))?(-.*)?)\\/(guides|reference)\\/(([a-z0-9-_ ]|[^\\\\x00-\\\\x7F])+)', nullable: true, - description: 'A URI to the parent page resource including the page ID or slug.', + description: + 'What should we call the next steps section of your guides? Defaults to "What\'s Next".', }, }, - required: ['uri'], + required: [ + 'brand', + 'changelog', + 'custom_code', + 'footer', + 'header', + 'logo', + 'markdown', + 'navigation', + 'whats_next_label', + ], additionalProperties: false, }, - privacy: { + canonical_url: { + type: 'string', + format: 'uri', + nullable: true, + description: + "The canonical base URL for your project defaults to your project's base URL, but you can override the canonical base URL with this field.", + }, + custom_login: { type: 'object', properties: { - view: { type: 'string', enum: ['public', 'anyone_with_link'], default: 'anyone_with_link' }, + jwt_secret: { type: 'string' }, + login_url: { type: 'string', format: 'uri', nullable: true }, + logout_url: { type: 'string', format: 'uri', nullable: true }, }, + required: ['jwt_secret', 'login_url', 'logout_url'], additionalProperties: false, }, - project: { + default_version: { type: 'object', properties: { - name: { type: 'string', description: 'The name of the project.' }, - subdomain: { - type: 'string', - pattern: '[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*', - maxLength: 30, - description: 'The subdomain of the project.', - }, - uri: { + name: { type: 'string', - pattern: '\\/projects\\/(me|[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*)', - description: 'A URI to the project that this page belongs to.', + pattern: 'stable|([0-9]+)(?:\\.([0-9]+))?(?:\\.([0-9]+))?(-.*)?', + description: 'The version of your project that users are directed to by default.', }, }, - required: ['name', 'subdomain', 'uri'], + required: ['name'], additionalProperties: false, }, - renderable: { + description: { + type: 'string', + nullable: true, + description: + 'The description of your project. This is used in the page meta description and is seen by search engines and sites like Facebook.', + }, + glossary: { + type: 'array', + items: { + type: 'object', + properties: { + term: { + type: 'string', + description: + 'Glossary term is what gets displayed in your documentation when embedded.', + }, + definition: { + type: 'string', + description: + 'Glossary definition is revealed to users when they mouse over the glossary term.', + }, + }, + required: ['term', 'definition'], + additionalProperties: false, + }, + default: [], + description: + 'List of glossary terms in your project that can be used within your documentation.', + }, + health_check: { type: 'object', properties: { - status: { - type: 'boolean', - default: true, - description: 'A flag for if the page is renderable or not.', - }, - error: { type: 'string', nullable: true, description: 'The rendering error.' }, - message: { + provider: { type: 'string', - nullable: true, - description: 'Additional details about the rendering error.', + enum: ['manual', 'statuspage', 'none'], + default: 'none', + description: + 'The type of provider you wish to use for for managing your APIs health: manually or through [Atlassian Statuspage](https://www.atlassian.com/software/statuspage).', + }, + settings: { + type: 'object', + properties: { + manual: { + type: 'object', + properties: { + status: { + type: 'string', + enum: ['up', 'down'], + default: 'up', + description: + 'If you are manually managing your APIs health this is a status boolean indicating if your API is up or down.', + }, + url: { + type: 'string', + nullable: true, + description: + 'The URL that we will show to your users when your API is down. This is only used when `health_check.provider` is set to `manual`.', + }, + }, + required: ['url'], + additionalProperties: false, + }, + statuspage: { + type: 'object', + properties: { + id: { + type: 'string', + nullable: true, + description: + 'If managing your APIs health through [Statuspage](https://www.atlassian.com/software/statuspage) this is your Statuspage ID.', + }, + }, + required: ['id'], + additionalProperties: false, + }, + }, + required: ['manual', 'statuspage'], + additionalProperties: false, }, }, + required: ['settings'], additionalProperties: false, }, - slug: { - allOf: [{ type: 'string' }, { type: 'string', minLength: 1 }], - description: 'The accessible URL slug for the page.', - }, - state: { type: 'string', enum: ['current', 'deprecated'], default: 'current' }, - title: { type: 'string' }, - type: { type: 'string', enum: ['api_config', 'basic', 'endpoint', 'link'], default: 'basic' }, - updated_at: { - type: 'string', - format: 'date-time', - description: 'An ISO 8601 formatted date for when the page was updated.', - }, - uri: { - type: 'string', - pattern: - '\\/versions\\/(stable|([0-9]+)(?:\\.([0-9]+))?(?:\\.([0-9]+))?(-.*)?)\\/(guides|reference)\\/(([a-z0-9-_ ]|[^\\\\x00-\\\\x7F])+)', - description: 'A URI to the page resource.', - }, - api_config: { + homepage_url: { type: 'string', - enum: ['authentication', 'getting-started', 'my-requests'], nullable: true, + description: + 'The URL for your company\'s main website. We\'ll link to it in various places so people can "Go Home".', }, - api: { + integrations: { type: 'object', properties: { - method: { - type: 'string', - enum: ['get', 'put', 'post', 'delete', 'options', 'head', 'patch', 'trace'], - description: 'The endpoint HTTP method.', + aws: { + type: 'object', + properties: { + readme_webhook_login: { + type: 'object', + properties: { + external_id: { type: 'string', nullable: true }, + region: { + type: 'string', + enum: [ + 'me-south-1', + 'cn-north-1', + 'ca-west-1', + 'us-west-1', + 'ca-central-1', + 'af-south-1', + 'eu-central-1', + 'ap-east-1', + 'ap-south-2', + 'eu-west-1', + 'ap-southeast-3', + 'eu-west-2', + 'ap-southeast-5', + 'ap-southeast-4', + 'eu-south-1', + 'ap-south-1', + 'cn-northwest-1', + 'us-east-2', + 'us-west-2', + 'ap-northeast-3', + 'eu-west-3', + 'sa-east-1', + 'ap-northeast-2', + 'ap-southeast-1', + 'eu-south-2', + 'eu-north-1', + 'ap-southeast-2', + 'il-central-1', + 'ap-northeast-1', + 'me-central-1', + 'us-east-1', + 'eu-central-2', + ], + nullable: true, + }, + role_arn: { type: 'string', nullable: true }, + usage_plan_id: { type: 'string', nullable: true }, + }, + required: ['external_id', 'region', 'role_arn', 'usage_plan_id'], + additionalProperties: false, + }, + }, + required: ['readme_webhook_login'], + additionalProperties: false, + }, + bing: { + type: 'object', + properties: { verify: { type: 'string', nullable: true } }, + required: ['verify'], + additionalProperties: false, + }, + google: { + type: 'object', + properties: { + analytics: { + type: 'string', + nullable: true, + description: + "Your Google Analytics ID. If it starts with UA-, we'll use Universal Analytics otherwise Google Analytics 4.", + }, + site_verification: { type: 'string', nullable: true }, + }, + required: ['analytics', 'site_verification'], + additionalProperties: false, + }, + heap: { + type: 'object', + properties: { id: { type: 'string', nullable: true } }, + required: ['id'], + additionalProperties: false, + }, + intercom: { + type: 'object', + properties: { + app_id: { type: 'string', nullable: true }, + secure_mode: { + type: 'object', + properties: { + key: { + type: 'string', + nullable: true, + description: + 'By supplying a secure mode key you will opt into [Intercoms Identity Verification](https://docs.intercom.io/configuring-intercom/enable-secure-mode) system.', + }, + email_only: { + type: 'boolean', + default: false, + description: + 'Should ReadMe only identify users by their email addresses? This integrates better with your existing Intercom but is possibly less secure.', + }, + }, + required: ['key'], + additionalProperties: false, + }, + }, + required: ['app_id', 'secure_mode'], + additionalProperties: false, }, - path: { type: 'string', description: 'The endpoint path.' }, - schema: { - nullable: true, - description: - 'The API schema for this reference endpoint. This schema is a reduced version of the full API definition and only contains the necessary information for this endpoint.', + koala: { + type: 'object', + properties: { key: { type: 'string', nullable: true } }, + required: ['key'], + additionalProperties: false, }, - stats: { + localize: { + type: 'object', + properties: { key: { type: 'string', nullable: true } }, + required: ['key'], + additionalProperties: false, + }, + recaptcha: { type: 'object', properties: { - additional_properties: { - type: 'boolean', - default: false, - description: - 'This API operation uses `additionalProperties` for handling extra schema properties.', - }, - callbacks: { - type: 'boolean', - default: false, - description: 'This API operation has `callbacks` documented.', - }, - circular_references: { - type: 'boolean', - default: false, - description: - 'This API operation contains `$ref` schema pointers that resolve to itself.', - }, - common_parameters: { - type: 'boolean', - default: false, - description: 'This API operation utilizes common parameters set at the path level.', - }, - discriminators: { - type: 'boolean', - default: false, - description: - 'This API operation utilizes `discriminator` for discriminating between different parts in a polymorphic schema.', - }, - links: { - type: 'boolean', - default: false, - description: 'This API operation has `links` documented.', - }, - polymorphism: { - type: 'boolean', - default: false, - description: 'This API operation contains polymorphic schemas.', - }, - server_variables: { - type: 'boolean', - default: false, - description: - 'This API operation has composable variables configured for its server definition.', - }, - style: { - type: 'boolean', - default: false, - description: - 'This API operation has parameters that have specific `style` serializations.', - }, - webhooks: { - type: 'boolean', - default: false, - description: 'This API definition has `webhooks` documented.', - }, - xml: { - type: 'boolean', - default: false, - description: 'This API operation has parameters or schemas that serialize to XML.', - }, - references: { - type: 'boolean', + site_key: { type: 'string', nullable: true }, + secret_key: { type: 'string', nullable: true }, + }, + required: ['site_key', 'secret_key'], + additionalProperties: false, + description: 'https://docs.readme.com/main/docs/recaptcha', + }, + segment: { + type: 'object', + properties: { + key: { type: 'string', nullable: true }, + domain: { + type: 'string', + nullable: true, description: - 'This API operation, after being dereferenced, has `x-readme-ref-name` entries defining what the original `$ref` schema pointers were named.', + 'If you are proxying [Segment](https://segment.com/) requests through a custom domain this is that domain. More information about this configuration can be found [here](https://docs.readme.com/main/docs/segment#using-a-custom-domain-with-segment).', }, }, - required: ['references'], + required: ['key', 'domain'], additionalProperties: false, - description: 'OpenAPI features that are utilized within this API operation.', }, - source: { - type: 'string', - enum: ['api', 'apidesigner', 'apieditor', 'bidi', 'form', 'rdme', 'rdme_github', 'url'], - nullable: true, - description: 'The source by which this API definition was ingested.', + typekit: { + type: 'object', + properties: { key: { type: 'string', nullable: true } }, + required: ['key'], + additionalProperties: false, }, - uri: { - type: 'string', - pattern: - '\\/versions\\/(stable|([0-9]+)(?:\\.([0-9]+))?(?:\\.([0-9]+))?(-.*)?)\\/apis\\/((([a-z0-9-_ ]|[^\\\\x00-\\\\x7F])+.(json|yaml|yml)))', - nullable: true, - description: 'A URI to the API resource.', + zendesk: { + type: 'object', + properties: { subdomain: { type: 'string', nullable: true } }, + required: ['subdomain'], + additionalProperties: false, }, }, - required: ['method', 'path', 'stats', 'source', 'uri'], + required: [ + 'aws', + 'bing', + 'google', + 'heap', + 'intercom', + 'koala', + 'localize', + 'recaptcha', + 'segment', + 'typekit', + 'zendesk', + ], additionalProperties: false, - description: 'Information about the API that this reference page is attached to.', }, - connections: { + name: { type: 'string', description: 'The name of the project.' }, + onboarding_completed: { type: 'object', properties: { - recipes: { - type: 'array', - items: { - type: 'object', - properties: { - uri: { - type: 'string', - pattern: - '\\/versions\\/(stable|([0-9]+)(?:\\.([0-9]+))?(?:\\.([0-9]+))?(-.*)?)\\/recipes\\/(([a-z0-9-_ ]|[^\\\\x00-\\\\x7F])+)', - description: - 'URI of the recipe that this API reference is connected to. The recipe and API reference must exist within the same version.', - }, - }, - required: ['uri'], - additionalProperties: false, - }, - nullable: true, - description: 'A collection of recipes that are displayed on this API reference.', - }, + api: { type: 'boolean', default: false }, + appearance: { type: 'boolean', default: false }, + documentation: { type: 'boolean', default: false }, + domain: { type: 'boolean', default: false }, + jwt: { type: 'boolean', default: false }, + logs: { type: 'boolean', default: false }, + metricsSDK: { type: 'boolean', default: false }, }, - required: ['recipes'], additionalProperties: false, }, - }, - required: [ - 'category', - 'content', - 'href', - 'metadata', - 'parent', - 'privacy', - 'project', - 'renderable', - 'slug', - 'title', - 'updated_at', - 'uri', - 'api_config', - 'api', - 'connections', - ], - additionalProperties: false, - }, - }, - required: ['data'], - additionalProperties: false, - }, - }, - }, - }, - }, - }, - }, - '/versions/{version}/reference/{slug}': { - get: { - operationId: 'getReference', - summary: 'Retrieve a reference', - tags: ['API Reference'], - description: - "Retrieves a page from the API reference section of your developer hub. \n\n>πŸ“˜\n> This route is only available to projects that are using [ReadMe Refactored](https://docs.readme.com/main/docs/welcome-to-readme-refactored).\n\n>🚧 ReadMe's API v2 is currently in beta.\n> This API and its docs are a work in progress. While we don’t expect any major breaking changes, you may encounter occasional issues as we work toward a stable release. Make sure to [check out our API migration guide](https://docs.readme.com/main/reference/api-migration-guide), and [feel free to reach out](mailto:support@readme.io) if you have any questions or feedback!", - parameters: [ - { - schema: { type: 'string', pattern: 'stable|([0-9]+)(?:\\.([0-9]+))?(?:\\.([0-9]+))?(-.*)?' }, - in: 'path', - name: 'version', - required: true, - description: 'Project version number or stable.', - }, - { - schema: { type: 'string', pattern: '([a-z0-9-_ ]|[^\\\\x00-\\\\x7F])+' }, - in: 'path', - name: 'slug', - required: true, - description: 'A URL-safe representation of the resource.', - }, - ], - responses: { - '200': { - description: 'OK', - content: { - 'application/json': { - schema: { - type: 'object', - properties: { - data: { - type: 'object', - properties: { - allow_crawlers: { - type: 'string', - enum: ['enabled', 'disabled'], - default: 'enabled', - description: 'Allow indexing by robots.', - }, - category: { + pages: { type: 'object', properties: { - uri: { + not_found: { type: 'string', pattern: - '\\/versions\\/(stable|([0-9]+)(?:\\.([0-9]+))?(?:\\.([0-9]+))?(-.*)?)\\/categories\\/(guides|reference)\\/((.*))', - description: 'A URI to the category resource.', + '\\/versions\\/(stable|([0-9]+)(?:\\.([0-9]+))?(?:\\.([0-9]+))?(-.*)?)\\/custom_pages\\/([a-f\\d]{24}|([a-z0-9-_ ]|[^\\\\x00-\\\\x7F])+)', + nullable: true, + description: + 'The page you wish to be served to your users when they encounter a 404. This can either map to the `uri` of a Custom Page on your project or be set to `null`. If `null` then the default ReadMe 404 page will be served. The version within the `uri` must be mapped to your stable version.', }, }, - required: ['uri'], + required: ['not_found'], additionalProperties: false, }, - content: { + parent: { + type: 'string', + nullable: true, + description: + "Does the project have a parent project (enterprise)? If so, this resolves to the parent's subdomain.", + }, + plan: { type: 'object', properties: { - body: { type: 'string', nullable: true }, - excerpt: { type: 'string', nullable: true }, - link: { + type: { + type: 'string', + enum: [ + 'business', + 'business2018', + 'business-annual-2024', + 'enterprise', + 'free', + 'freelaunch', + 'opensource', + 'startup', + 'startup2018', + 'startup-annual-2024', + ], + default: 'free', + }, + grace_period: { type: 'object', properties: { - url: { type: 'string', nullable: true }, - new_tab: { - type: 'boolean', - nullable: true, - description: 'Should this URL be opened up in a new tab?', - }, + enabled: { type: 'boolean', default: false }, + end_date: { type: 'string', format: 'date-time', nullable: true, default: null }, }, - required: ['url', 'new_tab'], additionalProperties: false, - description: - 'Information about where this page should redirect to; only available when `type` is `link`.', }, - next: { + trial: { type: 'object', properties: { - description: { type: 'string', nullable: true }, - pages: { - type: 'array', - items: { - anyOf: [ - { - type: 'object', - properties: { - slug: { type: 'string' }, - title: { type: 'string', nullable: true }, - type: { type: 'string', enum: ['basic', 'endpoint'] }, - }, - required: ['slug', 'title', 'type'], - additionalProperties: false, - }, - { - type: 'object', - properties: { - title: { type: 'string', nullable: true }, - type: { type: 'string', enum: ['link'] }, - url: { type: 'string' }, - }, - required: ['title', 'type', 'url'], - additionalProperties: false, - }, - ], - }, + expired: { type: 'boolean', default: false }, + end_date: { + type: 'string', + format: 'date-time', + description: 'The end date for your two week trial.', }, }, - required: ['description', 'pages'], + required: ['end_date'], additionalProperties: false, }, }, - required: ['body', 'excerpt', 'link', 'next'], + required: ['grace_period', 'trial'], additionalProperties: false, }, - href: { + privacy: { type: 'object', properties: { - dash: { + view: { type: 'string', - format: 'uri', - description: 'A URL to this page in your ReadMe Dash.', + enum: ['public', 'admin', 'password', 'custom_login'], + default: 'public', + description: + '* `public` - Site is available to the public.\n* `admin` - Site is only available to users that have project permissions.\n* `password` - Site is gated behind a password authentication system.\n* `custom_login` - Users who view your site will be forwarded to a URL of your choice, having them login there and be forwarded back to your ReadMe site.', + }, + password: { + type: 'string', + nullable: true, + description: + "The project's password for when `privacy.view` is `password`. This field can be set, but it will not be returned by the API.", }, }, - required: ['dash'], + required: ['password'], additionalProperties: false, }, - metadata: { + redirects: { + type: 'array', + items: { + type: 'object', + properties: { from: { type: 'string' }, to: { type: 'string' } }, + required: ['from', 'to'], + additionalProperties: false, + }, + description: + 'A collection of page redirects that ReadMe will permanently redirect users to when attempting to render a 404. Check out our [redirect docs](https://docs.readme.com/main/docs/error-pages#section-redirects) for more information on how they are handled.', + }, + reference: { type: 'object', properties: { - description: { type: 'string', nullable: true }, - image: { - type: 'object', - properties: { - uri: { - type: 'string', - pattern: '\\/images\\/([a-f\\d]{24})', - nullable: true, - description: - 'A URI to the `getImages` endpoint for this image. If the is a legacy image then this `uri` will be `null`. And if you wish to delete this image then you should set this to `null`.', - }, - url: { type: 'string', format: 'uri', nullable: true }, - }, - required: ['uri', 'url'], - additionalProperties: false, + api_sdk_snippets: { + type: 'string', + enum: ['enabled', 'disabled'], + default: 'enabled', + description: 'Enable SDK-generated request code snippets.', }, - keywords: { + defaults: { type: 'string', - nullable: true, - description: 'A comma-separated list of keywords to place into your page metadata.', + enum: ['always_use', 'use_only_if_required'], + default: 'use_only_if_required', + description: + 'When `always_use`, any `default` values defined in your API definition are used to populate your request data in the API Explorer, even if the parameter is not marked as `required`.', + }, + json_editor: { + type: 'string', + enum: ['enabled', 'disabled'], + default: 'disabled', + description: 'When `enabled`, allows editing the request body with a JSON editor.', + }, + request_history: { + type: 'string', + enum: ['enabled', 'disabled'], + default: 'enabled', + description: 'When `enabled`, request history for API endpoints are shown.', + }, + oauth_flows: { + type: 'string', + enum: ['enabled', 'disabled'], + default: 'disabled', + description: + 'When `enabled`, enable the new OAuth Flows experience in the API Reference section.', + }, + response_examples: { + type: 'string', + enum: ['expanded', 'collapsed'], + default: 'collapsed', + description: + 'When `expanded`, response examples will be expanded by default if a 200 level response exists.', + }, + response_schemas: { + type: 'string', + enum: ['expanded', 'collapsed'], + default: 'collapsed', + description: + 'When `expanded`, response schemas will be expanded by default if a 200 level response schema exists.', }, - title: { type: 'string', nullable: true }, }, - required: ['description', 'image', 'keywords', 'title'], additionalProperties: false, + description: + 'Contains options to configure interactive sections on your API Reference pages.', }, - parent: { + seo: { type: 'object', properties: { - uri: { + overwrite_title_tag: { type: 'string', - pattern: - '\\/versions\\/(stable|([0-9]+)(?:\\.([0-9]+))?(?:\\.([0-9]+))?(-.*)?)\\/(guides|reference)\\/(([a-z0-9-_ ]|[^\\\\x00-\\\\x7F])+)', - nullable: true, - description: 'A URI to the parent page resource including the page ID or slug.', + enum: ['enabled', 'disabled'], + default: 'disabled', + description: + "Overwrite pages' tag with their custom metadata title (if present).", }, }, - required: ['uri'], additionalProperties: false, }, - privacy: { - type: 'object', - properties: { - view: { type: 'string', enum: ['public', 'anyone_with_link'], default: 'anyone_with_link' }, + sitemap: { + type: 'string', + enum: ['enabled', 'disabled'], + default: 'disabled', + description: 'Expose a `sitemap.xml` directory on your project.', + }, + subdomain: { type: 'string', pattern: '[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*', maxLength: 30 }, + suggested_edits: { + type: 'string', + enum: ['enabled', 'disabled'], + default: 'enabled', + description: 'Allow users to suggest edits to your documentation.', + }, + variable_defaults: { + type: 'array', + items: { + type: 'object', + properties: { + id: { type: 'string', description: 'Variable Identifier' }, + name: { type: 'string', description: 'The key name of the variable.' }, + default: { type: 'string', description: 'The default value of the variable.' }, + source: { + type: 'string', + enum: ['server', 'security', 'custom', ''], + default: '', + description: + 'The variables source. This can come from a user input or from syncing an OpenAPI definition.', + }, + type: { + type: 'string', + enum: ['http', 'apiKey', 'openIdConnect', 'oauth2', ''], + description: + 'If variable `source` is `security`, include the OpenAPI security auth type.', + }, + scheme: { + type: 'string', + description: + 'If variable `source` is `security`, include the OpenAPI security auth scheme.', + }, + }, + required: ['id', 'name'], + additionalProperties: false, }, - additionalProperties: false, + default: [], }, - project: { + webhooks: { + type: 'array', + items: { + type: 'object', + properties: { + action: { type: 'string', enum: ['login'], default: 'login' }, + timeout: { type: 'number', default: 5000 }, + url: { type: 'string', format: 'uri' }, + }, + required: ['url'], + additionalProperties: false, + }, + default: [], + }, + id: { + type: 'string', + pattern: '^[a-f\\d]{24}$', + description: 'The unique, immutable, identifier for the project.', + }, + features: { type: 'object', properties: { - name: { type: 'string', description: 'The name of the project.' }, - subdomain: { - type: 'string', - pattern: '[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*', - maxLength: 30, - description: 'The subdomain of the project.', + custom_components: { + type: 'string', + enum: ['enabled', 'disabled'], + default: 'disabled', + description: 'If this project supports creating custom components.', }, - uri: { + mdx: { type: 'string', - pattern: '\\/projects\\/(me|[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*)', - description: 'A URI to the project that this page belongs to.', + enum: ['enabled', 'disabled'], + default: 'disabled', + description: 'If this project supports MDX.', }, }, - required: ['name', 'subdomain', 'uri'], additionalProperties: false, }, - renderable: { + git: { type: 'object', properties: { - status: { - type: 'boolean', - default: true, - description: 'A flag for if the page is renderable or not.', - }, - error: { type: 'string', nullable: true, description: 'The rendering error.' }, - message: { - type: 'string', + connection: { + type: 'object', + properties: { + repository: { + type: 'object', + properties: { + id: { type: 'string', description: 'The stringified external repo id.' }, + name: { type: 'string', description: 'The name of the repo.' }, + url: { type: 'string', description: 'The url to the repo.' }, + organization: { + type: 'object', + properties: { + name: { + type: 'string', + description: 'The name of the organization this repo belongs to.', + }, + type: { + type: 'string', + enum: ['User', 'Organization'], + description: 'The type of the organization this repo belongs to.', + }, + active: { + type: 'boolean', + default: true, + description: 'Whether the organization is currently suspended or not.', + }, + }, + required: ['name', 'type'], + additionalProperties: false, + }, + connected_at: { + type: 'string', + format: 'date-time', + description: + 'An ISO 8601 formatted date for when the repository was last connected.', + }, + connected_by: { + type: 'string', + nullable: true, + description: 'The email of the User who last connected the external repository.', + }, + }, + required: ['id', 'name', 'url', 'organization', 'connected_at', 'connected_by'], + additionalProperties: false, + }, + }, + required: ['repository'], + additionalProperties: false, nullable: true, - description: 'Additional details about the rendering error.', }, }, + required: ['connection'], additionalProperties: false, }, - slug: { - allOf: [{ type: 'string' }, { type: 'string', minLength: 1 }], - description: 'The accessible URL slug for the page.', - }, - state: { type: 'string', enum: ['current', 'deprecated'], default: 'current' }, - title: { type: 'string' }, - type: { type: 'string', enum: ['api_config', 'basic', 'endpoint', 'link'], default: 'basic' }, - updated_at: { - type: 'string', - format: 'date-time', - description: 'An ISO 8601 formatted date for when the page was updated.', - }, - uri: { - type: 'string', - pattern: - '\\/versions\\/(stable|([0-9]+)(?:\\.([0-9]+))?(?:\\.([0-9]+))?(-.*)?)\\/(guides|reference)\\/(([a-z0-9-_ ]|[^\\\\x00-\\\\x7F])+)', - description: 'A URI to the page resource.', - }, - api_config: { - type: 'string', - enum: ['authentication', 'getting-started', 'my-requests'], - nullable: true, - }, - api: { + permissions: { type: 'object', properties: { - method: { - type: 'string', - enum: ['get', 'put', 'post', 'delete', 'options', 'head', 'patch', 'trace'], - description: 'The endpoint HTTP method.', - }, - path: { type: 'string', description: 'The endpoint path.' }, - schema: { - nullable: true, - description: - 'The API schema for this reference endpoint. This schema is a reduced version of the full API definition and only contains the necessary information for this endpoint.', - }, - stats: { + appearance: { type: 'object', properties: { - additional_properties: { - type: 'boolean', - default: false, - description: - 'This API operation uses `additionalProperties` for handling extra schema properties.', - }, - callbacks: { - type: 'boolean', - default: false, - description: 'This API operation has `callbacks` documented.', - }, - circular_references: { - type: 'boolean', - default: false, - description: - 'This API operation contains `$ref` schema pointers that resolve to itself.', - }, - common_parameters: { - type: 'boolean', - default: false, - description: 'This API operation utilizes common parameters set at the path level.', - }, - discriminators: { - type: 'boolean', - default: false, - description: - 'This API operation utilizes `discriminator` for discriminating between different parts in a polymorphic schema.', - }, - links: { - type: 'boolean', - default: false, - description: 'This API operation has `links` documented.', - }, - polymorphism: { - type: 'boolean', - default: false, - description: 'This API operation contains polymorphic schemas.', - }, - server_variables: { - type: 'boolean', - default: false, - description: - 'This API operation has composable variables configured for its server definition.', - }, - style: { - type: 'boolean', - default: false, + private_label: { + type: 'string', + enum: ['enabled', 'disabled'], + default: 'disabled', description: - 'This API operation has parameters that have specific `style` serializations.', - }, - webhooks: { - type: 'boolean', - default: false, - description: 'This API definition has `webhooks` documented.', - }, - xml: { - type: 'boolean', - default: false, - description: 'This API operation has parameters or schemas that serialize to XML.', + 'If this project is allowed to private label their Hub and remove all ReadMe branding.', }, - references: { - type: 'boolean', - description: - 'This API operation, after being dereferenced, has `x-readme-ref-name` entries defining what the original `$ref` schema pointers were named.', + custom_code: { + type: 'object', + properties: { + css: { + type: 'string', + enum: ['enabled', 'disabled'], + default: 'disabled', + description: 'If this project is allowed to utilize custom CSS.', + }, + html: { + type: 'string', + enum: ['enabled', 'disabled'], + default: 'disabled', + description: 'If this project is allowed to utilize custom HTML.', + }, + js: { + type: 'string', + enum: ['enabled', 'disabled'], + default: 'disabled', + description: 'If this project is allowed to utilize custom JS.', + }, + }, + additionalProperties: false, }, }, - required: ['references'], + required: ['custom_code'], additionalProperties: false, - description: 'OpenAPI features that are utilized within this API operation.', - }, - source: { - type: 'string', - enum: ['api', 'apidesigner', 'apieditor', 'bidi', 'form', 'rdme', 'rdme_github', 'url'], - nullable: true, - description: 'The source by which this API definition was ingested.', - }, - uri: { - type: 'string', - pattern: - '\\/versions\\/(stable|([0-9]+)(?:\\.([0-9]+))?(?:\\.([0-9]+))?(-.*)?)\\/apis\\/((([a-z0-9-_ ]|[^\\\\x00-\\\\x7F])+.(json|yaml|yml)))', - nullable: true, - description: 'A URI to the API resource.', }, }, - required: ['method', 'path', 'stats', 'source', 'uri'], + required: ['appearance'], additionalProperties: false, - description: 'Information about the API that this reference page is attached to.', }, - connections: { + refactored: { type: 'object', properties: { - recipes: { - type: 'array', - items: { - type: 'object', - properties: { - uri: { - type: 'string', - pattern: - '\\/versions\\/(stable|([0-9]+)(?:\\.([0-9]+))?(?:\\.([0-9]+))?(-.*)?)\\/recipes\\/(([a-z0-9-_ ]|[^\\\\x00-\\\\x7F])+)', - description: - 'URI of the recipe that this API reference is connected to. The recipe and API reference must exist within the same version.', - }, - }, - required: ['uri'], - additionalProperties: false, - }, - nullable: true, - description: 'A collection of recipes that are displayed on this API reference.', + status: { + type: 'string', + enum: ['enabled', 'disabled'], + default: 'disabled', + description: 'Indicates if the project has our new Unified UI experience.', + }, + migrated: { + type: 'string', + enum: ['failed', 'processing', 'successful', 'unknown'], + default: 'unknown', + description: 'Indicates if the project has been migrated from Dash to Superhub.', }, }, - required: ['recipes'], additionalProperties: false, }, + uri: { + type: 'string', + pattern: '\\/projects\\/(me|[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*)', + description: 'A URI to the project resource.', + }, }, required: [ - 'category', - 'content', - 'href', - 'metadata', + 'api_designer', + 'appearance', + 'canonical_url', + 'custom_login', + 'default_version', + 'description', + 'health_check', + 'homepage_url', + 'integrations', + 'name', + 'onboarding_completed', + 'pages', 'parent', + 'plan', 'privacy', - 'project', - 'renderable', - 'slug', - 'title', - 'updated_at', + 'redirects', + 'reference', + 'seo', + 'subdomain', + 'id', + 'features', + 'git', + 'permissions', + 'refactored', 'uri', - 'api_config', - 'api', - 'connections', ], additionalProperties: false, }, @@ -3588,36 +3895,14 @@ export const readmeAPIv2Oas = { }, }, }, - delete: { - operationId: 'deleteReference', - summary: 'Delete a reference', - tags: ['API Reference'], - description: - "Deletes a page from the API reference section of your developer hub. \n\n>πŸ“˜\n> This route is only available to projects that are using [ReadMe Refactored](https://docs.readme.com/main/docs/welcome-to-readme-refactored).\n\n>🚧 ReadMe's API v2 is currently in beta.\n> This API and its docs are a work in progress. While we don’t expect any major breaking changes, you may encounter occasional issues as we work toward a stable release. Make sure to [check out our API migration guide](https://docs.readme.com/main/reference/api-migration-guide), and [feel free to reach out](mailto:support@readme.io) if you have any questions or feedback!", - parameters: [ - { - schema: { type: 'string', pattern: 'stable|([0-9]+)(?:\\.([0-9]+))?(?:\\.([0-9]+))?(-.*)?' }, - in: 'path', - name: 'version', - required: true, - description: 'Project version number or stable.', - }, - { - schema: { type: 'string', pattern: '([a-z0-9-_ ]|[^\\\\x00-\\\\x7F])+' }, - in: 'path', - name: 'slug', - required: true, - description: 'A URL-safe representation of the resource.', - }, - ], - responses: { '204': { description: 'No Content' } }, - }, - patch: { - operationId: 'updateReference', - summary: 'Update a reference', + }, + '/versions/{version}/reference': { + post: { + operationId: 'createReference', + summary: 'Create a reference', tags: ['API Reference'], description: - "Updates a page in the API reference section of your developer hub. \n\n>πŸ“˜\n> This route is only available to projects that are using [ReadMe Refactored](https://docs.readme.com/main/docs/welcome-to-readme-refactored).\n\n>🚧 ReadMe's API v2 is currently in beta.\n> This API and its docs are a work in progress. While we don’t expect any major breaking changes, you may encounter occasional issues as we work toward a stable release. Make sure to [check out our API migration guide](https://docs.readme.com/main/reference/api-migration-guide), and [feel free to reach out](mailto:support@readme.io) if you have any questions or feedback!", + "Creates a reference page in the API Reference section of your developer hub. \n\n>πŸ“˜\n> This route is only available to projects that are using [ReadMe Refactored](https://docs.readme.com/main/docs/welcome-to-readme-refactored).\n\n>🚧 ReadMe's API v2 is currently in beta.\n> This API and its docs are a work in progress. While we don’t expect any major breaking changes, you may encounter occasional issues as we work toward a stable release. Make sure to [check out our API migration guide](https://docs.readme.com/main/reference/api-migration-guide), and [feel free to reach out](mailto:support@readme.io) if you have any questions or feedback!", requestBody: { content: { 'application/json': { @@ -3640,6 +3925,7 @@ export const readmeAPIv2Oas = { description: 'A URI to the category resource.', }, }, + required: ['uri'], additionalProperties: false, }, content: { @@ -3777,6 +4063,8 @@ export const readmeAPIv2Oas = { }, additionalProperties: false, }, + position: { type: 'number' }, + api_config: { type: 'string', enum: ['authentication', 'getting-started', 'my-requests'] }, api: { type: 'object', properties: { @@ -3870,15 +4158,14 @@ export const readmeAPIv2Oas = { }, }, additionalProperties: false, - description: - 'Information about the API that this reference page is attached to. If you wish to detach this page from an API definition, making it a stand page, set `api.uri` to `null`.', }, - position: { type: 'number' }, }, + required: ['category', 'title'], additionalProperties: false, }, }, }, + required: true, }, parameters: [ { @@ -3888,17 +4175,10 @@ export const readmeAPIv2Oas = { required: true, description: 'Project version number or stable.', }, - { - schema: { type: 'string', pattern: '([a-z0-9-_ ]|[^\\\\x00-\\\\x7F])+' }, - in: 'path', - name: 'slug', - required: true, - description: 'A URL-safe representation of the resource.', - }, ], responses: { - '200': { - description: 'OK', + '201': { + description: 'Created', content: { 'application/json': { schema: { @@ -3906,1635 +4186,1426 @@ export const readmeAPIv2Oas = { properties: { data: { type: 'object', - properties: { - allow_crawlers: { - type: 'string', - enum: ['enabled', 'disabled'], - default: 'enabled', - description: 'Allow indexing by robots.', - }, - category: { - type: 'object', - properties: { - uri: { - type: 'string', - pattern: - '\\/versions\\/(stable|([0-9]+)(?:\\.([0-9]+))?(?:\\.([0-9]+))?(-.*)?)\\/categories\\/(guides|reference)\\/((.*))', - description: 'A URI to the category resource.', - }, - }, - required: ['uri'], - additionalProperties: false, - }, - content: { - type: 'object', - properties: { - body: { type: 'string', nullable: true }, - excerpt: { type: 'string', nullable: true }, - link: { - type: 'object', - properties: { - url: { type: 'string', nullable: true }, - new_tab: { - type: 'boolean', - nullable: true, - description: 'Should this URL be opened up in a new tab?', - }, - }, - required: ['url', 'new_tab'], - additionalProperties: false, - description: - 'Information about where this page should redirect to; only available when `type` is `link`.', - }, - next: { - type: 'object', - properties: { - description: { type: 'string', nullable: true }, - pages: { - type: 'array', - items: { - anyOf: [ - { - type: 'object', - properties: { - slug: { type: 'string' }, - title: { type: 'string', nullable: true }, - type: { type: 'string', enum: ['basic', 'endpoint'] }, - }, - required: ['slug', 'title', 'type'], - additionalProperties: false, - }, - { - type: 'object', - properties: { - title: { type: 'string', nullable: true }, - type: { type: 'string', enum: ['link'] }, - url: { type: 'string' }, - }, - required: ['title', 'type', 'url'], - additionalProperties: false, - }, - ], - }, - }, - }, - required: ['description', 'pages'], - additionalProperties: false, - }, - }, - required: ['body', 'excerpt', 'link', 'next'], - additionalProperties: false, - }, - href: { - type: 'object', - properties: { - dash: { - type: 'string', - format: 'uri', - description: 'A URL to this page in your ReadMe Dash.', - }, - }, - required: ['dash'], - additionalProperties: false, - }, - metadata: { - type: 'object', - properties: { - description: { type: 'string', nullable: true }, - image: { - type: 'object', - properties: { - uri: { - type: 'string', - pattern: '\\/images\\/([a-f\\d]{24})', - nullable: true, - description: - 'A URI to the `getImages` endpoint for this image. If the is a legacy image then this `uri` will be `null`. And if you wish to delete this image then you should set this to `null`.', - }, - url: { type: 'string', format: 'uri', nullable: true }, - }, - required: ['uri', 'url'], - additionalProperties: false, - }, - keywords: { - type: 'string', - nullable: true, - description: 'A comma-separated list of keywords to place into your page metadata.', - }, - title: { type: 'string', nullable: true }, - }, - required: ['description', 'image', 'keywords', 'title'], - additionalProperties: false, - }, - parent: { - type: 'object', - properties: { - uri: { - type: 'string', - pattern: - '\\/versions\\/(stable|([0-9]+)(?:\\.([0-9]+))?(?:\\.([0-9]+))?(-.*)?)\\/(guides|reference)\\/(([a-z0-9-_ ]|[^\\\\x00-\\\\x7F])+)', - nullable: true, - description: 'A URI to the parent page resource including the page ID or slug.', - }, - }, - required: ['uri'], - additionalProperties: false, - }, - privacy: { - type: 'object', - properties: { - view: { type: 'string', enum: ['public', 'anyone_with_link'], default: 'anyone_with_link' }, - }, - additionalProperties: false, - }, - project: { - type: 'object', - properties: { - name: { type: 'string', description: 'The name of the project.' }, - subdomain: { - type: 'string', - pattern: '[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*', - maxLength: 30, - description: 'The subdomain of the project.', - }, - uri: { - type: 'string', - pattern: '\\/projects\\/(me|[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*)', - description: 'A URI to the project that this page belongs to.', - }, - }, - required: ['name', 'subdomain', 'uri'], - additionalProperties: false, - }, - renderable: { - type: 'object', - properties: { - status: { - type: 'boolean', - default: true, - description: 'A flag for if the page is renderable or not.', - }, - error: { type: 'string', nullable: true, description: 'The rendering error.' }, - message: { - type: 'string', - nullable: true, - description: 'Additional details about the rendering error.', - }, - }, - additionalProperties: false, - }, - slug: { - allOf: [{ type: 'string' }, { type: 'string', minLength: 1 }], - description: 'The accessible URL slug for the page.', - }, - state: { type: 'string', enum: ['current', 'deprecated'], default: 'current' }, - title: { type: 'string' }, - type: { type: 'string', enum: ['api_config', 'basic', 'endpoint', 'link'], default: 'basic' }, - updated_at: { - type: 'string', - format: 'date-time', - description: 'An ISO 8601 formatted date for when the page was updated.', - }, - uri: { - type: 'string', - pattern: - '\\/versions\\/(stable|([0-9]+)(?:\\.([0-9]+))?(?:\\.([0-9]+))?(-.*)?)\\/(guides|reference)\\/(([a-z0-9-_ ]|[^\\\\x00-\\\\x7F])+)', - description: 'A URI to the page resource.', - }, - api_config: { + properties: { + allow_crawlers: { type: 'string', - enum: ['authentication', 'getting-started', 'my-requests'], - nullable: true, + enum: ['enabled', 'disabled'], + default: 'enabled', + description: 'Allow indexing by robots.', }, - api: { + category: { type: 'object', properties: { - method: { + uri: { type: 'string', - enum: ['get', 'put', 'post', 'delete', 'options', 'head', 'patch', 'trace'], - description: 'The endpoint HTTP method.', - }, - path: { type: 'string', description: 'The endpoint path.' }, - schema: { - nullable: true, - description: - 'The API schema for this reference endpoint. This schema is a reduced version of the full API definition and only contains the necessary information for this endpoint.', + pattern: + '\\/versions\\/(stable|([0-9]+)(?:\\.([0-9]+))?(?:\\.([0-9]+))?(-.*)?)\\/categories\\/(guides|reference)\\/((.*))', + description: 'A URI to the category resource.', }, - stats: { + }, + required: ['uri'], + additionalProperties: false, + }, + content: { + type: 'object', + properties: { + body: { type: 'string', nullable: true }, + excerpt: { type: 'string', nullable: true }, + link: { type: 'object', properties: { - additional_properties: { - type: 'boolean', - default: false, - description: - 'This API operation uses `additionalProperties` for handling extra schema properties.', - }, - callbacks: { - type: 'boolean', - default: false, - description: 'This API operation has `callbacks` documented.', - }, - circular_references: { - type: 'boolean', - default: false, - description: - 'This API operation contains `$ref` schema pointers that resolve to itself.', - }, - common_parameters: { - type: 'boolean', - default: false, - description: 'This API operation utilizes common parameters set at the path level.', - }, - discriminators: { - type: 'boolean', - default: false, - description: - 'This API operation utilizes `discriminator` for discriminating between different parts in a polymorphic schema.', - }, - links: { - type: 'boolean', - default: false, - description: 'This API operation has `links` documented.', - }, - polymorphism: { - type: 'boolean', - default: false, - description: 'This API operation contains polymorphic schemas.', - }, - server_variables: { - type: 'boolean', - default: false, - description: - 'This API operation has composable variables configured for its server definition.', - }, - style: { - type: 'boolean', - default: false, - description: - 'This API operation has parameters that have specific `style` serializations.', - }, - webhooks: { - type: 'boolean', - default: false, - description: 'This API definition has `webhooks` documented.', - }, - xml: { - type: 'boolean', - default: false, - description: 'This API operation has parameters or schemas that serialize to XML.', - }, - references: { + url: { type: 'string', nullable: true }, + new_tab: { type: 'boolean', - description: - 'This API operation, after being dereferenced, has `x-readme-ref-name` entries defining what the original `$ref` schema pointers were named.', + nullable: true, + description: 'Should this URL be opened up in a new tab?', }, }, - required: ['references'], + required: ['url', 'new_tab'], additionalProperties: false, - description: 'OpenAPI features that are utilized within this API operation.', + description: + 'Information about where this page should redirect to; only available when `type` is `link`.', }, - source: { - type: 'string', - enum: ['api', 'apidesigner', 'apieditor', 'bidi', 'form', 'rdme', 'rdme_github', 'url'], - nullable: true, - description: 'The source by which this API definition was ingested.', + next: { + type: 'object', + properties: { + description: { type: 'string', nullable: true }, + pages: { + type: 'array', + items: { + anyOf: [ + { + type: 'object', + properties: { + slug: { type: 'string' }, + title: { type: 'string', nullable: true }, + type: { type: 'string', enum: ['basic', 'endpoint'] }, + }, + required: ['slug', 'title', 'type'], + additionalProperties: false, + }, + { + type: 'object', + properties: { + title: { type: 'string', nullable: true }, + type: { type: 'string', enum: ['link'] }, + url: { type: 'string' }, + }, + required: ['title', 'type', 'url'], + additionalProperties: false, + }, + ], + }, + }, + }, + required: ['description', 'pages'], + additionalProperties: false, }, - uri: { + }, + required: ['body', 'excerpt', 'link', 'next'], + additionalProperties: false, + }, + href: { + type: 'object', + properties: { + dash: { type: 'string', - pattern: - '\\/versions\\/(stable|([0-9]+)(?:\\.([0-9]+))?(?:\\.([0-9]+))?(-.*)?)\\/apis\\/((([a-z0-9-_ ]|[^\\\\x00-\\\\x7F])+.(json|yaml|yml)))', - nullable: true, - description: 'A URI to the API resource.', + format: 'uri', + description: 'A URL to this page in your ReadMe Dash.', }, }, - required: ['method', 'path', 'stats', 'source', 'uri'], + required: ['dash'], additionalProperties: false, - description: 'Information about the API that this reference page is attached to.', }, - connections: { + metadata: { type: 'object', properties: { - recipes: { - type: 'array', - items: { - type: 'object', - properties: { - uri: { - type: 'string', - pattern: - '\\/versions\\/(stable|([0-9]+)(?:\\.([0-9]+))?(?:\\.([0-9]+))?(-.*)?)\\/recipes\\/(([a-z0-9-_ ]|[^\\\\x00-\\\\x7F])+)', - description: - 'URI of the recipe that this API reference is connected to. The recipe and API reference must exist within the same version.', - }, + description: { type: 'string', nullable: true }, + image: { + type: 'object', + properties: { + uri: { + type: 'string', + pattern: '\\/images\\/([a-f\\d]{24})', + nullable: true, + description: + 'A URI to the `getImages` endpoint for this image. If the is a legacy image then this `uri` will be `null`. And if you wish to delete this image then you should set this to `null`.', }, - required: ['uri'], - additionalProperties: false, + url: { type: 'string', format: 'uri', nullable: true }, }, + required: ['uri', 'url'], + additionalProperties: false, + }, + keywords: { + type: 'string', nullable: true, - description: 'A collection of recipes that are displayed on this API reference.', + description: 'A comma-separated list of keywords to place into your page metadata.', }, + title: { type: 'string', nullable: true }, }, - required: ['recipes'], + required: ['description', 'image', 'keywords', 'title'], additionalProperties: false, }, - }, - required: [ - 'category', - 'content', - 'href', - 'metadata', - 'parent', - 'privacy', - 'project', - 'renderable', - 'slug', - 'title', - 'updated_at', - 'uri', - 'api_config', - 'api', - 'connections', - ], - additionalProperties: false, - }, - }, - required: ['data'], - additionalProperties: false, - }, - }, - }, - }, - }, - }, - }, - '/projects/me': { - get: { - operationId: 'getProject', - summary: 'Get project metadata', - tags: ['Projects'], - description: - "Returns data about your project.\n\n>🚧 ReadMe's API v2 is currently in beta.\n> This API and its docs are a work in progress. While we don’t expect any major breaking changes, you may encounter occasional issues as we work toward a stable release. Make sure to [check out our API migration guide](https://docs.readme.com/main/reference/api-migration-guide), and [feel free to reach out](mailto:support@readme.io) if you have any questions or feedback!", - responses: { - '200': { - description: 'OK', - content: { - 'application/json': { - schema: { - type: 'object', - properties: { - data: { - type: 'object', - properties: { - allow_crawlers: { - type: 'string', - enum: ['enabled', 'disabled'], - default: 'enabled', - description: 'Allow indexing by robots.', + parent: { + type: 'object', + properties: { + uri: { + type: 'string', + pattern: + '\\/versions\\/(stable|([0-9]+)(?:\\.([0-9]+))?(?:\\.([0-9]+))?(-.*)?)\\/(guides|reference)\\/(([a-z0-9-_ ]|[^\\\\x00-\\\\x7F])+)', + nullable: true, + description: 'A URI to the parent page resource including the page ID or slug.', + }, + }, + required: ['uri'], + additionalProperties: false, }, - api_designer: { + privacy: { type: 'object', properties: { - allow_editing: { + view: { type: 'string', enum: ['public', 'anyone_with_link'], default: 'anyone_with_link' }, + }, + additionalProperties: false, + }, + project: { + type: 'object', + properties: { + name: { type: 'string', description: 'The name of the project.' }, + subdomain: { type: 'string', - enum: ['enabled', 'disabled'], - default: 'enabled', - description: 'API Designer is enabled for this project.', + pattern: '[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*', + maxLength: 30, + description: 'The subdomain of the project.', + }, + uri: { + type: 'string', + pattern: '\\/projects\\/(me|[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*)', + description: 'A URI to the project that this page belongs to.', }, }, + required: ['name', 'subdomain', 'uri'], additionalProperties: false, }, - appearance: { + renderable: { type: 'object', properties: { - brand: { - type: 'object', - properties: { - primary_color: { type: 'string', nullable: true }, - link_color: { type: 'string', nullable: true }, - theme: { type: 'string', enum: ['system', 'light', 'dark'], default: 'light' }, - }, - required: ['primary_color', 'link_color'], - additionalProperties: false, + status: { + type: 'boolean', + default: true, + description: 'A flag for if the page is renderable or not.', }, - changelog: { + error: { type: 'string', nullable: true, description: 'The rendering error.' }, + message: { + type: 'string', + nullable: true, + description: 'Additional details about the rendering error.', + }, + }, + additionalProperties: false, + }, + slug: { + allOf: [{ type: 'string' }, { type: 'string', minLength: 1 }], + description: 'The accessible URL slug for the page.', + }, + state: { type: 'string', enum: ['current', 'deprecated'], default: 'current' }, + title: { type: 'string' }, + type: { type: 'string', enum: ['api_config', 'basic', 'endpoint', 'link'], default: 'basic' }, + updated_at: { + type: 'string', + format: 'date-time', + description: 'An ISO 8601 formatted date for when the page was updated.', + }, + uri: { + type: 'string', + pattern: + '\\/versions\\/(stable|([0-9]+)(?:\\.([0-9]+))?(?:\\.([0-9]+))?(-.*)?)\\/(guides|reference)\\/(([a-z0-9-_ ]|[^\\\\x00-\\\\x7F])+)', + description: 'A URI to the page resource.', + }, + api_config: { + type: 'string', + enum: ['authentication', 'getting-started', 'my-requests'], + nullable: true, + }, + api: { + type: 'object', + properties: { + method: { + type: 'string', + enum: ['get', 'put', 'post', 'delete', 'options', 'head', 'patch', 'trace'], + description: 'The endpoint HTTP method.', + }, + path: { type: 'string', description: 'The endpoint path.' }, + schema: { + nullable: true, + description: + 'The API schema for this reference endpoint. This schema is a reduced version of the full API definition and only contains the necessary information for this endpoint.', + }, + stats: { type: 'object', properties: { - layout: { type: 'string', enum: ['collapsed', 'continuous'], default: 'collapsed' }, - show_author: { - type: 'boolean', - default: true, - description: 'Should the changelog author be shown?', - }, - show_exact_date: { + additional_properties: { type: 'boolean', default: false, description: - 'Should the exact date of the changelog entry be shown, or should it be relative?', + 'This API operation uses `additionalProperties` for handling extra schema properties.', }, - }, - additionalProperties: false, - }, - custom_code: { - type: 'object', - properties: { - css: { - type: 'string', - nullable: true, - description: - 'A chunk of custom CSS that you can use to override default CSS that we provide.', + callbacks: { + type: 'boolean', + default: false, + description: 'This API operation has `callbacks` documented.', }, - js: { - type: 'string', - nullable: true, + circular_references: { + type: 'boolean', + default: false, description: - 'A chunk of custom JS that you can use to override or add new behaviors to your documentation. Please note that we do not do any validation on the code that goes in here so you have the potential to negatively impact your users with broken code.', - }, - html: { - type: 'object', - properties: { - header: { - type: 'string', - nullable: true, - description: - 'A block of custom HTML that will be added to your `<head>` tag. Good for things like `<meta>` tags or loading external CSS.', - }, - home_footer: { - type: 'string', - nullable: true, - description: - 'A block of custom HTML that will be added before the closing `</body>` tag of your **home page**.', - }, - page_footer: { - type: 'string', - nullable: true, - description: - 'A block of custom HTML that will be added before the closing `</body>` tag of your pages.', - }, - }, - required: ['header', 'home_footer', 'page_footer'], - additionalProperties: false, - }, - }, - required: ['css', 'js', 'html'], - additionalProperties: false, - }, - footer: { - type: 'object', - properties: { readme_logo: { type: 'string', enum: ['hide', 'show'], default: 'show' } }, - additionalProperties: false, - }, - header: { - type: 'object', - properties: { - type: { - type: 'string', - enum: ['solid', 'gradient', 'line', 'overlay'], - default: 'solid', + 'This API operation contains `$ref` schema pointers that resolve to itself.', }, - gradient_color: { type: 'string', nullable: true }, - overlay: { - type: 'object', - properties: { - image: { - type: 'object', - properties: { - name: { type: 'string', nullable: true }, - width: { - type: 'number', - nullable: true, - description: 'The pixel width of the image. This is not present for SVGs.', - }, - height: { - type: 'number', - nullable: true, - description: 'The pixel height of the image. This is not present for SVGs.', - }, - color: { - type: 'string', - pattern: - '^(?:#[0-9a-fA-F]{3}|#[0-9a-fA-F]{4}|#[0-9a-fA-F]{6}|#[0-9a-fA-F]{8})$', - nullable: true, - description: 'The primary color contained within your image.', - }, - links: { - type: 'object', - properties: { - original_url: { - type: 'string', - format: 'uri', - nullable: true, - description: - 'If your image was resized upon upload this will be a URL to the original file.', - }, - }, - required: ['original_url'], - additionalProperties: false, - }, - uri: { - type: 'string', - pattern: '\\/images\\/([a-f\\d]{24})', - nullable: true, - description: - 'A URI to the `getImages` endpoint for this image. If the is a legacy image then this `uri` will be `null`. And if you wish to delete this image then you should set this to `null`.', - }, - url: { type: 'string', format: 'uri', nullable: true }, - }, - required: ['name', 'width', 'height', 'color', 'links', 'uri', 'url'], - additionalProperties: false, - }, - type: { - type: 'string', - enum: ['triangles', 'blueprint', 'grain', 'map', 'circuits', 'custom'], - default: 'triangles', - description: - 'The header overlay type. This value is only used if `appearance.header.type` is `overlay`.', - }, - fill: { - type: 'string', - enum: ['auto', 'tile', 'tile-x', 'tile-y', 'cover', 'contain'], - default: 'auto', - description: - 'The header fill type. This is only used if `appearance.header.overlay.type` is `custom`.', - }, - position: { - type: 'string', - enum: [ - 'top-left', - 'top-center', - 'top-right', - 'center-left', - 'center-center', - 'center-right', - 'bottom-left', - 'bottom-center', - 'bottom-right', - ], - default: 'top-left', - description: - 'The positioning of the header. This is only used if `appearance.header.overlay.type` is `custom`.', - }, - }, - required: ['image'], - additionalProperties: false, + common_parameters: { + type: 'boolean', + default: false, + description: 'This API operation utilizes common parameters set at the path level.', + }, + discriminators: { + type: 'boolean', + default: false, + description: + 'This API operation utilizes `discriminator` for discriminating between different parts in a polymorphic schema.', + }, + links: { + type: 'boolean', + default: false, + description: 'This API operation has `links` documented.', + }, + polymorphism: { + type: 'boolean', + default: false, + description: 'This API operation contains polymorphic schemas.', + }, + server_variables: { + type: 'boolean', + default: false, + description: + 'This API operation has composable variables configured for its server definition.', + }, + style: { + type: 'boolean', + default: false, + description: + 'This API operation has parameters that have specific `style` serializations.', + }, + webhooks: { + type: 'boolean', + default: false, + description: 'This API definition has `webhooks` documented.', + }, + xml: { + type: 'boolean', + default: false, + description: 'This API operation has parameters or schemas that serialize to XML.', + }, + references: { + type: 'boolean', + description: + 'This API operation, after being dereferenced, has `x-readme-ref-name` entries defining what the original `$ref` schema pointers were named.', }, }, - required: ['gradient_color', 'overlay'], + required: ['references'], additionalProperties: false, + description: 'OpenAPI features that are utilized within this API operation.', }, - logo: { - type: 'object', - properties: { - dark_mode: { - type: 'object', - properties: { - name: { type: 'string', nullable: true }, - width: { - type: 'number', - nullable: true, - description: 'The pixel width of the image. This is not present for SVGs.', - }, - height: { - type: 'number', - nullable: true, - description: 'The pixel height of the image. This is not present for SVGs.', - }, - color: { - type: 'string', - pattern: '^(?:#[0-9a-fA-F]{3}|#[0-9a-fA-F]{4}|#[0-9a-fA-F]{6}|#[0-9a-fA-F]{8})$', - nullable: true, - description: 'The primary color contained within your image.', - }, - links: { - type: 'object', - properties: { - original_url: { - type: 'string', - format: 'uri', - nullable: true, - description: - 'If your image was resized upon upload this will be a URL to the original file.', - }, - }, - required: ['original_url'], - additionalProperties: false, - }, - uri: { - type: 'string', - pattern: '\\/images\\/([a-f\\d]{24})', - nullable: true, - description: - 'A URI to the `getImages` endpoint for this image. If the is a legacy image then this `uri` will be `null`. And if you wish to delete this image then you should set this to `null`.', - }, - url: { type: 'string', format: 'uri', nullable: true }, + source: { + type: 'string', + enum: ['api', 'apidesigner', 'apieditor', 'bidi', 'form', 'rdme', 'rdme_github', 'url'], + nullable: true, + description: 'The source by which this API definition was ingested.', + }, + uri: { + type: 'string', + pattern: + '\\/versions\\/(stable|([0-9]+)(?:\\.([0-9]+))?(?:\\.([0-9]+))?(-.*)?)\\/apis\\/((([a-z0-9-_ ]|[^\\\\x00-\\\\x7F])+.(json|yaml|yml)))', + nullable: true, + description: 'A URI to the API resource.', + }, + }, + required: ['method', 'path', 'stats', 'source', 'uri'], + additionalProperties: false, + description: 'Information about the API that this reference page is attached to.', + }, + connections: { + type: 'object', + properties: { + recipes: { + type: 'array', + items: { + type: 'object', + properties: { + uri: { + type: 'string', + pattern: + '\\/versions\\/(stable|([0-9]+)(?:\\.([0-9]+))?(?:\\.([0-9]+))?(-.*)?)\\/recipes\\/(([a-z0-9-_ ]|[^\\\\x00-\\\\x7F])+)', + description: + 'URI of the recipe that this API reference is connected to. The recipe and API reference must exist within the same version.', }, - required: ['name', 'width', 'height', 'color', 'links', 'uri', 'url'], - additionalProperties: false, }, - main: { - type: 'object', - properties: { - name: { type: 'string', nullable: true }, - width: { - type: 'number', - nullable: true, - description: 'The pixel width of the image. This is not present for SVGs.', - }, - height: { - type: 'number', - nullable: true, - description: 'The pixel height of the image. This is not present for SVGs.', - }, - color: { - type: 'string', - pattern: '^(?:#[0-9a-fA-F]{3}|#[0-9a-fA-F]{4}|#[0-9a-fA-F]{6}|#[0-9a-fA-F]{8})$', - nullable: true, - description: 'The primary color contained within your image.', - }, - links: { - type: 'object', - properties: { - original_url: { - type: 'string', - format: 'uri', - nullable: true, - description: - 'If your image was resized upon upload this will be a URL to the original file.', + required: ['uri'], + additionalProperties: false, + }, + nullable: true, + description: 'A collection of recipes that are displayed on this API reference.', + }, + }, + required: ['recipes'], + additionalProperties: false, + }, + }, + required: [ + 'category', + 'content', + 'href', + 'metadata', + 'parent', + 'privacy', + 'project', + 'renderable', + 'slug', + 'title', + 'updated_at', + 'uri', + 'api_config', + 'api', + 'connections', + ], + additionalProperties: false, + }, + }, + required: ['data'], + additionalProperties: false, + }, + }, + }, + }, + }, + }, + }, + '/versions/{version}/reference/{slug}': { + get: { + operationId: 'getReference', + summary: 'Retrieve a reference', + tags: ['API Reference'], + description: + "Retrieves a page from the API reference section of your developer hub. \n\n>πŸ“˜\n> This route is only available to projects that are using [ReadMe Refactored](https://docs.readme.com/main/docs/welcome-to-readme-refactored).\n\n>🚧 ReadMe's API v2 is currently in beta.\n> This API and its docs are a work in progress. While we don’t expect any major breaking changes, you may encounter occasional issues as we work toward a stable release. Make sure to [check out our API migration guide](https://docs.readme.com/main/reference/api-migration-guide), and [feel free to reach out](mailto:support@readme.io) if you have any questions or feedback!", + parameters: [ + { + schema: { type: 'string', pattern: 'stable|([0-9]+)(?:\\.([0-9]+))?(?:\\.([0-9]+))?(-.*)?' }, + in: 'path', + name: 'version', + required: true, + description: 'Project version number or stable.', + }, + { + schema: { type: 'string', pattern: '([a-z0-9-_ ]|[^\\\\x00-\\\\x7F])+' }, + in: 'path', + name: 'slug', + required: true, + description: 'A URL-safe representation of the resource.', + }, + ], + responses: { + '200': { + description: 'OK', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + data: { + type: 'object', + properties: { + allow_crawlers: { + type: 'string', + enum: ['enabled', 'disabled'], + default: 'enabled', + description: 'Allow indexing by robots.', + }, + category: { + type: 'object', + properties: { + uri: { + type: 'string', + pattern: + '\\/versions\\/(stable|([0-9]+)(?:\\.([0-9]+))?(?:\\.([0-9]+))?(-.*)?)\\/categories\\/(guides|reference)\\/((.*))', + description: 'A URI to the category resource.', + }, + }, + required: ['uri'], + additionalProperties: false, + }, + content: { + type: 'object', + properties: { + body: { type: 'string', nullable: true }, + excerpt: { type: 'string', nullable: true }, + link: { + type: 'object', + properties: { + url: { type: 'string', nullable: true }, + new_tab: { + type: 'boolean', + nullable: true, + description: 'Should this URL be opened up in a new tab?', + }, + }, + required: ['url', 'new_tab'], + additionalProperties: false, + description: + 'Information about where this page should redirect to; only available when `type` is `link`.', + }, + next: { + type: 'object', + properties: { + description: { type: 'string', nullable: true }, + pages: { + type: 'array', + items: { + anyOf: [ + { + type: 'object', + properties: { + slug: { type: 'string' }, + title: { type: 'string', nullable: true }, + type: { type: 'string', enum: ['basic', 'endpoint'] }, }, + required: ['slug', 'title', 'type'], + additionalProperties: false, }, - required: ['original_url'], - additionalProperties: false, - }, - uri: { - type: 'string', - pattern: '\\/images\\/([a-f\\d]{24})', - nullable: true, - description: - 'A URI to the `getImages` endpoint for this image. If the is a legacy image then this `uri` will be `null`. And if you wish to delete this image then you should set this to `null`.', - }, - url: { type: 'string', format: 'uri', nullable: true }, - }, - required: ['name', 'width', 'height', 'color', 'links', 'uri', 'url'], - additionalProperties: false, - }, - favicon: { - type: 'object', - properties: { - name: { type: 'string', nullable: true }, - width: { - type: 'number', - nullable: true, - description: 'The pixel width of the image. This is not present for SVGs.', - }, - height: { - type: 'number', - nullable: true, - description: 'The pixel height of the image. This is not present for SVGs.', - }, - color: { - type: 'string', - pattern: '^(?:#[0-9a-fA-F]{3}|#[0-9a-fA-F]{4}|#[0-9a-fA-F]{6}|#[0-9a-fA-F]{8})$', - nullable: true, - description: 'The primary color contained within your image.', - }, - links: { - type: 'object', - properties: { - original_url: { - type: 'string', - format: 'uri', - nullable: true, - description: - 'If your image was resized upon upload this will be a URL to the original file.', + { + type: 'object', + properties: { + title: { type: 'string', nullable: true }, + type: { type: 'string', enum: ['link'] }, + url: { type: 'string' }, }, + required: ['title', 'type', 'url'], + additionalProperties: false, }, - required: ['original_url'], - additionalProperties: false, - }, - uri: { - type: 'string', - pattern: '\\/images\\/([a-f\\d]{24})', - nullable: true, - description: - 'A URI to the `getImages` endpoint for this image. If the is a legacy image then this `uri` will be `null`. And if you wish to delete this image then you should set this to `null`.', - }, - url: { type: 'string', format: 'uri', nullable: true }, + ], }, - required: ['name', 'width', 'height', 'color', 'links', 'uri', 'url'], - additionalProperties: false, }, - size: { type: 'string', enum: ['default', 'large'], default: 'default' }, }, - required: ['dark_mode', 'main', 'favicon'], + required: ['description', 'pages'], additionalProperties: false, }, - markdown: { + }, + required: ['body', 'excerpt', 'link', 'next'], + additionalProperties: false, + }, + href: { + type: 'object', + properties: { + dash: { + type: 'string', + format: 'uri', + description: 'A URL to this page in your ReadMe Dash.', + }, + }, + required: ['dash'], + additionalProperties: false, + }, + metadata: { + type: 'object', + properties: { + description: { type: 'string', nullable: true }, + image: { type: 'object', properties: { - callouts: { - type: 'object', - properties: { - icon_font: { - type: 'string', - enum: ['emojis', 'fontawesome'], - default: 'emojis', - description: 'Handles the types of icons that are shown in Markdown callouts.', - }, - }, - additionalProperties: false, + uri: { + type: 'string', + pattern: '\\/images\\/([a-f\\d]{24})', + nullable: true, + description: + 'A URI to the `getImages` endpoint for this image. If the is a legacy image then this `uri` will be `null`. And if you wish to delete this image then you should set this to `null`.', }, + url: { type: 'string', format: 'uri', nullable: true }, }, - required: ['callouts'], + required: ['uri', 'url'], additionalProperties: false, }, - navigation: { + keywords: { + type: 'string', + nullable: true, + description: 'A comma-separated list of keywords to place into your page metadata.', + }, + title: { type: 'string', nullable: true }, + }, + required: ['description', 'image', 'keywords', 'title'], + additionalProperties: false, + }, + parent: { + type: 'object', + properties: { + uri: { + type: 'string', + pattern: + '\\/versions\\/(stable|([0-9]+)(?:\\.([0-9]+))?(?:\\.([0-9]+))?(-.*)?)\\/(guides|reference)\\/(([a-z0-9-_ ]|[^\\\\x00-\\\\x7F])+)', + nullable: true, + description: 'A URI to the parent page resource including the page ID or slug.', + }, + }, + required: ['uri'], + additionalProperties: false, + }, + privacy: { + type: 'object', + properties: { + view: { type: 'string', enum: ['public', 'anyone_with_link'], default: 'anyone_with_link' }, + }, + additionalProperties: false, + }, + project: { + type: 'object', + properties: { + name: { type: 'string', description: 'The name of the project.' }, + subdomain: { + type: 'string', + pattern: '[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*', + maxLength: 30, + description: 'The subdomain of the project.', + }, + uri: { + type: 'string', + pattern: '\\/projects\\/(me|[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*)', + description: 'A URI to the project that this page belongs to.', + }, + }, + required: ['name', 'subdomain', 'uri'], + additionalProperties: false, + }, + renderable: { + type: 'object', + properties: { + status: { + type: 'boolean', + default: true, + description: 'A flag for if the page is renderable or not.', + }, + error: { type: 'string', nullable: true, description: 'The rendering error.' }, + message: { + type: 'string', + nullable: true, + description: 'Additional details about the rendering error.', + }, + }, + additionalProperties: false, + }, + slug: { + allOf: [{ type: 'string' }, { type: 'string', minLength: 1 }], + description: 'The accessible URL slug for the page.', + }, + state: { type: 'string', enum: ['current', 'deprecated'], default: 'current' }, + title: { type: 'string' }, + type: { type: 'string', enum: ['api_config', 'basic', 'endpoint', 'link'], default: 'basic' }, + updated_at: { + type: 'string', + format: 'date-time', + description: 'An ISO 8601 formatted date for when the page was updated.', + }, + uri: { + type: 'string', + pattern: + '\\/versions\\/(stable|([0-9]+)(?:\\.([0-9]+))?(?:\\.([0-9]+))?(-.*)?)\\/(guides|reference)\\/(([a-z0-9-_ ]|[^\\\\x00-\\\\x7F])+)', + description: 'A URI to the page resource.', + }, + api_config: { + type: 'string', + enum: ['authentication', 'getting-started', 'my-requests'], + nullable: true, + }, + api: { + type: 'object', + properties: { + method: { + type: 'string', + enum: ['get', 'put', 'post', 'delete', 'options', 'head', 'patch', 'trace'], + description: 'The endpoint HTTP method.', + }, + path: { type: 'string', description: 'The endpoint path.' }, + schema: { + nullable: true, + description: + 'The API schema for this reference endpoint. This schema is a reduced version of the full API definition and only contains the necessary information for this endpoint.', + }, + stats: { type: 'object', properties: { - first_page: { - type: 'string', - enum: ['documentation', 'reference', 'landing_page'], - default: 'landing_page', + additional_properties: { + type: 'boolean', + default: false, + description: + 'This API operation uses `additionalProperties` for handling extra schema properties.', + }, + callbacks: { + type: 'boolean', + default: false, + description: 'This API operation has `callbacks` documented.', + }, + circular_references: { + type: 'boolean', + default: false, description: - 'The page that users will first see when they access your documentation hub.', + 'This API operation contains `$ref` schema pointers that resolve to itself.', }, - left: { - type: 'array', - items: { - type: 'object', - properties: { - type: { - type: 'string', - enum: [ - 'home', - 'guides', - 'discussions', - 'changelog', - 'search_box', - 'link_url', - 'custom_page', - 'user_controls', - 'reference', - 'recipes', - ], - }, - title: { type: 'string', nullable: true }, - url: { type: 'string', nullable: true }, - custom_page: { type: 'string', nullable: true }, - }, - required: ['type', 'title', 'url', 'custom_page'], - additionalProperties: false, - }, + common_parameters: { + type: 'boolean', + default: false, + description: 'This API operation utilizes common parameters set at the path level.', + }, + discriminators: { + type: 'boolean', + default: false, description: - 'The navigation settings for the left side of your projects navigation bar.', + 'This API operation utilizes `discriminator` for discriminating between different parts in a polymorphic schema.', }, links: { - type: 'object', - properties: { - changelog: { - type: 'object', - properties: { - label: { type: 'string', enum: ['Changelog'] }, - alias: { type: 'string', nullable: true }, - visibility: { - type: 'string', - enum: ['enabled', 'disabled'], - default: 'enabled', - }, - }, - required: ['label', 'alias'], - additionalProperties: false, - }, - discussions: { - type: 'object', - properties: { - label: { type: 'string', enum: ['Discussions'] }, - alias: { type: 'string', nullable: true }, - visibility: { - type: 'string', - enum: ['enabled', 'disabled'], - default: 'enabled', - }, - }, - required: ['label', 'alias'], - additionalProperties: false, - }, - home: { - type: 'object', - properties: { - label: { type: 'string', enum: ['Home'] }, - visibility: { - type: 'string', - enum: ['enabled', 'disabled'], - default: 'enabled', - }, - }, - required: ['label'], - additionalProperties: false, - }, - graphql: { - type: 'object', - properties: { - label: { type: 'string', enum: ['GraphQL'] }, - visibility: { - type: 'string', - enum: ['enabled', 'disabled'], - default: 'disabled', - nullable: true, - }, - }, - required: ['label'], - additionalProperties: false, - }, - guides: { - type: 'object', - properties: { - label: { type: 'string', enum: ['Guides'] }, - alias: { type: 'string', nullable: true }, - visibility: { - type: 'string', - enum: ['enabled', 'disabled'], - default: 'enabled', - }, - }, - required: ['label', 'alias'], - additionalProperties: false, - }, - recipes: { - type: 'object', - properties: { - label: { type: 'string', enum: ['Recipes'] }, - alias: { type: 'string', nullable: true }, - visibility: { - type: 'string', - enum: ['enabled', 'disabled'], - default: 'disabled', - }, - }, - required: ['label', 'alias'], - additionalProperties: false, - }, - reference: { - type: 'object', - properties: { - label: { type: 'string', enum: ['API Reference'] }, - alias: { type: 'string', nullable: true }, - visibility: { - type: 'string', - enum: ['enabled', 'disabled'], - default: 'enabled', - }, - }, - required: ['label', 'alias'], - additionalProperties: false, - }, - }, - required: [ - 'changelog', - 'discussions', - 'home', - 'graphql', - 'guides', - 'recipes', - 'reference', - ], - additionalProperties: false, + type: 'boolean', + default: false, + description: 'This API operation has `links` documented.', }, - logo_link: { - type: 'string', - enum: ['landing_page', 'homepage'], - default: 'homepage', + polymorphism: { + type: 'boolean', + default: false, + description: 'This API operation contains polymorphic schemas.', + }, + server_variables: { + type: 'boolean', + default: false, description: - 'Where users will be directed to when they click on your logo in the navigation bar.', + 'This API operation has composable variables configured for its server definition.', }, - right: { - type: 'array', - items: { - type: 'object', - properties: { - type: { - type: 'string', - enum: [ - 'home', - 'guides', - 'discussions', - 'changelog', - 'search_box', - 'link_url', - 'custom_page', - 'user_controls', - 'reference', - 'recipes', - ], - }, - title: { type: 'string', nullable: true }, - url: { type: 'string', nullable: true }, - custom_page: { type: 'string', nullable: true }, - }, - required: ['type', 'title', 'url', 'custom_page'], - additionalProperties: false, - }, + style: { + type: 'boolean', + default: false, description: - 'The navigation settings for the right side of your projects navigation bar.', + 'This API operation has parameters that have specific `style` serializations.', }, - sub_nav: { - type: 'array', - items: { - type: 'object', - properties: { - type: { - type: 'string', - enum: [ - 'home', - 'guides', - 'discussions', - 'changelog', - 'search_box', - 'link_url', - 'custom_page', - 'user_controls', - 'reference', - 'recipes', - ], - }, - title: { type: 'string', nullable: true }, - url: { type: 'string', nullable: true }, - custom_page: { type: 'string', nullable: true }, - }, - required: ['type', 'title', 'url', 'custom_page'], - additionalProperties: false, - }, - description: 'The navigation settings for your projects subnavigation bar.', + webhooks: { + type: 'boolean', + default: false, + description: 'This API definition has `webhooks` documented.', }, - subheader_layout: { type: 'string', enum: ['links', 'dropdown'], default: 'links' }, - version: { - type: 'string', - enum: ['enabled', 'disabled'], - default: 'enabled', + xml: { + type: 'boolean', + default: false, + description: 'This API operation has parameters or schemas that serialize to XML.', + }, + references: { + type: 'boolean', description: - 'Should your current documentation version be shown in the navigation bar?', + 'This API operation, after being dereferenced, has `x-readme-ref-name` entries defining what the original `$ref` schema pointers were named.', }, }, - required: ['left', 'links', 'right', 'sub_nav'], + required: ['references'], additionalProperties: false, + description: 'OpenAPI features that are utilized within this API operation.', }, - table_of_contents: { + source: { type: 'string', - enum: ['enabled', 'disabled'], - default: 'enabled', - description: 'Should your guides show a table of contents?', + enum: ['api', 'apidesigner', 'apieditor', 'bidi', 'form', 'rdme', 'rdme_github', 'url'], + nullable: true, + description: 'The source by which this API definition was ingested.', }, - whats_next_label: { + uri: { type: 'string', + pattern: + '\\/versions\\/(stable|([0-9]+)(?:\\.([0-9]+))?(?:\\.([0-9]+))?(-.*)?)\\/apis\\/((([a-z0-9-_ ]|[^\\\\x00-\\\\x7F])+.(json|yaml|yml)))', nullable: true, - description: - 'What should we call the next steps section of your guides? Defaults to "What\'s Next".', + description: 'A URI to the API resource.', }, }, - required: [ - 'brand', - 'changelog', - 'custom_code', - 'footer', - 'header', - 'logo', - 'markdown', - 'navigation', - 'whats_next_label', - ], + required: ['method', 'path', 'stats', 'source', 'uri'], additionalProperties: false, + description: 'Information about the API that this reference page is attached to.', }, - canonical_url: { - type: 'string', - format: 'uri', - nullable: true, - description: - "The canonical base URL for your project defaults to your project's base URL, but you can override the canonical base URL with this field.", - }, - custom_login: { + connections: { type: 'object', properties: { - jwt_secret: { type: 'string' }, - login_url: { type: 'string', format: 'uri', nullable: true }, - logout_url: { type: 'string', format: 'uri', nullable: true }, + recipes: { + type: 'array', + items: { + type: 'object', + properties: { + uri: { + type: 'string', + pattern: + '\\/versions\\/(stable|([0-9]+)(?:\\.([0-9]+))?(?:\\.([0-9]+))?(-.*)?)\\/recipes\\/(([a-z0-9-_ ]|[^\\\\x00-\\\\x7F])+)', + description: + 'URI of the recipe that this API reference is connected to. The recipe and API reference must exist within the same version.', + }, + }, + required: ['uri'], + additionalProperties: false, + }, + nullable: true, + description: 'A collection of recipes that are displayed on this API reference.', + }, }, - required: ['jwt_secret', 'login_url', 'logout_url'], + required: ['recipes'], additionalProperties: false, }, - description: { - type: 'string', - nullable: true, - description: - 'The description of your project. This is used in the page meta description and is seen by search engines and sites like Facebook.', + }, + required: [ + 'category', + 'content', + 'href', + 'metadata', + 'parent', + 'privacy', + 'project', + 'renderable', + 'slug', + 'title', + 'updated_at', + 'uri', + 'api_config', + 'api', + 'connections', + ], + additionalProperties: false, + }, + }, + required: ['data'], + additionalProperties: false, + }, + }, + }, + }, + }, + }, + delete: { + operationId: 'deleteReference', + summary: 'Delete a reference', + tags: ['API Reference'], + description: + "Deletes a page from the API reference section of your developer hub. \n\n>πŸ“˜\n> This route is only available to projects that are using [ReadMe Refactored](https://docs.readme.com/main/docs/welcome-to-readme-refactored).\n\n>🚧 ReadMe's API v2 is currently in beta.\n> This API and its docs are a work in progress. While we don’t expect any major breaking changes, you may encounter occasional issues as we work toward a stable release. Make sure to [check out our API migration guide](https://docs.readme.com/main/reference/api-migration-guide), and [feel free to reach out](mailto:support@readme.io) if you have any questions or feedback!", + parameters: [ + { + schema: { type: 'string', pattern: 'stable|([0-9]+)(?:\\.([0-9]+))?(?:\\.([0-9]+))?(-.*)?' }, + in: 'path', + name: 'version', + required: true, + description: 'Project version number or stable.', + }, + { + schema: { type: 'string', pattern: '([a-z0-9-_ ]|[^\\\\x00-\\\\x7F])+' }, + in: 'path', + name: 'slug', + required: true, + description: 'A URL-safe representation of the resource.', + }, + ], + responses: { '204': { description: 'No Content' } }, + }, + patch: { + operationId: 'updateReference', + summary: 'Update a reference', + tags: ['API Reference'], + description: + "Updates a page in the API reference section of your developer hub. \n\n>πŸ“˜\n> This route is only available to projects that are using [ReadMe Refactored](https://docs.readme.com/main/docs/welcome-to-readme-refactored).\n\n>🚧 ReadMe's API v2 is currently in beta.\n> This API and its docs are a work in progress. While we don’t expect any major breaking changes, you may encounter occasional issues as we work toward a stable release. Make sure to [check out our API migration guide](https://docs.readme.com/main/reference/api-migration-guide), and [feel free to reach out](mailto:support@readme.io) if you have any questions or feedback!", + requestBody: { + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + allow_crawlers: { + type: 'string', + enum: ['enabled', 'disabled'], + default: 'enabled', + description: 'Allow indexing by robots.', + }, + category: { + type: 'object', + properties: { + uri: { + type: 'string', + pattern: + '\\/versions\\/(stable|([0-9]+)(?:\\.([0-9]+))?(?:\\.([0-9]+))?(-.*)?)\\/categories\\/(guides|reference)\\/((.*))', + description: 'A URI to the category resource.', + }, + }, + additionalProperties: false, + }, + content: { + type: 'object', + properties: { + body: { type: 'string', nullable: true }, + excerpt: { type: 'string', nullable: true }, + link: { + type: 'object', + properties: { + url: { type: 'string', nullable: true }, + new_tab: { type: 'boolean', nullable: true }, }, - glossary: { - type: 'array', - items: { - type: 'object', - properties: { - term: { - type: 'string', - description: - 'Glossary term is what gets displayed in your documentation when embedded.', - }, - definition: { - type: 'string', - description: - 'Glossary definition is revealed to users when they mouse over the glossary term.', - }, + additionalProperties: false, + description: + 'Information about where this page should redirect to; only available when `type` is `link`.', + }, + next: { + type: 'object', + properties: { + description: { type: 'string', nullable: true }, + pages: { + type: 'array', + items: { + anyOf: [ + { + type: 'object', + properties: { + slug: { type: 'string' }, + title: { type: 'string', nullable: true }, + type: { type: 'string', enum: ['basic', 'endpoint'] }, + }, + required: ['slug', 'title', 'type'], + additionalProperties: false, + }, + { + type: 'object', + properties: { + title: { type: 'string', nullable: true }, + type: { type: 'string', enum: ['link'] }, + url: { type: 'string' }, + }, + required: ['title', 'type', 'url'], + additionalProperties: false, + }, + ], }, - required: ['term', 'definition'], - additionalProperties: false, }, - default: [], - description: - 'List of glossary terms in your project that can be used within your documentation.', }, - health_check: { + additionalProperties: false, + }, + }, + additionalProperties: false, + }, + href: { + type: 'object', + properties: { + dash: { type: 'string', format: 'uri', description: 'A URL to this page in your ReadMe Dash.' }, + }, + additionalProperties: false, + }, + metadata: { + type: 'object', + properties: { + description: { type: 'string', nullable: true }, + keywords: { type: 'string', nullable: true }, + title: { type: 'string', nullable: true }, + image: { + type: 'object', + properties: { uri: { type: 'string', pattern: '\\/images\\/([a-f\\d]{24})', nullable: true } }, + additionalProperties: false, + }, + }, + additionalProperties: false, + }, + parent: { + type: 'object', + properties: { + uri: { + type: 'string', + pattern: + '\\/versions\\/(stable|([0-9]+)(?:\\.([0-9]+))?(?:\\.([0-9]+))?(-.*)?)\\/(guides|reference)\\/(([a-z0-9-_ ]|[^\\\\x00-\\\\x7F])+)', + nullable: true, + }, + }, + additionalProperties: false, + }, + privacy: { + type: 'object', + properties: { + view: { type: 'string', enum: ['public', 'anyone_with_link'], default: 'anyone_with_link' }, + }, + additionalProperties: false, + }, + renderable: { + type: 'object', + properties: { + status: { + type: 'boolean', + default: true, + description: 'A flag for if the page is renderable or not.', + }, + error: { type: 'string', nullable: true }, + message: { type: 'string', nullable: true }, + }, + additionalProperties: false, + }, + slug: { + allOf: [{ type: 'string' }, { type: 'string', minLength: 1 }], + description: 'The accessible URL slug for the page.', + }, + state: { type: 'string', enum: ['current', 'deprecated'], default: 'current' }, + title: { type: 'string' }, + type: { type: 'string', enum: ['api_config', 'basic', 'endpoint', 'link'], default: 'basic' }, + connections: { + type: 'object', + properties: { + recipes: { + type: 'array', + items: { type: 'object', properties: { - provider: { + uri: { type: 'string', - enum: ['manual', 'statuspage', 'none'], - default: 'none', + pattern: + '\\/versions\\/(stable|([0-9]+)(?:\\.([0-9]+))?(?:\\.([0-9]+))?(-.*)?)\\/recipes\\/(([a-z0-9-_ ]|[^\\\\x00-\\\\x7F])+)', description: - 'The type of provider you wish to use for for managing your APIs health: manually or through [Atlassian Statuspage](https://www.atlassian.com/software/statuspage).', - }, - settings: { - type: 'object', - properties: { - manual: { - type: 'object', - properties: { - status: { - type: 'string', - enum: ['up', 'down'], - default: 'up', - description: - 'If you are manually managing your APIs health this is a status boolean indicating if your API is up or down.', - }, - url: { - type: 'string', - nullable: true, - description: - 'The URL that we will show to your users when your API is down. This is only used when `health_check.provider` is set to `manual`.', - }, - }, - required: ['url'], - additionalProperties: false, - }, - statuspage: { - type: 'object', - properties: { - id: { - type: 'string', - nullable: true, - description: - 'If managing your APIs health through [Statuspage](https://www.atlassian.com/software/statuspage) this is your Statuspage ID.', - }, - }, - required: ['id'], - additionalProperties: false, - }, - }, - required: ['manual', 'statuspage'], - additionalProperties: false, + 'URI of the recipe that this API reference is connected to. The recipe and API reference must exist within the same version.', }, }, - required: ['settings'], additionalProperties: false, }, - homepage_url: { - type: 'string', - nullable: true, - description: - 'The URL for your company\'s main website. We\'ll link to it in various places so people can "Go Home".', + nullable: true, + }, + }, + additionalProperties: false, + }, + api: { + type: 'object', + properties: { + method: { + type: 'string', + enum: ['get', 'put', 'post', 'delete', 'options', 'head', 'patch', 'trace'], + description: 'The endpoint HTTP method.', + }, + path: { type: 'string', description: 'The endpoint path.' }, + schema: { nullable: true }, + stats: { + type: 'object', + properties: { + additional_properties: { + type: 'boolean', + default: false, + description: + 'This API operation uses `additionalProperties` for handling extra schema properties.', + }, + callbacks: { + type: 'boolean', + default: false, + description: 'This API operation has `callbacks` documented.', + }, + circular_references: { + type: 'boolean', + default: false, + description: 'This API operation contains `$ref` schema pointers that resolve to itself.', + }, + common_parameters: { + type: 'boolean', + default: false, + description: 'This API operation utilizes common parameters set at the path level.', + }, + discriminators: { + type: 'boolean', + default: false, + description: + 'This API operation utilizes `discriminator` for discriminating between different parts in a polymorphic schema.', + }, + links: { + type: 'boolean', + default: false, + description: 'This API operation has `links` documented.', + }, + polymorphism: { + type: 'boolean', + default: false, + description: 'This API operation contains polymorphic schemas.', + }, + server_variables: { + type: 'boolean', + default: false, + description: + 'This API operation has composable variables configured for its server definition.', + }, + style: { + type: 'boolean', + default: false, + description: 'This API operation has parameters that have specific `style` serializations.', + }, + webhooks: { + type: 'boolean', + default: false, + description: 'This API definition has `webhooks` documented.', + }, + xml: { + type: 'boolean', + default: false, + description: 'This API operation has parameters or schemas that serialize to XML.', + }, + references: { + type: 'boolean', + description: + 'This API operation, after being dereferenced, has `x-readme-ref-name` entries defining what the original `$ref` schema pointers were named.', + }, }, - integrations: { - type: 'object', - properties: { - aws: { - type: 'object', - properties: { - readme_webhook_login: { - type: 'object', - properties: { - external_id: { type: 'string', nullable: true }, - region: { - type: 'string', - enum: [ - 'me-south-1', - 'cn-north-1', - 'ca-west-1', - 'us-west-1', - 'ca-central-1', - 'af-south-1', - 'eu-central-1', - 'ap-east-1', - 'ap-south-2', - 'eu-west-1', - 'ap-southeast-3', - 'eu-west-2', - 'ap-southeast-5', - 'ap-southeast-4', - 'eu-south-1', - 'ap-south-1', - 'cn-northwest-1', - 'us-east-2', - 'us-west-2', - 'ap-northeast-3', - 'eu-west-3', - 'sa-east-1', - 'ap-northeast-2', - 'ap-southeast-1', - 'eu-south-2', - 'eu-north-1', - 'ap-southeast-2', - 'il-central-1', - 'ap-northeast-1', - 'me-central-1', - 'us-east-1', - 'eu-central-2', - ], - nullable: true, - }, - role_arn: { type: 'string', nullable: true }, - usage_plan_id: { type: 'string', nullable: true }, - }, - required: ['external_id', 'region', 'role_arn', 'usage_plan_id'], - additionalProperties: false, - }, - }, - required: ['readme_webhook_login'], - additionalProperties: false, - }, - bing: { - type: 'object', - properties: { verify: { type: 'string', nullable: true } }, - required: ['verify'], - additionalProperties: false, + additionalProperties: false, + description: 'OpenAPI features that are utilized within this API operation.', + }, + source: { + type: 'string', + enum: ['api', 'apidesigner', 'apieditor', 'bidi', 'form', 'rdme', 'rdme_github', 'url'], + nullable: true, + }, + uri: { + type: 'string', + pattern: + '\\/versions\\/(stable|([0-9]+)(?:\\.([0-9]+))?(?:\\.([0-9]+))?(-.*)?)\\/apis\\/((([a-z0-9-_ ]|[^\\\\x00-\\\\x7F])+.(json|yaml|yml)))', + nullable: true, + }, + }, + additionalProperties: false, + description: + 'Information about the API that this reference page is attached to. If you wish to detach this page from an API definition, making it a stand page, set `api.uri` to `null`.', + }, + position: { type: 'number' }, + }, + additionalProperties: false, + }, + }, + }, + }, + parameters: [ + { + schema: { type: 'string', pattern: 'stable|([0-9]+)(?:\\.([0-9]+))?(?:\\.([0-9]+))?(-.*)?' }, + in: 'path', + name: 'version', + required: true, + description: 'Project version number or stable.', + }, + { + schema: { type: 'string', pattern: '([a-z0-9-_ ]|[^\\\\x00-\\\\x7F])+' }, + in: 'path', + name: 'slug', + required: true, + description: 'A URL-safe representation of the resource.', + }, + ], + responses: { + '200': { + description: 'OK', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + data: { + type: 'object', + properties: { + allow_crawlers: { + type: 'string', + enum: ['enabled', 'disabled'], + default: 'enabled', + description: 'Allow indexing by robots.', + }, + category: { + type: 'object', + properties: { + uri: { + type: 'string', + pattern: + '\\/versions\\/(stable|([0-9]+)(?:\\.([0-9]+))?(?:\\.([0-9]+))?(-.*)?)\\/categories\\/(guides|reference)\\/((.*))', + description: 'A URI to the category resource.', }, - google: { + }, + required: ['uri'], + additionalProperties: false, + }, + content: { + type: 'object', + properties: { + body: { type: 'string', nullable: true }, + excerpt: { type: 'string', nullable: true }, + link: { type: 'object', properties: { - analytics: { - type: 'string', + url: { type: 'string', nullable: true }, + new_tab: { + type: 'boolean', nullable: true, - description: - "Your Google Analytics ID. If it starts with UA-, we'll use Universal Analytics otherwise Google Analytics 4.", + description: 'Should this URL be opened up in a new tab?', }, - site_verification: { type: 'string', nullable: true }, }, - required: ['analytics', 'site_verification'], - additionalProperties: false, - }, - heap: { - type: 'object', - properties: { id: { type: 'string', nullable: true } }, - required: ['id'], + required: ['url', 'new_tab'], additionalProperties: false, + description: + 'Information about where this page should redirect to; only available when `type` is `link`.', }, - intercom: { + next: { type: 'object', properties: { - app_id: { type: 'string', nullable: true }, - secure_mode: { - type: 'object', - properties: { - key: { - type: 'string', - nullable: true, - description: - 'By supplying a secure mode key you will opt into [Intercoms Identity Verification](https://docs.intercom.io/configuring-intercom/enable-secure-mode) system.', - }, - email_only: { - type: 'boolean', - default: false, - description: - 'Should ReadMe only identify users by their email addresses? This integrates better with your existing Intercom but is possibly less secure.', - }, + description: { type: 'string', nullable: true }, + pages: { + type: 'array', + items: { + anyOf: [ + { + type: 'object', + properties: { + slug: { type: 'string' }, + title: { type: 'string', nullable: true }, + type: { type: 'string', enum: ['basic', 'endpoint'] }, + }, + required: ['slug', 'title', 'type'], + additionalProperties: false, + }, + { + type: 'object', + properties: { + title: { type: 'string', nullable: true }, + type: { type: 'string', enum: ['link'] }, + url: { type: 'string' }, + }, + required: ['title', 'type', 'url'], + additionalProperties: false, + }, + ], }, - required: ['key'], - additionalProperties: false, - }, - }, - required: ['app_id', 'secure_mode'], - additionalProperties: false, - }, - koala: { - type: 'object', - properties: { key: { type: 'string', nullable: true } }, - required: ['key'], - additionalProperties: false, - }, - localize: { - type: 'object', - properties: { key: { type: 'string', nullable: true } }, - required: ['key'], - additionalProperties: false, - }, - recaptcha: { - type: 'object', - properties: { - site_key: { type: 'string', nullable: true }, - secret_key: { type: 'string', nullable: true }, - }, - required: ['site_key', 'secret_key'], - additionalProperties: false, - description: 'https://docs.readme.com/main/docs/recaptcha', - }, - segment: { - type: 'object', - properties: { - key: { type: 'string', nullable: true }, - domain: { - type: 'string', - nullable: true, - description: - 'If you are proxying [Segment](https://segment.com/) requests through a custom domain this is that domain. More information about this configuration can be found [here](https://docs.readme.com/main/docs/segment#using-a-custom-domain-with-segment).', }, }, - required: ['key', 'domain'], - additionalProperties: false, - }, - typekit: { - type: 'object', - properties: { key: { type: 'string', nullable: true } }, - required: ['key'], - additionalProperties: false, - }, - zendesk: { - type: 'object', - properties: { subdomain: { type: 'string', nullable: true } }, - required: ['subdomain'], + required: ['description', 'pages'], additionalProperties: false, }, }, - required: [ - 'aws', - 'bing', - 'google', - 'heap', - 'intercom', - 'koala', - 'localize', - 'recaptcha', - 'segment', - 'typekit', - 'zendesk', - ], - additionalProperties: false, - }, - name: { type: 'string', description: 'The name of the project.' }, - onboarding_completed: { - type: 'object', - properties: { - api: { type: 'boolean', default: false }, - appearance: { type: 'boolean', default: false }, - documentation: { type: 'boolean', default: false }, - domain: { type: 'boolean', default: false }, - jwt: { type: 'boolean', default: false }, - logs: { type: 'boolean', default: false }, - metricsSDK: { type: 'boolean', default: false }, - }, + required: ['body', 'excerpt', 'link', 'next'], additionalProperties: false, }, - pages: { + href: { type: 'object', properties: { - not_found: { + dash: { type: 'string', - pattern: - '\\/versions\\/(stable|([0-9]+)(?:\\.([0-9]+))?(?:\\.([0-9]+))?(-.*)?)\\/custom_pages\\/([a-f\\d]{24}|([a-z0-9-_ ]|[^\\\\x00-\\\\x7F])+)', - nullable: true, - description: - 'The page you wish to be served to your users when they encounter a 404. This can either map to the `uri` of a Custom Page on your project or be set to `null`. If `null` then the default ReadMe 404 page will be served. The version within the `uri` must be mapped to your stable version.', + format: 'uri', + description: 'A URL to this page in your ReadMe Dash.', }, }, - required: ['not_found'], + required: ['dash'], additionalProperties: false, }, - parent: { - type: 'string', - nullable: true, - description: - "Does the project have a parent project (enterprise)? If so, this resolves to the parent's subdomain.", - }, - plan: { + metadata: { type: 'object', properties: { - type: { - type: 'string', - enum: [ - 'business', - 'business2018', - 'business-annual-2024', - 'enterprise', - 'free', - 'freelaunch', - 'opensource', - 'startup', - 'startup2018', - 'startup-annual-2024', - ], - default: 'free', - }, - grace_period: { - type: 'object', - properties: { - enabled: { type: 'boolean', default: false }, - end_date: { type: 'string', format: 'date-time', nullable: true, default: null }, - }, - additionalProperties: false, - }, - trial: { + description: { type: 'string', nullable: true }, + image: { type: 'object', properties: { - expired: { type: 'boolean', default: false }, - end_date: { + uri: { type: 'string', - format: 'date-time', - description: 'The end date for your two week trial.', + pattern: '\\/images\\/([a-f\\d]{24})', + nullable: true, + description: + 'A URI to the `getImages` endpoint for this image. If the is a legacy image then this `uri` will be `null`. And if you wish to delete this image then you should set this to `null`.', }, + url: { type: 'string', format: 'uri', nullable: true }, }, - required: ['end_date'], + required: ['uri', 'url'], additionalProperties: false, }, + keywords: { + type: 'string', + nullable: true, + description: 'A comma-separated list of keywords to place into your page metadata.', + }, + title: { type: 'string', nullable: true }, }, - required: ['grace_period', 'trial'], + required: ['description', 'image', 'keywords', 'title'], additionalProperties: false, }, - privacy: { + parent: { type: 'object', properties: { - view: { - type: 'string', - enum: ['public', 'admin', 'password', 'custom_login'], - default: 'public', - description: - '* `public` - Site is available to the public.\n* `admin` - Site is only available to users that have project permissions.\n* `password` - Site is gated behind a password authentication system.\n* `custom_login` - Users who view your site will be forwarded to a URL of your choice, having them login there and be forwarded back to your ReadMe site.', - }, - password: { + uri: { type: 'string', + pattern: + '\\/versions\\/(stable|([0-9]+)(?:\\.([0-9]+))?(?:\\.([0-9]+))?(-.*)?)\\/(guides|reference)\\/(([a-z0-9-_ ]|[^\\\\x00-\\\\x7F])+)', nullable: true, - description: - "The project's password for when `privacy.view` is `password`. This field can be set, but it will not be returned by the API.", + description: 'A URI to the parent page resource including the page ID or slug.', }, }, - required: ['password'], + required: ['uri'], additionalProperties: false, }, - redirects: { - type: 'array', - items: { - type: 'object', - properties: { from: { type: 'string' }, to: { type: 'string' } }, - required: ['from', 'to'], - additionalProperties: false, - }, - description: - 'A collection of page redirects that ReadMe will permanently redirect users to when attempting to render a 404. Check out our [redirect docs](https://docs.readme.com/main/docs/error-pages#section-redirects) for more information on how they are handled.', - }, - refactored: { + privacy: { type: 'object', properties: { - status: { - type: 'string', - enum: ['enabled', 'disabled'], - default: 'disabled', - description: 'Indicates if the project has our new Unified UI experience.', - }, - migrated: { - type: 'string', - enum: ['failed', 'processing', 'successful', 'unknown'], - default: 'unknown', - description: 'Indicates if the project has been migrated from Dash to Superhub.', - }, + view: { type: 'string', enum: ['public', 'anyone_with_link'], default: 'anyone_with_link' }, }, additionalProperties: false, }, - reference: { + project: { type: 'object', properties: { - api_sdk_snippets: { - type: 'string', - enum: ['enabled', 'disabled'], - default: 'enabled', - description: 'Enable SDK-generated request code snippets.', - }, - defaults: { - type: 'string', - enum: ['always_use', 'use_only_if_required'], - default: 'use_only_if_required', - description: - 'When `always_use`, any `default` values defined in your API definition are used to populate your request data in the API Explorer, even if the parameter is not marked as `required`.', - }, - json_editor: { - type: 'string', - enum: ['enabled', 'disabled'], - default: 'disabled', - description: 'When `enabled`, allows editing the request body with a JSON editor.', - }, - request_history: { - type: 'string', - enum: ['enabled', 'disabled'], - default: 'enabled', - description: 'When `enabled`, request history for API endpoints are shown.', - }, - oauth_flows: { - type: 'string', - enum: ['enabled', 'disabled'], - default: 'disabled', - description: - 'When `enabled`, enable the new OAuth Flows experience in the API Reference section.', - }, - response_examples: { + name: { type: 'string', description: 'The name of the project.' }, + subdomain: { type: 'string', - enum: ['expanded', 'collapsed'], - default: 'collapsed', - description: - 'When `expanded`, response examples will be expanded by default if a 200 level response exists.', + pattern: '[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*', + maxLength: 30, + description: 'The subdomain of the project.', }, - response_schemas: { + uri: { type: 'string', - enum: ['expanded', 'collapsed'], - default: 'collapsed', - description: - 'When `expanded`, response schemas will be expanded by default if a 200 level response schema exists.', + pattern: '\\/projects\\/(me|[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*)', + description: 'A URI to the project that this page belongs to.', }, }, + required: ['name', 'subdomain', 'uri'], additionalProperties: false, - description: - 'Contains options to configure interactive sections on your API Reference pages.', }, - seo: { + renderable: { type: 'object', properties: { - overwrite_title_tag: { + status: { + type: 'boolean', + default: true, + description: 'A flag for if the page is renderable or not.', + }, + error: { type: 'string', nullable: true, description: 'The rendering error.' }, + message: { type: 'string', - enum: ['enabled', 'disabled'], - default: 'disabled', - description: - "Overwrite pages' <title> tag with their custom metadata title (if present).", + nullable: true, + description: 'Additional details about the rendering error.', }, }, additionalProperties: false, }, - sitemap: { + slug: { + allOf: [{ type: 'string' }, { type: 'string', minLength: 1 }], + description: 'The accessible URL slug for the page.', + }, + state: { type: 'string', enum: ['current', 'deprecated'], default: 'current' }, + title: { type: 'string' }, + type: { type: 'string', enum: ['api_config', 'basic', 'endpoint', 'link'], default: 'basic' }, + updated_at: { type: 'string', - enum: ['enabled', 'disabled'], - default: 'disabled', - description: 'Expose a `sitemap.xml` directory on your project.', + format: 'date-time', + description: 'An ISO 8601 formatted date for when the page was updated.', }, - subdomain: { type: 'string', pattern: '[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*', maxLength: 30 }, - suggested_edits: { + uri: { type: 'string', - enum: ['enabled', 'disabled'], - default: 'enabled', - description: 'Allow users to suggest edits to your documentation.', + pattern: + '\\/versions\\/(stable|([0-9]+)(?:\\.([0-9]+))?(?:\\.([0-9]+))?(-.*)?)\\/(guides|reference)\\/(([a-z0-9-_ ]|[^\\\\x00-\\\\x7F])+)', + description: 'A URI to the page resource.', }, - variable_defaults: { - type: 'array', - items: { - type: 'object', - properties: { - id: { type: 'string', description: 'Variable Identifier' }, - name: { type: 'string', description: 'The key name of the variable.' }, - default: { type: 'string', description: 'The default value of the variable.' }, - source: { - type: 'string', - enum: ['server', 'security', 'custom', ''], - default: '', - description: - 'The variables source. This can come from a user input or from syncing an OpenAPI definition.', - }, - type: { - type: 'string', - enum: ['http', 'apiKey', 'openIdConnect', 'oauth2', ''], - description: - 'If variable `source` is `security`, include the OpenAPI security auth type.', - }, - scheme: { - type: 'string', - description: - 'If variable `source` is `security`, include the OpenAPI security auth scheme.', - }, - }, - required: ['id', 'name'], - additionalProperties: false, - }, - default: [], + api_config: { + type: 'string', + enum: ['authentication', 'getting-started', 'my-requests'], + nullable: true, }, - webhooks: { - type: 'array', - items: { - type: 'object', - properties: { - action: { type: 'string', enum: ['login'], default: 'login' }, - timeout: { type: 'number', default: 5000 }, - url: { type: 'string', format: 'uri' }, + api: { + type: 'object', + properties: { + method: { + type: 'string', + enum: ['get', 'put', 'post', 'delete', 'options', 'head', 'patch', 'trace'], + description: 'The endpoint HTTP method.', + }, + path: { type: 'string', description: 'The endpoint path.' }, + schema: { + nullable: true, + description: + 'The API schema for this reference endpoint. This schema is a reduced version of the full API definition and only contains the necessary information for this endpoint.', + }, + stats: { + type: 'object', + properties: { + additional_properties: { + type: 'boolean', + default: false, + description: + 'This API operation uses `additionalProperties` for handling extra schema properties.', + }, + callbacks: { + type: 'boolean', + default: false, + description: 'This API operation has `callbacks` documented.', + }, + circular_references: { + type: 'boolean', + default: false, + description: + 'This API operation contains `$ref` schema pointers that resolve to itself.', + }, + common_parameters: { + type: 'boolean', + default: false, + description: 'This API operation utilizes common parameters set at the path level.', + }, + discriminators: { + type: 'boolean', + default: false, + description: + 'This API operation utilizes `discriminator` for discriminating between different parts in a polymorphic schema.', + }, + links: { + type: 'boolean', + default: false, + description: 'This API operation has `links` documented.', + }, + polymorphism: { + type: 'boolean', + default: false, + description: 'This API operation contains polymorphic schemas.', + }, + server_variables: { + type: 'boolean', + default: false, + description: + 'This API operation has composable variables configured for its server definition.', + }, + style: { + type: 'boolean', + default: false, + description: + 'This API operation has parameters that have specific `style` serializations.', + }, + webhooks: { + type: 'boolean', + default: false, + description: 'This API definition has `webhooks` documented.', + }, + xml: { + type: 'boolean', + default: false, + description: 'This API operation has parameters or schemas that serialize to XML.', + }, + references: { + type: 'boolean', + description: + 'This API operation, after being dereferenced, has `x-readme-ref-name` entries defining what the original `$ref` schema pointers were named.', + }, + }, + required: ['references'], + additionalProperties: false, + description: 'OpenAPI features that are utilized within this API operation.', }, - required: ['url'], - additionalProperties: false, - }, - default: [], - }, - id: { - type: 'string', - pattern: '^[a-f\\d]{24}$', - description: 'The unique, immutable, identifier for the project.', - }, - features: { - type: 'object', - properties: { - custom_components: { + source: { type: 'string', - enum: ['enabled', 'disabled'], - default: 'disabled', - description: 'If this project supports creating custom components.', + enum: ['api', 'apidesigner', 'apieditor', 'bidi', 'form', 'rdme', 'rdme_github', 'url'], + nullable: true, + description: 'The source by which this API definition was ingested.', }, - mdx: { + uri: { type: 'string', - enum: ['enabled', 'disabled'], - default: 'disabled', - description: 'If this project supports MDX.', + pattern: + '\\/versions\\/(stable|([0-9]+)(?:\\.([0-9]+))?(?:\\.([0-9]+))?(-.*)?)\\/apis\\/((([a-z0-9-_ ]|[^\\\\x00-\\\\x7F])+.(json|yaml|yml)))', + nullable: true, + description: 'A URI to the API resource.', }, }, + required: ['method', 'path', 'stats', 'source', 'uri'], additionalProperties: false, + description: 'Information about the API that this reference page is attached to.', }, - permissions: { + connections: { type: 'object', properties: { - appearance: { - type: 'object', - properties: { - private_label: { - type: 'string', - enum: ['enabled', 'disabled'], - default: 'disabled', - description: - 'If this project is allowed to private label their Hub and remove all ReadMe branding.', - }, - custom_code: { - type: 'object', - properties: { - css: { - type: 'string', - enum: ['enabled', 'disabled'], - default: 'disabled', - description: 'If this project is allowed to utilize custom CSS.', - }, - html: { - type: 'string', - enum: ['enabled', 'disabled'], - default: 'disabled', - description: 'If this project is allowed to utilize custom HTML.', - }, - js: { - type: 'string', - enum: ['enabled', 'disabled'], - default: 'disabled', - description: 'If this project is allowed to utilize custom JS.', - }, + recipes: { + type: 'array', + items: { + type: 'object', + properties: { + uri: { + type: 'string', + pattern: + '\\/versions\\/(stable|([0-9]+)(?:\\.([0-9]+))?(?:\\.([0-9]+))?(-.*)?)\\/recipes\\/(([a-z0-9-_ ]|[^\\\\x00-\\\\x7F])+)', + description: + 'URI of the recipe that this API reference is connected to. The recipe and API reference must exist within the same version.', }, - additionalProperties: false, }, + required: ['uri'], + additionalProperties: false, }, - required: ['custom_code'], - additionalProperties: false, + nullable: true, + description: 'A collection of recipes that are displayed on this API reference.', }, }, - required: ['appearance'], + required: ['recipes'], additionalProperties: false, }, - uri: { - type: 'string', - pattern: '\\/projects\\/(me|[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*)', - description: 'A URI to the project resource.', - }, }, required: [ - 'api_designer', - 'appearance', - 'canonical_url', - 'custom_login', - 'description', - 'health_check', - 'homepage_url', - 'integrations', - 'name', - 'onboarding_completed', - 'pages', + 'category', + 'content', + 'href', + 'metadata', 'parent', - 'plan', 'privacy', - 'redirects', - 'refactored', - 'reference', - 'seo', - 'subdomain', - 'id', - 'features', - 'permissions', + 'project', + 'renderable', + 'slug', + 'title', + 'updated_at', 'uri', + 'api_config', + 'api', + 'connections', ], additionalProperties: false, }, @@ -5677,10 +5748,170 @@ export const readmeAPIv2Oas = { }, }, }, + '/versions': { + get: { + operationId: 'getVersions', + summary: 'Get versions', + tags: ['Versions'], + description: + "Get a collection of versions. \n\n>πŸ“˜\n> This route is only available to projects that are using [ReadMe Refactored](https://docs.readme.com/main/docs/welcome-to-readme-refactored).\n\n>🚧 ReadMe's API v2 is currently in beta.\n> This API and its docs are a work in progress. While we don’t expect any major breaking changes, you may encounter occasional issues as we work toward a stable release. Make sure to [check out our API migration guide](https://docs.readme.com/main/reference/api-migration-guide), and [feel free to reach out](mailto:support@readme.io) if you have any questions or feedback!", + parameters: [ + { + schema: { type: 'number', minimum: 1, default: 1 }, + in: 'query', + name: 'page', + required: false, + description: 'Used to specify further pages (starts at 1).', + }, + { + schema: { type: 'number', minimum: 1, maximum: 100, default: 10 }, + in: 'query', + name: 'per_page', + required: false, + description: 'Number of items to include in pagination (up to 100, defaults to 10).', + }, + { + schema: { type: 'string', enum: ['created', 'updated', 'semver'], default: 'semver' }, + in: 'query', + name: 'sort_by', + required: false, + description: 'The sort that should be used for the returned versions list.', + }, + ], + responses: { + '200': { + description: 'OK', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + total: { type: 'number' }, + page: { type: 'number' }, + per_page: { type: 'number' }, + paging: { + type: 'object', + properties: { + next: { type: 'string', nullable: true }, + previous: { type: 'string', nullable: true }, + first: { type: 'string', nullable: true }, + last: { type: 'string', nullable: true }, + }, + required: ['next', 'previous', 'first', 'last'], + additionalProperties: false, + }, + data: { + type: 'array', + items: { + type: 'object', + properties: { + base: { + type: 'string', + nullable: true, + description: 'The name of the version this version was forked from.', + }, + display_name: { + type: 'string', + nullable: true, + description: 'A non-semver display name for the version.', + }, + git: { + type: 'object', + properties: { + latest_commit: { + type: 'object', + properties: { + created_at: { + type: 'string', + format: 'date-time', + description: 'An ISO 8601 formatted date for when the latest commit was created.', + nullable: true, + }, + hash: { + type: 'string', + nullable: true, + description: 'The sha for the latest commit.', + }, + }, + required: ['created_at', 'hash'], + additionalProperties: false, + }, + branch_ref: { + type: 'string', + nullable: true, + description: 'A reference to the associated branch for the version.', + }, + }, + required: ['latest_commit', 'branch_ref'], + additionalProperties: false, + }, + name: { + type: 'string', + pattern: 'stable|([0-9]+)(?:\\.([0-9]+))?(?:\\.([0-9]+))?(-.*)?', + description: 'The semver name for the version.', + }, + privacy: { + type: 'object', + properties: { + view: { + type: 'string', + enum: ['default', 'hidden', 'public'], + description: + "Whether the version is public, hidden, or the stable version that's visible by default.", + }, + }, + required: ['view'], + additionalProperties: false, + }, + release_stage: { + type: 'string', + enum: ['beta', 'release'], + description: 'Whether the version is released or in beta', + }, + state: { + type: 'string', + enum: ['current', 'deprecated'], + description: 'Whether the version is current or deprecated', + }, + updated_at: { + type: 'string', + format: 'date-time', + description: 'An ISO 8601 formatted date for when the version was last updated.', + }, + uri: { + type: 'string', + pattern: '\\/versions\\/(stable|([0-9]+)(?:\\.([0-9]+))?(?:\\.([0-9]+))?(-.*)?)', + description: 'A URI to the version resource.', + }, + }, + required: [ + 'base', + 'display_name', + 'git', + 'name', + 'privacy', + 'release_stage', + 'state', + 'updated_at', + 'uri', + ], + additionalProperties: false, + }, + }, + }, + required: ['total', 'page', 'per_page', 'paging', 'data'], + additionalProperties: false, + }, + }, + }, + }, + }, + }, + }, }, servers: [{ url: 'https://api.readme.com/v2', description: 'The ReadMe API' }], security: [{ bearer: [] }], - 'x-readme': { 'proxy-enabled': true }, + 'x-readme': { 'proxy-enabled': false }, tags: [ { name: 'API Reference' }, { name: 'APIs' }, @@ -5689,9 +5920,9 @@ export const readmeAPIv2Oas = { { name: 'Guides' }, { name: 'Projects' }, { name: 'Search' }, + { name: 'Versions' }, ], } as const satisfies OASDocument; - export const categoryUriRegexPattern = readmeAPIv2Oas.paths['/versions/{version}/guides'].post.requestBody.content['application/json'].schema.properties .category.properties.uri.pattern; @@ -5703,6 +5934,9 @@ export const parentUriRegexPattern = type guidesRequestBodySchema = (typeof readmeAPIv2Oas)['paths']['/versions/{version}/guides/{slug}']['patch']['requestBody']['content']['application/json']['schema']; +type projectSchema = + (typeof readmeAPIv2Oas)['paths']['/projects/me']['get']['responses']['200']['content']['application/json']['schema']; + /** * Derived from our API documentation, this is the schema for the `guides` object * as we send it to the ReadMe API. @@ -5714,3 +5948,5 @@ export type GuidesRequestRepresentation = FromSchema< guidesRequestBodySchema, { keepDefaultedPropertiesOptional: true } >; + +export type ProjectRepresentation = FromSchema<projectSchema, { keepDefaultedPropertiesOptional: true }>; From 6ef3b16b9b279868e0840ab31f0127a7aeb148f5 Mon Sep 17 00:00:00 2001 From: Emily Kuo <em.s.kuo@gmail.com> Date: Fri, 28 Feb 2025 10:57:28 -0800 Subject: [PATCH 3/7] test: add tests/fix broken tests for bidi validation --- .../docs/__snapshots__/upload.test.ts.snap | 34 +++++++++ __tests__/commands/docs/upload.test.ts | 72 +++++++++++++++++++ 2 files changed, 106 insertions(+) diff --git a/__tests__/commands/docs/__snapshots__/upload.test.ts.snap b/__tests__/commands/docs/__snapshots__/upload.test.ts.snap index 361e49797..d88315657 100644 --- a/__tests__/commands/docs/__snapshots__/upload.test.ts.snap +++ b/__tests__/commands/docs/__snapshots__/upload.test.ts.snap @@ -1,5 +1,39 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html +exports[`rdme docs upload > given that ReadMe project has bidirection sync set up > should should error if validation is not skipped 1`] = ` +{ + "error": [Error: Bi-directional syncing is enabled for this project. Uploading these docs will overwrite what's currently synced from Git. To proceed with uploading via \`rdme\`, please use the \`--skip-validation\` flag.], + "stderr": "", + "stdout": "", +} +`; + +exports[`rdme docs upload > given that ReadMe project has bidirection sync set up > should upload if validation is skipped 1`] = ` +{ + "result": { + "created": [ + { + "filePath": "__tests__/__fixtures__/docs/new-docs/new-doc.md", + "response": {}, + "result": "created", + "slug": "new-doc", + }, + ], + "failed": [], + "skipped": [], + "updated": [], + }, + "stderr": " β€Ί Warning: Skipping pre-upload validation of the Markdown file(s). This is + β€Ί not recommended. +- πŸš€ Uploading files to ReadMe... +βœ” πŸš€ Uploading files to ReadMe... done! +", + "stdout": "🌱 Successfully created 1 page(s) in ReadMe: + - new-doc (__tests__/__fixtures__/docs/new-docs/new-doc.md) +", +} +`; + exports[`rdme docs upload > given that the file path is a directory > given that the directory contains parent/child docs > should upload parents before children 1`] = ` { "result": { diff --git a/__tests__/commands/docs/upload.test.ts b/__tests__/commands/docs/upload.test.ts index b0925e604..7b827b931 100644 --- a/__tests__/commands/docs/upload.test.ts +++ b/__tests__/commands/docs/upload.test.ts @@ -24,6 +24,9 @@ describe('rdme docs upload', () => { categories: {}, parentPages: {}, }); + getAPIv2Mock({ authorization }).get('/projects/me').reply(200, { + data: {}, + }); vi.spyOn(fs, 'writeFileSync').mockImplementation(() => {}); }); @@ -195,6 +198,10 @@ describe('rdme docs upload', () => { }) .reply(201, {}); + const projectsMeMock = getAPIv2Mock({ authorization }).get('/projects/me').reply(200, { + data: {}, + }); + prompts.inject([true]); const result = await run(['__tests__/__fixtures__/docs/mixed-docs/legacy-category.md', '--key', key]); @@ -208,6 +215,7 @@ describe('rdme docs upload', () => { mappingsMock.done(); mock.done(); + projectsMeMock.done(); }); it('should exit if the user declines to fix the issues', async () => { @@ -289,6 +297,10 @@ describe('rdme docs upload', () => { }) .reply(201, {}); + const projectsMeMock2 = getAPIv2MockForGHA({ authorization }).get('/projects/me').reply(200, { + data: {}, + }); + const result = await run(['__tests__/__fixtures__/docs/new-docs/new-doc.md', '--key', key]); expect(result).toMatchSnapshot(); @@ -296,13 +308,18 @@ describe('rdme docs upload', () => { headMock.done(); postMock.done(); + projectsMeMock2.done(); }); it('should error out if the file has validation errors', async () => { + const projectsMeMock2 = getAPIv2MockForGHA({ authorization }).get('/projects/me').reply(200, { + data: {}, + }); const result = await run(['__tests__/__fixtures__/docs/mixed-docs/legacy-category.md', '--key', key]); expect(result).toMatchSnapshot(); expect(fs.writeFileSync).not.toHaveBeenCalled(); + projectsMeMock2.done(); }); }); @@ -508,4 +525,59 @@ describe('rdme docs upload', () => { mock.done(); }); }); + + describe.only('given that ReadMe project has bidirection sync set up', () => { + it('should should error if validation is not skipped', async () => { + nock.cleanAll(); + + const mock = getAPIv2Mock({ authorization }) + .get('/projects/me') + .reply(200, { + data: { git: { connection: 'some-repo' } }, + }); + + const result = await run(['__tests__/__fixtures__/docs/new-docs/new-doc.md', '--key', key]); + + expect(result).toMatchSnapshot(); + expect(fs.writeFileSync).not.toHaveBeenCalled(); + + mock.done(); + }); + + it('should upload if validation is skipped', async () => { + nock.cleanAll(); + + const mock = getAPIv2Mock({ authorization }) + .head('/versions/1.2.3/guides/new-doc') + .reply(404) + .post('/versions/1.2.3/guides', { + category: { uri: '/versions/1.2.3/categories/guides/category-slug' }, + slug: 'new-doc', + title: 'This is the document title', + content: { body: '\nBody\n' }, + }) + .reply(201, {}); + + const projectsMeMock = getAPIv2Mock({ authorization }) + .get('/projects/me') + .reply(200, { + data: { git: { connection: 'some-repo' } }, + }); + + const result = await run([ + '__tests__/__fixtures__/docs/new-docs/new-doc.md', + '--key', + key, + '--version', + '1.2.3', + '--skip-validation', + ]); + + expect(result).toMatchSnapshot(); + expect(fs.writeFileSync).not.toHaveBeenCalled(); + + mock.done(); + projectsMeMock.done(); + }); + }); }); From 105c7153ef181914484f1194e634d4d01f50d2cb Mon Sep 17 00:00:00 2001 From: Emily Kuo <em.s.kuo@gmail.com> Date: Fri, 28 Feb 2025 11:43:12 -0800 Subject: [PATCH 4/7] chore: lint --- __tests__/commands/docs/upload.test.ts | 2 +- src/lib/syncPagePath.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/__tests__/commands/docs/upload.test.ts b/__tests__/commands/docs/upload.test.ts index 7b827b931..28ec64a74 100644 --- a/__tests__/commands/docs/upload.test.ts +++ b/__tests__/commands/docs/upload.test.ts @@ -526,7 +526,7 @@ describe('rdme docs upload', () => { }); }); - describe.only('given that ReadMe project has bidirection sync set up', () => { + describe('given that ReadMe project has bidirection sync set up', () => { it('should should error if validation is not skipped', async () => { nock.cleanAll(); diff --git a/src/lib/syncPagePath.ts b/src/lib/syncPagePath.ts index 58e06a5ad..df3716f2d 100644 --- a/src/lib/syncPagePath.ts +++ b/src/lib/syncPagePath.ts @@ -221,7 +221,7 @@ export default async function syncPagePath(this: CommandsThatSyncMarkdown) { const biDiConnection = projectMetadata.data.git?.connection; if (biDiConnection && !skipValidation) { throw new Error( - `Bi-directional syncing is enabled for this project. Uploading these docs will overwrite what's currently synced from Git. To proceed with uploading via \`rdme\`, please use the \`--skip-validation\` flag.`, + "Bi-directional syncing is enabled for this project. Uploading these docs will overwrite what's currently synced from Git. To proceed with uploading via \`rdme\`, please use the \`--skip-validation\` flag.", ); } From 52f577678a6008f0c018fb1cc3515bce730c2f93 Mon Sep 17 00:00:00 2001 From: Emily Kuo <em.s.kuo@gmail.com> Date: Fri, 28 Feb 2025 11:44:56 -0800 Subject: [PATCH 5/7] chore: more lint --- src/lib/syncPagePath.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/syncPagePath.ts b/src/lib/syncPagePath.ts index df3716f2d..26f3eea54 100644 --- a/src/lib/syncPagePath.ts +++ b/src/lib/syncPagePath.ts @@ -221,7 +221,7 @@ export default async function syncPagePath(this: CommandsThatSyncMarkdown) { const biDiConnection = projectMetadata.data.git?.connection; if (biDiConnection && !skipValidation) { throw new Error( - "Bi-directional syncing is enabled for this project. Uploading these docs will overwrite what's currently synced from Git. To proceed with uploading via \`rdme\`, please use the \`--skip-validation\` flag.", + "Bi-directional syncing is enabled for this project. Uploading these docs will overwrite what's currently synced from Git. To proceed with uploading via `rdme`, please use the `--skip-validation` flag.", ); } From ce065c21eb771da0e9cadc917af53d6cb35a8c90 Mon Sep 17 00:00:00 2001 From: Emily Kuo <em.s.kuo@gmail.com> Date: Fri, 28 Feb 2025 11:47:29 -0800 Subject: [PATCH 6/7] chore: rename vars --- __tests__/commands/docs/upload.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/__tests__/commands/docs/upload.test.ts b/__tests__/commands/docs/upload.test.ts index 28ec64a74..7c711386c 100644 --- a/__tests__/commands/docs/upload.test.ts +++ b/__tests__/commands/docs/upload.test.ts @@ -297,7 +297,7 @@ describe('rdme docs upload', () => { }) .reply(201, {}); - const projectsMeMock2 = getAPIv2MockForGHA({ authorization }).get('/projects/me').reply(200, { + const projectsMeMock = getAPIv2MockForGHA({ authorization }).get('/projects/me').reply(200, { data: {}, }); @@ -308,18 +308,18 @@ describe('rdme docs upload', () => { headMock.done(); postMock.done(); - projectsMeMock2.done(); + projectsMeMock.done(); }); it('should error out if the file has validation errors', async () => { - const projectsMeMock2 = getAPIv2MockForGHA({ authorization }).get('/projects/me').reply(200, { + const projectsMeMock = getAPIv2MockForGHA({ authorization }).get('/projects/me').reply(200, { data: {}, }); const result = await run(['__tests__/__fixtures__/docs/mixed-docs/legacy-category.md', '--key', key]); expect(result).toMatchSnapshot(); expect(fs.writeFileSync).not.toHaveBeenCalled(); - projectsMeMock2.done(); + projectsMeMock.done(); }); }); From 8d4ea7ad856a1fec8b20a31fd8597e985508c4b7 Mon Sep 17 00:00:00 2001 From: Emily Kuo <em.s.kuo@gmail.com> Date: Fri, 28 Feb 2025 11:54:43 -0800 Subject: [PATCH 7/7] test: better mocks --- __tests__/commands/docs/upload.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/__tests__/commands/docs/upload.test.ts b/__tests__/commands/docs/upload.test.ts index 7c711386c..6bbf93262 100644 --- a/__tests__/commands/docs/upload.test.ts +++ b/__tests__/commands/docs/upload.test.ts @@ -533,7 +533,7 @@ describe('rdme docs upload', () => { const mock = getAPIv2Mock({ authorization }) .get('/projects/me') .reply(200, { - data: { git: { connection: 'some-repo' } }, + data: { git: { connection: { name: 'some-repo' } } }, }); const result = await run(['__tests__/__fixtures__/docs/new-docs/new-doc.md', '--key', key]); @@ -561,7 +561,7 @@ describe('rdme docs upload', () => { const projectsMeMock = getAPIv2Mock({ authorization }) .get('/projects/me') .reply(200, { - data: { git: { connection: 'some-repo' } }, + data: { git: { connection: { name: 'some-repo' } } }, }); const result = await run([