## What
This PR adds support for generating OAS in the docblock generator tool.
## How
As OAS are generated in a different manner/location than regular TSDocs, it requires a new type of generator within the tool. As such, the existing docblock generator now only handles files that aren't under the `packages/medusa/src/api` and `packages/medusa/src/api-v2` directories. The new generator handles files under these directories. However, it only considers a node to be an API route if it's a function having two parameters of types `MedusaRequest` and `MedusaResponse` respectively. So, only new API Routes are considered.
The new generator runs the same way as the existing docblock generator with the same method. The generators will detect whether they can run on the file or not and the docblocks/oas are generated based on that. I've also added a `--type` option to the CLI commands of the docblock generator tool to further filter and choose which generator to use.
When the OAS generator finds an API route, it will generate its OAS under the `docs-util/oas-output/operations` directory in a TypeScript file. I chose to generate in TS files rather than YAML files to maintain the functionality of `medusa-oas` without major changes.
Schemas detected in the OAS operation, such as the request and response schemas, are generated as OAS schemas under the `docs-util/oas-output/schemas` directory and referenced in operations and other resources.
The OAS generator also handles updating OAS. When you run the same command on a file/directory and an API route already has OAS associated with it, its information and associated schemas are updated instead of generating new schemas/operations. However, summaries and descriptions aren't updated unless they're not available or their values are the default value SUMMARY.
## API Route Handling
### Request and Response Types
The tool extracts the type of request/response schemas from the type arguments passed to the `MedusaRequest` and `MedusaResponse` respectively. For example:
```ts
export const POST = async (
req: MedusaRequest<{
id: string
}>,
res: MedusaResponse<ResponseType>
) => {
// ...
}
```
If these types aren't provided, the request/response is considered empty.
### Path Parameters
Path parameters are extracted from the file's path name. For example, for `packages/medusa/src/api-v2/admin/campaigns/[id]/route.ts` the `id` path parameter is extracted.
### Query Parameters
The tool extracts the query parameters of an API route based on the type of `request.validatedQuery`. Once we narrow down how we're typing query parameters, we can revisit this implementation.
## Changes to Medusa Oas CLI
I added a `--v2` option to the Medusa OAS CLI to support loading OAS from `docs-util/oas-output` directory rather than the `medusa` package. This will output the OAS in `www/apps/api-reference/specs`, wiping out old OAS. This is only helpful for testing purposes to check how the new OAS looks like in the API reference. It also allows us to slowly start adapting the new OAS.
## Other Notes and Changes
- I've added a GitHub action that creates a PR for generated OAS when Version Packages is merged (similar to regular TSDocs). However, this will only generate the OAS in the `docs-util/oas-output` directory and will not affect the existing OAS in the API reference. Once we're ready to include it those OAS, we can talk about next steps.
- I've moved the base YAML from the `medusa` package to the `docs-util/oas-output/base` directory and changed the `medusa-oas` tool to load them from there.
- I added a `clean:oas` command to the docblock generator CLI tool that removes unused OAS operations, schemas, and tags from `docs-util/oas-output`. The tool also supports updating OAS operations and their associated schemas. However, I didn't add a specific mechanism to update schemas on their own as that's a bit tricky and would require the help of typedoc. I believe with the process of running the tool on the `api-v2` directory whenever there's a new release should be enough to update associated schemas, but if we find that not enough, we can revisit updating schemas individually.
- Because of the `clean:oas` command which makes changes to tags (removing the existing ones, more details on this one later), I've added new base YAML under `docs-util/oas-output/base-v2`. This is used by the tool when generating/cleaning OAS, and the Medusa OAS CLI when the `--v2` option is used.
## Testing
### Prerequisites
To test with request/response types, I recommend minimally modifying `packages/medusa/src/types/routing.ts` to allow type arguments of `MedusaRequest` and `MedusaResponse`:
```ts
import type { NextFunction, Request, Response } from "express"
import type { Customer, User } from "../models"
import type { MedusaContainer } from "./global"
export interface MedusaRequest<T = unknown> extends Request {
user?: (User | Customer) & { customer_id?: string; userId?: string }
scope: MedusaContainer
}
export type MedusaResponse<T = unknown> = Response
export type MedusaNextFunction = NextFunction
export type MedusaRequestHandler = (
req: MedusaRequest,
res: MedusaResponse,
next: MedusaNextFunction
) => Promise<void> | void
```
You can then add type arguments to the routes in `packages/medusa/src/api-v2/admin/campaigns/[id]/route.ts`. For example:
```ts
import {
deleteCampaignsWorkflow,
updateCampaignsWorkflow,
} from "@medusajs/core-flows"
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
import { CampaignDTO, IPromotionModuleService } from "@medusajs/types"
import { MedusaRequest, MedusaResponse } from "../../../../types/routing"
interface ResponseType {
campaign: CampaignDTO
}
export const GET = async (
req: MedusaRequest,
res: MedusaResponse<ResponseType>
) => {
const promotionModuleService: IPromotionModuleService = req.scope.resolve(
ModuleRegistrationName.PROMOTION
)
const campaign = await promotionModuleService.retrieveCampaign(
req.params.id,
{
select: req.retrieveConfig.select,
relations: req.retrieveConfig.relations,
}
)
res.status(200).json({ campaign })
}
export const POST = async (
req: MedusaRequest<{
id: string
}>,
res: MedusaResponse<ResponseType>
) => {
const updateCampaigns = updateCampaignsWorkflow(req.scope)
const campaignsData = [
{
id: req.params.id,
...(req.validatedBody || {}),
},
]
const { result, errors } = await updateCampaigns.run({
input: { campaignsData },
throwOnError: false,
})
if (Array.isArray(errors) && errors[0]) {
throw errors[0].error
}
res.status(200).json({ campaign: result[0] })
}
export const DELETE = async (
req: MedusaRequest,
res: MedusaResponse<{
id: string
object: string
deleted: boolean
}>
) => {
const id = req.params.id
const manager = req.scope.resolve("manager")
const deleteCampaigns = deleteCampaignsWorkflow(req.scope)
const { errors } = await deleteCampaigns.run({
input: { ids: [id] },
context: { manager },
throwOnError: false,
})
if (Array.isArray(errors) && errors[0]) {
throw errors[0].error
}
res.status(200).json({
id,
object: "campaign",
deleted: true,
})
}
```
### Generate OAS
- Install dependencies in the `docs-util` directory
- Run the following command in the `docs-util/packages/docblock-generator` directory:
```bash
yarn dev run "../../../packages/medusa/src/api-v2/admin/campaigns/[id]/route.ts"
```
This will generate the OAS operation and schemas and necessary and update the base YAML to include the new tags.
### Generate OAS with Examples
By default, the tool will only generate cURL examples for OAS operations. To generate templated JS Client and (placeholder) Medusa React examples, add the `--generate-examples` option to the command:
```bash
yarn dev run "../../../packages/medusa/src/api-v2/admin/campaigns/[id]/route.ts" --generate-examples
```
> Note: the command will update the existing OAS you generated in the previous test.
### Testing Updates
To test updating OAS, you can try updating request/response types, then running the command, and the associated OAS/schemas will be updated.
### Clean OAS
The `clean:oas` command will remove any unused operation, tags, or schemas. To test it out you can try:
- Remove an API Route => this removes its associated operation and schemas (if not referenced anywhere else).
- Remove all references to a schema => this removes the schema.
- Remove all operations in `docs-util/oas-output/operations` associated with a tag => this removes the tag from the base YAML.
```bash
yarn dev clean:oas
```
> Note: when running this command, existing tags in the base YAML (such as Products) will be removed since there are no operations using it. As it's running on the base YAML under `base-v2`, this doesn't affect base YAML used for the API reference.
### Medusa Oas CLI
- Install and build dependencies in the root of the monorepo
- Run the following command to generate reference OAS for v2 API Routes (must have generated OAS previously using the docblock generator tool):
```bash
yarn openapi:generate --v2
```
- This wipes out existing OAS in `www/apps/api-reference/specs` and replaces them with the new ones. At this point, you can view the new API routes in the API reference by running the `yarn dev` command in `www/apps/api-reference` (although not necessary for testing here).
- Run the command again without the `--v2` option:
```bash
yarn openapi:generate
```
The specs in `www/apps/api-reference/specs` are reverted back to the old routes.
medusa-oas-cli - 0.1.0 - experimental
A command-line tool for all OpenAPI Specifications (OAS) related tooling.
Install
yarn add --dev @medusajs/medusa-oas-cli
Install in the global namespace is not yet supported.
npm install -g @medusajs/medusa-oas-cli
Configuration / First time setup
N/A
How to use
yarn medusa-oas <command>
Command - oas
This command will scan the @medusajs/medusa package in order to extract JSDoc OAS into a json file.
The command will output one of three the files admin.oas.json, store.oas.json or combined.oas.json in the same
directory that the command was run.
Invalid OAS with throw an error and will prevent the files from being outputted.
--type <string>
Specify which API OAS to create. Accepts admin, store, combined.
The combined option will merge both the admin and the store APIs into a single OAS file.
yarn medusa-oas oas --type admin
--out-dir <path>
Specify in which directory should the files be outputted. It accepts a relative or absolute path.
If the directory doesn't exist, it will be created. Defaults to ./.
yarm medusa-oas oas --out-dir
--paths <paths...>
Allows passing additional directory paths to crawl for JSDoc OAS and include in the generated OAS. It accepts multiple entries.
yarn medusa-oas oas --paths ~/medusa-server/src
--base <path>
Allows overwriting the content the API's base.yaml OAS that is fed to swagger-inline. Paths, tags, and components will be merged together. Other OAS properties will be overwritten.
yarn medusa-oas oas --base ~/medusa-server/oas/custom.oas.base.yaml
--dry-run
Will package the OAS but will not output file. Useful for validating OAS.
yarn medusa-oas oas --dry-run
--force
Ignore OAS errors and attempt to output generated OAS files.
yarn medusa-oas oas --force
Command - client
Will generate API client files from a given OAS file.
--src-file <path>
Specify the path to the OAS JSON file.
yarn medusa-oas client --src-file ./store.oas.json`
--name <name>
Namespace for the generated client. Usually admin or store.
yarn medusa-oas client --name admin`
--out-dir <path>
Specify in which directory should the files be outputted. Accepts relative and absolute path.
If the directory doesn't exist, it will be created. Defaults to ./.
yarn medusa-oas client --out-dir ./client`
--type <type>
Client component types to generate. Accepts all, types, client, hooks.
Defaults to all.
yarn medusa-oas client --type types`
--types-packages <name>
Replace relative import statements by types package name. Mandatory when using --type client or --type hooks.
yarn medusa-oas client --types-packages @medusajs/client-types`
--client-packages <name>
Replace relative import statements by client package name. Mandatory when using --type hooks.
yarn medusa-oas client --client-packages @medusajs/medusa-js`
--clean
Delete destination directory content before generating client.
yarn medusa-oas client --clean
Command - docs
Will sanitize OAS for use with Redocly's API documentation viewer.
--src-file <path>
Specify the path to the OAS JSON file.
yarm medusa-oas docs --src-file ./store.oas.json
--out-dir <path>
Specify in which directory should the files be outputted. Accepts relative and absolute path.
If the directory doesn't exist, it will be created. Defaults to ./.
yarn medusa-oas docs --src-file ./store.oas.json --out-dir ./docs`
--config <path>
Specify the path to a Redocly config file.
yarn medusa-oas --src-file ./store.oas.json --config ./redocly-config.yaml
--dry-run
Will sanitize the OAS but will not output file. Useful for troubleshooting circular reference issues.
yarn medusa-oas docs --src-file ./store.oas.json --dry-run
--clean
Delete destination directory content before generating client.
yarn medusa-oas docs --src-file ./store.oas.json --clean
--split
Creates a multi-file structure output. Uses redocly split internally.
yarn medusa-oas docs --src-file ./store.oas.json --split
--preview
Generate a preview of the API documentation in a browser. Does not output files. Uses redocly preview-docs internally.
yarn medusa-oas docs --src-file ../../../www/apps/api-reference/specs/store.oas.json --preview
--html
Generate a zero-dependency static HTML file. Uses redocly build-docs internally.
yarn medusa-oas docs --src-file ./store.oas.json --html