From de7ebd9d74c6602a2d73df8979598439df683aab Mon Sep 17 00:00:00 2001 From: matthewrgourd Date: Fri, 17 Apr 2026 07:09:27 +0100 Subject: [PATCH] feat(platzi): add Platzi docset and Scalar API playground with full navbar/mobile/config integration --- docs/devdocify/reference/index.md | 5 +- .../reference/navigation-contract.md | 1 + docs/devdocify/tutorials/index.md | 2 + docs/platzi/getting-started/authentication.md | 36 ++ docs/platzi/getting-started/error-handling.md | 30 ++ docs/platzi/getting-started/index.md | 33 ++ docs/platzi/getting-started/playground.md | 32 ++ docs/platzi/getting-started/quickstart.md | 45 +++ docs/platzi/index.md | 12 + docs/platzi/locations/index.md | 17 + docs/platzi/navigation.json | 37 ++ docs/platzi/products/index.md | 15 + docs/platzi/products/list.md | 21 + docs/platzi/users/check-email.md | 23 ++ docs/platzi/users/index.md | 15 + docs/platzi/variables.json | 3 + docusaurus.config.ts | 22 ++ scripts/docset.config.ts | 4 + scripts/generate-llms-txt.ts | 3 +- sidebarsPlatzi.ts | 8 + src/components/ApiReferenceClient.tsx | 68 +++- src/pages/index.tsx | 8 +- src/pages/platzi/api-playground.tsx | 23 ++ src/theme/Navbar/Content/index.tsx | 4 +- .../MobileSidebar/PrimaryMenu/index.tsx | 38 +- static/openapi/platzi-playground.json | 369 ++++++++++++++++++ 26 files changed, 865 insertions(+), 9 deletions(-) create mode 100644 docs/platzi/getting-started/authentication.md create mode 100644 docs/platzi/getting-started/error-handling.md create mode 100644 docs/platzi/getting-started/index.md create mode 100644 docs/platzi/getting-started/playground.md create mode 100644 docs/platzi/getting-started/quickstart.md create mode 100644 docs/platzi/index.md create mode 100644 docs/platzi/locations/index.md create mode 100644 docs/platzi/navigation.json create mode 100644 docs/platzi/products/index.md create mode 100644 docs/platzi/products/list.md create mode 100644 docs/platzi/users/check-email.md create mode 100644 docs/platzi/users/index.md create mode 100644 docs/platzi/variables.json create mode 100644 sidebarsPlatzi.ts create mode 100644 src/pages/platzi/api-playground.tsx create mode 100644 static/openapi/platzi-playground.json diff --git a/docs/devdocify/reference/index.md b/docs/devdocify/reference/index.md index 9991e3b..b7b7112 100644 --- a/docs/devdocify/reference/index.md +++ b/docs/devdocify/reference/index.md @@ -30,13 +30,14 @@ Use reference pages when you need exact facts, supported options, and stable tec

/docs first-party technical product docs (Diataxis)

/tfl demo TfL product docs

/petstore demo Petstore product docs

-

/tfl/api-playground and /petstore/api-playground interactive demo routes

+

/platzi demo Platzi Fake Store docs

+

/tfl/api-playground, /petstore/api-playground, and /platzi/api-playground interactive demo routes

