├── .commitlintrc
├── .editorconfig
├── .eslintrc
├── .github
├── dependabot.yml
└── workflows
│ ├── check-linked-issues.yml
│ ├── ci.yml
│ ├── notify-release.yml
│ └── release.yml
├── .gitignore
├── .husky
├── commit-msg
├── pre-commit
└── pre-push
├── .nvmrc
├── .prettierrc
├── README.md
├── example
├── json-schema-to-typescript-config.json
├── openapi.yml
└── output
│ ├── json
│ └── components.schemas
│ │ ├── Address.json
│ │ ├── ApiResponse.json
│ │ ├── Category.json
│ │ ├── Customer.json
│ │ ├── DateExample.json
│ │ ├── FooBARBaz.json
│ │ ├── Order.json
│ │ ├── Pet.json
│ │ ├── Tag.json
│ │ └── User.json
│ ├── ts
│ ├── Address.ts
│ ├── ApiResponse.ts
│ ├── Category.ts
│ ├── Customer.ts
│ ├── DateExample.ts
│ ├── FooBARBaz.ts
│ ├── Order.ts
│ ├── Pet.ts
│ ├── Tag.ts
│ └── User.ts
│ └── types
│ ├── Address.d.ts
│ ├── ApiResponse.d.ts
│ ├── Category.d.ts
│ ├── Customer.d.ts
│ ├── DateExample.d.ts
│ ├── FooBARBaz.d.ts
│ ├── Order.d.ts
│ ├── Pet.d.ts
│ ├── Tag.d.ts
│ └── User.d.ts
├── index.ts
├── package-lock.json
├── package.json
├── scripts
└── generate-toc.mjs
├── src
├── cli.ts
├── commands
│ ├── json2ts.ts
│ ├── oas2json.ts
│ ├── oas2ts.ts
│ └── oas2tson.ts
├── types
│ ├── Json2TsOptions.d.ts
│ ├── Oas2Tson.d.ts
│ └── SchemasMetaData.d.ts
└── utils
│ ├── do-not-edit-text.ts
│ ├── openapi-schema-to-json-schema-wrapper.ts
│ ├── paths.ts
│ └── read-config-file.ts
├── test
├── fixtures
│ ├── openapi.yml
│ └── schemas
│ │ ├── Address.json
│ │ ├── ApiResponse.json
│ │ ├── Category.json
│ │ ├── Customer.json
│ │ ├── DateExample.json
│ │ ├── Order.json
│ │ ├── Pet.json
│ │ ├── Tag.json
│ │ └── User.json
├── json2ts.test.ts
├── oas2json.test.ts
├── oas2ts.test.ts
└── oas2tson.test.ts
├── tsconfig.json
└── tsup.config.ts
/.commitlintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "@commitlint/config-conventional"
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | [*]
2 | end_of_line = lf
3 | insert_final_newline = true
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 2
7 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "@typescript-eslint/parser",
3 | "plugins": [
4 | "@typescript-eslint"
5 | ],
6 | "extends": [
7 | "eslint:recommended",
8 | "plugin:@typescript-eslint/eslint-recommended",
9 | "plugin:@typescript-eslint/recommended",
10 | "plugin:prettier/recommended"
11 | ],
12 | "ignorePatterns": ["test/temp/**/*", "example/**/*", "dist/**/*"],
13 | "env": {
14 | "node": true,
15 | "es2021": true
16 | },
17 | "parserOptions": {
18 | "ecmaVersion": 2021,
19 | "sourceType": "module"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: npm
4 | directory: /
5 | schedule:
6 | interval: weekly
7 | - package-ecosystem: github-actions
8 | directory: /
9 | schedule:
10 | interval: weekly
11 |
--------------------------------------------------------------------------------
/.github/workflows/check-linked-issues.yml:
--------------------------------------------------------------------------------
1 | name: Check linked issues
2 | 'on':
3 | pull_request_target:
4 | types:
5 | - opened
6 | - edited
7 | - reopened
8 | - synchronize
9 | jobs:
10 | check_pull_requests:
11 | runs-on: ubuntu-latest
12 | name: Check linked issues
13 | permissions:
14 | issues: read
15 | pull-requests: write
16 | steps:
17 | - uses: nearform-actions/github-action-check-linked-issues@v1
18 | id: check-linked-issues
19 | with:
20 | exclude-branches: release/**, dependabot/**
21 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: Continuous Integration
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | pull_request:
8 |
9 | jobs:
10 | test:
11 | name: Lint and test
12 | runs-on: ubuntu-latest
13 | steps:
14 | - uses: actions/checkout@v4
15 | - uses: actions/setup-node@v4
16 | with:
17 | node-version-file: '.nvmrc'
18 | - run: |
19 | npm ci
20 | npm run lint
21 | npm test
22 | npm run build
23 |
24 | automerge:
25 | name: Merge dependabot's PRs
26 | needs: test
27 | runs-on: ubuntu-latest
28 | permissions:
29 | pull-requests: write
30 | contents: write
31 | steps:
32 | - uses: fastify/github-action-merge-dependabot@v3
33 |
--------------------------------------------------------------------------------
/.github/workflows/notify-release.yml:
--------------------------------------------------------------------------------
1 | name: Notify release
2 | 'on':
3 | workflow_dispatch:
4 | schedule:
5 | - cron: 30 8 * * *
6 | release:
7 | types:
8 | - published
9 | issues:
10 | types:
11 | - closed
12 | jobs:
13 | setup:
14 | runs-on: ubuntu-latest
15 | permissions:
16 | issues: write
17 | contents: read
18 | steps:
19 | - uses: nearform-actions/github-action-notify-release@v1
20 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: release
2 |
3 | on:
4 | workflow_dispatch:
5 | inputs:
6 | semver:
7 | description: The semver to use
8 | required: true
9 | default: patch
10 | type: choice
11 | options:
12 | - patch
13 | - minor
14 | - major
15 | pull_request:
16 | types: [closed]
17 |
18 | jobs:
19 | release:
20 | runs-on: ubuntu-latest
21 | permissions:
22 | contents: write
23 | issues: write
24 | pull-requests: write
25 | id-token: write
26 | steps:
27 | - uses: nearform-actions/optic-release-automation-action@v4
28 | with:
29 | semver: ${{ github.event.inputs.semver }}
30 | commit-message: 'chore: release {version}'
31 | build-command: npm ci && npm run build
32 | npm-token: ${{ secrets[format('NPM_TOKEN_{0}', github.actor)] || secrets.NPM_TOKEN }}
33 | optic-token: ${{ secrets[format('OPTIC_TOKEN_{0}', github.actor)] || secrets.OPTIC_TOKEN }}
34 | provenance: true
35 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .eslintcache
3 |
4 | # JetBrains IDEs
5 | .idea
6 | # Visual Studio Code
7 | .vscode
8 |
9 | # OS X
10 | .DS_Store
11 |
12 | # tests
13 | test/temp
14 | .nyc_output
15 | .tap
16 |
17 | # build
18 | dist
19 |
--------------------------------------------------------------------------------
/.husky/commit-msg:
--------------------------------------------------------------------------------
1 | npx --no -- commitlint --edit $1
2 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | npx lint-staged
2 |
--------------------------------------------------------------------------------
/.husky/pre-push:
--------------------------------------------------------------------------------
1 |
2 | npm run generate-toc
3 |
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | lts/*
2 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "semi": false,
3 | "singleQuote": true,
4 | "arrowParens": "avoid",
5 | "trailingComma": "none"
6 | }
7 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # OpenAPI Transformer Toolkit
2 |
3 | Effortlessly automate your API design-first development workflow by generating [JSON schemas](https://json-schema.org/) and [TypeScript types](https://www.typescriptlang.org/) from an [OpenAPI specification](https://spec.openapis.org/oas/v3.1.0).
4 |
5 | ## Table of Contents
6 |
7 | * [Installation](#installation)
8 | * [CLI](#cli)
9 | * [Create JSON Schema From OpenAPI Definitions](#create-json-schema-from-openapi-definitions)
10 | * [Usage](#usage)
11 | * [Example](#example)
12 | * [Options](#options)
13 | * [Generate TypeScript types from OpenAPI Defintions](#generate-typescript-types-from-openapi-defintions)
14 | * [Usage](#usage-1)
15 | * [Example](#example-1)
16 | * [Options](#options-1)
17 | * [Generate TypeScript types from JSON schemas](#generate-typescript-types-from-json-schemas)
18 | * [Usage](#usage-2)
19 | * [Example](#example-2)
20 | * [Options](#options-2)
21 | * [Create TypeScript JSON Schema From OpenAPI Definitions](#create-typescript-json-schema-from-openapi-definitions)
22 | * [Usage](#usage-3)
23 | * [Example](#example-3)
24 | * [Options](#options-3)
25 | * [Programmatic Usage](#programmatic-usage)
26 | * [Generate JSON Schemas from OpenAPI](#generate-json-schemas-from-openapi)
27 | * [Generate TypeScript Types from OpenAPI](#generate-typescript-types-from-openapi)
28 | * [Generate TypeScript Types from JSON Schemas](#generate-typescript-types-from-json-schemas-1)
29 | * [Generate TypeScript exported JSON Schemas from OpenAPI](#generate-typescript-exported-json-schemas-from-openapi)
30 | * [Example](#example-4)
31 | * [Additional Configuration](#additional-configuration)
32 |
33 | ## Installation
34 |
35 | You can install the package with npm (or another package manager):
36 |
37 | ```sh
38 | $ npm install openapi-transformer-toolkit
39 | ```
40 |
41 | If you want to install it globally, you can provide the `-g` flag.
42 |
43 | Alternatively, you can run the CLI using `npx`:
44 |
45 | ```sh
46 | $ npx openapi-transformer-toolkit [command] [options]
47 | ```
48 |
49 | ## CLI
50 |
51 | For easier usage, the package includes the `openapi-transformer-toolkit` executable you can use from your CLI.
52 |
53 |
54 |
55 |
56 | ### Create JSON Schema From OpenAPI Definitions
57 |
58 |
59 |
60 | Using the `oas2json` command you can create JSON schema records from OpenAPI definitions.
61 |
62 | #### Usage
63 |
64 | ```sh
65 | openapi-transformer-toolkit oas2json [options]
66 | ```
67 |
68 | #### Example
69 |
70 | ```sh
71 | $ openapi-transformer-toolkit oas2json -i ./openapi.yml -o ./schemas -p paths
72 | ```
73 |
74 | #### Options
75 |
76 | ```
77 | -i, --input Specify the path to the OpenAPI file
78 | -o, --output Specify the path to the folder where you wish to output the schemas
79 | -p, --properties Specify the properties/definitions in the OpenAPI file to convert in a comma-separated list (optional)
80 | -h, --help Display help for command
81 | ```
82 |
83 |
84 |
85 |
86 |
87 |
88 | ### Generate TypeScript types from OpenAPI Defintions
89 |
90 |
91 |
92 | Using the `oas2ts` command you can create TypeScript types from your OpenAPI definitions.
93 |
94 | #### Usage
95 |
96 | ```sh
97 | openapi-transformer-toolkit oas2ts [options]
98 | ```
99 |
100 | #### Example
101 |
102 | ```sh
103 | $ openapi-transformer-toolkit oas2ts -i ./openapi.yml -o ./types
104 | ```
105 |
106 | ```sh
107 | $ openapi-transformer-toolkit oas2ts -i ./openapi.yml -o ./types -c ./config.json
108 | ```
109 |
110 | #### Options
111 |
112 | ```
113 | -i, --input Path to the OpenAPI file
114 | -o, --output Path to the folder where to output the TypeScript types
115 | -c, --config Path to the JSON/JS config file
116 | -h, --help Display help for command
117 | ```
118 |
119 | See [Additional Configuration](#additional-configuration) for the `-c, --config` option.
120 |
121 |
122 |
123 |
124 |
125 |
126 | ### Generate TypeScript types from JSON schemas
127 |
128 |
129 |
130 | Using the `json2ts` command you can create TypeScript types from your JSON Schema definitions.
131 |
132 | #### Usage
133 |
134 | ```sh
135 | openapi-transformer-toolkit json2ts [options]
136 | ```
137 |
138 | #### Example
139 |
140 | ```sh
141 | $ openapi-transformer-toolkit json2ts -i ./schemas -o ./types
142 | ```
143 |
144 | ```sh
145 | $ openapi-transformer-toolkit json2ts -i ./schemas -o ./types -c ./config.json
146 | ```
147 |
148 | #### Options
149 |
150 | ```
151 | -i, --input Path to the JSON schemas folder
152 | -o, --output Path to the folder where to output the TS files
153 | -c, --config Path to the JSON/JS config file
154 | -h, --help Display help for command
155 | ```
156 |
157 | See [Additional Configuration](#additional-configuration) for the `-c, --config` option.
158 |
159 |
160 |
161 |
162 |
163 |
164 | ### Create TypeScript JSON Schema From OpenAPI Definitions
165 |
166 |
167 |
168 | Using the `oas2tson` command you can create Typescript exported JSON schema records from OpenAPI definitions.
169 |
170 | #### Usage
171 |
172 | ```sh
173 | openapi-transformer-toolkit oas2tson [options]
174 | ```
175 |
176 | #### Example
177 |
178 | ```sh
179 | $ openapi-transformer-toolkit oas2tson -i ./openapi.yml -o ./schemas -p paths
180 | ```
181 |
182 | #### Options
183 |
184 | ```
185 | -i, --input Specify the path to the OpenAPI file
186 | -o, --output Specify the path to the folder where you wish to output the schemas
187 | -p, --properties Specify the properties/definitions in the OpenAPI file to convert in a comma-separated list (optional)
188 | -h, --help Display help for command
189 | ```
190 |
191 |
192 |
193 | ## Programmatic Usage
194 |
195 | You can also use the package programmatically by importing the necessary functions:
196 |
197 | ```javascript
198 | import { oas2json, oas2ts, json2ts, oas2tson } from 'openapi-transformer-toolkit'
199 | ```
200 |
201 | ### Generate JSON Schemas from OpenAPI
202 |
203 | To generate JSON schemas from your OpenAPI specification, provide the path to the OpenAPI file and the output directory for the generated schemas:
204 |
205 | ```javascript
206 | const openAPIPath = 'path/to/openapi.yml'
207 | const schemasPath = 'path/to/output/schemas'
208 | const propertiesToConvert = 'paths'
209 |
210 | oas2json(openAPIPath, schemasPath, propertiesToConvert)
211 | ```
212 |
213 | ### Generate TypeScript Types from OpenAPI
214 |
215 | To generate TypeScript types from the OpenAPI specification, provide the path to the OpenAPI file and the output directory for the TypeScript types. Optionally, the third parameter can contain [configuration options](#additional-configuration)
216 |
217 | ```javascript
218 | const openAPIPath = 'path/to/openapi.yml'
219 | const tsTypesPath = 'path/to/output/types'
220 | //
221 | const options = {
222 | bannerComment: 'Custom banner content'
223 | }
224 |
225 | await oas2ts(openAPIPath, tsTypesPath, options)
226 | ```
227 |
228 | ### Generate TypeScript Types from JSON Schemas
229 |
230 | To generate TypeScript types from the generated JSON schemas, provide the path to the JSON schema directory and the output directory for the TypeScript types. Optionally, the third parameter can contain [configuration options](#additional-configuration)
231 |
232 | ```javascript
233 | const schemasPath = 'path/to/output/schemas'
234 | const tsTypesPath = 'path/to/output/types'
235 |
236 | await json2ts(schemasPath, tsTypesPath)
237 | ```
238 |
239 | ### Generate TypeScript exported JSON Schemas from OpenAPI
240 |
241 | To generate TypeScript exported JSON schemas from your OpenAPI specification, provide the path to the OpenAPI file and the output directory for the generated schemas:
242 |
243 | ```javascript
244 | const openAPIPath = 'path/to/openapi.yml'
245 | const schemasPath = 'path/to/output/schemas'
246 | const propertiesToConvert = 'paths'
247 |
248 | oas2tson(openAPIPath, schemasPath, propertiesToConvert)
249 | ```
250 |
251 | ## Example
252 |
253 | The [example](./example) folder contains an example OpenAPI specification and the generated JSON schemas and TypeScript types. To generate the JSON schemas and TypeScript types from the example OpenAPI specification, run:
254 |
255 | ```sh
256 | $ npm run oas2json
257 | ```
258 |
259 | and then:
260 |
261 | ```sh
262 | $ npm run oas2ts
263 | ```
264 |
265 | or:
266 |
267 | ```sh
268 | $ npm run json2ts
269 | ```
270 |
271 | ```sh
272 | $ npm run oas2json
273 | ```
274 |
275 | And to generate TypeScript exported JSON schema from example OpenAPI specification, run:
276 |
277 | ```sh
278 | $ npm run oas2tson
279 | ```
280 |
281 | The generated JSON schemas and TypeScript types will be saved in the output schemas and types folders respectively.
282 |
283 | ## Additional Configuration
284 |
285 | OpenAPI Transformer Toolkit package utilises the [json-schema-to-typescript](https://www.npmjs.com/package/json-schema-to-typescript) package.
286 |
287 | This package allows you to specify [additional options which can be passed to the command when executing](https://www.npmjs.com/package/json-schema-to-typescript#user-content-options), for example to affect the style of output, or change how `additionalProperties` from your API definition is handled.
288 |
289 | To utilise this feature, OpenAPI Transformer Toolkit can read these additional options from a file when being used from a CLI. [An example of this can be found here](https://github.com/nearform/openapi-transformer-toolkit/blob/master/example/json-schema-to-typescript-config.json).
290 |
291 | When using OpenAPI Transformer Toolkit programmatically, these options can optionally be supplied as the third argument to the `oas2ts` and `json2ts` functions.
292 |
293 | [](https://www.nearform.com/contact/?utm_source=open-source&utm_medium=banner&utm_campaign=os-project-pages)
294 |
--------------------------------------------------------------------------------
/example/json-schema-to-typescript-config.json:
--------------------------------------------------------------------------------
1 | {
2 | "additionalProperties": true,
3 | "style": {
4 | "tabWidth": 2,
5 | "singleQuote": true
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/example/openapi.yml:
--------------------------------------------------------------------------------
1 | openapi: 3.0.3
2 | info:
3 | title: Swagger Petstore - OpenAPI 3.0
4 | description: |-
5 | This is a sample Pet Store Server based on the OpenAPI 3.0 specification. You can find out more about
6 | Swagger at [https://swagger.io](https://swagger.io). In the third iteration of the pet store, we've switched to the design first approach!
7 | You can now help us improve the API whether it's by making changes to the definition itself or to the code.
8 | That way, with time, we can improve the API in general, and expose some of the new features in OAS3.
9 |
10 | _If you're looking for the Swagger 2.0/OAS 2.0 version of Petstore, then click [here](https://editor.swagger.io/?url=https://petstore.swagger.io/v2/swagger.yaml). Alternatively, you can load via the `Edit > Load Petstore OAS 2.0` menu option!_
11 |
12 | Some useful links:
13 | - [The Pet Store repository](https://github.com/swagger-api/swagger-petstore)
14 | - [The source API definition for the Pet Store](https://github.com/swagger-api/swagger-petstore/blob/master/src/main/resources/openapi.yaml)
15 | termsOfService: http://swagger.io/terms/
16 | contact:
17 | email: apiteam@swagger.io
18 | license:
19 | name: Apache 2.0
20 | url: http://www.apache.org/licenses/LICENSE-2.0.html
21 | version: 1.0.11
22 | externalDocs:
23 | description: Find out more about Swagger
24 | url: http://swagger.io
25 | servers:
26 | - url: https://petstore3.swagger.io/api/v3
27 | tags:
28 | - name: pet
29 | description: Everything about your Pets
30 | externalDocs:
31 | description: Find out more
32 | url: http://swagger.io
33 | - name: store
34 | description: Access to Petstore orders
35 | externalDocs:
36 | description: Find out more about our store
37 | url: http://swagger.io
38 | - name: user
39 | description: Operations about user
40 | paths:
41 | /pet:
42 | put:
43 | tags:
44 | - pet
45 | summary: Update an existing pet
46 | description: Update an existing pet by Id
47 | operationId: updatePet
48 | requestBody:
49 | description: Update an existent pet in the store
50 | content:
51 | application/json:
52 | schema:
53 | $ref: '#/components/schemas/Pet'
54 | application/xml:
55 | schema:
56 | $ref: '#/components/schemas/Pet'
57 | application/x-www-form-urlencoded:
58 | schema:
59 | $ref: '#/components/schemas/Pet'
60 | required: true
61 | responses:
62 | '200':
63 | description: Successful operation
64 | content:
65 | application/json:
66 | schema:
67 | $ref: '#/components/schemas/Pet'
68 | application/xml:
69 | schema:
70 | $ref: '#/components/schemas/Pet'
71 | '400':
72 | description: Invalid ID supplied
73 | '404':
74 | description: Pet not found
75 | '405':
76 | description: Validation exception
77 | security:
78 | - petstore_auth:
79 | - write:pets
80 | - read:pets
81 | post:
82 | tags:
83 | - pet
84 | summary: Add a new pet to the store
85 | description: Add a new pet to the store
86 | operationId: addPet
87 | requestBody:
88 | description: Create a new pet in the store
89 | content:
90 | application/json:
91 | schema:
92 | $ref: '#/components/schemas/Pet'
93 | application/xml:
94 | schema:
95 | $ref: '#/components/schemas/Pet'
96 | application/x-www-form-urlencoded:
97 | schema:
98 | $ref: '#/components/schemas/Pet'
99 | required: true
100 | responses:
101 | '200':
102 | description: Successful operation
103 | content:
104 | application/json:
105 | schema:
106 | $ref: '#/components/schemas/Pet'
107 | application/xml:
108 | schema:
109 | $ref: '#/components/schemas/Pet'
110 | '405':
111 | description: Invalid input
112 | security:
113 | - petstore_auth:
114 | - write:pets
115 | - read:pets
116 | /pet/findByStatus:
117 | get:
118 | tags:
119 | - pet
120 | summary: Finds Pets by status
121 | description: Multiple status values can be provided with comma separated strings
122 | operationId: findPetsByStatus
123 | parameters:
124 | - name: status
125 | in: query
126 | description: Status values that need to be considered for filter
127 | required: false
128 | explode: true
129 | schema:
130 | type: string
131 | default: available
132 | enum:
133 | - available
134 | - pending
135 | - sold
136 | responses:
137 | '200':
138 | description: successful operation
139 | content:
140 | application/json:
141 | schema:
142 | type: array
143 | items:
144 | $ref: '#/components/schemas/Pet'
145 | application/xml:
146 | schema:
147 | type: array
148 | items:
149 | $ref: '#/components/schemas/Pet'
150 | '400':
151 | description: Invalid status value
152 | security:
153 | - petstore_auth:
154 | - write:pets
155 | - read:pets
156 | /pet/findByTags:
157 | get:
158 | tags:
159 | - pet
160 | summary: Finds Pets by tags
161 | description: Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.
162 | operationId: findPetsByTags
163 | parameters:
164 | - name: tags
165 | in: query
166 | description: Tags to filter by
167 | required: false
168 | explode: true
169 | schema:
170 | type: array
171 | items:
172 | type: string
173 | responses:
174 | '200':
175 | description: successful operation
176 | content:
177 | application/json:
178 | schema:
179 | type: array
180 | items:
181 | $ref: '#/components/schemas/Pet'
182 | application/xml:
183 | schema:
184 | type: array
185 | items:
186 | $ref: '#/components/schemas/Pet'
187 | '400':
188 | description: Invalid tag value
189 | security:
190 | - petstore_auth:
191 | - write:pets
192 | - read:pets
193 | /pet/{petId}:
194 | get:
195 | tags:
196 | - pet
197 | summary: Find pet by ID
198 | description: Returns a single pet
199 | operationId: getPetById
200 | parameters:
201 | - name: petId
202 | in: path
203 | description: ID of pet to return
204 | required: true
205 | schema:
206 | type: integer
207 | format: int64
208 | responses:
209 | '200':
210 | description: successful operation
211 | content:
212 | application/json:
213 | schema:
214 | $ref: '#/components/schemas/Pet'
215 | application/xml:
216 | schema:
217 | $ref: '#/components/schemas/Pet'
218 | '400':
219 | description: Invalid ID supplied
220 | '404':
221 | description: Pet not found
222 | security:
223 | - api_key: []
224 | - petstore_auth:
225 | - write:pets
226 | - read:pets
227 | post:
228 | tags:
229 | - pet
230 | summary: Updates a pet in the store with form data
231 | description: ''
232 | operationId: updatePetWithForm
233 | parameters:
234 | - name: petId
235 | in: path
236 | description: ID of pet that needs to be updated
237 | required: true
238 | schema:
239 | type: integer
240 | format: int64
241 | - name: name
242 | in: query
243 | description: Name of pet that needs to be updated
244 | schema:
245 | type: string
246 | - name: status
247 | in: query
248 | description: Status of pet that needs to be updated
249 | schema:
250 | type: string
251 | responses:
252 | '405':
253 | description: Invalid input
254 | security:
255 | - petstore_auth:
256 | - write:pets
257 | - read:pets
258 | delete:
259 | tags:
260 | - pet
261 | summary: Deletes a pet
262 | description: delete a pet
263 | operationId: deletePet
264 | parameters:
265 | - name: api_key
266 | in: header
267 | description: ''
268 | required: false
269 | schema:
270 | type: string
271 | - name: petId
272 | in: path
273 | description: Pet id to delete
274 | required: true
275 | schema:
276 | type: integer
277 | format: int64
278 | responses:
279 | '400':
280 | description: Invalid pet value
281 | security:
282 | - petstore_auth:
283 | - write:pets
284 | - read:pets
285 | /pet/{petId}/uploadImage:
286 | post:
287 | tags:
288 | - pet
289 | summary: uploads an image
290 | description: ''
291 | operationId: uploadFile
292 | parameters:
293 | - name: petId
294 | in: path
295 | description: ID of pet to update
296 | required: true
297 | schema:
298 | type: integer
299 | format: int64
300 | - name: additionalMetadata
301 | in: query
302 | description: Additional Metadata
303 | required: false
304 | schema:
305 | type: string
306 | requestBody:
307 | content:
308 | application/octet-stream:
309 | schema:
310 | type: string
311 | format: binary
312 | responses:
313 | '200':
314 | description: successful operation
315 | content:
316 | application/json:
317 | schema:
318 | $ref: '#/components/schemas/ApiResponse'
319 | security:
320 | - petstore_auth:
321 | - write:pets
322 | - read:pets
323 | /store/inventory:
324 | get:
325 | tags:
326 | - store
327 | summary: Returns pet inventories by status
328 | description: Returns a map of status codes to quantities
329 | operationId: getInventory
330 | responses:
331 | '200':
332 | description: successful operation
333 | content:
334 | application/json:
335 | schema:
336 | type: object
337 | additionalProperties:
338 | type: integer
339 | format: int32
340 | security:
341 | - api_key: []
342 | /store/order:
343 | post:
344 | tags:
345 | - store
346 | summary: Place an order for a pet
347 | description: Place a new order in the store
348 | operationId: placeOrder
349 | requestBody:
350 | content:
351 | application/json:
352 | schema:
353 | $ref: '#/components/schemas/Order'
354 | application/xml:
355 | schema:
356 | $ref: '#/components/schemas/Order'
357 | application/x-www-form-urlencoded:
358 | schema:
359 | $ref: '#/components/schemas/Order'
360 | responses:
361 | '200':
362 | description: successful operation
363 | content:
364 | application/json:
365 | schema:
366 | $ref: '#/components/schemas/Order'
367 | '405':
368 | description: Invalid input
369 | /store/order/{orderId}:
370 | get:
371 | tags:
372 | - store
373 | summary: Find purchase order by ID
374 | description: For valid response try integer IDs with value <= 5 or > 10. Other values will generate exceptions.
375 | operationId: getOrderById
376 | parameters:
377 | - name: orderId
378 | in: path
379 | description: ID of order that needs to be fetched
380 | required: true
381 | schema:
382 | type: integer
383 | format: int64
384 | responses:
385 | '200':
386 | description: successful operation
387 | content:
388 | application/json:
389 | schema:
390 | $ref: '#/components/schemas/Order'
391 | application/xml:
392 | schema:
393 | $ref: '#/components/schemas/Order'
394 | '400':
395 | description: Invalid ID supplied
396 | '404':
397 | description: Order not found
398 | delete:
399 | tags:
400 | - store
401 | summary: Delete purchase order by ID
402 | description: For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors
403 | operationId: deleteOrder
404 | parameters:
405 | - name: orderId
406 | in: path
407 | description: ID of the order that needs to be deleted
408 | required: true
409 | schema:
410 | type: integer
411 | format: int64
412 | responses:
413 | '400':
414 | description: Invalid ID supplied
415 | '404':
416 | description: Order not found
417 | /user:
418 | post:
419 | tags:
420 | - user
421 | summary: Create user
422 | description: This can only be done by the logged in user.
423 | operationId: createUser
424 | requestBody:
425 | description: Created user object
426 | content:
427 | application/json:
428 | schema:
429 | $ref: '#/components/schemas/User'
430 | application/xml:
431 | schema:
432 | $ref: '#/components/schemas/User'
433 | application/x-www-form-urlencoded:
434 | schema:
435 | $ref: '#/components/schemas/User'
436 | responses:
437 | default:
438 | description: successful operation
439 | content:
440 | application/json:
441 | schema:
442 | $ref: '#/components/schemas/User'
443 | application/xml:
444 | schema:
445 | $ref: '#/components/schemas/User'
446 | /user/createWithList:
447 | post:
448 | tags:
449 | - user
450 | summary: Creates list of users with given input array
451 | description: Creates list of users with given input array
452 | operationId: createUsersWithListInput
453 | requestBody:
454 | content:
455 | application/json:
456 | schema:
457 | type: array
458 | items:
459 | $ref: '#/components/schemas/User'
460 | responses:
461 | '200':
462 | description: Successful operation
463 | content:
464 | application/json:
465 | schema:
466 | $ref: '#/components/schemas/User'
467 | application/xml:
468 | schema:
469 | $ref: '#/components/schemas/User'
470 | default:
471 | description: successful operation
472 | /user/login:
473 | get:
474 | tags:
475 | - user
476 | summary: Logs user into the system
477 | description: ''
478 | operationId: loginUser
479 | parameters:
480 | - name: username
481 | in: query
482 | description: The user name for login
483 | required: false
484 | schema:
485 | type: string
486 | - name: password
487 | in: query
488 | description: The password for login in clear text
489 | required: false
490 | schema:
491 | type: string
492 | responses:
493 | '200':
494 | description: successful operation
495 | headers:
496 | X-Rate-Limit:
497 | description: calls per hour allowed by the user
498 | schema:
499 | type: integer
500 | format: int32
501 | X-Expires-After:
502 | description: date in UTC when token expires
503 | schema:
504 | type: string
505 | format: date-time
506 | content:
507 | application/xml:
508 | schema:
509 | type: string
510 | application/json:
511 | schema:
512 | type: string
513 | '400':
514 | description: Invalid username/password supplied
515 | /user/logout:
516 | get:
517 | tags:
518 | - user
519 | summary: Logs out current logged in user session
520 | description: ''
521 | operationId: logoutUser
522 | parameters: []
523 | responses:
524 | default:
525 | description: successful operation
526 | /user/{username}:
527 | get:
528 | tags:
529 | - user
530 | summary: Get user by user name
531 | description: ''
532 | operationId: getUserByName
533 | parameters:
534 | - name: username
535 | in: path
536 | description: 'The name that needs to be fetched. Use user1 for testing. '
537 | required: true
538 | schema:
539 | type: string
540 | responses:
541 | '200':
542 | description: successful operation
543 | content:
544 | application/json:
545 | schema:
546 | $ref: '#/components/schemas/User'
547 | application/xml:
548 | schema:
549 | $ref: '#/components/schemas/User'
550 | '400':
551 | description: Invalid username supplied
552 | '404':
553 | description: User not found
554 | put:
555 | tags:
556 | - user
557 | summary: Update user
558 | description: This can only be done by the logged in user.
559 | operationId: updateUser
560 | parameters:
561 | - name: username
562 | in: path
563 | description: name that need to be deleted
564 | required: true
565 | schema:
566 | type: string
567 | requestBody:
568 | description: Update an existent user in the store
569 | content:
570 | application/json:
571 | schema:
572 | $ref: '#/components/schemas/User'
573 | application/xml:
574 | schema:
575 | $ref: '#/components/schemas/User'
576 | application/x-www-form-urlencoded:
577 | schema:
578 | $ref: '#/components/schemas/User'
579 | responses:
580 | default:
581 | description: successful operation
582 | delete:
583 | tags:
584 | - user
585 | summary: Delete user
586 | description: This can only be done by the logged in user.
587 | operationId: deleteUser
588 | parameters:
589 | - name: username
590 | in: path
591 | description: The name that needs to be deleted
592 | required: true
593 | schema:
594 | type: string
595 | responses:
596 | '400':
597 | description: Invalid username supplied
598 | '404':
599 | description: User not found
600 | components:
601 | schemas:
602 | DateExample:
603 | type: string
604 | format: date-time
605 | Order:
606 | type: object
607 | properties:
608 | id:
609 | type: integer
610 | format: int64
611 | example: 10
612 | petId:
613 | type: integer
614 | format: int64
615 | example: 198772
616 | quantity:
617 | type: integer
618 | format: int32
619 | example: 7
620 | shipDate:
621 | type: string
622 | format: date-time #changed
623 | status:
624 | type: string
625 | description: Order Status
626 | example: approved
627 | enum:
628 | - placed
629 | - approved
630 | - delivered
631 | complete:
632 | type: boolean
633 | xml:
634 | name: order
635 | Customer:
636 | type: object
637 | properties:
638 | id:
639 | type: integer
640 | format: int64
641 | example: 100000
642 | username:
643 | type: string
644 | example: fehguy
645 | address:
646 | type: array
647 | xml:
648 | name: addresses
649 | wrapped: true
650 | items:
651 | $ref: '#/components/schemas/Address'
652 | xml:
653 | name: customer
654 | Address:
655 | type: object
656 | properties:
657 | street:
658 | type: string
659 | example: 437 Lytton
660 | city:
661 | type: string
662 | example: Palo Alto
663 | state:
664 | type: string
665 | example: CA
666 | zip:
667 | type: string
668 | example: '94301'
669 | xml:
670 | name: address
671 | Category:
672 | type: object
673 | properties:
674 | id:
675 | type: integer
676 | format: int64
677 | example: 1
678 | name:
679 | type: string
680 | example: Dogs
681 | xml:
682 | name: category
683 | User:
684 | type: object
685 | properties:
686 | id:
687 | type: integer
688 | format: int64
689 | example: 10
690 | username:
691 | type: string
692 | example: theUser
693 | firstName:
694 | type: string
695 | example: John
696 | lastName:
697 | type: string
698 | example: James
699 | email:
700 | type: string
701 | example: john@email.com
702 | password:
703 | type: string
704 | example: '12345'
705 | phone:
706 | type: string
707 | example: '12345'
708 | userStatus:
709 | type: integer
710 | description: User Status
711 | format: int32
712 | example: 1
713 | xml:
714 | name: user
715 | Tag:
716 | type: object
717 | properties:
718 | id:
719 | type: integer
720 | format: int64
721 | name:
722 | type: string
723 | xml:
724 | name: tag
725 | Pet:
726 | required:
727 | - name
728 | - photoUrls
729 | type: object
730 | properties:
731 | id:
732 | type: integer
733 | format: int64
734 | example: 10
735 | name:
736 | type: string
737 | example: doggie
738 | category:
739 | $ref: '#/components/schemas/Category'
740 | photoUrls:
741 | type: array
742 | xml:
743 | wrapped: true
744 | items:
745 | type: string
746 | xml:
747 | name: photoUrl
748 | tags:
749 | type: array
750 | xml:
751 | wrapped: true
752 | items:
753 | $ref: '#/components/schemas/Tag'
754 | status:
755 | type: string
756 | description: pet status in the store
757 | enum:
758 | - available
759 | - pending
760 | - sold
761 | nullableValue:
762 | type: string
763 | nullable: true
764 | description: example nullable value
765 | xml:
766 | name: pet
767 | ApiResponse:
768 | type: object
769 | properties:
770 | code:
771 | type: integer
772 | format: int32
773 | type:
774 | type: string
775 | message:
776 | type: string
777 | FooBARBaz:
778 | $ref: '#/components/schemas/FooBARBaz'
779 | xml:
780 | name: '##default'
781 | FooBARBaz:
782 | type: string
783 | description: this name is valid and should be FooBARBaz everywhere it appears
784 | requestBodies:
785 | Pet:
786 | description: Pet object that needs to be added to the store
787 | content:
788 | application/json:
789 | schema:
790 | $ref: '#/components/schemas/Pet'
791 | application/xml:
792 | schema:
793 | $ref: '#/components/schemas/Pet'
794 | UserArray:
795 | description: List of user object
796 | content:
797 | application/json:
798 | schema:
799 | type: array
800 | items:
801 | $ref: '#/components/schemas/User'
802 | securitySchemes:
803 | petstore_auth:
804 | type: oauth2
805 | flows:
806 | implicit:
807 | authorizationUrl: https://petstore3.swagger.io/oauth/authorize
808 | scopes:
809 | write:pets: modify pets in your account
810 | read:pets: read your pets
811 | api_key:
812 | type: apiKey
813 | name: api_key
814 | in: header
815 |
--------------------------------------------------------------------------------
/example/output/json/components.schemas/Address.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "object",
3 | "properties": {
4 | "street": {
5 | "type": "string"
6 | },
7 | "city": {
8 | "type": "string"
9 | },
10 | "state": {
11 | "type": "string"
12 | },
13 | "zip": {
14 | "type": "string"
15 | }
16 | },
17 | "title": "Address",
18 | "$id": "Address.json"
19 | }
--------------------------------------------------------------------------------
/example/output/json/components.schemas/ApiResponse.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "object",
3 | "properties": {
4 | "code": {
5 | "type": "integer",
6 | "format": "int32",
7 | "minimum": -2147483648,
8 | "maximum": 2147483647
9 | },
10 | "type": {
11 | "type": "string"
12 | },
13 | "message": {
14 | "type": "string"
15 | },
16 | "FooBARBaz": {
17 | "$ref": "FooBARBaz.json"
18 | }
19 | },
20 | "title": "ApiResponse",
21 | "$id": "ApiResponse.json"
22 | }
--------------------------------------------------------------------------------
/example/output/json/components.schemas/Category.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "object",
3 | "properties": {
4 | "id": {
5 | "type": "integer",
6 | "format": "int64",
7 | "minimum": -9223372036854776000,
8 | "maximum": 9223372036854776000
9 | },
10 | "name": {
11 | "type": "string"
12 | }
13 | },
14 | "title": "Category",
15 | "$id": "Category.json"
16 | }
--------------------------------------------------------------------------------
/example/output/json/components.schemas/Customer.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "object",
3 | "properties": {
4 | "id": {
5 | "type": "integer",
6 | "format": "int64",
7 | "minimum": -9223372036854776000,
8 | "maximum": 9223372036854776000
9 | },
10 | "username": {
11 | "type": "string"
12 | },
13 | "address": {
14 | "type": "array",
15 | "items": {
16 | "$ref": "Address.json"
17 | }
18 | }
19 | },
20 | "title": "Customer",
21 | "$id": "Customer.json"
22 | }
--------------------------------------------------------------------------------
/example/output/json/components.schemas/DateExample.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "string",
3 | "format": "date-time",
4 | "title": "DateExample",
5 | "$id": "DateExample.json",
6 | "tsType": "Date"
7 | }
--------------------------------------------------------------------------------
/example/output/json/components.schemas/FooBARBaz.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "string",
3 | "description": "this name is valid and should be FooBARBaz everywhere it appears",
4 | "title": "FooBARBaz",
5 | "$id": "FooBARBaz.json"
6 | }
--------------------------------------------------------------------------------
/example/output/json/components.schemas/Order.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "object",
3 | "properties": {
4 | "id": {
5 | "type": "integer",
6 | "format": "int64",
7 | "minimum": -9223372036854776000,
8 | "maximum": 9223372036854776000
9 | },
10 | "petId": {
11 | "type": "integer",
12 | "format": "int64",
13 | "minimum": -9223372036854776000,
14 | "maximum": 9223372036854776000
15 | },
16 | "quantity": {
17 | "type": "integer",
18 | "format": "int32",
19 | "minimum": -2147483648,
20 | "maximum": 2147483647
21 | },
22 | "shipDate": {
23 | "type": "string",
24 | "format": "date-time"
25 | },
26 | "status": {
27 | "type": "string",
28 | "description": "Order Status",
29 | "enum": [
30 | "placed",
31 | "approved",
32 | "delivered"
33 | ]
34 | },
35 | "complete": {
36 | "type": "boolean"
37 | }
38 | },
39 | "title": "Order",
40 | "$id": "Order.json"
41 | }
--------------------------------------------------------------------------------
/example/output/json/components.schemas/Pet.json:
--------------------------------------------------------------------------------
1 | {
2 | "required": [
3 | "name",
4 | "photoUrls"
5 | ],
6 | "type": "object",
7 | "properties": {
8 | "id": {
9 | "type": "integer",
10 | "format": "int64",
11 | "minimum": -9223372036854776000,
12 | "maximum": 9223372036854776000
13 | },
14 | "name": {
15 | "type": "string"
16 | },
17 | "category": {
18 | "$ref": "Category.json"
19 | },
20 | "photoUrls": {
21 | "type": "array",
22 | "items": {
23 | "type": "string"
24 | }
25 | },
26 | "tags": {
27 | "type": "array",
28 | "items": {
29 | "$ref": "Tag.json"
30 | }
31 | },
32 | "status": {
33 | "type": "string",
34 | "description": "pet status in the store",
35 | "enum": [
36 | "available",
37 | "pending",
38 | "sold"
39 | ]
40 | },
41 | "nullableValue": {
42 | "type": [
43 | "string",
44 | "null"
45 | ],
46 | "description": "example nullable value"
47 | }
48 | },
49 | "title": "Pet",
50 | "$id": "Pet.json"
51 | }
--------------------------------------------------------------------------------
/example/output/json/components.schemas/Tag.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "object",
3 | "properties": {
4 | "id": {
5 | "type": "integer",
6 | "format": "int64",
7 | "minimum": -9223372036854776000,
8 | "maximum": 9223372036854776000
9 | },
10 | "name": {
11 | "type": "string"
12 | }
13 | },
14 | "title": "Tag",
15 | "$id": "Tag.json"
16 | }
--------------------------------------------------------------------------------
/example/output/json/components.schemas/User.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "object",
3 | "properties": {
4 | "id": {
5 | "type": "integer",
6 | "format": "int64",
7 | "minimum": -9223372036854776000,
8 | "maximum": 9223372036854776000
9 | },
10 | "username": {
11 | "type": "string"
12 | },
13 | "firstName": {
14 | "type": "string"
15 | },
16 | "lastName": {
17 | "type": "string"
18 | },
19 | "email": {
20 | "type": "string"
21 | },
22 | "password": {
23 | "type": "string"
24 | },
25 | "phone": {
26 | "type": "string"
27 | },
28 | "userStatus": {
29 | "type": "integer",
30 | "description": "User Status",
31 | "format": "int32",
32 | "minimum": -2147483648,
33 | "maximum": 2147483647
34 | }
35 | },
36 | "title": "User",
37 | "$id": "User.json"
38 | }
--------------------------------------------------------------------------------
/example/output/ts/Address.ts:
--------------------------------------------------------------------------------
1 | export const Address = {
2 | type: "object",
3 | properties: {
4 | street: { type: "string" },
5 | city: { type: "string" },
6 | state: { type: "string" },
7 | zip: { type: "string" },
8 | },
9 | title: "Address",
10 | $id: "Address.json",
11 | } as const;
12 |
--------------------------------------------------------------------------------
/example/output/ts/ApiResponse.ts:
--------------------------------------------------------------------------------
1 | export const ApiResponse = {
2 | type: "object",
3 | properties: {
4 | code: {
5 | type: "integer",
6 | format: "int32",
7 | minimum: -2147483648,
8 | maximum: 2147483647,
9 | },
10 | type: { type: "string" },
11 | message: { type: "string" },
12 | FooBARBaz: {
13 | type: "string",
14 | description:
15 | "this name is valid and should be FooBARBaz everywhere it appears",
16 | title: "FooBARBaz",
17 | $id: "FooBARBaz.json",
18 | },
19 | },
20 | title: "ApiResponse",
21 | $id: "ApiResponse.json",
22 | } as const;
23 |
--------------------------------------------------------------------------------
/example/output/ts/Category.ts:
--------------------------------------------------------------------------------
1 | export const Category = {
2 | type: "object",
3 | properties: {
4 | id: {
5 | type: "integer",
6 | format: "int64",
7 | minimum: -9223372036854776000,
8 | maximum: 9223372036854776000,
9 | },
10 | name: { type: "string" },
11 | },
12 | title: "Category",
13 | $id: "Category.json",
14 | } as const;
15 |
--------------------------------------------------------------------------------
/example/output/ts/Customer.ts:
--------------------------------------------------------------------------------
1 | export const Customer = {
2 | type: "object",
3 | properties: {
4 | id: {
5 | type: "integer",
6 | format: "int64",
7 | minimum: -9223372036854776000,
8 | maximum: 9223372036854776000,
9 | },
10 | username: { type: "string" },
11 | address: {
12 | type: "array",
13 | items: {
14 | type: "object",
15 | properties: {
16 | street: { type: "string" },
17 | city: { type: "string" },
18 | state: { type: "string" },
19 | zip: { type: "string" },
20 | },
21 | title: "Address",
22 | $id: "Address.json",
23 | },
24 | },
25 | },
26 | title: "Customer",
27 | $id: "Customer.json",
28 | } as const;
29 |
--------------------------------------------------------------------------------
/example/output/ts/DateExample.ts:
--------------------------------------------------------------------------------
1 | export const DateExample = {
2 | type: "string",
3 | format: "date-time",
4 | title: "DateExample",
5 | $id: "DateExample.json",
6 | tsType: "Date",
7 | } as const;
8 |
--------------------------------------------------------------------------------
/example/output/ts/FooBARBaz.ts:
--------------------------------------------------------------------------------
1 | export const FooBARBaz = {
2 | type: "string",
3 | description:
4 | "this name is valid and should be FooBARBaz everywhere it appears",
5 | title: "FooBARBaz",
6 | $id: "FooBARBaz.json",
7 | } as const;
8 |
--------------------------------------------------------------------------------
/example/output/ts/Order.ts:
--------------------------------------------------------------------------------
1 | export const Order = {
2 | type: "object",
3 | properties: {
4 | id: {
5 | type: "integer",
6 | format: "int64",
7 | minimum: -9223372036854776000,
8 | maximum: 9223372036854776000,
9 | },
10 | petId: {
11 | type: "integer",
12 | format: "int64",
13 | minimum: -9223372036854776000,
14 | maximum: 9223372036854776000,
15 | },
16 | quantity: {
17 | type: "integer",
18 | format: "int32",
19 | minimum: -2147483648,
20 | maximum: 2147483647,
21 | },
22 | shipDate: { type: "string", format: "date-time" },
23 | status: {
24 | type: "string",
25 | description: "Order Status",
26 | enum: ["placed", "approved", "delivered"],
27 | },
28 | complete: { type: "boolean" },
29 | },
30 | title: "Order",
31 | $id: "Order.json",
32 | } as const;
33 |
--------------------------------------------------------------------------------
/example/output/ts/Pet.ts:
--------------------------------------------------------------------------------
1 | export const Pet = {
2 | required: ["name", "photoUrls"],
3 | type: "object",
4 | properties: {
5 | id: {
6 | type: "integer",
7 | format: "int64",
8 | minimum: -9223372036854776000,
9 | maximum: 9223372036854776000,
10 | },
11 | name: { type: "string" },
12 | category: {
13 | type: "object",
14 | properties: {
15 | id: {
16 | type: "integer",
17 | format: "int64",
18 | minimum: -9223372036854776000,
19 | maximum: 9223372036854776000,
20 | },
21 | name: { type: "string" },
22 | },
23 | title: "Category",
24 | $id: "Category.json",
25 | },
26 | photoUrls: { type: "array", items: { type: "string" } },
27 | tags: {
28 | type: "array",
29 | items: {
30 | type: "object",
31 | properties: {
32 | id: {
33 | type: "integer",
34 | format: "int64",
35 | minimum: -9223372036854776000,
36 | maximum: 9223372036854776000,
37 | },
38 | name: { type: "string" },
39 | },
40 | title: "Tag",
41 | $id: "Tag.json",
42 | },
43 | },
44 | status: {
45 | type: "string",
46 | description: "pet status in the store",
47 | enum: ["available", "pending", "sold"],
48 | },
49 | nullableValue: {
50 | type: ["string", "null"],
51 | description: "example nullable value",
52 | },
53 | },
54 | title: "Pet",
55 | $id: "Pet.json",
56 | } as const;
57 |
--------------------------------------------------------------------------------
/example/output/ts/Tag.ts:
--------------------------------------------------------------------------------
1 | export const Tag = {
2 | type: "object",
3 | properties: {
4 | id: {
5 | type: "integer",
6 | format: "int64",
7 | minimum: -9223372036854776000,
8 | maximum: 9223372036854776000,
9 | },
10 | name: { type: "string" },
11 | },
12 | title: "Tag",
13 | $id: "Tag.json",
14 | } as const;
15 |
--------------------------------------------------------------------------------
/example/output/ts/User.ts:
--------------------------------------------------------------------------------
1 | export const User = {
2 | type: "object",
3 | properties: {
4 | id: {
5 | type: "integer",
6 | format: "int64",
7 | minimum: -9223372036854776000,
8 | maximum: 9223372036854776000,
9 | },
10 | username: { type: "string" },
11 | firstName: { type: "string" },
12 | lastName: { type: "string" },
13 | email: { type: "string" },
14 | password: { type: "string" },
15 | phone: { type: "string" },
16 | userStatus: {
17 | type: "integer",
18 | description: "User Status",
19 | format: "int32",
20 | minimum: -2147483648,
21 | maximum: 2147483647,
22 | },
23 | },
24 | title: "User",
25 | $id: "User.json",
26 | } as const;
27 |
--------------------------------------------------------------------------------
/example/output/types/Address.d.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | /**
3 | * This file was automatically generated by openapi-transformer-toolkit CLI/methods.
4 | * DO NOT MODIFY IT BY HAND. Instead, modify the source OpenAPI file,
5 | * and run openapi-transformer-toolkit CLI/methods to regenerate this file.
6 | */
7 |
8 | export interface Address {
9 | street?: string;
10 | city?: string;
11 | state?: string;
12 | zip?: string;
13 | [k: string]: unknown;
14 | }
15 |
--------------------------------------------------------------------------------
/example/output/types/ApiResponse.d.ts:
--------------------------------------------------------------------------------
1 | import { FooBARBaz } from './FooBARBaz';
2 |
3 | /* eslint-disable */
4 | /**
5 | * This file was automatically generated by openapi-transformer-toolkit CLI/methods.
6 | * DO NOT MODIFY IT BY HAND. Instead, modify the source OpenAPI file,
7 | * and run openapi-transformer-toolkit CLI/methods to regenerate this file.
8 | */
9 |
10 | export interface ApiResponse {
11 | code?: number;
12 | type?: string;
13 | message?: string;
14 | FooBARBaz?: FooBARBaz;
15 | [k: string]: unknown;
16 | }
17 |
--------------------------------------------------------------------------------
/example/output/types/Category.d.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | /**
3 | * This file was automatically generated by openapi-transformer-toolkit CLI/methods.
4 | * DO NOT MODIFY IT BY HAND. Instead, modify the source OpenAPI file,
5 | * and run openapi-transformer-toolkit CLI/methods to regenerate this file.
6 | */
7 |
8 | export interface Category {
9 | id?: number;
10 | name?: string;
11 | [k: string]: unknown;
12 | }
13 |
--------------------------------------------------------------------------------
/example/output/types/Customer.d.ts:
--------------------------------------------------------------------------------
1 | import { Address } from './Address';
2 |
3 | /* eslint-disable */
4 | /**
5 | * This file was automatically generated by openapi-transformer-toolkit CLI/methods.
6 | * DO NOT MODIFY IT BY HAND. Instead, modify the source OpenAPI file,
7 | * and run openapi-transformer-toolkit CLI/methods to regenerate this file.
8 | */
9 |
10 | export interface Customer {
11 | id?: number;
12 | username?: string;
13 | address?: Address[];
14 | [k: string]: unknown;
15 | }
16 |
--------------------------------------------------------------------------------
/example/output/types/DateExample.d.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | /**
3 | * This file was automatically generated by openapi-transformer-toolkit CLI/methods.
4 | * DO NOT MODIFY IT BY HAND. Instead, modify the source OpenAPI file,
5 | * and run openapi-transformer-toolkit CLI/methods to regenerate this file.
6 | */
7 |
8 | export type DateExample = Date;
9 |
--------------------------------------------------------------------------------
/example/output/types/FooBARBaz.d.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | /**
3 | * This file was automatically generated by openapi-transformer-toolkit CLI/methods.
4 | * DO NOT MODIFY IT BY HAND. Instead, modify the source OpenAPI file,
5 | * and run openapi-transformer-toolkit CLI/methods to regenerate this file.
6 | */
7 |
8 | /**
9 | * this name is valid and should be FooBARBaz everywhere it appears
10 | */
11 | export type FooBARBaz = string;
12 |
--------------------------------------------------------------------------------
/example/output/types/Order.d.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | /**
3 | * This file was automatically generated by openapi-transformer-toolkit CLI/methods.
4 | * DO NOT MODIFY IT BY HAND. Instead, modify the source OpenAPI file,
5 | * and run openapi-transformer-toolkit CLI/methods to regenerate this file.
6 | */
7 |
8 | export interface Order {
9 | id?: number;
10 | petId?: number;
11 | quantity?: number;
12 | shipDate?: string;
13 | /**
14 | * Order Status
15 | */
16 | status?: 'placed' | 'approved' | 'delivered';
17 | complete?: boolean;
18 | [k: string]: unknown;
19 | }
20 |
--------------------------------------------------------------------------------
/example/output/types/Pet.d.ts:
--------------------------------------------------------------------------------
1 | import { Category } from './Category';
2 | import { Tag } from './Tag';
3 |
4 | /* eslint-disable */
5 | /**
6 | * This file was automatically generated by openapi-transformer-toolkit CLI/methods.
7 | * DO NOT MODIFY IT BY HAND. Instead, modify the source OpenAPI file,
8 | * and run openapi-transformer-toolkit CLI/methods to regenerate this file.
9 | */
10 |
11 | export interface Pet {
12 | id?: number;
13 | name: string;
14 | category?: Category;
15 | photoUrls: string[];
16 | tags?: Tag[];
17 | /**
18 | * pet status in the store
19 | */
20 | status?: 'available' | 'pending' | 'sold';
21 | /**
22 | * example nullable value
23 | */
24 | nullableValue?: string | null;
25 | [k: string]: unknown;
26 | }
27 |
--------------------------------------------------------------------------------
/example/output/types/Tag.d.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | /**
3 | * This file was automatically generated by openapi-transformer-toolkit CLI/methods.
4 | * DO NOT MODIFY IT BY HAND. Instead, modify the source OpenAPI file,
5 | * and run openapi-transformer-toolkit CLI/methods to regenerate this file.
6 | */
7 |
8 | export interface Tag {
9 | id?: number;
10 | name?: string;
11 | [k: string]: unknown;
12 | }
13 |
--------------------------------------------------------------------------------
/example/output/types/User.d.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | /**
3 | * This file was automatically generated by openapi-transformer-toolkit CLI/methods.
4 | * DO NOT MODIFY IT BY HAND. Instead, modify the source OpenAPI file,
5 | * and run openapi-transformer-toolkit CLI/methods to regenerate this file.
6 | */
7 |
8 | export interface User {
9 | id?: number;
10 | username?: string;
11 | firstName?: string;
12 | lastName?: string;
13 | email?: string;
14 | password?: string;
15 | phone?: string;
16 | /**
17 | * User Status
18 | */
19 | userStatus?: number;
20 | [k: string]: unknown;
21 | }
22 |
--------------------------------------------------------------------------------
/index.ts:
--------------------------------------------------------------------------------
1 | export { runCommand as json2ts } from './src/commands/json2ts.js'
2 | export { runCommand as oas2json } from './src/commands/oas2json.js'
3 | export { runCommand as oas2ts } from './src/commands/oas2ts.js'
4 | export { runCommand as oas2tson } from './src/commands/oas2tson.js'
5 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "openapi-transformer-toolkit",
3 | "version": "1.5.0",
4 | "description": "Generates schemas and types from OpenAPI specifications",
5 | "main": "dist/esm/index.js",
6 | "type": "module",
7 | "license": "ISC",
8 | "scripts": {
9 | "generate-toc": "node ./scripts/generate-toc.mjs",
10 | "prejson2ts": "npm run build",
11 | "json2ts": "./dist/esm/src/cli.js json2ts -i ./example/output/json/components.schemas -o ./example/output/types -c ./example/json-schema-to-typescript-config.json",
12 | "lint": "eslint .",
13 | "preoas2json": "npm run build",
14 | "oas2json": "./dist/esm/src/cli.js oas2json -i ./example/openapi.yml -o ./example/output/json",
15 | "preoas2ts": "npm run build",
16 | "oas2ts": "./dist/esm/src/cli.js oas2ts -i ./example/openapi.yml -o ./example/output/types -c ./example/json-schema-to-typescript-config.json",
17 | "preoas2tson": "npm run build",
18 | "oas2tson": "./dist/esm/src/cli.js oas2tson -i ./example/openapi.yml -o ./example/output/ts",
19 | "prepare": "husky",
20 | "test": "node --test --loader ts-node/esm test/*.test.ts",
21 | "build": "tsup-node",
22 | "postbuild": "cp ./package.json ./dist/esm"
23 | },
24 | "bin": {
25 | "openapi-transformer-toolkit": "./dist/esm/src/cli.js"
26 | },
27 | "files": [
28 | "dist",
29 | "example"
30 | ],
31 | "repository": {
32 | "type": "git",
33 | "url": "git+https://github.com/nearform/openapi-transformer-toolkit.git"
34 | },
35 | "author": "",
36 | "bugs": {
37 | "url": "https://github.com/nearform/openapi-transformer-toolkit/issues"
38 | },
39 | "homepage": "https://github.com/nearform/openapi-transformer-toolkit#readme",
40 | "dependencies": {
41 | "@apidevtools/json-schema-ref-parser": "^12.0.1",
42 | "@openapi-contrib/openapi-schema-to-json-schema": "^5.1.0",
43 | "commander": "^14.0.0",
44 | "desm": "^1.3.1",
45 | "filenamify": "^6.0.0",
46 | "fs-extra": "^11.1.1",
47 | "json-schema-to-typescript": "^15.0.2",
48 | "lodash.get": "^4.4.2",
49 | "lodash.trimstart": "^4.5.1",
50 | "pino": "^9.3.1",
51 | "prettier": "^3.3.3",
52 | "yaml": "^2.4.5"
53 | },
54 | "devDependencies": {
55 | "@commitlint/cli": "^19.3.0",
56 | "@commitlint/config-conventional": "^19.2.2",
57 | "@types/fs-extra": "^11.0.4",
58 | "@types/lodash": "^4.17.13",
59 | "@types/lodash.get": "^4.4.9",
60 | "@types/lodash.trimstart": "^4.5.9",
61 | "@types/node": "^22.9.0",
62 | "@typescript-eslint/eslint-plugin": "^7.16.1",
63 | "@typescript-eslint/parser": "^7.16.1",
64 | "eslint": "^8.57.0",
65 | "eslint-config-prettier": "^10.0.1",
66 | "eslint-plugin-prettier": "^5.2.1",
67 | "husky": "^9.1.1",
68 | "lint-staged": "^16.0.0",
69 | "remark": "^15.0.1",
70 | "remark-toc": "^9.0.0",
71 | "ts-node": "^10.9.2",
72 | "tsup": "^8.2.0",
73 | "typescript": "^5.5.3"
74 | },
75 | "lint-staged": {
76 | "*.{js,jsx}": "eslint --cache --fix"
77 | },
78 | "keywords": [
79 | "openapi",
80 | "openapi3",
81 | "swagger",
82 | "api",
83 | "json-schema",
84 | "typescript",
85 | "codegen",
86 | "code-generation",
87 | "autogenerate",
88 | "schema",
89 | "api-design",
90 | "api-development",
91 | "rest-api",
92 | "oas",
93 | "json",
94 | "transformer",
95 | "toolkit"
96 | ]
97 | }
98 |
--------------------------------------------------------------------------------
/scripts/generate-toc.mjs:
--------------------------------------------------------------------------------
1 | import { readFile, writeFile } from 'fs/promises'
2 | import { remark } from 'remark'
3 | import remarkToc from 'remark-toc'
4 |
5 | const README_PATH = 'README.md'
6 |
7 | const readmeContent = await readFile(README_PATH, 'utf8')
8 |
9 | const file = await remark().use(remarkToc).process(readmeContent)
10 |
11 | await writeFile(README_PATH, String(file))
12 |
--------------------------------------------------------------------------------
/src/cli.ts:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | import { Command } from 'commander'
4 | import packageJson from '../package.json' with { type: 'json' }
5 | import { json2ts } from './commands/json2ts.js'
6 | import { oas2json } from './commands/oas2json.js'
7 | import { oas2ts } from './commands/oas2ts.js'
8 | import { oas2tson } from './commands/oas2tson.js'
9 |
10 | const program = new Command()
11 | program
12 | .name('openapi-transformer-toolkit')
13 | .description('Generates schemas and types from OpenAPI specifications')
14 | .version(packageJson.version)
15 | .option('-d, --debug', 'Output debugging information')
16 |
17 | program.addCommand(oas2json)
18 | program.addCommand(json2ts)
19 | program.addCommand(oas2ts)
20 | program.addCommand(oas2tson)
21 |
22 | program.parse()
23 |
--------------------------------------------------------------------------------
/src/commands/json2ts.ts:
--------------------------------------------------------------------------------
1 | import { $RefParser } from '@apidevtools/json-schema-ref-parser'
2 | import { Command } from 'commander'
3 | import fs from 'fs-extra'
4 | import { compileFromFile } from 'json-schema-to-typescript'
5 | import path from 'path'
6 | import pino from 'pino'
7 | import { format } from 'prettier'
8 | import { exit } from 'process'
9 | import type {
10 | Json2TsArgs,
11 | Json2TsDefaultOptions,
12 | Json2TsOptions
13 | } from '../types/Json2TsOptions'
14 | import { doNotEditText } from '../utils/do-not-edit-text.js'
15 | import { readConfigFile } from '../utils/read-config-file.js'
16 |
17 | const generateAndWriteTsFile = async (
18 | schemaPath: string,
19 | tsTypesPath: string,
20 | options: Json2TsOptions
21 | ) => {
22 | const ts = await compileFromFile(schemaPath, options)
23 |
24 | const interfaceName = path.basename(schemaPath, '.json')
25 |
26 | const parser = new $RefParser()
27 | await parser.dereference(schemaPath)
28 |
29 | const imports = Object.values(parser.$refs.values())
30 | .filter(
31 | refSchema =>
32 | refSchema.$id && refSchema.title && refSchema.title !== interfaceName
33 | )
34 | .map(
35 | refSchema =>
36 | `import { ${refSchema.title} } from './${refSchema.$id.replace(
37 | '.json',
38 | ''
39 | )}'`
40 | )
41 | .join('\n')
42 |
43 | const tsWithImports = `${imports ? `${imports}\n\n` : ''}${ts}`
44 | const tsFormatted = await format(tsWithImports, {
45 | parser: 'typescript',
46 | ...options.style
47 | })
48 |
49 | const tsFileName = path.basename(schemaPath, '.json') + '.d.ts'
50 |
51 | fs.writeFileSync(path.join(tsTypesPath, tsFileName), tsFormatted)
52 | }
53 |
54 | export const runCommand = async (
55 | schemasPath: string,
56 | tsTypesPath: string,
57 | customOptions?: Json2TsOptions,
58 | logger = pino()
59 | ) => {
60 | fs.removeSync(tsTypesPath)
61 | fs.ensureDirSync(tsTypesPath)
62 |
63 | let schemaPaths
64 |
65 | try {
66 | schemaPaths = fs.readdirSync(schemasPath)
67 | } catch (e) {
68 | logger.error('❌ Could not find the JSON schemas folder')
69 | exit(1)
70 | }
71 |
72 | const defaultOptions: Json2TsDefaultOptions = {
73 | cwd: schemasPath,
74 | bannerComment: doNotEditText,
75 | declareExternallyReferenced: false
76 | }
77 |
78 | const options = { ...defaultOptions, ...customOptions }
79 |
80 | for (const schemaFileName of schemaPaths) {
81 | const schemaPath = path.join(schemasPath, schemaFileName)
82 | await generateAndWriteTsFile(schemaPath, tsTypesPath, options)
83 | }
84 |
85 | logger.info('✅ TypeScript types generated successfully from JSON schemas')
86 | }
87 |
88 | const main = () => {
89 | const options = json2ts.optsWithGlobals()
90 | const customOptions = options.config ? readConfigFile(options.config) : {}
91 | runCommand(options.input, options.output, customOptions, options.muteLogger)
92 | }
93 |
94 | const json2ts = new Command('json2ts')
95 |
96 | const description = `This command will generate TypeScript types from JSON schemas.
97 |
98 | Examples:
99 | $ openapi-transformer-toolkit json2ts -i ./schemas -o ./types
100 | $ openapi-transformer-toolkit json2ts -i ./schemas -o ./types -c ./config.json
101 | `
102 |
103 | json2ts
104 | .summary('Creates TypeScript types from JSON schemas')
105 | .description(description)
106 | .requiredOption('-i, --input ', 'Path to the JSON schemas folder')
107 | .requiredOption(
108 | '-o, --output ',
109 | 'Path to the folder where to output the TS files'
110 | )
111 | .option(
112 | '-c, --config ',
113 | 'Path to the JSON/JS config file with these possible options: https://www.npmjs.com/package/json-schema-to-typescript'
114 | )
115 | .allowUnknownOption()
116 | .allowExcessArguments(true)
117 | .action(main)
118 |
119 | export { json2ts }
120 |
--------------------------------------------------------------------------------
/src/commands/oas2json.ts:
--------------------------------------------------------------------------------
1 | import { Command } from 'commander'
2 | import filenamify from 'filenamify'
3 | import fs from 'fs-extra'
4 | import _get from 'lodash.get'
5 | import _trimStart from 'lodash.trimstart'
6 | import path from 'path'
7 | import pino from 'pino'
8 | import { exit } from 'process'
9 | import YAML from 'yaml'
10 |
11 | import type { JSONSchema4 } from 'json-schema'
12 |
13 | import { fromSchema } from '../utils/openapi-schema-to-json-schema-wrapper.js'
14 |
15 | const COMPONENT_REF_REGEXP =
16 | /#\/components\/(callbacks|examples|headers|links|parameters|requestBodies|responses|schemas|securitySchemes)\/[^"]+/g
17 | const INVALID_URI_CHARS_REGEXP = /[^a-zA-Z0-9\-._~:/?#[\]@!$&'()*+,;=]/g
18 |
19 | export const adaptSchema = (
20 | generatedSchema: JSONSchema4,
21 | name: string,
22 | filename: string
23 | ) => {
24 | const sanitizedFilename = filename.replace(INVALID_URI_CHARS_REGEXP, '')
25 | delete generatedSchema.$schema
26 | generatedSchema.title = name
27 | generatedSchema.$id = `${sanitizedFilename}.json`
28 |
29 | if (generatedSchema.format?.includes('date')) {
30 | generatedSchema.tsType = 'Date'
31 | }
32 | }
33 |
34 | const processSchema = (
35 | schema: JSONSchema4,
36 | schemasPath: string,
37 | definitionKeyword: string,
38 | isArray: boolean
39 | ) => {
40 | Object.entries(schema).forEach(([key, value]) => {
41 | // for elements in an array the name would be its index if we were
42 | // to just use its key, so go into the parsed schema and get the
43 | // actual name so the files are more easily identifiable
44 | const name = isArray ? value.name : key
45 | const filename = _trimStart(filenamify(name, { replacement: '-' }), '-')
46 |
47 | adaptSchema(value, name, filename)
48 |
49 | let schemaAsString = JSON.stringify(value, null, 2)
50 | const refs = schemaAsString.match(COMPONENT_REF_REGEXP)
51 | refs?.forEach(ref => {
52 | const refName = ref.split('/').slice(-1)
53 | schemaAsString = schemaAsString.replace(ref, `${refName}.json`)
54 | })
55 |
56 | const destinationDir = path.join(schemasPath, definitionKeyword)
57 | const destinationPath = path.join(destinationDir, `${filename}.json`)
58 |
59 | fs.ensureDirSync(destinationDir)
60 | fs.writeFileSync(destinationPath, schemaAsString)
61 | })
62 | }
63 |
64 | export const runCommand = (
65 | openApiPath: string,
66 | schemasPath: string,
67 | propertiesToExport?: string,
68 | logger = pino()
69 | ) => {
70 | fs.removeSync(schemasPath)
71 | fs.ensureDirSync(schemasPath)
72 |
73 | let openAPIContent
74 |
75 | try {
76 | openAPIContent = fs.readFileSync(openApiPath, 'utf8')
77 | } catch (e) {
78 | logger.error('❌ Could not find the OpenAPI file')
79 | exit(1)
80 | }
81 |
82 | const parsedOpenAPIContent = YAML.parse(openAPIContent)
83 |
84 | const definitionKeywords = [
85 | ...new Set([
86 | ...(propertiesToExport?.split(',') || []),
87 | 'components.schemas'
88 | ])
89 | ]
90 |
91 | try {
92 | const generatedSchema = fromSchema(parsedOpenAPIContent, {
93 | definitionKeywords
94 | })
95 |
96 | definitionKeywords.forEach(key => {
97 | const schema: JSONSchema4 = _get(generatedSchema, key)
98 | const isArray = Array.isArray(_get(parsedOpenAPIContent, key))
99 | processSchema(schema, schemasPath, key, isArray)
100 | })
101 | } catch (error) {
102 | logger.warn('Failed to convert non-object attribute, skipping')
103 | return
104 | }
105 |
106 | logger.info('✅ JSON schemas generated successfully from OpenAPI file')
107 | }
108 |
109 | const main = () => {
110 | const options = oas2json.optsWithGlobals()
111 | runCommand(options.input, options.output, options.properties, options.logger)
112 | }
113 |
114 | const oas2json = new Command('oas2json')
115 |
116 | const description = `This command will generate JSON schemas from an OpenAPI file.
117 |
118 | Examples:
119 | $ openapi-transformer-toolkit oas2json -i ./openapi.yml -o ./schemas
120 | `
121 |
122 | oas2json
123 | .summary('Create JSON schemas from an OpenAPI file')
124 | .description(description)
125 | .requiredOption('-i, --input ', 'Path to the OpenAPI file')
126 | .requiredOption(
127 | '-o, --output ',
128 | 'Path to the folder where to output the schemas'
129 | )
130 | .option(
131 | '-p, --properties ',
132 | 'Comma-separated list of properties to convert from the OpenAPI file'
133 | )
134 | .allowUnknownOption()
135 | .allowExcessArguments(true)
136 | .action(main)
137 |
138 | export { oas2json }
139 |
--------------------------------------------------------------------------------
/src/commands/oas2ts.ts:
--------------------------------------------------------------------------------
1 | import { Command } from 'commander'
2 | import fs from 'fs-extra'
3 | import os from 'os'
4 | import path from 'path'
5 | import pino, { Logger } from 'pino'
6 | import type { Json2TsOptions } from '../types/Json2TsOptions'
7 | import { readConfigFile } from '../utils/read-config-file.js'
8 | import { runCommand as runJson2TsCommand } from './json2ts.js'
9 | import { runCommand as runOas2JsonCommand } from './oas2json.js'
10 |
11 | const TEMP_FOLDER = path.join(os.tmpdir(), 'temp-json-schemas')
12 |
13 | const cleanUpTempFolder = (logger: Logger) =>
14 | fs.remove(TEMP_FOLDER).catch(error => {
15 | logger.error('❌ Failed to clean up temporary folder:', error.message)
16 | })
17 |
18 | export const runCommand = async (
19 | openApiPath: string,
20 | tsTypesPath: string,
21 | customOptions?: Json2TsOptions,
22 | logger: Logger = pino()
23 | ) => {
24 | try {
25 | const silentLogger = pino({ level: 'silent' })
26 | const schemasDir = path.join(TEMP_FOLDER, 'components.schemas')
27 | runOas2JsonCommand(openApiPath, TEMP_FOLDER, undefined, silentLogger)
28 |
29 | await runJson2TsCommand(
30 | schemasDir,
31 | tsTypesPath,
32 | customOptions,
33 | silentLogger
34 | )
35 |
36 | logger.info('✅ TypeScript types generated successfully from OpenAPI file')
37 | } catch (error) {
38 | logger.error(
39 | '❌ An error occurred during the process:',
40 | (error as Error).message
41 | )
42 | } finally {
43 | await cleanUpTempFolder(logger)
44 | }
45 | }
46 |
47 | const main = async () => {
48 | const options = oas2ts.optsWithGlobals()
49 | const customOptions = options.config ? readConfigFile(options.config) : {}
50 | runCommand(options.input, options.output, customOptions, options.logger)
51 | }
52 |
53 | const oas2ts = new Command('oas2ts')
54 |
55 | const description = `This command will generate TypeScript types from an OpenAPI file.
56 |
57 | Examples:
58 | $ openapi-transformer-toolkit oas2ts -i ./openapi.yml -o ./types
59 | $ openapi-transformer-toolkit oas2ts -i ./openapi.yml -o ./types -c ./config.json
60 | `
61 |
62 | oas2ts
63 | .summary('Create TypeScript types from an OpenAPI file')
64 | .description(description)
65 | .requiredOption('-i, --input ', 'Path to the OpenAPI file')
66 | .requiredOption(
67 | '-o, --output ',
68 | 'Path to the folder where to output the TypeScript types'
69 | )
70 | .option(
71 | '-c, --config ',
72 | 'Path to the JSON/JS config file with these possible options: https://www.npmjs.com/package/json-schema-to-typescript'
73 | )
74 | .allowUnknownOption()
75 | .allowExcessArguments(true)
76 | .action(main)
77 |
78 | export { oas2ts }
79 |
--------------------------------------------------------------------------------
/src/commands/oas2tson.ts:
--------------------------------------------------------------------------------
1 | import $RefParser, { ParserOptions } from '@apidevtools/json-schema-ref-parser'
2 | import { Command } from 'commander'
3 | import filenamify from 'filenamify'
4 | import fs from 'fs-extra'
5 | import _get from 'lodash.get'
6 | import _trimStart from 'lodash.trimstart'
7 | import path from 'path'
8 | import pino from 'pino'
9 | import { exit } from 'process'
10 | import YAML from 'yaml'
11 |
12 | import os from 'os'
13 | import prettier from 'prettier'
14 |
15 | import type { JSONSchema4 } from 'json-schema'
16 |
17 | import type { Oas2Tson } from '../types/Oas2Tson'
18 | import type SchemasMetaData from '../types/SchemasMetaData'
19 | import { fromSchema } from '../utils/openapi-schema-to-json-schema-wrapper.js'
20 |
21 | const COMPONENT_REF_REGEXP = /#\/components\/schemas\/[^"]+/g
22 | const outputSchemasMetaData: SchemasMetaData[] = []
23 |
24 | export const adaptSchema = (
25 | generatedSchema: JSONSchema4,
26 | name: string,
27 | filename: string
28 | ) => {
29 | delete generatedSchema.$schema
30 | generatedSchema.title = name
31 | generatedSchema.$id = `${filename}.json`
32 | }
33 |
34 | const processSchema = (
35 | schema: JSONSchema4,
36 | schemasPath: string,
37 | definitionKeyword: string,
38 | isArray: boolean
39 | ) => {
40 | Object.entries(schema).forEach(([key, value]) => {
41 | // for elements in an array the name would be its index if we were
42 | // to just use its key, so go into the parsed schema and get the
43 | // actual name so the files are more easily identifiable
44 | const name = isArray ? value.name : key
45 | const filename = _trimStart(filenamify(name, { replacement: '-' }), '-')
46 |
47 | adaptSchema(value, name, filename)
48 |
49 | let schemaAsString = JSON.stringify(value, null, 2)
50 | // N.B. - this obviously only supports refs where the string contains 'components/schemas'
51 | // if we want to support refs in places other than this, we'll need to revisit this
52 | // approach to be more flexible
53 | const refs = schemaAsString.match(COMPONENT_REF_REGEXP)
54 | refs?.forEach(ref => {
55 | const refName = ref.split('/').slice(-1)
56 | schemaAsString = schemaAsString.replace(ref, `${refName}.json`)
57 | })
58 |
59 | const destinationDir = path.join(schemasPath, 'tempjson')
60 | const destinationPath = path.join(destinationDir, `${filename}.json`)
61 |
62 | outputSchemasMetaData.push({ dir: destinationDir, path: destinationPath })
63 |
64 | fs.ensureDirSync(destinationDir)
65 | fs.writeFileSync(destinationPath, schemaAsString)
66 | })
67 | }
68 |
69 | const parserOptions: ParserOptions = {
70 | dereference: {
71 | onDereference: (path: string, value: JSONSchema4) => {
72 | delete value.$id
73 | }
74 | }
75 | }
76 |
77 | const processJSON = async (
78 | schemasPath: string,
79 | tempdir: string,
80 | excludeDereferencedIds?: boolean
81 | ) => {
82 | fs.ensureDirSync(schemasPath)
83 | for (const currentSchema of outputSchemasMetaData) {
84 | /**
85 | * monitor https://github.com/APIDevTools/json-schema-ref-parser/issues/342
86 | * to check if they accept a flag to exclude Ids and eventually remove the onDereference callback
87 | */
88 | const dereferencedSchema = await (excludeDereferencedIds
89 | ? $RefParser.dereference(currentSchema.path, parserOptions)
90 | : $RefParser.dereference(currentSchema.path))
91 |
92 | const fileName = path.parse(currentSchema.path).name
93 | const tsSchema = `export const ${fileName} = ${JSON.stringify(
94 | dereferencedSchema
95 | )} as const`
96 | const formattedSchema = await prettier.format(tsSchema, {
97 | parser: 'typescript'
98 | })
99 | fs.writeFileSync(path.join(schemasPath, `${fileName}.ts`), formattedSchema)
100 | }
101 | fs.removeSync(path.join(tempdir, 'tempjson'))
102 | }
103 |
104 | export const runCommand = async (
105 | openApiPath: string,
106 | schemasPath: string,
107 | propertiesToExport?: string,
108 | excludeDereferencedIds?: boolean,
109 | logger = pino()
110 | ) => {
111 | fs.removeSync(schemasPath)
112 | fs.ensureDirSync(schemasPath)
113 |
114 | let openAPIContent
115 |
116 | try {
117 | openAPIContent = fs.readFileSync(openApiPath, 'utf8')
118 | } catch (e) {
119 | logger.error('❌ Could not find the OpenAPI file')
120 | exit(1)
121 | }
122 |
123 | const parsedOpenAPIContent = YAML.parse(openAPIContent)
124 |
125 | const definitionKeywords = [
126 | ...new Set([
127 | ...(propertiesToExport?.split(',') || []),
128 | 'components.schemas'
129 | ])
130 | ]
131 |
132 | try {
133 | const generatedSchema = fromSchema(parsedOpenAPIContent, {
134 | definitionKeywords
135 | })
136 |
137 | const tempdir = os.tmpdir()
138 | definitionKeywords.forEach(key => {
139 | const schema = _get(generatedSchema, key)
140 | const isArray = Array.isArray(_get(parsedOpenAPIContent, key))
141 | processSchema(schema, tempdir, key, isArray)
142 | })
143 | await processJSON(schemasPath, tempdir, excludeDereferencedIds)
144 | } catch (error) {
145 | logger.warn(error, 'Failed to convert non-object attribute, skipping')
146 | return
147 | }
148 |
149 | logger.info('✅ TS schemas generated successfully from OpenAPI file')
150 | }
151 |
152 | const main = () => {
153 | const options = oas2tson.optsWithGlobals()
154 | runCommand(
155 | options.input,
156 | options.output,
157 | options.properties,
158 | options.excludeDereferencedIds,
159 | options.logger
160 | )
161 | }
162 |
163 | const oas2tson = new Command('oas2tson')
164 |
165 | const description = `This command will generate JSON schemas from an OpenAPI file.
166 |
167 | Examples:
168 | $ openapi-transformer-toolkit oas2tson -i ./openapi.yml -o ./schemas
169 | `
170 |
171 | oas2tson
172 | .summary('Create JSON schemas from an OpenAPI file')
173 | .description(description)
174 | .requiredOption('-i, --input ', 'Path to the OpenAPI file')
175 | .requiredOption(
176 | '-o, --output ',
177 | 'Path to the folder where to output the schemas'
178 | )
179 | .option(
180 | '-p, --properties ',
181 | 'Comma-separated list of properties to convert from the OpenAPI file'
182 | )
183 | .option('--excludeDereferencedIds', 'exclude $id of dereferenced schemas')
184 | .allowUnknownOption()
185 | .allowExcessArguments(true)
186 | .action(main)
187 |
188 | export { oas2tson }
189 |
--------------------------------------------------------------------------------
/src/types/Json2TsOptions.d.ts:
--------------------------------------------------------------------------------
1 | import { Options } from 'json-schema-to-typescript'
2 | import { Logger } from 'pino'
3 |
4 | export type Json2TsArgs = {
5 | input: string
6 | output: string
7 | config?: string
8 | muteLogger?: Logger
9 | }
10 |
11 | export type Json2TsDefaultOptions = Pick<
12 | Options,
13 | 'cwd' | 'bannerComment' | 'declareExternallyReferenced'
14 | >
15 |
16 | export type Json2TsOptions = Partial
17 |
--------------------------------------------------------------------------------
/src/types/Oas2Tson.d.ts:
--------------------------------------------------------------------------------
1 | import { Logger } from 'pino'
2 |
3 | export type Oas2Tson = {
4 | input: string
5 | output: string
6 | properties?: string
7 | excludeDereferencedIds?: boolean
8 | logger?: Logger
9 | }
10 |
--------------------------------------------------------------------------------
/src/types/SchemasMetaData.d.ts:
--------------------------------------------------------------------------------
1 | export default interface SchemasMetaData {
2 | dir: string
3 | path: string
4 | }
5 |
--------------------------------------------------------------------------------
/src/utils/do-not-edit-text.ts:
--------------------------------------------------------------------------------
1 | export const doNotEditText = `/* eslint-disable */
2 | /**
3 | * This file was automatically generated by openapi-transformer-toolkit CLI/methods.
4 | * DO NOT MODIFY IT BY HAND. Instead, modify the source OpenAPI file,
5 | * and run openapi-transformer-toolkit CLI/methods to regenerate this file.
6 | */
7 |
8 | `
9 |
--------------------------------------------------------------------------------
/src/utils/openapi-schema-to-json-schema-wrapper.ts:
--------------------------------------------------------------------------------
1 | export { fromSchema } from '@openapi-contrib/openapi-schema-to-json-schema'
2 |
--------------------------------------------------------------------------------
/src/utils/paths.ts:
--------------------------------------------------------------------------------
1 | import { join as joinDesm } from 'desm'
2 | import { join, resolve } from 'node:path'
3 | import { cwd } from 'node:process'
4 |
5 | const workingDirectory = cwd()
6 | const packageRoot = joinDesm(import.meta.url, '..', '..')
7 |
8 | const resolvePath = (basePath: string, ...pathParts: string[]): string =>
9 | resolve(join(basePath, ...pathParts))
10 |
11 | export const resolveFromPackageRoot = (...pathParts: string[]) =>
12 | resolvePath(packageRoot, ...pathParts)
13 |
14 | export const resolveFromWorkingDirectory = (...pathParts: string[]) =>
15 | resolvePath(workingDirectory, ...pathParts)
16 |
--------------------------------------------------------------------------------
/src/utils/read-config-file.ts:
--------------------------------------------------------------------------------
1 | import fs from 'fs-extra'
2 | import pino from 'pino'
3 | import { exit } from 'process'
4 | import { resolveFromWorkingDirectory } from './paths.js'
5 |
6 | const readConfigFile = (configPath: string) => {
7 | const logger = pino()
8 | const resolvedPath = resolveFromWorkingDirectory(configPath)
9 |
10 | try {
11 | return require(resolvedPath)
12 | } catch (error) {
13 | try {
14 | const fileContents = fs.readFileSync(resolvedPath, 'utf-8')
15 | return JSON.parse(fileContents)
16 | } catch (jsonError) {
17 | logger.error(
18 | '❌ Could not load the config file as a JS module or parse it as JSON. Please check the file content.'
19 | )
20 | exit(1)
21 | }
22 | }
23 | }
24 |
25 | export { readConfigFile }
26 |
--------------------------------------------------------------------------------
/test/fixtures/openapi.yml:
--------------------------------------------------------------------------------
1 | openapi: 3.0.3
2 | info:
3 | title: Swagger Petstore - OpenAPI 3.0
4 | description: |-
5 | This is a sample Pet Store Server based on the OpenAPI 3.0 specification. You can find out more about
6 | Swagger at [https://swagger.io](https://swagger.io). In the third iteration of the pet store, we've switched to the design first approach!
7 | You can now help us improve the API whether it's by making changes to the definition itself or to the code.
8 | That way, with time, we can improve the API in general, and expose some of the new features in OAS3.
9 |
10 | _If you're looking for the Swagger 2.0/OAS 2.0 version of Petstore, then click [here](https://editor.swagger.io/?url=https://petstore.swagger.io/v2/swagger.yaml). Alternatively, you can load via the `Edit > Load Petstore OAS 2.0` menu option!_
11 |
12 | Some useful links:
13 | - [The Pet Store repository](https://github.com/swagger-api/swagger-petstore)
14 | - [The source API definition for the Pet Store](https://github.com/swagger-api/swagger-petstore/blob/master/src/main/resources/openapi.yaml)
15 | termsOfService: http://swagger.io/terms/
16 | contact:
17 | email: apiteam@swagger.io
18 | license:
19 | name: Apache 2.0
20 | url: http://www.apache.org/licenses/LICENSE-2.0.html
21 | version: 1.0.11
22 | externalDocs:
23 | description: Find out more about Swagger
24 | url: http://swagger.io
25 | servers:
26 | - url: https://petstore3.swagger.io/api/v3
27 | tags:
28 | - name: pet
29 | description: Everything about your Pets
30 | externalDocs:
31 | description: Find out more
32 | url: http://swagger.io
33 | - name: store
34 | description: Access to Petstore orders
35 | externalDocs:
36 | description: Find out more about our store
37 | url: http://swagger.io
38 | - name: user
39 | description: Operations about user
40 | paths:
41 | /pet:
42 | put:
43 | tags:
44 | - pet
45 | summary: Update an existing pet
46 | description: Update an existing pet by Id
47 | operationId: updatePet
48 | requestBody:
49 | $ref: '#/components/requestBodies/PetBody'
50 | responses:
51 | '200':
52 | description: Successful operation
53 | content:
54 | application/json:
55 | schema:
56 | $ref: '#/components/schemas/Pet'
57 | application/xml:
58 | schema:
59 | $ref: '#/components/schemas/Pet'
60 | '400':
61 | description: Invalid ID supplied
62 | '404':
63 | description: Pet not found
64 | '405':
65 | description: Validation exception
66 | security:
67 | - petstore_auth:
68 | - write:pets
69 | - read:pets
70 | post:
71 | tags:
72 | - pet
73 | summary: Add a new pet to the store
74 | description: Add a new pet to the store
75 | operationId: addPet
76 | requestBody:
77 | $ref: '#/components/requestBodies/PetBody'
78 | responses:
79 | '200':
80 | description: Successful operation
81 | content:
82 | application/json:
83 | schema:
84 | $ref: '#/components/schemas/Pet'
85 | application/xml:
86 | schema:
87 | $ref: '#/components/schemas/Pet'
88 | '405':
89 | description: Invalid input
90 | security:
91 | - petstore_auth:
92 | - write:pets
93 | - read:pets
94 | /pet/findByStatus:
95 | get:
96 | tags:
97 | - pet
98 | summary: Finds Pets by status
99 | description: Multiple status values can be provided with comma separated strings
100 | operationId: findPetsByStatus
101 | parameters:
102 | - name: status
103 | in: query
104 | description: Status values that need to be considered for filter
105 | required: false
106 | explode: true
107 | schema:
108 | type: string
109 | default: available
110 | enum:
111 | - available
112 | - pending
113 | - sold
114 | responses:
115 | '200':
116 | description: successful operation
117 | content:
118 | application/json:
119 | schema:
120 | type: array
121 | items:
122 | $ref: '#/components/schemas/Pet'
123 | application/xml:
124 | schema:
125 | type: array
126 | items:
127 | $ref: '#/components/schemas/Pet'
128 | '400':
129 | description: Invalid status value
130 | security:
131 | - petstore_auth:
132 | - write:pets
133 | - read:pets
134 | /pet/findByTags:
135 | get:
136 | tags:
137 | - pet
138 | summary: Finds Pets by tags
139 | description: Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.
140 | operationId: findPetsByTags
141 | parameters:
142 | - name: tags
143 | in: query
144 | description: Tags to filter by
145 | required: false
146 | explode: true
147 | schema:
148 | type: array
149 | items:
150 | type: string
151 | responses:
152 | '200':
153 | description: successful operation
154 | content:
155 | application/json:
156 | schema:
157 | type: array
158 | items:
159 | $ref: '#/components/schemas/Pet'
160 | application/xml:
161 | schema:
162 | type: array
163 | items:
164 | $ref: '#/components/schemas/Pet'
165 | '400':
166 | description: Invalid tag value
167 | security:
168 | - petstore_auth:
169 | - write:pets
170 | - read:pets
171 | /pet/{petId}:
172 | get:
173 | tags:
174 | - pet
175 | summary: Find pet by ID
176 | description: Returns a single pet
177 | operationId: getPetById
178 | parameters:
179 | - name: petId
180 | in: path
181 | description: ID of pet to return
182 | required: true
183 | schema:
184 | type: integer
185 | format: int64
186 | responses:
187 | '200':
188 | description: successful operation
189 | content:
190 | application/json:
191 | schema:
192 | $ref: '#/components/schemas/Pet'
193 | application/xml:
194 | schema:
195 | $ref: '#/components/schemas/Pet'
196 | '400':
197 | description: Invalid ID supplied
198 | '404':
199 | description: Pet not found
200 | security:
201 | - api_key: []
202 | - petstore_auth:
203 | - write:pets
204 | - read:pets
205 | post:
206 | tags:
207 | - pet
208 | summary: Updates a pet in the store with form data
209 | description: ''
210 | operationId: updatePetWithForm
211 | parameters:
212 | - name: petId
213 | in: path
214 | description: ID of pet that needs to be updated
215 | required: true
216 | schema:
217 | type: integer
218 | format: int64
219 | - name: name
220 | in: query
221 | description: Name of pet that needs to be updated
222 | schema:
223 | type: string
224 | - name: status
225 | in: query
226 | description: Status of pet that needs to be updated
227 | schema:
228 | type: string
229 | responses:
230 | '405':
231 | description: Invalid input
232 | security:
233 | - petstore_auth:
234 | - write:pets
235 | - read:pets
236 | delete:
237 | tags:
238 | - pet
239 | summary: Deletes a pet
240 | description: delete a pet
241 | operationId: deletePet
242 | parameters:
243 | - name: api_key
244 | in: header
245 | description: ''
246 | required: false
247 | schema:
248 | type: string
249 | - name: petId
250 | in: path
251 | description: Pet id to delete
252 | required: true
253 | schema:
254 | type: integer
255 | format: int64
256 | responses:
257 | '400':
258 | description: Invalid pet value
259 | security:
260 | - petstore_auth:
261 | - write:pets
262 | - read:pets
263 | /pet/{petId}/uploadImage:
264 | post:
265 | tags:
266 | - pet
267 | summary: uploads an image
268 | description: ''
269 | operationId: uploadFile
270 | parameters:
271 | - name: petId
272 | in: path
273 | description: ID of pet to update
274 | required: true
275 | schema:
276 | type: integer
277 | format: int64
278 | - name: additionalMetadata
279 | in: query
280 | description: Additional Metadata
281 | required: false
282 | schema:
283 | type: string
284 | requestBody:
285 | content:
286 | application/octet-stream:
287 | schema:
288 | type: string
289 | format: binary
290 | responses:
291 | '200':
292 | description: successful operation
293 | content:
294 | application/json:
295 | schema:
296 | $ref: '#/components/schemas/ApiResponse'
297 | security:
298 | - petstore_auth:
299 | - write:pets
300 | - read:pets
301 | /store/inventory:
302 | get:
303 | tags:
304 | - store
305 | summary: Returns pet inventories by status
306 | description: Returns a map of status codes to quantities
307 | operationId: getInventory
308 | responses:
309 | '200':
310 | description: successful operation
311 | content:
312 | application/json:
313 | schema:
314 | type: object
315 | additionalProperties:
316 | type: integer
317 | format: int32
318 | security:
319 | - api_key: []
320 | /store/order:
321 | post:
322 | tags:
323 | - store
324 | summary: Place an order for a pet
325 | description: Place a new order in the store
326 | operationId: placeOrder
327 | requestBody:
328 | content:
329 | application/json:
330 | schema:
331 | $ref: '#/components/schemas/Order'
332 | application/xml:
333 | schema:
334 | $ref: '#/components/schemas/Order'
335 | application/x-www-form-urlencoded:
336 | schema:
337 | $ref: '#/components/schemas/Order'
338 | responses:
339 | '200':
340 | description: successful operation
341 | content:
342 | application/json:
343 | schema:
344 | $ref: '#/components/schemas/Order'
345 | '405':
346 | description: Invalid input
347 | /store/order/{orderId}:
348 | get:
349 | tags:
350 | - store
351 | summary: Find purchase order by ID
352 | description: For valid response try integer IDs with value <= 5 or > 10. Other values will generate exceptions.
353 | operationId: getOrderById
354 | parameters:
355 | - name: orderId
356 | in: path
357 | description: ID of order that needs to be fetched
358 | required: true
359 | schema:
360 | type: integer
361 | format: int64
362 | responses:
363 | '200':
364 | description: successful operation
365 | content:
366 | application/json:
367 | schema:
368 | $ref: '#/components/schemas/Order'
369 | application/xml:
370 | schema:
371 | $ref: '#/components/schemas/Order'
372 | '400':
373 | description: Invalid ID supplied
374 | '404':
375 | description: Order not found
376 | delete:
377 | tags:
378 | - store
379 | summary: Delete purchase order by ID
380 | description: For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors
381 | operationId: deleteOrder
382 | parameters:
383 | - name: orderId
384 | in: path
385 | description: ID of the order that needs to be deleted
386 | required: true
387 | schema:
388 | type: integer
389 | format: int64
390 | responses:
391 | '400':
392 | description: Invalid ID supplied
393 | '404':
394 | description: Order not found
395 | /user:
396 | post:
397 | tags:
398 | - user
399 | summary: Create user
400 | description: This can only be done by the logged in user.
401 | operationId: createUser
402 | requestBody:
403 | $ref: '#/components/requestBodies/UserBody'
404 | responses:
405 | default:
406 | description: successful operation
407 | content:
408 | application/json:
409 | schema:
410 | $ref: '#/components/schemas/User'
411 | application/xml:
412 | schema:
413 | $ref: '#/components/schemas/User'
414 | /user/createWithList:
415 | post:
416 | tags:
417 | - user
418 | summary: Creates list of users with given input array
419 | description: Creates list of users with given input array
420 | operationId: createUsersWithListInput
421 | requestBody:
422 | content:
423 | application/json:
424 | schema:
425 | type: array
426 | items:
427 | $ref: '#/components/schemas/User'
428 | responses:
429 | '200':
430 | description: Successful operation
431 | content:
432 | application/json:
433 | schema:
434 | $ref: '#/components/schemas/User'
435 | application/xml:
436 | schema:
437 | $ref: '#/components/schemas/User'
438 | default:
439 | description: successful operation
440 | /user/login:
441 | get:
442 | tags:
443 | - user
444 | summary: Logs user into the system
445 | description: ''
446 | operationId: loginUser
447 | parameters:
448 | - name: username
449 | in: query
450 | description: The user name for login
451 | required: false
452 | schema:
453 | type: string
454 | - name: password
455 | in: query
456 | description: The password for login in clear text
457 | required: false
458 | schema:
459 | type: string
460 | responses:
461 | '200':
462 | description: successful operation
463 | headers:
464 | X-Rate-Limit:
465 | description: calls per hour allowed by the user
466 | schema:
467 | type: integer
468 | format: int32
469 | X-Expires-After:
470 | description: date in UTC when token expires
471 | schema:
472 | type: string
473 | format: date-time
474 | content:
475 | application/xml:
476 | schema:
477 | type: string
478 | application/json:
479 | schema:
480 | type: string
481 | '400':
482 | description: Invalid username/password supplied
483 | /user/logout:
484 | get:
485 | tags:
486 | - user
487 | summary: Logs out current logged in user session
488 | description: ''
489 | operationId: logoutUser
490 | parameters: []
491 | responses:
492 | default:
493 | description: successful operation
494 | /user/{username}:
495 | get:
496 | tags:
497 | - user
498 | summary: Get user by user name
499 | description: ''
500 | operationId: getUserByName
501 | parameters:
502 | - name: username
503 | in: path
504 | description: 'The name that needs to be fetched. Use user1 for testing. '
505 | required: true
506 | schema:
507 | type: string
508 | responses:
509 | '200':
510 | description: successful operation
511 | content:
512 | application/json:
513 | schema:
514 | $ref: '#/components/schemas/User'
515 | application/xml:
516 | schema:
517 | $ref: '#/components/schemas/User'
518 | '400':
519 | description: Invalid username supplied
520 | '404':
521 | description: User not found
522 | put:
523 | tags:
524 | - user
525 | summary: Update user
526 | description: This can only be done by the logged in user.
527 | operationId: updateUser
528 | parameters:
529 | - name: username
530 | in: path
531 | description: name that need to be deleted
532 | required: true
533 | schema:
534 | type: string
535 | requestBody:
536 | $ref: '#/components/requestBodies/UserBody'
537 | responses:
538 | default:
539 | description: successful operation
540 | delete:
541 | tags:
542 | - user
543 | summary: Delete user
544 | description: This can only be done by the logged in user.
545 | operationId: deleteUser
546 | parameters:
547 | - name: username
548 | in: path
549 | description: The name that needs to be deleted
550 | required: true
551 | schema:
552 | type: string
553 | responses:
554 | '400':
555 | description: Invalid username supplied
556 | '404':
557 | description: User not found
558 | components:
559 | schemas:
560 | DateExample:
561 | type: string
562 | format: date-time
563 | Order:
564 | type: object
565 | properties:
566 | id:
567 | type: integer
568 | format: int64
569 | example: 10
570 | petId:
571 | type: integer
572 | format: int64
573 | example: 198772
574 | quantity:
575 | type: integer
576 | format: int32
577 | example: 7
578 | shipDate:
579 | type: string
580 | format: date-time
581 | status:
582 | type: string
583 | description: Order Status
584 | example: approved
585 | enum:
586 | - placed
587 | - approved
588 | - delivered
589 | complete:
590 | type: boolean
591 | xml:
592 | name: order
593 | Customer:
594 | type: object
595 | properties:
596 | id:
597 | type: integer
598 | format: int64
599 | example: 100000
600 | username:
601 | type: string
602 | example: fehguy
603 | address:
604 | type: array
605 | xml:
606 | name: addresses
607 | wrapped: true
608 | items:
609 | $ref: '#/components/schemas/Address'
610 | xml:
611 | name: customer
612 | Address:
613 | type: object
614 | properties:
615 | street:
616 | type: string
617 | example: 437 Lytton
618 | city:
619 | type: string
620 | example: Palo Alto
621 | state:
622 | type: string
623 | example: CA
624 | zip:
625 | type: string
626 | example: '94301'
627 | xml:
628 | name: address
629 | Category:
630 | type: object
631 | properties:
632 | id:
633 | type: integer
634 | format: int64
635 | example: 1
636 | name:
637 | type: string
638 | example: Dogs
639 | xml:
640 | name: category
641 | User:
642 | type: object
643 | properties:
644 | id:
645 | type: integer
646 | format: int64
647 | example: 10
648 | username:
649 | type: string
650 | example: theUser
651 | firstName:
652 | type: string
653 | example: John
654 | lastName:
655 | type: string
656 | example: James
657 | email:
658 | type: string
659 | example: john@email.com
660 | password:
661 | type: string
662 | example: '12345'
663 | phone:
664 | type: string
665 | example: '12345'
666 | userStatus:
667 | type: integer
668 | description: User Status
669 | format: int32
670 | example: 1
671 | xml:
672 | name: user
673 | Tag:
674 | type: object
675 | properties:
676 | id:
677 | type: integer
678 | format: int64
679 | name:
680 | type: string
681 | xml:
682 | name: tag
683 | Pet:
684 | required:
685 | - name
686 | - photoUrls
687 | type: object
688 | properties:
689 | id:
690 | type: integer
691 | format: int64
692 | example: 10
693 | name:
694 | type: string
695 | example: doggie
696 | category:
697 | $ref: '#/components/schemas/Category'
698 | photoUrls:
699 | type: array
700 | xml:
701 | wrapped: true
702 | items:
703 | type: string
704 | xml:
705 | name: photoUrl
706 | tags:
707 | type: array
708 | xml:
709 | wrapped: true
710 | items:
711 | $ref: '#/components/schemas/Tag'
712 | status:
713 | type: string
714 | description: pet status in the store
715 | enum:
716 | - available
717 | - pending
718 | - sold
719 | nullableValue:
720 | type: string
721 | nullable: true
722 | description: example nullable value
723 | xml:
724 | name: pet
725 | ApiResponse:
726 | type: object
727 | properties:
728 | code:
729 | type: integer
730 | format: int32
731 | type:
732 | type: string
733 | message:
734 | type: string
735 | fooBARBaz:
736 | $ref: '#/components/schemas/FooBARBaz'
737 | xml:
738 | name: '##default'
739 | FooBARBaz:
740 | type: string
741 | description: should not fail; should generate FooBARBaz.*
742 | requestBodies:
743 | PetBody:
744 | description: A JSON object containing pet information
745 | required: true
746 | content:
747 | application/json:
748 | schema:
749 | $ref: '#/components/schemas/Pet'
750 | application/xml:
751 | schema:
752 | $ref: '#/components/schemas/Pet'
753 | application/x-www-form-urlencoded:
754 | schema:
755 | $ref: '#/components/schemas/Pet'
756 | UserBody:
757 | description: A JSON object containing user information
758 | required: true
759 | content:
760 | application/json:
761 | schema:
762 | $ref: '#/components/schemas/User'
763 | application/xml:
764 | schema:
765 | $ref: '#/components/schemas/User'
766 | application/x-www-form-urlencoded:
767 | schema:
768 | $ref: '#/components/schemas/User'
769 | securitySchemes:
770 | petstore_auth:
771 | type: oauth2
772 | flows:
773 | implicit:
774 | authorizationUrl: https://petstore3.swagger.io/oauth/authorize
775 | scopes:
776 | write:pets: modify pets in your account
777 | read:pets: read your pets
778 | api_key:
779 | type: apiKey
780 | name: api_key
781 | in: header
782 |
--------------------------------------------------------------------------------
/test/fixtures/schemas/Address.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "object",
3 | "properties": {
4 | "street": {
5 | "type": "string"
6 | },
7 | "city": {
8 | "type": "string"
9 | },
10 | "state": {
11 | "type": "string"
12 | },
13 | "zip": {
14 | "type": "string"
15 | }
16 | },
17 | "title": "Address",
18 | "$id": "Address.json"
19 | }
--------------------------------------------------------------------------------
/test/fixtures/schemas/ApiResponse.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "object",
3 | "properties": {
4 | "code": {
5 | "type": "integer",
6 | "format": "int32",
7 | "minimum": -2147483648,
8 | "maximum": 2147483647
9 | },
10 | "type": {
11 | "type": "string"
12 | },
13 | "message": {
14 | "type": "string"
15 | }
16 | },
17 | "title": "ApiResponse",
18 | "$id": "ApiResponse.json"
19 | }
--------------------------------------------------------------------------------
/test/fixtures/schemas/Category.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "object",
3 | "properties": {
4 | "id": {
5 | "type": "integer",
6 | "format": "int64",
7 | "minimum": -9223372036854776000,
8 | "maximum": 9223372036854776000
9 | },
10 | "name": {
11 | "type": "string"
12 | }
13 | },
14 | "title": "Category",
15 | "$id": "Category.json"
16 | }
--------------------------------------------------------------------------------
/test/fixtures/schemas/Customer.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "object",
3 | "properties": {
4 | "id": {
5 | "type": "integer",
6 | "format": "int64",
7 | "minimum": -9223372036854776000,
8 | "maximum": 9223372036854776000
9 | },
10 | "username": {
11 | "type": "string"
12 | },
13 | "address": {
14 | "type": "array",
15 | "items": {
16 | "$ref": "Address.json"
17 | }
18 | }
19 | },
20 | "title": "Customer",
21 | "$id": "Customer.json"
22 | }
--------------------------------------------------------------------------------
/test/fixtures/schemas/DateExample.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "string",
3 | "format": "date-time",
4 | "title": "DateExample",
5 | "$id": "DateExample.json",
6 | "tsType": "Date"
7 | }
--------------------------------------------------------------------------------
/test/fixtures/schemas/Order.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "object",
3 | "properties": {
4 | "id": {
5 | "type": "integer",
6 | "format": "int64",
7 | "minimum": -9223372036854776000,
8 | "maximum": 9223372036854776000
9 | },
10 | "petId": {
11 | "type": "integer",
12 | "format": "int64",
13 | "minimum": -9223372036854776000,
14 | "maximum": 9223372036854776000
15 | },
16 | "quantity": {
17 | "type": "integer",
18 | "format": "int32",
19 | "minimum": -2147483648,
20 | "maximum": 2147483647
21 | },
22 | "shipDate": {
23 | "type": "string",
24 | "format": "date-time"
25 | },
26 | "status": {
27 | "type": "string",
28 | "description": "Order Status",
29 | "enum": [
30 | "placed",
31 | "approved",
32 | "delivered"
33 | ]
34 | },
35 | "complete": {
36 | "type": "boolean"
37 | }
38 | },
39 | "title": "Order",
40 | "$id": "Order.json"
41 | }
--------------------------------------------------------------------------------
/test/fixtures/schemas/Pet.json:
--------------------------------------------------------------------------------
1 | {
2 | "required": [
3 | "name",
4 | "photoUrls"
5 | ],
6 | "type": "object",
7 | "properties": {
8 | "id": {
9 | "type": "integer",
10 | "format": "int64",
11 | "minimum": -9223372036854776000,
12 | "maximum": 9223372036854776000
13 | },
14 | "name": {
15 | "type": "string"
16 | },
17 | "category": {
18 | "$ref": "Category.json"
19 | },
20 | "photoUrls": {
21 | "type": "array",
22 | "items": {
23 | "type": "string"
24 | }
25 | },
26 | "tags": {
27 | "type": "array",
28 | "items": {
29 | "$ref": "Tag.json"
30 | }
31 | },
32 | "status": {
33 | "type": "string",
34 | "description": "pet status in the store",
35 | "enum": [
36 | "available",
37 | "pending",
38 | "sold"
39 | ]
40 | }
41 | },
42 | "title": "Pet",
43 | "$id": "Pet.json"
44 | }
--------------------------------------------------------------------------------
/test/fixtures/schemas/Tag.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "object",
3 | "properties": {
4 | "id": {
5 | "type": "integer",
6 | "format": "int64",
7 | "minimum": -9223372036854776000,
8 | "maximum": 9223372036854776000
9 | },
10 | "name": {
11 | "type": "string"
12 | }
13 | },
14 | "title": "Tag",
15 | "$id": "Tag.json"
16 | }
--------------------------------------------------------------------------------
/test/fixtures/schemas/User.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "object",
3 | "properties": {
4 | "id": {
5 | "type": "integer",
6 | "format": "int64",
7 | "minimum": -9223372036854776000,
8 | "maximum": 9223372036854776000
9 | },
10 | "username": {
11 | "type": "string"
12 | },
13 | "firstName": {
14 | "type": "string"
15 | },
16 | "lastName": {
17 | "type": "string"
18 | },
19 | "email": {
20 | "type": "string"
21 | },
22 | "password": {
23 | "type": "string"
24 | },
25 | "phone": {
26 | "type": "string"
27 | },
28 | "userStatus": {
29 | "type": "integer",
30 | "description": "User Status",
31 | "format": "int32",
32 | "minimum": -2147483648,
33 | "maximum": 2147483647
34 | }
35 | },
36 | "title": "User",
37 | "$id": "User.json"
38 | }
--------------------------------------------------------------------------------
/test/json2ts.test.ts:
--------------------------------------------------------------------------------
1 | import fs from 'fs-extra'
2 | import { test, TestContext } from 'node:test'
3 | import { runCommand } from '../src/commands/json2ts.js'
4 | import { doNotEditText } from '../src/utils/do-not-edit-text.js'
5 | import { resolveFromPackageRoot } from '../src/utils/paths.js'
6 |
7 | const TEST_DIRECTORY = resolveFromPackageRoot('test', 'temp')
8 |
9 | const inputPath = './test/fixtures/schemas'
10 | const outputPath = './test/temp/types-from-json'
11 |
12 | test('json2ts runCommand', async (t: TestContext) => {
13 | fs.ensureDirSync(TEST_DIRECTORY)
14 |
15 | const customOptions = {
16 | bannerComment: doNotEditText
17 | }
18 |
19 | await runCommand(inputPath, outputPath, customOptions)
20 |
21 | const generatedFiles = fs.readdirSync(outputPath)
22 |
23 | t.assert.deepStrictEqual(
24 | generatedFiles,
25 | [
26 | 'Address.d.ts',
27 | 'ApiResponse.d.ts',
28 | 'Category.d.ts',
29 | 'Customer.d.ts',
30 | 'DateExample.d.ts',
31 | 'Order.d.ts',
32 | 'Pet.d.ts',
33 | 'Tag.d.ts',
34 | 'User.d.ts'
35 | ],
36 | 'generates the expected TS files'
37 | )
38 |
39 | const petFile = resolveFromPackageRoot(outputPath, 'Pet.d.ts')
40 | const generatedPetFile = fs.readFileSync(petFile, 'utf-8')
41 |
42 | t.assert.deepStrictEqual(
43 | generatedPetFile,
44 | `import { Category } from "./Category";
45 | import { Tag } from "./Tag";
46 |
47 | /* eslint-disable */
48 | /**
49 | * This file was automatically generated by openapi-transformer-toolkit CLI/methods.
50 | * DO NOT MODIFY IT BY HAND. Instead, modify the source OpenAPI file,
51 | * and run openapi-transformer-toolkit CLI/methods to regenerate this file.
52 | */
53 |
54 | export interface Pet {
55 | id?: number;
56 | name: string;
57 | category?: Category;
58 | photoUrls: string[];
59 | tags?: Tag[];
60 | /**
61 | * pet status in the store
62 | */
63 | status?: "available" | "pending" | "sold";
64 | [k: string]: unknown;
65 | }
66 | `,
67 | 'Pet.d.ts is created correctly'
68 | )
69 |
70 | const customerFile = resolveFromPackageRoot(outputPath, 'Customer.d.ts')
71 | const generatedCustomerFile = fs.readFileSync(customerFile, 'utf-8')
72 |
73 | t.assert.deepStrictEqual(
74 | generatedCustomerFile,
75 | `import { Address } from "./Address";
76 |
77 | /* eslint-disable */
78 | /**
79 | * This file was automatically generated by openapi-transformer-toolkit CLI/methods.
80 | * DO NOT MODIFY IT BY HAND. Instead, modify the source OpenAPI file,
81 | * and run openapi-transformer-toolkit CLI/methods to regenerate this file.
82 | */
83 |
84 | export interface Customer {
85 | id?: number;
86 | username?: string;
87 | address?: Address[];
88 | [k: string]: unknown;
89 | }
90 | `,
91 | 'Customer.d.ts is created correctly'
92 | )
93 | })
94 |
--------------------------------------------------------------------------------
/test/oas2json.test.ts:
--------------------------------------------------------------------------------
1 | import fs from 'fs-extra'
2 | import { describe, test, TestContext } from 'node:test'
3 | import { adaptSchema, runCommand } from '../src/commands/oas2json.js'
4 | import { resolveFromPackageRoot } from '../src/utils/paths.js'
5 |
6 | import type { JSONSchema4 } from 'json-schema'
7 |
8 | describe('oas2json', () => {
9 | test('adaptSchema function', async (t: TestContext) => {
10 | const schema: JSONSchema4 = {
11 | $schema: 'http://json-schema.org/draft-07/schema#',
12 | type: 'string',
13 | format: 'date'
14 | }
15 |
16 | adaptSchema(schema, 'TestSchema', 'TestSchema')
17 |
18 | t.assert.deepStrictEqual(
19 | schema,
20 | {
21 | type: 'string',
22 | format: 'date',
23 | title: 'TestSchema',
24 | $id: 'TestSchema.json',
25 | tsType: 'Date'
26 | },
27 | 'works as expected'
28 | )
29 | })
30 |
31 | const TEST_DIRECTORY = resolveFromPackageRoot('test', 'temp')
32 |
33 | describe('runCommand function', () => {
34 | fs.ensureDirSync(TEST_DIRECTORY)
35 | const inputPath = './test/fixtures/openapi.yml'
36 | const outputPath = './test/temp/schemas'
37 | const schemasDir = `${outputPath}/components.schemas`
38 | const pathsDir = `${outputPath}/paths`
39 |
40 | test('should generate JSON schema files from the OpenAPI input', async (t: TestContext) => {
41 | runCommand(inputPath, outputPath, 'paths')
42 |
43 | const generatedSchemas = fs.readdirSync(schemasDir)
44 | t.assert.deepStrictEqual(
45 | generatedSchemas,
46 | [
47 | 'Address.json',
48 | 'ApiResponse.json',
49 | 'Category.json',
50 | 'Customer.json',
51 | 'DateExample.json',
52 | 'FooBARBaz.json',
53 | 'Order.json',
54 | 'Pet.json',
55 | 'Tag.json',
56 | 'User.json'
57 | ],
58 | 'generates the expected JSON files for schemas'
59 | )
60 |
61 | const generatedPaths = fs.readdirSync(pathsDir)
62 | t.assert.deepStrictEqual(
63 | generatedPaths,
64 | [
65 | 'pet-findByStatus.json',
66 | 'pet-findByTags.json',
67 | 'pet-{petId}-uploadImage.json',
68 | 'pet-{petId}.json',
69 | 'pet.json',
70 | 'store-inventory.json',
71 | 'store-order-{orderId}.json',
72 | 'store-order.json',
73 | 'user-createWithList.json',
74 | 'user-login.json',
75 | 'user-logout.json',
76 | 'user-{username}.json',
77 | 'user.json'
78 | ],
79 | 'generates the expected JSON files for paths'
80 | )
81 |
82 | const petSchema = fs.readJsonSync(
83 | resolveFromPackageRoot(schemasDir, 'Pet.json')
84 | )
85 | t.assert.deepStrictEqual(
86 | petSchema,
87 | {
88 | required: ['name', 'photoUrls'],
89 | type: 'object',
90 | properties: {
91 | id: {
92 | type: 'integer',
93 | format: 'int64',
94 | minimum: -9223372036854776000,
95 | maximum: 9223372036854776000
96 | },
97 | name: {
98 | type: 'string'
99 | },
100 | category: {
101 | $ref: 'Category.json'
102 | },
103 | photoUrls: {
104 | type: 'array',
105 | items: {
106 | type: 'string'
107 | }
108 | },
109 | tags: {
110 | type: 'array',
111 | items: {
112 | $ref: 'Tag.json'
113 | }
114 | },
115 | status: {
116 | type: 'string',
117 | description: 'pet status in the store',
118 | enum: ['available', 'pending', 'sold']
119 | },
120 | nullableValue: {
121 | type: ['string', 'null'],
122 | description: 'example nullable value'
123 | }
124 | },
125 | title: 'Pet',
126 | $id: 'Pet.json'
127 | },
128 | 'Pet.json schema is created correctly'
129 | )
130 |
131 | const customerSchema = fs.readJsonSync(
132 | resolveFromPackageRoot(schemasDir, 'Customer.json')
133 | )
134 | t.assert.deepStrictEqual(
135 | customerSchema,
136 | {
137 | type: 'object',
138 | properties: {
139 | id: {
140 | type: 'integer',
141 | format: 'int64',
142 | minimum: -9223372036854776000,
143 | maximum: 9223372036854776000
144 | },
145 | username: {
146 | type: 'string'
147 | },
148 | address: {
149 | type: 'array',
150 | items: {
151 | $ref: 'Address.json'
152 | }
153 | }
154 | },
155 | title: 'Customer',
156 | $id: 'Customer.json'
157 | },
158 | 'Customer.json schema is created correctly'
159 | )
160 |
161 | const petPath = fs.readJsonSync(
162 | resolveFromPackageRoot(pathsDir, 'pet.json')
163 | )
164 | t.assert.deepStrictEqual(
165 | petPath,
166 | {
167 | put: {
168 | tags: ['pet'],
169 | summary: 'Update an existing pet',
170 | description: 'Update an existing pet by Id',
171 | operationId: 'updatePet',
172 | requestBody: {
173 | $ref: 'PetBody.json' // File generated from #/components/requestBodies/PetBody
174 | },
175 | responses: {
176 | 200: {
177 | description: 'Successful operation',
178 | content: {
179 | 'application/json': {
180 | schema: {
181 | $ref: 'Pet.json'
182 | }
183 | },
184 | 'application/xml': {
185 | schema: {
186 | $ref: 'Pet.json'
187 | }
188 | }
189 | }
190 | },
191 | 400: {
192 | description: 'Invalid ID supplied'
193 | },
194 | 404: {
195 | description: 'Pet not found'
196 | },
197 | 405: {
198 | description: 'Validation exception'
199 | }
200 | },
201 | security: [
202 | {
203 | petstore_auth: ['write:pets', 'read:pets']
204 | }
205 | ]
206 | },
207 | post: {
208 | tags: ['pet'],
209 | summary: 'Add a new pet to the store',
210 | description: 'Add a new pet to the store',
211 | operationId: 'addPet',
212 | requestBody: {
213 | $ref: 'PetBody.json'
214 | },
215 | responses: {
216 | 200: {
217 | description: 'Successful operation',
218 | content: {
219 | 'application/json': {
220 | schema: {
221 | $ref: 'Pet.json'
222 | }
223 | },
224 | 'application/xml': {
225 | schema: {
226 | $ref: 'Pet.json'
227 | }
228 | }
229 | }
230 | },
231 | 405: {
232 | description: 'Invalid input'
233 | }
234 | },
235 | security: [
236 | {
237 | petstore_auth: ['write:pets', 'read:pets']
238 | }
239 | ]
240 | },
241 | title: '/pet',
242 | $id: 'pet.json'
243 | },
244 | 'pet.json path is created correctly'
245 | )
246 |
247 | const petPetIdPath = fs.readJsonSync(
248 | resolveFromPackageRoot(pathsDir, 'pet-{petId}.json')
249 | )
250 | t.assert.deepStrictEqual(
251 | petPetIdPath,
252 | {
253 | get: {
254 | tags: ['pet'],
255 | summary: 'Find pet by ID',
256 | description: 'Returns a single pet',
257 | operationId: 'getPetById',
258 | parameters: [
259 | {
260 | name: 'petId',
261 | in: 'path',
262 | description: 'ID of pet to return',
263 | required: true,
264 | schema: {
265 | type: 'integer',
266 | format: 'int64'
267 | }
268 | }
269 | ],
270 | responses: {
271 | 200: {
272 | description: 'successful operation',
273 | content: {
274 | 'application/json': {
275 | schema: {
276 | $ref: 'Pet.json'
277 | }
278 | },
279 | 'application/xml': {
280 | schema: {
281 | $ref: 'Pet.json'
282 | }
283 | }
284 | }
285 | },
286 | 400: {
287 | description: 'Invalid ID supplied'
288 | },
289 | 404: {
290 | description: 'Pet not found'
291 | }
292 | },
293 | security: [
294 | {
295 | api_key: []
296 | },
297 | {
298 | petstore_auth: ['write:pets', 'read:pets']
299 | }
300 | ]
301 | },
302 | post: {
303 | tags: ['pet'],
304 | summary: 'Updates a pet in the store with form data',
305 | description: '',
306 | operationId: 'updatePetWithForm',
307 | parameters: [
308 | {
309 | name: 'petId',
310 | in: 'path',
311 | description: 'ID of pet that needs to be updated',
312 | required: true,
313 | schema: {
314 | type: 'integer',
315 | format: 'int64'
316 | }
317 | },
318 | {
319 | name: 'name',
320 | in: 'query',
321 | description: 'Name of pet that needs to be updated',
322 | schema: {
323 | type: 'string'
324 | }
325 | },
326 | {
327 | name: 'status',
328 | in: 'query',
329 | description: 'Status of pet that needs to be updated',
330 | schema: {
331 | type: 'string'
332 | }
333 | }
334 | ],
335 | responses: {
336 | 405: {
337 | description: 'Invalid input'
338 | }
339 | },
340 | security: [
341 | {
342 | petstore_auth: ['write:pets', 'read:pets']
343 | }
344 | ]
345 | },
346 | delete: {
347 | tags: ['pet'],
348 | summary: 'Deletes a pet',
349 | description: 'delete a pet',
350 | operationId: 'deletePet',
351 | parameters: [
352 | {
353 | name: 'api_key',
354 | in: 'header',
355 | description: '',
356 | required: false,
357 | schema: {
358 | type: 'string'
359 | }
360 | },
361 | {
362 | name: 'petId',
363 | in: 'path',
364 | description: 'Pet id to delete',
365 | required: true,
366 | schema: {
367 | type: 'integer',
368 | format: 'int64'
369 | }
370 | }
371 | ],
372 | responses: {
373 | 400: {
374 | description: 'Invalid pet value'
375 | }
376 | },
377 | security: [
378 | {
379 | petstore_auth: ['write:pets', 'read:pets']
380 | }
381 | ]
382 | },
383 | title: '/pet/{petId}',
384 | $id: 'pet-petId.json' // Does not contain any invalid URI chars (i.e. curly braces)
385 | },
386 | 'pet-{petId}.json path is created correctly'
387 | )
388 | })
389 |
390 | test('should convert components.schemas automatically', async (t: TestContext) => {
391 | runCommand(inputPath, outputPath, 'components.requestBodies')
392 |
393 | const generatedDirs = fs.readdirSync(outputPath)
394 | t.assert.deepStrictEqual(
395 | generatedDirs,
396 | ['components.requestBodies', 'components.schemas'],
397 | 'generates the expected directories'
398 | )
399 |
400 | const petReqBody = fs.readJSONSync(
401 | resolveFromPackageRoot(
402 | outputPath,
403 | 'components.requestBodies/PetBody.json'
404 | )
405 | )
406 | t.assert.deepStrictEqual(
407 | petReqBody,
408 | {
409 | description: 'A JSON object containing pet information',
410 | required: true,
411 | content: {
412 | 'application/json': {
413 | schema: {
414 | $ref: 'Pet.json'
415 | }
416 | },
417 | 'application/xml': {
418 | schema: {
419 | $ref: 'Pet.json'
420 | }
421 | },
422 | 'application/x-www-form-urlencoded': {
423 | schema: {
424 | $ref: 'Pet.json'
425 | }
426 | }
427 | },
428 | title: 'PetBody',
429 | $id: 'PetBody.json'
430 | },
431 | 'generates the expected JSON'
432 | )
433 | })
434 |
435 | test('should dynamically find the name when converting an array property', async (t: TestContext) => {
436 | runCommand(inputPath, outputPath, 'tags')
437 |
438 | const generatedDirs = fs.readdirSync(outputPath)
439 | t.assert.deepStrictEqual(
440 | generatedDirs,
441 | ['components.schemas', 'tags'],
442 | 'generates the expected directories'
443 | )
444 | const generatedFiles = fs.readdirSync(`${outputPath}/tags`)
445 | t.assert.deepStrictEqual(generatedFiles, [
446 | 'pet.json',
447 | 'store.json',
448 | 'user.json'
449 | ])
450 |
451 | const petTag = fs.readJSONSync(
452 | resolveFromPackageRoot(outputPath, 'tags/pet.json')
453 | )
454 | t.assert.deepStrictEqual(
455 | petTag,
456 | {
457 | name: 'pet',
458 | description: 'Everything about your Pets',
459 | title: 'pet',
460 | $id: 'pet.json'
461 | },
462 | 'generates the expected name for array elements'
463 | )
464 | })
465 | })
466 | })
467 |
--------------------------------------------------------------------------------
/test/oas2ts.test.ts:
--------------------------------------------------------------------------------
1 | import fs from 'fs-extra'
2 | import { describe, test, TestContext } from 'node:test'
3 | import { runCommand } from '../src/commands/oas2ts.js'
4 | import { resolveFromPackageRoot } from '../src/utils/paths.js'
5 |
6 | const TEST_DIRECTORY = resolveFromPackageRoot('test', 'temp')
7 |
8 | const inputPath = './test/fixtures/openapi.yml'
9 | const outputPath = './test/temp/types-from-oas'
10 |
11 | describe('oas2ts', () => {
12 | test('runCommand function', async (t: TestContext) => {
13 | fs.ensureDirSync(TEST_DIRECTORY)
14 |
15 | await runCommand(inputPath, outputPath)
16 |
17 | const generatedFiles = fs.readdirSync(outputPath)
18 | t.assert.deepStrictEqual(
19 | generatedFiles,
20 | [
21 | 'Address.d.ts',
22 | 'ApiResponse.d.ts',
23 | 'Category.d.ts',
24 | 'Customer.d.ts',
25 | 'DateExample.d.ts',
26 | 'FooBARBaz.d.ts',
27 | 'Order.d.ts',
28 | 'Pet.d.ts',
29 | 'Tag.d.ts',
30 | 'User.d.ts'
31 | ],
32 | 'generates the expected TS files'
33 | )
34 |
35 | const petFile = resolveFromPackageRoot(outputPath, 'Pet.d.ts')
36 | const generatedPetFile = fs.readFileSync(petFile, 'utf-8')
37 |
38 | t.assert.deepStrictEqual(
39 | generatedPetFile,
40 | `import { Category } from "./Category";
41 | import { Tag } from "./Tag";
42 |
43 | /* eslint-disable */
44 | /**
45 | * This file was automatically generated by openapi-transformer-toolkit CLI/methods.
46 | * DO NOT MODIFY IT BY HAND. Instead, modify the source OpenAPI file,
47 | * and run openapi-transformer-toolkit CLI/methods to regenerate this file.
48 | */
49 |
50 | export interface Pet {
51 | id?: number;
52 | name: string;
53 | category?: Category;
54 | photoUrls: string[];
55 | tags?: Tag[];
56 | /**
57 | * pet status in the store
58 | */
59 | status?: "available" | "pending" | "sold";
60 | /**
61 | * example nullable value
62 | */
63 | nullableValue?: string | null;
64 | [k: string]: unknown;
65 | }
66 | `,
67 | 'Pet.d.ts is created correctly'
68 | )
69 |
70 | const customerFile = resolveFromPackageRoot(outputPath, 'Customer.d.ts')
71 | const generatedCustomerFile = fs.readFileSync(customerFile, 'utf-8')
72 |
73 | t.assert.deepStrictEqual(
74 | generatedCustomerFile,
75 | `import { Address } from "./Address";
76 |
77 | /* eslint-disable */
78 | /**
79 | * This file was automatically generated by openapi-transformer-toolkit CLI/methods.
80 | * DO NOT MODIFY IT BY HAND. Instead, modify the source OpenAPI file,
81 | * and run openapi-transformer-toolkit CLI/methods to regenerate this file.
82 | */
83 |
84 | export interface Customer {
85 | id?: number;
86 | username?: string;
87 | address?: Address[];
88 | [k: string]: unknown;
89 | }
90 | `,
91 | 'Customer.d.ts is created correctly'
92 | )
93 | })
94 | })
95 |
--------------------------------------------------------------------------------
/test/oas2tson.test.ts:
--------------------------------------------------------------------------------
1 | import fs from 'fs-extra'
2 | import { describe, test, TestContext } from 'node:test'
3 | import { runCommand } from '../src/commands/oas2tson.js'
4 | import { resolveFromPackageRoot } from '../src/utils/paths.js'
5 |
6 | const TEST_DIRECTORY = resolveFromPackageRoot('test', 'temp')
7 |
8 | const inputPath = './test/fixtures/openapi.yml'
9 | const outputPath = './test/temp/typescriptobj-from-oas/ts'
10 |
11 | describe('oas2tson', () => {
12 | test('runCommand function', async (t: TestContext) => {
13 | fs.ensureDirSync(TEST_DIRECTORY)
14 |
15 | await runCommand(inputPath, outputPath)
16 |
17 | const generatedFiles = fs.readdirSync(outputPath)
18 |
19 | t.assert.deepStrictEqual(
20 | generatedFiles,
21 | [
22 | 'Address.ts',
23 | 'ApiResponse.ts',
24 | 'Category.ts',
25 | 'Customer.ts',
26 | 'DateExample.ts',
27 | 'FooBARBaz.ts',
28 | 'Order.ts',
29 | 'Pet.ts',
30 | 'Tag.ts',
31 | 'User.ts'
32 | ],
33 | 'generates the expected TS files'
34 | )
35 |
36 | const petFile = resolveFromPackageRoot(outputPath, 'Pet.ts')
37 | const generatedPetFile = fs.readFileSync(petFile, 'utf-8')
38 |
39 | t.assert.deepStrictEqual(
40 | generatedPetFile,
41 | `export const Pet = {
42 | required: ["name", "photoUrls"],
43 | type: "object",
44 | properties: {
45 | id: {
46 | type: "integer",
47 | format: "int64",
48 | minimum: -9223372036854776000,
49 | maximum: 9223372036854776000,
50 | },
51 | name: { type: "string" },
52 | category: {
53 | type: "object",
54 | properties: {
55 | id: {
56 | type: "integer",
57 | format: "int64",
58 | minimum: -9223372036854776000,
59 | maximum: 9223372036854776000,
60 | },
61 | name: { type: "string" },
62 | },
63 | title: "Category",
64 | $id: "Category.json",
65 | },
66 | photoUrls: { type: "array", items: { type: "string" } },
67 | tags: {
68 | type: "array",
69 | items: {
70 | type: "object",
71 | properties: {
72 | id: {
73 | type: "integer",
74 | format: "int64",
75 | minimum: -9223372036854776000,
76 | maximum: 9223372036854776000,
77 | },
78 | name: { type: "string" },
79 | },
80 | title: "Tag",
81 | $id: "Tag.json",
82 | },
83 | },
84 | status: {
85 | type: "string",
86 | description: "pet status in the store",
87 | enum: ["available", "pending", "sold"],
88 | },
89 | nullableValue: {
90 | type: ["string", "null"],
91 | description: "example nullable value",
92 | },
93 | },
94 | title: "Pet",
95 | $id: "Pet.json",
96 | } as const;
97 | `,
98 | 'Pet.ts is created correctly'
99 | )
100 |
101 | const customerFile = resolveFromPackageRoot(outputPath, 'Customer.ts')
102 | const generatedCustomerFile = fs.readFileSync(customerFile, 'utf-8')
103 |
104 | t.assert.deepStrictEqual(
105 | generatedCustomerFile,
106 | `export const Customer = {
107 | type: "object",
108 | properties: {
109 | id: {
110 | type: "integer",
111 | format: "int64",
112 | minimum: -9223372036854776000,
113 | maximum: 9223372036854776000,
114 | },
115 | username: { type: "string" },
116 | address: {
117 | type: "array",
118 | items: {
119 | type: "object",
120 | properties: {
121 | street: { type: "string" },
122 | city: { type: "string" },
123 | state: { type: "string" },
124 | zip: { type: "string" },
125 | },
126 | title: "Address",
127 | $id: "Address.json",
128 | },
129 | },
130 | },
131 | title: "Customer",
132 | $id: "Customer.json",
133 | } as const;
134 | `,
135 | 'Customer.ts is created correctly'
136 | )
137 |
138 | const orderFile = resolveFromPackageRoot(outputPath, 'Order.ts')
139 | const generatedOrderFile = fs.readFileSync(orderFile, 'utf-8')
140 |
141 | t.assert.deepStrictEqual(
142 | generatedOrderFile,
143 | `export const Order = {
144 | type: "object",
145 | properties: {
146 | id: {
147 | type: "integer",
148 | format: "int64",
149 | minimum: -9223372036854776000,
150 | maximum: 9223372036854776000,
151 | },
152 | petId: {
153 | type: "integer",
154 | format: "int64",
155 | minimum: -9223372036854776000,
156 | maximum: 9223372036854776000,
157 | },
158 | quantity: {
159 | type: "integer",
160 | format: "int32",
161 | minimum: -2147483648,
162 | maximum: 2147483647,
163 | },
164 | shipDate: { type: "string", format: "date-time" },
165 | status: {
166 | type: "string",
167 | description: "Order Status",
168 | enum: ["placed", "approved", "delivered"],
169 | },
170 | complete: { type: "boolean" },
171 | },
172 | title: "Order",
173 | $id: "Order.json",
174 | } as const;
175 | `,
176 | 'Order.ts is created correctly'
177 | )
178 |
179 | const dateExampleFile = resolveFromPackageRoot(outputPath, 'DateExample.ts')
180 | const generatedDateExampleFile = fs.readFileSync(dateExampleFile, 'utf-8')
181 |
182 | t.assert.deepStrictEqual(
183 | generatedDateExampleFile,
184 | `export const DateExample = {
185 | type: "string",
186 | format: "date-time",
187 | title: "DateExample",
188 | $id: "DateExample.json",
189 | } as const;
190 | `,
191 | 'DateExample.ts is created correctly'
192 | )
193 | })
194 |
195 | test('runCommand function with excludeDereferencedIds', async (t: TestContext) => {
196 | fs.ensureDirSync(TEST_DIRECTORY)
197 |
198 | await runCommand(inputPath, outputPath, undefined, true)
199 |
200 | const generatedFiles = fs.readdirSync(outputPath)
201 |
202 | t.assert.deepStrictEqual(
203 | generatedFiles,
204 | [
205 | 'Address.ts',
206 | 'ApiResponse.ts',
207 | 'Category.ts',
208 | 'Customer.ts',
209 | 'DateExample.ts',
210 | 'FooBARBaz.ts',
211 | 'Order.ts',
212 | 'Pet.ts',
213 | 'Tag.ts',
214 | 'User.ts'
215 | ],
216 | 'generates the expected TS files'
217 | )
218 |
219 | const petFile = resolveFromPackageRoot(outputPath, 'Pet.ts')
220 | const generatedPetFile = fs.readFileSync(petFile, 'utf-8')
221 |
222 | t.assert.deepStrictEqual(
223 | generatedPetFile,
224 | `export const Pet = {
225 | required: ["name", "photoUrls"],
226 | type: "object",
227 | properties: {
228 | id: {
229 | type: "integer",
230 | format: "int64",
231 | minimum: -9223372036854776000,
232 | maximum: 9223372036854776000,
233 | },
234 | name: { type: "string" },
235 | category: {
236 | type: "object",
237 | properties: {
238 | id: {
239 | type: "integer",
240 | format: "int64",
241 | minimum: -9223372036854776000,
242 | maximum: 9223372036854776000,
243 | },
244 | name: { type: "string" },
245 | },
246 | title: "Category",
247 | },
248 | photoUrls: { type: "array", items: { type: "string" } },
249 | tags: {
250 | type: "array",
251 | items: {
252 | type: "object",
253 | properties: {
254 | id: {
255 | type: "integer",
256 | format: "int64",
257 | minimum: -9223372036854776000,
258 | maximum: 9223372036854776000,
259 | },
260 | name: { type: "string" },
261 | },
262 | title: "Tag",
263 | },
264 | },
265 | status: {
266 | type: "string",
267 | description: "pet status in the store",
268 | enum: ["available", "pending", "sold"],
269 | },
270 | nullableValue: {
271 | type: ["string", "null"],
272 | description: "example nullable value",
273 | },
274 | },
275 | title: "Pet",
276 | $id: "Pet.json",
277 | } as const;
278 | `,
279 | 'Pet.ts is created correctly'
280 | )
281 |
282 | const customerFile = resolveFromPackageRoot(outputPath, 'Customer.ts')
283 | const generatedCustomerFile = fs.readFileSync(customerFile, 'utf-8')
284 |
285 | t.assert.deepStrictEqual(
286 | generatedCustomerFile,
287 | `export const Customer = {
288 | type: "object",
289 | properties: {
290 | id: {
291 | type: "integer",
292 | format: "int64",
293 | minimum: -9223372036854776000,
294 | maximum: 9223372036854776000,
295 | },
296 | username: { type: "string" },
297 | address: {
298 | type: "array",
299 | items: {
300 | type: "object",
301 | properties: {
302 | street: { type: "string" },
303 | city: { type: "string" },
304 | state: { type: "string" },
305 | zip: { type: "string" },
306 | },
307 | title: "Address",
308 | },
309 | },
310 | },
311 | title: "Customer",
312 | $id: "Customer.json",
313 | } as const;
314 | `,
315 | 'Customer.ts is created correctly'
316 | )
317 | })
318 | })
319 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "module": "esnext",
5 | "lib": [
6 | "esnext"
7 | ],
8 | "moduleResolution": "node",
9 | "esModuleInterop": true,
10 | "strict": true,
11 | "strictNullChecks": true,
12 | "resolveJsonModule": true,
13 | "outDir": "dist",
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/tsup.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'tsup'
2 |
3 | export default defineConfig({
4 | entry: ['index.ts', 'src/**/*.ts'],
5 | dts: true,
6 | splitting: true,
7 | clean: true,
8 | minify: true,
9 | format: ['esm'],
10 | bundle: false,
11 | legacyOutput: true
12 | })
13 |
--------------------------------------------------------------------------------