geut / openapi-box Goto Github PK
View Code? Open in Web Editor NEWGenerate TypeBox schemas from your openapi.
License: MIT License
Generate TypeBox schemas from your openapi.
License: MIT License
Related to #4, I'm using openapi-box to work with the OpenAI OpenAPI schema which is YAML vs. JSON. It would be nice if this was supported out of the box using something like yaml to turn it JSON before parsing.
I'm working on the OpenAI OpenAPI spec and hitting some issues with generated code. It defines a Json()
function which expects an options
argument, but then often doesn't seem to pass anything to it.
Consider this example with metadata
in the body
:
/threads/{thread_id}/messages/{message_id}:
post:
operationId: modifyMessage
tags:
- Assistants
summary: Modifies a message.
parameters:
- in: path
name: thread_id
required: true
schema:
type: string
description: The ID of the thread to which this message belongs.
- in: path
name: message_id
required: true
schema:
type: string
description: The ID of the message to modify.
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/ModifyMessageRequest"
responses:
"200":
description: OK
content:
application/json:
schema:
$ref: "#/components/schemas/MessageObject"
x-oaiMeta:
name: Modify message
group: threads
beta: true
returns: The modified [message](/docs/api-reference/messages/object) object.
examples:
request:
curl: |
curl https://api.openai.com/v1/threads/thread_abc123/messages/msg_abc123 \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "OpenAI-Beta: assistants=v2" \
-d '{
"metadata": {
"modified": "true",
"user": "abc123"
}
}'
python: |
from openai import OpenAI
client = OpenAI()
message = client.beta.threads.messages.update(
message_id="msg_abc12",
thread_id="thread_abc123",
metadata={
"modified": "true",
"user": "abc123",
},
)
print(message)
node.js: |-
import OpenAI from "openai";
const openai = new OpenAI();
async function main() {
const message = await openai.beta.threads.messages.update(
"thread_abc123",
"msg_abc123",
{
metadata: {
modified: "true",
user: "abc123",
},
}
}'
response: |
{
"id": "msg_abc123",
"object": "thread.message",
"created_at": 1699017614,
"assistant_id": null,
"thread_id": "thread_abc123",
"run_id": null,
"role": "user",
"content": [
{
"type": "text",
"text": {
"value": "How does AI work? Explain it in simple terms.",
"annotations": []
}
}
],
"file_ids": [],
"metadata": {
"modified": "true",
"user": "abc123"
}
}
...
ModifyMessageRequest:
type: object
additionalProperties: false
properties:
metadata:
description: *metadata_description
type: object
x-oaiTypeLabel: map
nullable: true
Here is what gets generated:
/**
* @param {JsonArray | JsonRecord} [options]
* @returns {ReturnType<typeof T.Unsafe<Json>>}
*/
const Json = (options) => T.Unsafe(T.Any(options));
// ...
cache["e72516203ab15db84c83493e3c34ce40"] = {
metadata: T.Optional(Json()),
};
// ...
"/threads/{thread_id}/messages/{message_id}": {
POST: {
args: T.Object({
params: T.Object(cache["fbca0ddc436612e084f356472689bcb2"]),
body: T.Object(cache["e72516203ab15db84c83493e3c34ce40"], {
"x-content-type": "application/json",
"additionalProperties": false,
}),
}),
data: T.Object(cache["9919ce439b06e6cb45eba3bc75cbb9a6"], {
"x-status-code": "200",
"x-content-type": "application/json",
}),
error: T.Union([T.Any({ "x-status-code": "default" })]),
},
In some cases, an object does get passed to Json()
, but most of them are empty.
If typescript config enables noImpicitAny then there are multiple errors in the generated schema code.
example:
./src/schema.ts:47:15
Type error: Parameter 'options' implicitly has an 'any' type.
45 | * @returns {ReturnType<typeof T.Unsafe<Json>>}
46 | */
> 47 | const Json = (options) => T.Unsafe(T.Any(options))
| ^
48 |
49 | const cache = {}
50 | cache['7c09545938bb5aebc0c280502225d1d9'] = {
There will also be an error on the generated cache type.
Hi. Could you please provide a basic implementation of import { createClient } from './src/client.js'
that you used in your readme along with an example of using it with the endpoint /pet/{petId}
for reference?
I'm trying to create TypeBox types from the OpenAI OpenAPI schema, but it's failing without much info:
yq eval -o=json resources/openapi.yaml > output/openai.json
openapi-box output/openai.json -o openai.js
โ ',' expected. (412:26)
410 | response_format: T.Optional(T.Union(cache['69fde07c72b934b7c91a2c1808b4bc59'],{"default":"json"})),
411 | temperature: T.Optional(T.Number({"default":0})),
> 412 | timestamp_granularities[]: T.Optional(T.Array(cache['1272a49c80b3dc8bc038af92e19f1d72'],{"default":["segment"]})),
| ^
413 | }
414 | cache['ce56d7a02eda5fc5e1e6ae8f44b7df4d'] = {
415 | file: T.String({"format":"binary"}),
Is this an issue in the schema, generated code, or openapi-box?
I was in the process of implementing a similar tool for OpenAPI to Typebox conversion and runtime checks through Typebox validation. Then I came across this project, whose codebase seems understandable and well-written.
I wonder: is there any particular reason why you chose to implement the JSON Schema to Typebox conversion yourself instead of using https://github.com/xddq/schema2typebox?
Thank you for your effort!
Hi there! I gave this library a try on one of our schemas and it seems to be working as described. :)
But in order to replace our current manually written validators we'd also need individual schemas defined in components.schemas
for typing implementation details, not just the api endpoints defined in paths. Example of what this part of our spec looks like:
components:
schemas:
Cat:
properties:
name:
type: string
Dog:
properties:
name:
type: string
Looks like currently all the types for these schemas are being generated, but hidden inside an internal cache, and identified using hashes instead of names. Curious if you think it'd be straightforward to turn those into an exported object with their actual names as keys instead? E.g. for the above ideally it'd look something like this:
export const components = {
schemas: {
Cat: T.Object({
name: T.String()
}),
Dog: T.Object({
name: T.String()
})
},
}
I'm having an issue with @geut/openapi-box v5.0.5 (and @sinclair/typebox v0.33.7). Nullable values with OpenAPI schema version 3.0.3 seem to be ignored in the generated types.
openapi: 3.0.3
info:
description: Title
version: 1.0.0
servers:
- url: "https"
components:
schemas:
Test:
type: object
properties:
testVal:
type: string
description: Should be a 2 character string, null or undefined
minLength: 2
maxLength: 2
nullable: true
results in
Test: T.Object({
testVal: T.Optional(T.String({ minLength: 2, maxLength: 2 })),
})
which does not allow null.
However when using OpenAPI 3.1.0 with
openapi: 3.1.0
info:
description: Title
version: 1.0.0
servers:
- url: "https"
components:
schemas:
TestObject:
type: object
properties:
testValue:
oneOf:
- type: string
description: Should be a 2 character string, null or undefined
minLength: 2
maxLength: 2
- type: "null"
the result is
TestObject: T.Object({
testValue: T.Optional(
T.Union([T.String({ minLength: 2, maxLength: 2 }), T.Null()]),
),
})
which works as expected, allowing null values.
Would it be possible to handle nullable in OpenAPI 3.0.3 like this, generating a null union for nullable fields?
This may not be something you want to address, but I'm trying to use the generated file in TypeScript. That mostly works, but one thing that breaks is:
const cache = {};
TypeScript is unhappy with this due to the keys:
Element implicitly has an 'any' type because expression of type '"80a44415499f66d2e4772a7be3d417a1"' can't be used to index type '{}'.
Property '80a44415499f66d2e4772a7be3d417a1' does not exist on type '{}'
In my code I'm switching it to this, which side-steps it:
const cache: { [key: string]: any } = {};
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.