## Configuration entry points - Site config: `docusaurus.config.ts` -- Sidebars: `sidebarsDevDocify.ts`, `sidebarsTfl.ts`, `sidebarsPetstore.ts` +- Sidebars: `sidebarsDevDocify.ts`, `sidebarsTfl.ts`, `sidebarsPetstore.ts`, `sidebarsPlatzi.ts` - Navigation contract loader: `navigation-contract.ts` - Navigation definitions: `docs/*/navigation.json` - Workflows: `.github/workflows/ci.yml`, `.github/workflows/deploy.yml`, `.github/workflows/preview.yml` diff --git a/docs/devdocify/reference/navigation-contract.md b/docs/devdocify/reference/navigation-contract.md index b5e6c0e..1dc927f 100644 --- a/docs/devdocify/reference/navigation-contract.md +++ b/docs/devdocify/reference/navigation-contract.md @@ -11,6 +11,7 @@ Navigation for each docset is defined in one file: - `docs/devdocify/navigation.json` - `docs/tfl/navigation.json` - `docs/petstore/navigation.json` +- `docs/platzi/navigation.json` These files are loaded and validated by `navigation-contract.ts` and then consumed by each sidebar entrypoint. diff --git a/docs/devdocify/tutorials/index.md b/docs/devdocify/tutorials/index.md index 4dc8c59..da8b4f6 100644 --- a/docs/devdocify/tutorials/index.md +++ b/docs/devdocify/tutorials/index.md @@ -32,5 +32,7 @@ After completing the quick start, explore the demo docsets to see multi-docset n - [TfL getting started](/tfl/getting-started) - [Petstore getting started](/petstore/getting-started) +- [Platzi getting started](/platzi/getting-started) - [TfL API playground](/tfl/api-playground) - [Petstore API playground](/petstore/api-playground) +- [Platzi API playground](/platzi/api-playground) diff --git a/docs/platzi/getting-started/authentication.md b/docs/platzi/getting-started/authentication.md new file mode 100644 index 0000000..32275fa --- /dev/null +++ b/docs/platzi/getting-started/authentication.md @@ -0,0 +1,36 @@ +--- +title: Authentication +sidebar_position: 3 +description: JWT login, refresh, and profile retrieval for Platzi demo endpoints. +--- + +# Authentication + +The Platzi demo API uses JWT for protected endpoints. + +## Login + +```bash +curl -X POST "https://api.escuelajs.co/api/v1/auth/login" \ + -H "content-type: application/json" \ + -d '{"email":"john@mail.com","password":"changeme"}' +``` + +Response includes `access_token` and `refresh_token`. + +## Profile (requires bearer token) + +```bash +curl "https://api.escuelajs.co/api/v1/auth/profile" \ + -H "authorization: Bearer " +``` + +## Refresh token + +```bash +curl -X POST "https://api.escuelajs.co/api/v1/auth/refresh-token" \ + -H "content-type: application/json" \ + -d '{"refreshToken":""}' +``` + +Use the [API playground](/platzi/api-playground) to run these flows interactively. diff --git a/docs/platzi/getting-started/error-handling.md b/docs/platzi/getting-started/error-handling.md new file mode 100644 index 0000000..5a9fda4 --- /dev/null +++ b/docs/platzi/getting-started/error-handling.md @@ -0,0 +1,30 @@ +--- +title: Error handling +sidebar_position: 4 +description: Common response codes for Platzi playground operations. +--- + +# Error handling + +Common response patterns in this demo API: + +- `200`: Request succeeded. +- `400`: Bad request (missing/invalid payload or parameters). +- `401`: Missing or invalid bearer token for protected endpoints. +- `404`: Resource not found. +- `429`: Rate limiting may be applied. +- `5xx`: Upstream server error. + +## Expected example: profile without token + +```bash +curl -i "https://api.escuelajs.co/api/v1/auth/profile" +``` + +Expected status: `401`. + +## Debug checklist + +- Confirm request path and method match the OpenAPI operation. +- Confirm `content-type: application/json` for POST payloads. +- Confirm bearer token is present for `/auth/profile`. diff --git a/docs/platzi/getting-started/index.md b/docs/platzi/getting-started/index.md new file mode 100644 index 0000000..05efb9e --- /dev/null +++ b/docs/platzi/getting-started/index.md @@ -0,0 +1,33 @@ +--- +slug: /getting-started +title: Platzi Fake Store API +sidebar_position: 1 +description: Start here for the Platzi demo docset and curated Scalar playground operations. +--- + +# Platzi Fake Store API + +This docset demonstrates the public [Platzi Fake Store API](https://fakeapi.platzi.com/) in the same style as the TfL and Petstore demos. + +## Base URL + +```text +https://api.escuelajs.co/api/v1 +``` + +## What this playground includes + +- Product listing with pagination +- Categories listing +- Users listing and email availability checks +- JWT login and profile retrieval +- Locations endpoint for map-style demos + +Use the [API playground](/platzi/api-playground) for interactive requests in-browser. + +## Start with these pages + +- [Quickstart](/platzi/getting-started/quickstart) +- [Authentication](/platzi/getting-started/authentication) +- [Error handling](/platzi/getting-started/error-handling) +- [Playground guide](/platzi/getting-started/playground) diff --git a/docs/platzi/getting-started/playground.md b/docs/platzi/getting-started/playground.md new file mode 100644 index 0000000..52eb3d9 --- /dev/null +++ b/docs/platzi/getting-started/playground.md @@ -0,0 +1,32 @@ +--- +title: Playground guide +sidebar_position: 5 +description: Scope and intent of the Platzi Scalar playground. +--- + +# Playground guide + +The Platzi playground is a curated subset of the live API for predictable demos. + +## Included operations + +- `GET /products` +- `GET /categories` +- `GET /users` +- `POST /users/is-available` +- `POST /auth/login` +- `POST /auth/refresh-token` +- `GET /auth/profile` +- `GET /locations` + +## Scope notes + +- Data is shared and mutable on the public demo backend. +- IDs and content can change between requests. +- Use list/search endpoints for the most stable demos. + +## Links + +- Playground: [/platzi/api-playground](/platzi/api-playground) +- Download spec: [/openapi/platzi-playground.json](/openapi/platzi-playground.json) +- Intro docs: [/platzi/getting-started](/platzi/getting-started) diff --git a/docs/platzi/getting-started/quickstart.md b/docs/platzi/getting-started/quickstart.md new file mode 100644 index 0000000..64a9142 --- /dev/null +++ b/docs/platzi/getting-started/quickstart.md @@ -0,0 +1,45 @@ +--- +title: Quickstart +sidebar_position: 2 +description: Run the core Platzi playground operations with curl. +--- + +# Quickstart + +Run the same operations exposed in the [API playground](/platzi/api-playground). + +## 1) List products + +```bash +curl "https://api.escuelajs.co/api/v1/products?offset=0&limit=5" +``` + +## 2) List categories + +```bash +curl "https://api.escuelajs.co/api/v1/categories" +``` + +## 3) Check if an email is available + +```bash +curl -X POST "https://api.escuelajs.co/api/v1/users/is-available" \ + -H "content-type: application/json" \ + -d '{"email":"john@mail.com"}' +``` + +## 4) Login to get tokens + +```bash +curl -X POST "https://api.escuelajs.co/api/v1/auth/login" \ + -H "content-type: application/json" \ + -d '{"email":"john@mail.com","password":"changeme"}' +``` + +## 5) Query locations + +```bash +curl "https://api.escuelajs.co/api/v1/locations?size=3" +``` + +Next: [Authentication](/platzi/getting-started/authentication) diff --git a/docs/platzi/index.md b/docs/platzi/index.md new file mode 100644 index 0000000..91bc9b8 --- /dev/null +++ b/docs/platzi/index.md @@ -0,0 +1,12 @@ +--- +slug: / +title: Platzi demo docs +description: Landing page for the Platzi Fake Store demo docset. +--- + +# Platzi demo docs + +Use this docset to explore a curated set of real, public Fake Store API endpoints. + +- Start here: [Getting started](/platzi/getting-started) +- Run live requests: [API playground](/platzi/api-playground) diff --git a/docs/platzi/locations/index.md b/docs/platzi/locations/index.md new file mode 100644 index 0000000..17bf36d --- /dev/null +++ b/docs/platzi/locations/index.md @@ -0,0 +1,17 @@ +--- +title: Locations +sidebar_position: 1 +description: Geospatial demo endpoint for store-like location data. +--- + +# Locations + +Use `GET /locations` for geospatial examples in maps or "stores near me" style demos. + +## Example + +```bash +curl "https://api.escuelajs.co/api/v1/locations?size=5" +``` + +You can also provide `origin` and `radius` query parameters in the [API playground](/platzi/api-playground). diff --git a/docs/platzi/navigation.json b/docs/platzi/navigation.json new file mode 100644 index 0000000..fbac716 --- /dev/null +++ b/docs/platzi/navigation.json @@ -0,0 +1,37 @@ +[ + { + "type": "category", + "label": "Getting started", + "items": [ + {"type": "doc", "id": "getting-started/index", "label": "Overview"}, + {"type": "doc", "id": "getting-started/quickstart", "label": "Quickstart"}, + {"type": "doc", "id": "getting-started/authentication", "label": "Authentication"}, + {"type": "doc", "id": "getting-started/error-handling", "label": "Error handling"}, + {"type": "doc", "id": "getting-started/playground", "label": "Playground guide"} + ] + }, + { + "type": "category", + "label": "Products", + "items": [ + {"type": "doc", "id": "products/index", "label": "Overview"}, + {"type": "doc", "id": "products/list", "label": "List and paginate"} + ] + }, + { + "type": "category", + "label": "Users", + "items": [ + {"type": "doc", "id": "users/index", "label": "Overview"}, + {"type": "doc", "id": "users/check-email", "label": "Check email"} + ] + }, + { + "type": "category", + "label": "Locations", + "items": [ + {"type": "doc", "id": "locations/index", "label": "Overview"} + ] + }, + {"type": "link", "label": "API playground", "href": "/platzi/api-playground"} +] diff --git a/docs/platzi/products/index.md b/docs/platzi/products/index.md new file mode 100644 index 0000000..d4b9979 --- /dev/null +++ b/docs/platzi/products/index.md @@ -0,0 +1,15 @@ +--- +title: Products +sidebar_position: 1 +description: Products surface used in the Platzi playground. +--- + +# Products + +The demo playground focuses on list-style product retrieval. + +## Included operation + +- [List and paginate](/platzi/products/list): `GET /products` + +Use the [API playground](/platzi/api-playground) to execute requests and inspect live responses. diff --git a/docs/platzi/products/list.md b/docs/platzi/products/list.md new file mode 100644 index 0000000..fb2de2b --- /dev/null +++ b/docs/platzi/products/list.md @@ -0,0 +1,21 @@ +--- +title: List and paginate +sidebar_position: 2 +description: Retrieve product lists with offset and limit. +--- + +# List and paginate products + +Use `GET /products` with `offset` and `limit`. + +## Example + +```bash +curl "https://api.escuelajs.co/api/v1/products?offset=0&limit=10" +``` + +## Notes + +- `offset` controls how many records to skip. +- `limit` controls max records returned. +- Data can change because this is a shared public demo API. diff --git a/docs/platzi/users/check-email.md b/docs/platzi/users/check-email.md new file mode 100644 index 0000000..d9761c5 --- /dev/null +++ b/docs/platzi/users/check-email.md @@ -0,0 +1,23 @@ +--- +title: Check email +sidebar_position: 2 +description: Validate whether an email can be used for a new account. +--- + +# Check email availability + +Use `POST /users/is-available` with an `email` payload. + +## Example + +```bash +curl -X POST "https://api.escuelajs.co/api/v1/users/is-available" \ + -H "content-type: application/json" \ + -d '{"email":"john@mail.com"}' +``` + +## Response shape + +```json +{"isAvailable": false} +``` diff --git a/docs/platzi/users/index.md b/docs/platzi/users/index.md new file mode 100644 index 0000000..6c39513 --- /dev/null +++ b/docs/platzi/users/index.md @@ -0,0 +1,15 @@ +--- +title: Users +sidebar_position: 1 +description: User endpoints used in the Platzi playground. +--- + +# Users + +This docset includes user listing and email availability checks. + +## Included pages + +- [Check email](/platzi/users/check-email): `POST /users/is-available` + +Authentication flows are covered in [Getting started > Authentication](/platzi/getting-started/authentication). diff --git a/docs/platzi/variables.json b/docs/platzi/variables.json new file mode 100644 index 0000000..c99434c --- /dev/null +++ b/docs/platzi/variables.json @@ -0,0 +1,3 @@ +{ + "api_base_url": "https://api.escuelajs.co/api/v1" +} diff --git a/docusaurus.config.ts b/docusaurus.config.ts index 1fabc6d..8daccd1 100644 --- a/docusaurus.config.ts +++ b/docusaurus.config.ts @@ -78,6 +78,15 @@ const config: Config = { sidebarPath: './sidebarsPetstore.ts', }, ], + [ + '@docusaurus/plugin-content-docs', + { + id: 'platzi', + path: 'docs/platzi', + routeBasePath: 'platzi', + sidebarPath: './sidebarsPlatzi.ts', + }, + ], [ '@docusaurus/plugin-client-redirects', { @@ -126,6 +135,12 @@ const config: Config = { position: 'left', activeBasePath: '/petstore', }, + { + to: '/platzi/getting-started', + label: 'Platzi', + position: 'left', + activeBasePath: '/platzi', + }, ], }, footer: { @@ -145,6 +160,13 @@ const config: Config = { {label: 'API playground', to: '/tfl/api-playground'}, ], }, + { + title: 'Platzi', + items: [ + {label: 'Getting started', to: '/platzi/getting-started'}, + {label: 'API playground', to: '/platzi/api-playground'}, + ], + }, { title: 'Resources', items: [ diff --git a/scripts/docset.config.ts b/scripts/docset.config.ts index 5fcb888..e239d10 100644 --- a/scripts/docset.config.ts +++ b/scripts/docset.config.ts @@ -184,6 +184,10 @@ export const DOCSET_REGISTRY: DocsetConfig[] = [ id: 'petstore', name: 'Petstore', }, + { + id: 'platzi', + name: 'Platzi Fake Store', + }, ]; // --------------------------------------------------------------------------- diff --git a/scripts/generate-llms-txt.ts b/scripts/generate-llms-txt.ts index 804c196..01e49ad 100644 --- a/scripts/generate-llms-txt.ts +++ b/scripts/generate-llms-txt.ts @@ -136,7 +136,7 @@ function buildLlmsTxt( const lines: string[] = [ '# DevDocify', '', - '> Developer documentation platform. Covers the Transport for London (TfL) Unified API and the Swagger Petstore API, with guides, reference, and interactive playgrounds.', + '> Developer documentation platform. Covers the Transport for London (TfL) Unified API, Swagger Petstore API, and Platzi Fake Store API, with guides, reference, and interactive playgrounds.', '', 'DevDocify is a documentation platform that demonstrates multi-docset API documentation, versioning, and interactive API playgrounds. The following links are the canonical sources for all content.', '', @@ -150,6 +150,7 @@ function buildLlmsTxt( devdocify: 'DevDocify docs', tfl: 'Transport for London (TfL) API', petstore: 'Swagger Petstore API', + platzi: 'Platzi Fake Store API', }[docset.id] ?? docset.id; lines.push(`## ${docsetName}`, ''); diff --git a/sidebarsPlatzi.ts b/sidebarsPlatzi.ts new file mode 100644 index 0000000..6371d07 --- /dev/null +++ b/sidebarsPlatzi.ts @@ -0,0 +1,8 @@ +import {loadNavigationSidebar} from './navigation-contract'; + +const sidebars = loadNavigationSidebar({ + filePath: 'docs/platzi/navigation.json', + sidebarId: 'platziSidebar', +}); + +export default sidebars; diff --git a/src/components/ApiReferenceClient.tsx b/src/components/ApiReferenceClient.tsx index 9f6ec89..65aadd4 100644 --- a/src/components/ApiReferenceClient.tsx +++ b/src/components/ApiReferenceClient.tsx @@ -4,9 +4,11 @@ import {ApiReferenceReact} from '@scalar/api-reference-react'; import '@scalar/api-reference-react/style.css'; import '@scalar/docusaurus/dist/theme.css'; +type ApiProfile = 'petstore' | 'tfl' | 'platzi'; + type ApiReferenceClientProps = { specUrl: string; - profile: 'petstore' | 'tfl'; + profile: ApiProfile; downloadUrl: string; specLabel: string; }; @@ -26,6 +28,24 @@ function setParamDefault(parameter: JsonObject, value: any): void { } } +function setRequestBodyExample( + operation: JsonObject | undefined, + value: JsonObject, + contentType = 'application/json' +): void { + if (!operation || typeof operation !== 'object') { + return; + } + const mediaType = operation.requestBody?.content?.[contentType]; + if (!mediaType || typeof mediaType !== 'object') { + return; + } + mediaType.example = mediaType.example ?? value; + if (mediaType.schema && typeof mediaType.schema === 'object') { + mediaType.schema.example = mediaType.schema.example ?? value; + } +} + function applyPetstorePlaygroundDefaults(spec: JsonObject): JsonObject { const paths = spec.paths ?? {}; @@ -66,10 +86,54 @@ function applyTflPlaygroundDefaults(spec: JsonObject): JsonObject { return spec; } -function applyPlaygroundDefaults(spec: JsonObject, profile: 'petstore' | 'tfl'): JsonObject { +function applyPlatziPlaygroundDefaults(spec: JsonObject): JsonObject { + const paths = spec.paths ?? {}; + + const products = paths['/products']?.get?.parameters; + if (Array.isArray(products)) { + const offset = products.find((p: JsonObject) => p.name === 'offset'); + const limit = products.find((p: JsonObject) => p.name === 'limit'); + if (offset) setParamDefault(offset, 0); + if (limit) setParamDefault(limit, 10); + } + + const users = paths['/users']?.get?.parameters; + if (Array.isArray(users)) { + const offset = users.find((p: JsonObject) => p.name === 'offset'); + const limit = users.find((p: JsonObject) => p.name === 'limit'); + if (offset) setParamDefault(offset, 0); + if (limit) setParamDefault(limit, 10); + } + + const locations = paths['/locations']?.get?.parameters; + if (Array.isArray(locations)) { + const size = locations.find((p: JsonObject) => p.name === 'size'); + if (size) setParamDefault(size, 5); + } + + setRequestBodyExample(paths['/users/is-available']?.post, { + email: 'john@mail.com', + }); + + setRequestBodyExample(paths['/auth/login']?.post, { + email: 'john@mail.com', + password: 'changeme', + }); + + setRequestBodyExample(paths['/auth/refresh-token']?.post, { + refreshToken: '', + }); + + return spec; +} + +function applyPlaygroundDefaults(spec: JsonObject, profile: ApiProfile): JsonObject { if (profile === 'petstore') { return applyPetstorePlaygroundDefaults(spec); } + if (profile === 'platzi') { + return applyPlatziPlaygroundDefaults(spec); + } return applyTflPlaygroundDefaults(spec); } diff --git a/src/pages/index.tsx b/src/pages/index.tsx index b1efef4..c97d3fb 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -34,7 +34,7 @@ export default function Home(): React.JSX.Element {

