Skip to content

Commit

Permalink
Add OpenAI docs and relative paths for schema urls (#45)
Browse files Browse the repository at this point in the history
  • Loading branch information
G4brym authored Apr 14, 2023
2 parents 4e1fe31 + 5ceed2b commit 42cdc99
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 35 deletions.
141 changes: 114 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,40 +22,14 @@ There is a Tutorial Section [available here](https://github.com/cloudflare/itty-
- [x] Query parameters validator
- [x] Path parameters validator
- [x] Body request validator
- [x] Out of the box [OpenAI plugin support](#aiplugin)

## Installation

```
npm i @cloudflare/itty-router-openapi --save
```

## FAQ

Q. Is this package production ready?

A. Yes. This package was created during the [Cloudflare Radar 2.0](https://radar.cloudflare.com/) development and is
currently used by the Radar website to serve the web app and the public API.

---

Q. When will this package reach stable maturity?

A. This package is already heavily used in [Cloudflare Radar](https://radar.cloudflare.com/), and we are committed to
not introducing breaking changes to it.

## Options API

#### `OpenAPIRouter(options = {})`

| Name | Type(s) | Description | Examples |
| ------------- | --------------------------------- | ----------------------------------------------------------------------- | -------------------------------------------------------------------------- |
| `base` | `string` | prefixes all routes with this string | `Router({ base: '/api' })` |
| `routes` | `array of routes` | array of manual routes for preloading | [see documentation](https://github.com/kwhitley/itty-router#manual-routes) |
| `schema` | `object` | Object of the common OpenAPI customizations | [see documentation](#4-core-openapi-schema-customizations) |
| `docs_url` | `string` or `null` or `undefined` | Path for swagger docs, `null`: disabled, `undefined`: `/docs` | `/docs` |
| `redoc_url` | `string` or `null` or `undefined` | Path for redoc docs, `null`: disabled, `undefined`: `/redocs` | `/redocs` |
| `openapi_url` | `string` or `null` or `undefined` | Path for openapi schema, `null`: disabled, `undefined`: `/openapi.json` | `/openapi.json` |

## Basic Usage

Creating a new OpenAPI route is simple:
Expand Down Expand Up @@ -155,6 +129,38 @@ router.get('/todos/:id', ({ params }) => new Response(`Todo #${params.id}`))
Now, when running the application, go to `/docs`. You will see your endpoints listed with the query parameters parsed
and ready to be invoked.

## Options API

#### `OpenAPIRouter(options = {})`

| Name | Type(s) | Description | Examples |
| ------------- | --------------------------------- | ----------------------------------------------------------------------- | -------------------------------------------------------------------------- |
| `base` | `string` | prefixes all routes with this string | `Router({ base: '/api' })` |
| `routes` | `array of routes` | array of manual routes for preloading | [see documentation](https://github.com/kwhitley/itty-router#manual-routes) |
| `schema` | `object` | Object of the common OpenAPI customizations | [see documentation](#4-core-openapi-schema-customizations) |
| `docs_url` | `string` or `null` or `undefined` | Path for swagger docs, `null`: disabled, `undefined`: `/docs` | `/docs` |
| `redoc_url` | `string` or `null` or `undefined` | Path for redoc docs, `null`: disabled, `undefined`: `/redocs` | `/redocs` |
| `openapi_url` | `string` or `null` or `undefined` | Path for openapi schema, `null`: disabled, `undefined`: `/openapi.json` | `/openapi.json` |
| `aiPlugin` | `object` or `undefined` | Object that will be used to generate the `ai-plugin.json` schema | [see schema bellow](#aiplugin) |

#### `aiPlugin`

Example configurations are [available here](#openai-plugin-support)

| Name | Type(s) | Description | Examples |
| ----------------------- | ------------------------------------------ | ------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
| `schema_version` | `SchemaVersion` or `string` or `undefined` | Schema Version, if `undefined` this is filled with `V1` | `Router({ base: '/api' })` |
| `name_for_model` | `string` | Name for model | [see documentation](https://github.com/kwhitley/itty-router#manual-routes) |
| `name_for_human` | `string` | Name for Human | [see documentation](https://github.com/kwhitley/itty-router#manual-routes) |
| `description_for_model` | `string` | Description for model | [see documentation](https://github.com/kwhitley/itty-router#manual-routes) |
| `description_for_human` | `string` | Description for human | [see documentation](https://github.com/kwhitley/itty-router#manual-routes) |
| `logo_url` | `string` | Logo url | [see documentation](https://github.com/kwhitley/itty-router#manual-routes) |
| `contact_email` | `string` | Contact email | [see documentation](https://github.com/kwhitley/itty-router#manual-routes) |
| `legal_info_url` | `string` | Legal info url | [see documentation](https://github.com/kwhitley/itty-router#manual-routes) |
| `auth` | `object` or `undefined` | Object for Auth configuration, `undefined`: defaults to no Auth | [see documentation](#4-core-openapi-schema-customizations) |
| `api` | `object` or `undefined` | Object for Api configuration, `undefined`: defaults to openapi.json spec | [see documentation](#4-core-openapi-schema-customizations) |
| `is_dev` | `boolean` or `undefined` | Boolean to let chatgpt know it is in development mode, `undefined`: defaults to false | [see documentation](#4-core-openapi-schema-customizations) |

## Schema types

Schema types can be used in parameters, requestBody and responses.
Expand Down Expand Up @@ -407,6 +413,87 @@ export class ToDoCreate extends OpenAPIRoute {
router.post('/todos', ToDoCreate)
```

## OpenAI plugin support

In the `aiPlugin` field you can define all fields from
the [plugin manifest](https://platform.openai.com/docs/plugins/getting-started)

This library include default values for the following plugin manifest fields:

```ts
import {AuthType, SchemaVersion, APIType} from '@cloudflare/itty-router-openapi'

const default = {
schema_version: SchemaVersion.V1,
auth: {
type: AuthType.NONE,
},
api: {
type: APIType.OPENAPI,
has_user_authentication: false,
url: '/openai.json', // The path to the schema will be the same as the `openapi_url` field in the router configuration
}
}
```

Taking into consideration the default values included, we can build a very minimal configuration, assuming the api
doesn't require Auth:

```ts
import { OpenAPIRouter } from '@cloudflare/itty-router-openapi'

const router = OpenAPIRouter({
aiPlugin: {
name_for_human: 'Cloudflare Radar API',
name_for_model: 'cloudflare_radar',
description_for_human: "Get data insights from Cloudflare's point of view.",
description_for_model:
"Plugin for retrieving the data based on Cloudflare Radar's data. Use it whenever a user asks something that might be related to Internet usage, eg. outages, Internet traffic, or Cloudflare Radar's data in particular.",
contact_email: '[email protected]',
legal_info_url: 'https://www.cloudflare.com/website-terms/',
logo_url: 'https://cdn-icons-png.flaticon.com/512/5969/5969044.png',
},
})

// ...
```

Now when calling the `/.well-known/ai-plugin.json` path in our worker, we will see a full ai-plugin schema, that
automatically points to our generated OpenAPI 3 Schema.

### Serving the OpenAI schema from multiple domains/hosts

When serving from multiple domains, the OpenAPI schema should automatically update to the domain being served.

Thats why we made the `aiPlugin.api.url` to allow relative paths, and when doing so, the itty-router-openapi will
automatically fill the domain.

```ts
import { OpenAPIRouter } from '@cloudflare/itty-router-openapi'

const router = OpenAPIRouter({
aiPlugin: {
// other fields
api: {
url: '/my-schema-for-openai.json',
},
},
})

// ...
```

Then when calling for `https://example.com/.well-known/ai-plugin.json` the response will have the absolut url

```json
{
...
api: {
url: 'https://example.com/my-schema-for-openai.json'
}
}
```

## Advanced Usage

### 1. Cloudflare ES6 Module Worker
Expand Down
19 changes: 14 additions & 5 deletions src/openapi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,18 +150,27 @@ export function OpenAPIRouter(options?: RouterOptions): OpenAPIRouterSchema {

if (options?.aiPlugin && options?.openapi_url !== null) {
router.get('/.well-known/ai-plugin.json', (request: IRequest) => {
const schemaApi = {
type: APIType.OPENAPI,
has_user_authentication: false,
url: options?.openapi_url || '/openapi.json',
...options?.aiPlugin?.api,
}

// Check if schema path is relative
if (!schemaApi.url.startsWith('http')) {
// dynamically add the host
schemaApi.url = `https://${request.headers.get('host')}${schemaApi.url}`
}

return new Response(
JSON.stringify({
schema_version: SchemaVersion.V1,
api: {
type: APIType.OPENAPI,
has_user_authentication: false,
url: `https://${request.headers.get('host')}${options?.openapi_url || '/openapi.json'}`,
},
auth: {
type: AuthType.NONE,
},
...options?.aiPlugin,
api: schemaApi,
}),
{
headers: {
Expand Down
7 changes: 4 additions & 3 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ export enum APIType {
}

export interface AIPlugin {
schema_version?: SchemaVersion
schema_version?: SchemaVersion | string
name_for_model: string
name_for_human: string
description_for_model: string
Expand All @@ -123,16 +123,17 @@ export interface AIPlugin {
logo_url: string
contact_email: string
legal_info_url: string
is_dev?: boolean
}

export interface API {
type: APIType
type: APIType | string
url: string
has_user_authentication: boolean
}

export interface Auth {
type: AuthType
type: AuthType | string
authorization_type?: string
authorization_url?: string
client_url?: string
Expand Down

0 comments on commit 42cdc99

Please sign in to comment.