Skip to content

Commit

Permalink
Add schema to postTransform options
Browse files Browse the repository at this point in the history
  • Loading branch information
duncanbeevers committed Dec 13, 2024
1 parent 713ea1b commit 7bb97e9
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 7 deletions.
14 changes: 12 additions & 2 deletions docs/6.x/node.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,16 @@ Resultant diff with correctly-typed `file` property:
+ file?: Blob;
```
Any [Schema Object](https://spec.openapis.org/oas/latest.html#schema-object) present in your schema will be run through this formatter (even remote ones!). Also be sure to check the `metadata` parameter for additional context that may be helpful.
#### transform / postTransform metadata
There are many other uses for this besides checking `format`. Because this must return a **string** you can produce any arbitrary TypeScript code you’d like (even your own custom types).
Any [Schema Object](https://spec.openapis.org/oas/latest.html#schema-object) present in your schema will be run through `transform`, prior to its conversion to a TypeScript AST node, and `postTransform` after its conversion, including remote schemas!
The `metadata` parameter present on both `transform` and `postTransform` has additional context that may be helpful.
| Property | Description |
|-|-|
| `metadata.path` | A [`$ref`](https://json-schema.org/understanding-json-schema/structuring#dollarref) URI string, pointing to the current schema object |
| `metadata.schema` | The schema object being transformed (only present for `postTransform`) |
| `metadata.ctx` | The GlobalContext object, containing
There are many other uses for this besides checking `format`. Because `tranform` may return a **string** you can produce any arbitrary TypeScript code you’d like (even your own custom types).
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,18 @@ export default function transformComponentsObject(componentsObject: ComponentsOb

const items: ts.TypeElement[] = [];
if (componentsObject[key]) {
for (const [name, item] of getEntries(componentsObject[key], ctx)) {
for (const [name, item] of getEntries<SchemaObject>(componentsObject[key], ctx)) {
let subType = transformers[key](item, {
path: createRef(["components", key, name]),
schema: item,
ctx,
});

let hasQuestionToken = false;
if (ctx.transform) {
const result = ctx.transform(item as SchemaObject, {
const result = ctx.transform(item, {
path: createRef(["components", key, name]),
schema: item,
ctx,
});
if (result) {
Expand Down
2 changes: 1 addition & 1 deletion packages/openapi-typescript/src/transform/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const transformers: Record<SchemaTransforms, (node: any, options: GlobalContext)
paths: transformPathsObject,
webhooks: transformWebhooksObject,
components: transformComponentsObject,
$defs: (node, options) => transformSchemaObject(node, { path: createRef(["$defs"]), ctx: options }),
$defs: (node, options) => transformSchemaObject(node, { path: createRef(["$defs"]), ctx: options, schema: node }),
};

export default function transformSchema(schema: OpenAPI3, ctx: GlobalContext) {
Expand Down
1 change: 1 addition & 0 deletions packages/openapi-typescript/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -707,5 +707,6 @@ export type $defs = Record<string, SchemaObject>;
/** generic options for most internal transform* functions */
export interface TransformNodeOptions {
path?: string;
schema?: SchemaObject | ReferenceObject;
ctx: GlobalContext;
}
44 changes: 42 additions & 2 deletions packages/openapi-typescript/test/node-api.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { fileURLToPath } from "node:url";
import ts from "typescript";
import openapiTS, { COMMENT_HEADER, astToString } from "../src/index.js";
import type { OpenAPITSOptions } from "../src/types.js";
import type { OpenAPITSOptions, ReferenceObject, SchemaObject } from "../src/types.js";
import type { TestCase } from "./test-helpers.js";

const EXAMPLES_DIR = new URL("../examples/", import.meta.url);
Expand Down Expand Up @@ -537,6 +537,11 @@ export type operations = Record<string, never>;`,
components: {
schemas: {
Date: { type: "string", format: "date-time" },
Set: {
["x-string-enum-to-set"]: true,
type: "string",
enum: ["low", "medium", "high"],
},
},
},
},
Expand All @@ -546,6 +551,8 @@ export interface components {
schemas: {
/** Format: date-time */
Date: DateOrTime;
/** @enum {string} */
Set: Set<"low" | "medium" | "high">;
};
responses: never;
parameters: never;
Expand All @@ -563,7 +570,40 @@ export type operations = Record<string, never>;`,
* then use the `typescript` parser and it will tell you the desired
* AST
*/
return ts.factory.createTypeReferenceNode(ts.factory.createIdentifier("DateOrTime"));
return ts.factory.createTypeReferenceNode(
ts.factory.createIdentifier("DateOrTime")
);
}

// Previously, in order to access the schema in postTransform,
// you could resolve the schema using the path.
// Now, the schema is made available directly on the options.
// const schema = options.path
// ? options.ctx.resolve<ReferenceObject | SchemaObject>(options.path)
// : undefined;
const schema = options.schema;

if (
schema &&
!("$ref" in schema) &&
Object.hasOwn(schema, "x-string-enum-to-set") &&
schema.type === "string" &&
schema.enum?.every((enumMember) => {
return typeof enumMember === "string";
})
) {
return ts.factory.createTypeReferenceNode(
ts.factory.createIdentifier("Set"),
[
ts.factory.createUnionTypeNode(
schema.enum.map((value) => {
return ts.factory.createLiteralTypeNode(
ts.factory.createStringLiteral(value)
);
})
),
]
);
}
},
},
Expand Down

0 comments on commit 7bb97e9

Please sign in to comment.