Skip to content

Commit

Permalink
Merge branch 'vladkens:main' into fix-base-path
Browse files Browse the repository at this point in the history
  • Loading branch information
philipodev authored Nov 25, 2024
2 parents 171ab9d + c73c923 commit 150a90c
Show file tree
Hide file tree
Showing 11 changed files with 182 additions and 135 deletions.
48 changes: 48 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: ci

on:
push:
pull_request:

jobs:
test:
runs-on: ubuntu-latest
name: node-${{ matrix.node-version }}
strategy:
matrix:
node-version: [18, 20, 22]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: "yarn"

- run: yarn install --frozen-lockfile
- run: yarn ci

release:
runs-on: ubuntu-latest
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
needs: test
permissions:
contents: write
id-token: write
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "22"
registry-url: "https://registry.npmjs.org"

- run: yarn install --frozen-lockfile
- run: yarn ci

- run: npm publish --provenance --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

- name: Create Github Release
uses: softprops/action-gh-release@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
23 changes: 0 additions & 23 deletions .github/workflows/publish.yml

This file was deleted.

25 changes: 0 additions & 25 deletions .github/workflows/test.yml

This file was deleted.

1 change: 1 addition & 0 deletions .yarnrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
save-prefix ""
2 changes: 1 addition & 1 deletion examples/petstore-v2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ export class ApiClient {

store = {
getInventory: () => {
return this.Fetch<object>("get", "/store/inventory", {})
return this.Fetch<Record<string, number>>("get", "/store/inventory", {})
},

placeOrder: (body: Order) => {
Expand Down
2 changes: 1 addition & 1 deletion examples/petstore-v3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ export class ApiClient {

store = {
getInventory: () => {
return this.Fetch<object>("get", "/store/inventory", {})
return this.Fetch<Record<string, number>>("get", "/store/inventory", {})
},

placeOrder: (body: Order) => {
Expand Down
36 changes: 18 additions & 18 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"type": "module",
"name": "apigen-ts",
"version": "0.2.0",
"version": "1.0.1",
"license": "MIT",
"author": "vladkens <[email protected]>",
"repository": "vladkens/apigen-ts",
"description": "OpenAPI TypeScript client generator",
"description": "Simple typed OpenAPI client generator",
"keywords": [
"openapi",
"swagger",
Expand All @@ -22,24 +22,24 @@
"ci": "tsc --noEmit && yarn test-cov && yarn build"
},
"dependencies": {
"@redocly/openapi-core": "^1.22.1",
"@types/lodash-es": "^4.17.12",
"@types/swagger2openapi": "^7.0.4",
"array-utils-ts": "^0.1.2",
"cleye": "^1.3.2",
"lodash-es": "^4.17.21",
"swagger2openapi": "^7.0.8"
"@redocly/openapi-core": "1.25.5",
"@types/lodash-es": "4.17.12",
"@types/swagger2openapi": "7.0.4",
"array-utils-ts": "0.1.2",
"cleye": "1.3.2",
"lodash-es": "4.17.21",
"swagger2openapi": "7.0.8"
},
"devDependencies": {
"@types/node": "^22.5.2",
"c8": "^10.1.2",
"fetch-mock": "^11.1.3",
"pkgroll": "^2.4.2",
"prettier": "^3.3.3",
"prettier-plugin-organize-imports": "^4.0.0",
"tsm": "^2.3.0",
"typescript": "^5.5.4",
"uvu": "^0.5.6"
"@types/node": "22.7.5",
"c8": "10.1.2",
"fetch-mock": "11.1.5",
"pkgroll": "2.5.0",
"prettier": "3.3.3",
"prettier-plugin-organize-imports": "4.1.0",
"tsm": "2.3.0",
"typescript": "5.6.3",
"uvu": "0.5.6"
},
"peerDependencies": {
"prettier": "^3.0.0",
Expand Down
29 changes: 28 additions & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

<div align="center">
<img src="./logo.svg" alt="apigen-ts logo" height="80" />
<div>TypeScript client generator from OpenAPI schema</div>
<div>Simple typed OpenAPI client generator</div>
</div>

## Features
Expand Down Expand Up @@ -168,6 +168,33 @@ await apigen({

Then run with: `node apigen.mjs`

## Usage with different backend frameworks

### FastAPI

By default `apigen-ts` generates noisy method names when used with FastAPI. This can be fixed by [custom resolving](https://fastapi.tiangolo.com/advanced/path-operation-advanced-configuration/#using-the-path-operation-function-name-as-the-operationid) for operations ids.

```py
from fastapi import FastAPI
from fastapi.routing import APIRoute

app = FastAPI()

# add your routes here

def update_operation_ids(app: FastAPI) -> None:
for route in app.routes:
if isinstance(route, APIRoute):
ns = route.tags[0] if route.tags else "general"
route.operation_id = f"{ns}_{route.name}".lower()


# this function should be after all routes added
update_operation_ids(app)
```

_Note: If you want FastAPI to be added as preset, open PR please._

## Compare

- [openapi-typescript-codegen](https://github.com/ferdikoomen/openapi-typescript-codegen) ([npm](https://www.npmjs.com/package/openapi-typescript-codegen)): no single file mode [#1263](https://github.com/ferdikoomen/openapi-typescript-codegen/issues/1263#issuecomment-1502890838)
Expand Down
17 changes: 15 additions & 2 deletions src/type-gen.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Oas3Schema, Referenced } from "@redocly/openapi-core/lib/typings/openapi"
import { filterEmpty } from "array-utils-ts"
import { isArray, uniq, upperFirst } from "lodash-es"
import { isArray, isBoolean, uniq, upperFirst } from "lodash-es"
import ts from "typescript"
import { Context } from "./config"
import { OAS3, unref } from "./schema"
Expand Down Expand Up @@ -68,6 +68,19 @@ const makeInlineEnum = (s: OAS3) => {
return undefined
}

const makeObject = (ctx: Context, s: OAS3): ts.TypeNode => {
if (s.type !== "object") throw new Error(`makeObject: not an object ${JSON.stringify(s)}`)

if (s.additionalProperties && !isBoolean(s.additionalProperties)) {
return f.createTypeReferenceNode("Record", [
f.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
makeType(ctx, s.additionalProperties),
])
}

return f.createKeywordTypeNode(ts.SyntaxKind.ObjectKeyword)
}

export const makeType = (ctx: Context, s?: Referenced<OAS3>): ts.TypeNode => {
const mk = makeType.bind(null, ctx)

Expand Down Expand Up @@ -123,7 +136,7 @@ export const makeType = (ctx: Context, s?: Referenced<OAS3>): ts.TypeNode => {

let t: ts.TypeNode
// if (s.type === "object") t = f.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword)
if (s.type === "object") t = f.createKeywordTypeNode(ts.SyntaxKind.ObjectKeyword)
if (s.type === "object") t = makeObject(ctx, s)
else if (s.type === "boolean") t = f.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword)
else if (s.type === "number") t = f.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword)
else if (s.type === "string") t = f.createKeywordTypeNode(ts.SyntaxKind.StringKeyword)
Expand Down
13 changes: 13 additions & 0 deletions test/type-gen.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,19 @@ test("type inline", async () => {
{ type: "object", properties: { a: { type: "string" } }, additionalProperties: true },
"{ a?: string }",
)

t({ type: "object", additionalProperties: { type: "number" } }, "Record<string, number>")

t(
{
type: "object",
additionalProperties: {
type: "array",
items: { oneOf: [{ type: "string" }, { type: "number" }] },
},
},
"Record<string, (string | number)[]>",
)
})

test("type alias", async () => {
Expand Down
Loading

0 comments on commit 150a90c

Please sign in to comment.