👷🏻 Auto-generate frontend client (#1320)

This commit is contained in:
Alejandra
2024-08-29 20:10:49 +02:00
committed by GitHub
parent e8c73ff614
commit bbad34aa8d
7 changed files with 100 additions and 31 deletions

49
.github/workflows/generate-client.yml vendored Normal file
View File

@@ -0,0 +1,49 @@
name: Generate Client
on:
pull_request:
types:
- opened
- synchronize
jobs:
generate-client:
permissions:
contents: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.head_ref }}
token: ${{ secrets.FULL_STACK_FASTAPI_TEMPLATE_REPO_TOKEN }}
- uses: actions/setup-node@v4
with:
node-version: lts/*
- uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Install dependencies
run: npm ci
working-directory: frontend
- run: pip install ./backend
- run: bash scripts/generate-client.sh
- name: Commit changes
run: |
git config --local user.email "github-actions@github.com"
git config --local user.name "github-actions"
git add frontend/src/client
git diff --staged --quiet || git commit -m "✨ Autogenerate frontend client"
git push
# https://github.com/marketplace/actions/alls-green#why
generate-client-alls-green: # This job does nothing and is only used for the branch protection
if: always()
needs:
- generate-client
runs-on: ubuntu-latest
steps:
- name: Decide whether the needed jobs succeeded or failed
uses: re-actors/alls-green@release/v1
with:
jobs: ${{ toJSON(needs) }}

View File

@@ -74,6 +74,19 @@ But it would be only to clean them up, leaving them won't really have any effect
## Generate Client ## Generate Client
### Automatically
* Activate the backend virtual environment.
* From the top level project directory, run the script:
```bash
./scripts/generate-frontend-client.sh
```
* Commit the changes.
### Manually
* Start the Docker Compose stack. * Start the Docker Compose stack.
* Download the OpenAPI JSON file from `http://localhost/api/v1/openapi.json` and copy it to a new file `openapi.json` at the root of the `frontend` directory. * Download the OpenAPI JSON file from `http://localhost/api/v1/openapi.json` and copy it to a new file `openapi.json` at the root of the `frontend` directory.

View File

@@ -6,7 +6,6 @@
"files": { "files": {
"ignore": [ "ignore": [
"node_modules", "node_modules",
"src/client/",
"src/routeTree.gen.ts", "src/routeTree.gen.ts",
"playwright.config.ts", "playwright.config.ts",
"playwright-report" "playwright-report"

View File

@@ -8,7 +8,7 @@
"build": "tsc && vite build", "build": "tsc && vite build",
"lint": "biome check --apply-unsafe --no-errors-on-unmatched --files-ignore-unknown=true ./", "lint": "biome check --apply-unsafe --no-errors-on-unmatched --files-ignore-unknown=true ./",
"preview": "vite preview", "preview": "vite preview",
"generate-client": "openapi-ts --input ./openapi.json --output ./src/client --client axios --exportSchemas true && biome format --write ./src/client" "generate-client": "openapi-ts --input ./openapi.json --output ./src/client --client axios --exportSchemas true"
}, },
"dependencies": { "dependencies": {
"@chakra-ui/icons": "2.1.1", "@chakra-ui/icons": "2.1.1",

View File

@@ -1,9 +1,9 @@
import axios from "axios" import axios from "axios"
import type { import type {
AxiosError, AxiosError,
AxiosInstance,
AxiosRequestConfig, AxiosRequestConfig,
AxiosResponse, AxiosResponse,
AxiosInstance,
} from "axios" } from "axios"
import { ApiError } from "./ApiError" import { ApiError } from "./ApiError"
@@ -151,12 +151,12 @@ export const getHeaders = async (
) )
if (isStringWithValue(token)) { if (isStringWithValue(token)) {
headers.Authorization = `Bearer ${token}` headers["Authorization"] = `Bearer ${token}`
} }
if (isStringWithValue(username) && isStringWithValue(password)) { if (isStringWithValue(username) && isStringWithValue(password)) {
const credentials = base64(`${username}:${password}`) const credentials = base64(`${username}:${password}`)
headers.Authorization = `Basic ${credentials}` headers["Authorization"] = `Basic ${credentials}`
} }
if (options.body !== undefined) { if (options.body !== undefined) {

View File

@@ -4,20 +4,20 @@ import { request as __request } from "./core/request"
import type { import type {
Body_login_login_access_token, Body_login_login_access_token,
ItemCreate,
ItemPublic,
ItemUpdate,
ItemsPublic,
Message, Message,
NewPassword, NewPassword,
Token, Token,
UserPublic,
UpdatePassword, UpdatePassword,
UserCreate, UserCreate,
UserPublic,
UserRegister, UserRegister,
UsersPublic,
UserUpdate, UserUpdate,
UserUpdateMe, UserUpdateMe,
UsersPublic, ItemCreate,
ItemPublic,
ItemsPublic,
ItemUpdate,
} from "./models" } from "./models"
export type TDataLoginAccessToken = { export type TDataLoginAccessToken = {
@@ -50,7 +50,7 @@ export class LoginService {
formData: formData, formData: formData,
mediaType: "application/x-www-form-urlencoded", mediaType: "application/x-www-form-urlencoded",
errors: { errors: {
422: "Validation Error", 422: `Validation Error`,
}, },
}) })
} }
@@ -85,7 +85,7 @@ export class LoginService {
email, email,
}, },
errors: { errors: {
422: "Validation Error", 422: `Validation Error`,
}, },
}) })
} }
@@ -106,7 +106,7 @@ export class LoginService {
body: requestBody, body: requestBody,
mediaType: "application/json", mediaType: "application/json",
errors: { errors: {
422: "Validation Error", 422: `Validation Error`,
}, },
}) })
} }
@@ -128,7 +128,7 @@ export class LoginService {
email, email,
}, },
errors: { errors: {
422: "Validation Error", 422: `Validation Error`,
}, },
}) })
} }
@@ -180,7 +180,7 @@ export class UsersService {
limit, limit,
}, },
errors: { errors: {
422: "Validation Error", 422: `Validation Error`,
}, },
}) })
} }
@@ -201,7 +201,7 @@ export class UsersService {
body: requestBody, body: requestBody,
mediaType: "application/json", mediaType: "application/json",
errors: { errors: {
422: "Validation Error", 422: `Validation Error`,
}, },
}) })
} }
@@ -248,7 +248,7 @@ export class UsersService {
body: requestBody, body: requestBody,
mediaType: "application/json", mediaType: "application/json",
errors: { errors: {
422: "Validation Error", 422: `Validation Error`,
}, },
}) })
} }
@@ -269,7 +269,7 @@ export class UsersService {
body: requestBody, body: requestBody,
mediaType: "application/json", mediaType: "application/json",
errors: { errors: {
422: "Validation Error", 422: `Validation Error`,
}, },
}) })
} }
@@ -290,7 +290,7 @@ export class UsersService {
body: requestBody, body: requestBody,
mediaType: "application/json", mediaType: "application/json",
errors: { errors: {
422: "Validation Error", 422: `Validation Error`,
}, },
}) })
} }
@@ -312,7 +312,7 @@ export class UsersService {
user_id: userId, user_id: userId,
}, },
errors: { errors: {
422: "Validation Error", 422: `Validation Error`,
}, },
}) })
} }
@@ -336,7 +336,7 @@ export class UsersService {
body: requestBody, body: requestBody,
mediaType: "application/json", mediaType: "application/json",
errors: { errors: {
422: "Validation Error", 422: `Validation Error`,
}, },
}) })
} }
@@ -356,7 +356,7 @@ export class UsersService {
user_id: userId, user_id: userId,
}, },
errors: { errors: {
422: "Validation Error", 422: `Validation Error`,
}, },
}) })
} }
@@ -382,7 +382,7 @@ export class UtilsService {
email_to: emailTo, email_to: emailTo,
}, },
errors: { errors: {
422: "Validation Error", 422: `Validation Error`,
}, },
}) })
} }
@@ -425,7 +425,7 @@ export class ItemsService {
limit, limit,
}, },
errors: { errors: {
422: "Validation Error", 422: `Validation Error`,
}, },
}) })
} }
@@ -446,7 +446,7 @@ export class ItemsService {
body: requestBody, body: requestBody,
mediaType: "application/json", mediaType: "application/json",
errors: { errors: {
422: "Validation Error", 422: `Validation Error`,
}, },
}) })
} }
@@ -466,7 +466,7 @@ export class ItemsService {
id, id,
}, },
errors: { errors: {
422: "Validation Error", 422: `Validation Error`,
}, },
}) })
} }
@@ -490,7 +490,7 @@ export class ItemsService {
body: requestBody, body: requestBody,
mediaType: "application/json", mediaType: "application/json",
errors: { errors: {
422: "Validation Error", 422: `Validation Error`,
}, },
}) })
} }
@@ -510,7 +510,7 @@ export class ItemsService {
id, id,
}, },
errors: { errors: {
422: "Validation Error", 422: `Validation Error`,
}, },
}) })
} }

View File

@@ -0,0 +1,8 @@
#! /usr/bin/env bash
PYTHONPATH=backend python -c "import app.main; import json; print(json.dumps(app.main.app.openapi()))" > openapi.json
node frontend/modify-openapi-operationids.js
mv openapi.json frontend/
cd frontend
npm run generate-client
npx biome format --write ./src/client