Interactive API demos

-

TfL and Petstore playgrounds are live with explanatory context and defaults.

+

TfL, Petstore, and Platzi playgrounds are live with explanatory context and defaults.

Preview-first delivery

@@ -59,6 +59,10 @@ export default function Home(): React.JSX.Element { Petstore demo docs Sample API docset and playground routes + + Platzi demo docs + Retail-style fake store API docset and playground +
@@ -66,7 +70,7 @@ export default function Home(): React.JSX.Element {

Stub marketing routes

These pages are placeholders. Technical content and demos live under{' '} - /docs, /tfl, and /petstore. + /docs, /tfl, /petstore, and /platzi.

Customers diff --git a/src/pages/platzi/api-playground.tsx b/src/pages/platzi/api-playground.tsx new file mode 100644 index 0000000..e196784 --- /dev/null +++ b/src/pages/platzi/api-playground.tsx @@ -0,0 +1,23 @@ +import React from 'react'; +import Layout from '@theme/Layout'; +import Link from '@docusaurus/Link'; +import ApiReferenceClient from '@site/src/components/ApiReferenceClient'; + +export default function PlatziApiPlaygroundPage(): React.JSX.Element { + return ( + +
+

+ Curated demo playground against the Platzi Fake Store API. For scope and caveats, read the{' '} + Platzi playground guide. +

+
+ +
+ ); +} diff --git a/src/theme/Navbar/Content/index.tsx b/src/theme/Navbar/Content/index.tsx index e83c735..4c59040 100644 --- a/src/theme/Navbar/Content/index.tsx +++ b/src/theme/Navbar/Content/index.tsx @@ -81,7 +81,9 @@ export default function NavbarContent(): ReactNode { ? '/openapi/petstore-playground.json' : pathname === '/tfl/api-playground' ? '/openapi/tfl-playground.json' - : null; + : pathname === '/platzi/api-playground' + ? '/openapi/platzi-playground.json' + : null; const items = useNavbarItems(); const [leftItems, rightItems] = splitNavbarItems(items); diff --git a/src/theme/Navbar/MobileSidebar/PrimaryMenu/index.tsx b/src/theme/Navbar/MobileSidebar/PrimaryMenu/index.tsx index 9894fe4..ce5dccf 100644 --- a/src/theme/Navbar/MobileSidebar/PrimaryMenu/index.tsx +++ b/src/theme/Navbar/MobileSidebar/PrimaryMenu/index.tsx @@ -112,6 +112,40 @@ const sections: MenuSection[] = [ ], apiPlaygroundPath: '/petstore/api-playground', }, + { + heading: 'Platzi', + groups: [ + { + label: 'Getting started', + items: [ + {label: 'Overview', to: '/platzi/getting-started'}, + {label: 'Quickstart', to: '/platzi/getting-started/quickstart'}, + {label: 'Authentication', to: '/platzi/getting-started/authentication'}, + {label: 'Error handling', to: '/platzi/getting-started/error-handling'}, + {label: 'Playground guide', to: '/platzi/getting-started/playground'}, + ], + }, + { + label: 'Products', + items: [ + {label: 'Overview', to: '/platzi/products'}, + {label: 'List and paginate', to: '/platzi/products/list'}, + ], + }, + { + label: 'Users', + items: [ + {label: 'Overview', to: '/platzi/users'}, + {label: 'Check email', to: '/platzi/users/check-email'}, + ], + }, + { + label: 'Locations', + items: [{label: 'Overview', to: '/platzi/locations'}], + }, + ], + apiPlaygroundPath: '/platzi/api-playground', + }, ]; function normalizePath(route: string): string { @@ -135,7 +169,9 @@ export default function NavbarMobilePrimaryMenu(): ReactNode { ? '/openapi/petstore-playground.json' : pathname === '/tfl/api-playground' ? '/openapi/tfl-playground.json' - : null; + : pathname === '/platzi/api-playground' + ? '/openapi/platzi-playground.json' + : null; return (
    diff --git a/static/openapi/platzi-playground.json b/static/openapi/platzi-playground.json new file mode 100644 index 0000000..1551c80 --- /dev/null +++ b/static/openapi/platzi-playground.json @@ -0,0 +1,369 @@ +{ + "openapi": "3.0.3", + "info": { + "title": "Platzi Fake Store API — DevDocify playground", + "version": "playground-1.0.0", + "description": "Curated OpenAPI subset for the DevDocify Platzi playground. This file documents a stable demo-focused surface of the public API hosted at api.escuelajs.co." + }, + "servers": [ + { + "url": "https://api.escuelajs.co/api/v1", + "description": "Platzi Fake Store public API" + } + ], + "tags": [ + {"name": "Products", "description": "Product listing and pagination."}, + {"name": "Catalog", "description": "Category listing."}, + {"name": "Users", "description": "User listing and email availability checks."}, + {"name": "Auth", "description": "JWT login, refresh, and profile operations."}, + {"name": "Locations", "description": "Geospatial location examples."} + ], + "paths": { + "/products": { + "get": { + "tags": ["Products"], + "summary": "List products", + "description": "Returns products with optional offset pagination.", + "operationId": "platziListProducts", + "parameters": [ + { + "name": "offset", + "in": "query", + "required": false, + "schema": {"type": "integer", "minimum": 0, "default": 0}, + "example": 0 + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": {"type": "integer", "minimum": 1, "maximum": 100, "default": 10}, + "example": 10 + } + ], + "responses": { + "200": { + "description": "Success.", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": {"$ref": "#/components/schemas/Product"} + } + } + } + } + } + } + }, + "/categories": { + "get": { + "tags": ["Catalog"], + "summary": "List categories", + "operationId": "platziListCategories", + "responses": { + "200": { + "description": "Success.", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": {"$ref": "#/components/schemas/Category"} + } + } + } + } + } + } + }, + "/users": { + "get": { + "tags": ["Users"], + "summary": "List users", + "operationId": "platziListUsers", + "parameters": [ + { + "name": "offset", + "in": "query", + "required": false, + "schema": {"type": "integer", "minimum": 0, "default": 0}, + "example": 0 + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": {"type": "integer", "minimum": 1, "maximum": 100, "default": 10}, + "example": 10 + } + ], + "responses": { + "200": { + "description": "Success.", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": {"$ref": "#/components/schemas/User"} + } + } + } + } + } + } + }, + "/users/is-available": { + "post": { + "tags": ["Users"], + "summary": "Check email availability", + "operationId": "platziIsUserEmailAvailable", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": {"$ref": "#/components/schemas/EmailCheckRequest"}, + "example": {"email": "john@mail.com"} + } + } + }, + "responses": { + "200": { + "description": "Success.", + "content": { + "application/json": { + "schema": {"$ref": "#/components/schemas/EmailCheckResponse"}, + "example": {"isAvailable": false} + } + } + }, + "400": {"description": "Bad request."} + } + } + }, + "/auth/login": { + "post": { + "tags": ["Auth"], + "summary": "Login", + "description": "Returns an access token and refresh token.", + "operationId": "platziLogin", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": {"$ref": "#/components/schemas/LoginRequest"}, + "example": {"email": "john@mail.com", "password": "changeme"} + } + } + }, + "responses": { + "201": { + "description": "Login success.", + "content": { + "application/json": { + "schema": {"$ref": "#/components/schemas/AuthTokens"} + } + } + }, + "200": { + "description": "Login success (alternative status).", + "content": { + "application/json": { + "schema": {"$ref": "#/components/schemas/AuthTokens"} + } + } + }, + "401": {"description": "Invalid credentials."} + } + } + }, + "/auth/refresh-token": { + "post": { + "tags": ["Auth"], + "summary": "Refresh access token", + "operationId": "platziRefreshToken", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": {"$ref": "#/components/schemas/RefreshTokenRequest"}, + "example": {"refreshToken": ""} + } + } + }, + "responses": { + "201": { + "description": "Refresh success.", + "content": { + "application/json": { + "schema": {"$ref": "#/components/schemas/AuthTokens"} + } + } + }, + "200": { + "description": "Refresh success (alternative status).", + "content": { + "application/json": { + "schema": {"$ref": "#/components/schemas/AuthTokens"} + } + } + }, + "401": {"description": "Invalid or expired refresh token."} + } + } + }, + "/auth/profile": { + "get": { + "tags": ["Auth"], + "summary": "Get authenticated profile", + "operationId": "platziProfile", + "security": [{"bearerAuth": []}], + "responses": { + "200": { + "description": "Success.", + "content": { + "application/json": { + "schema": {"$ref": "#/components/schemas/User"} + } + } + }, + "401": {"description": "Missing or invalid bearer token."} + } + } + }, + "/locations": { + "get": { + "tags": ["Locations"], + "summary": "List locations", + "description": "Returns random or origin-filtered locations for map-style demos.", + "operationId": "platziListLocations", + "parameters": [ + { + "name": "size", + "in": "query", + "required": false, + "schema": {"type": "integer", "minimum": 1, "maximum": 50, "default": 5}, + "example": 5 + }, + { + "name": "origin", + "in": "query", + "required": false, + "description": "Latitude,longitude pair.", + "schema": {"type": "string"}, + "example": "6.2071641,-75.5720321" + }, + { + "name": "radius", + "in": "query", + "required": false, + "schema": {"type": "number", "format": "float"}, + "example": 10 + } + ], + "responses": { + "200": { + "description": "Success.", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": {"$ref": "#/components/schemas/Location"} + } + } + } + } + } + } + } + }, + "components": { + "securitySchemes": { + "bearerAuth": { + "type": "http", + "scheme": "bearer", + "bearerFormat": "JWT" + } + }, + "schemas": { + "Category": { + "type": "object", + "properties": { + "id": {"type": "integer"}, + "name": {"type": "string"}, + "slug": {"type": "string"}, + "image": {"type": "string", "format": "uri"} + } + }, + "Product": { + "type": "object", + "properties": { + "id": {"type": "integer"}, + "title": {"type": "string"}, + "slug": {"type": "string"}, + "price": {"type": "number"}, + "description": {"type": "string"}, + "category": {"$ref": "#/components/schemas/Category"}, + "images": { + "type": "array", + "items": {"type": "string", "format": "uri"} + } + } + }, + "User": { + "type": "object", + "properties": { + "id": {"type": "integer"}, + "email": {"type": "string", "format": "email"}, + "name": {"type": "string"}, + "role": {"type": "string"}, + "avatar": {"type": "string", "format": "uri"} + } + }, + "EmailCheckRequest": { + "type": "object", + "required": ["email"], + "properties": { + "email": {"type": "string", "format": "email"} + } + }, + "EmailCheckResponse": { + "type": "object", + "properties": { + "isAvailable": {"type": "boolean"} + } + }, + "LoginRequest": { + "type": "object", + "required": ["email", "password"], + "properties": { + "email": {"type": "string", "format": "email"}, + "password": {"type": "string"} + } + }, + "RefreshTokenRequest": { + "type": "object", + "required": ["refreshToken"], + "properties": { + "refreshToken": {"type": "string"} + } + }, + "AuthTokens": { + "type": "object", + "properties": { + "access_token": {"type": "string"}, + "refresh_token": {"type": "string"} + } + }, + "Location": { + "type": "object", + "properties": { + "id": {"type": "number"}, + "name": {"type": "string"}, + "description": {"type": "string"}, + "latitude": {"type": "number"}, + "longitude": {"type": "number"} + } + } + } + } +}