diff --git a/.github/labeler.yml b/.github/labeler.yml index a0a1c1aab0..ed657c23d7 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -3,6 +3,13 @@ docs: - changed-files: - any-glob-to-any-file: - '**/*.md' + - all-globs-to-all-files: + - '!frontend/**' + - '!backend/**' + - '!.github/**' + - '!scripts/**' + - '!.gitignore' + - '!.pre-commit-config.yaml' internal: - all: diff --git a/.github/workflows/generate-client.yml b/.github/workflows/generate-client.yml new file mode 100644 index 0000000000..a81f78cb51 --- /dev/null +++ b/.github/workflows/generate-client.yml @@ -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) }} + \ No newline at end of file diff --git a/.github/workflows/issue-manager.yml b/.github/workflows/issue-manager.yml index f172dfb6a4..b50a77f185 100644 --- a/.github/workflows/issue-manager.yml +++ b/.github/workflows/issue-manager.yml @@ -2,7 +2,7 @@ name: Issue Manager on: schedule: - - cron: "13 4 * * *" + - cron: "21 17 * * *" issue_comment: types: - created @@ -16,6 +16,7 @@ on: permissions: issues: write + pull-requests: write jobs: issue-manager: @@ -35,8 +36,8 @@ jobs: "delay": 864000, "message": "Assuming the original need was handled, this will be automatically closed now. But feel free to add more comments or create new issues or PRs." }, - "changes-requested": { + "waiting": { "delay": 2628000, - "message": "As this PR had requested changes to be applied but has been inactive for a while, it's now going to be closed. But if there's anyone interested, feel free to create a new PR." + "message": "As this PR has been waiting for the original user for a while but seems to be inactive, it's now going to be closed. But if there's anyone interested, feel free to create a new PR." } } diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index d242af4f18..60f4ceac14 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -41,9 +41,9 @@ jobs: working-directory: frontend - run: docker compose build - run: docker compose down -v --remove-orphans - - run: docker compose up -d + - run: docker compose up -d --wait - name: Run Playwright tests - run: npx playwright test + run: npx playwright test --fail-on-flaky-tests --trace=retain-on-failure working-directory: frontend - run: docker compose down -v --remove-orphans - uses: actions/upload-artifact@v4 @@ -52,6 +52,7 @@ jobs: name: playwright-report path: frontend/playwright-report/ retention-days: 30 + include-hidden-files: true # https://github.com/marketplace/actions/alls-green#why e2e-alls-green: # This job does nothing and is only used for the branch protection diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 03e4ba1ef6..5849d19749 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -35,6 +35,7 @@ jobs: with: name: coverage-html path: backend/htmlcov + include-hidden-files: true # https://github.com/marketplace/actions/alls-green#why alls-green: # This job does nothing and is only used for the branch protection diff --git a/backend/app/api/routes/utils.py b/backend/app/api/routes/utils.py index 82f6d2b821..a73b80d761 100644 --- a/backend/app/api/routes/utils.py +++ b/backend/app/api/routes/utils.py @@ -24,3 +24,8 @@ def test_email(email_to: EmailStr) -> Message: html_content=email_data.html_content, ) return Message(message="Test email sent") + + +@router.get("/health-check/") +async def health_check() -> bool: + return True diff --git a/docker-compose.yml b/docker-compose.yml index d614942cbd..3c2d79cc32 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -63,6 +63,12 @@ services: - POSTGRES_PASSWORD=${POSTGRES_PASSWORD?Variable not set} - SENTRY_DSN=${SENTRY_DSN} + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost/api/v1/utils/health-check/"] + interval: 10s + timeout: 5s + retries: 5 + build: context: ./backend args: diff --git a/frontend/README.md b/frontend/README.md index 13e3a8c488..9a01970fda 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -4,7 +4,7 @@ The frontend is built with [Vite](https://vitejs.dev/), [React](https://reactjs. ## Frontend development -Before you begin, ensure that you have either the Node Version Manager (nvm) or Fast Node Manager (fnm) installed on your system. +Before you begin, ensure that you have either the Node Version Manager (nvm) or Fast Node Manager (fnm) installed on your system. * To install fnm follow the [official fnm guide](https://github.com/Schniz/fnm#installation). If you prefer nvm, you can install it using the [official nvm guide](https://github.com/nvm-sh/nvm#installing-and-updating). @@ -27,7 +27,7 @@ nvm install ```bash # If using fnm -fnm use +fnm use # If using nvm nvm use @@ -74,6 +74,19 @@ But it would be only to clean them up, leaving them won't really have any effect ## 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. * 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. diff --git a/frontend/biome.json b/frontend/biome.json index 14597ce328..a06315dc2a 100644 --- a/frontend/biome.json +++ b/frontend/biome.json @@ -6,7 +6,6 @@ "files": { "ignore": [ "node_modules", - "src/client/", "src/routeTree.gen.ts", "playwright.config.ts", "playwright-report" diff --git a/frontend/package.json b/frontend/package.json index b9b2e3b51a..1a7a547f68 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -8,7 +8,7 @@ "build": "tsc && vite build", "lint": "biome check --apply-unsafe --no-errors-on-unmatched --files-ignore-unknown=true ./", "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": { "@chakra-ui/icons": "2.1.1", diff --git a/frontend/src/client/core/request.ts b/frontend/src/client/core/request.ts index 6abb0e8f41..99d38b46f1 100644 --- a/frontend/src/client/core/request.ts +++ b/frontend/src/client/core/request.ts @@ -1,9 +1,9 @@ import axios from "axios" import type { AxiosError, - AxiosInstance, AxiosRequestConfig, AxiosResponse, + AxiosInstance, } from "axios" import { ApiError } from "./ApiError" @@ -151,12 +151,12 @@ export const getHeaders = async ( ) if (isStringWithValue(token)) { - headers.Authorization = `Bearer ${token}` + headers["Authorization"] = `Bearer ${token}` } if (isStringWithValue(username) && isStringWithValue(password)) { const credentials = base64(`${username}:${password}`) - headers.Authorization = `Basic ${credentials}` + headers["Authorization"] = `Basic ${credentials}` } if (options.body !== undefined) { diff --git a/frontend/src/client/services.ts b/frontend/src/client/services.ts index be024e4d11..a7d58e9c02 100644 --- a/frontend/src/client/services.ts +++ b/frontend/src/client/services.ts @@ -4,20 +4,20 @@ import { request as __request } from "./core/request" import type { Body_login_login_access_token, - ItemCreate, - ItemPublic, - ItemUpdate, - ItemsPublic, Message, NewPassword, Token, + UserPublic, UpdatePassword, UserCreate, - UserPublic, UserRegister, + UsersPublic, UserUpdate, UserUpdateMe, - UsersPublic, + ItemCreate, + ItemPublic, + ItemsPublic, + ItemUpdate, } from "./models" export type TDataLoginAccessToken = { @@ -50,7 +50,7 @@ export class LoginService { formData: formData, mediaType: "application/x-www-form-urlencoded", errors: { - 422: "Validation Error", + 422: `Validation Error`, }, }) } @@ -85,7 +85,7 @@ export class LoginService { email, }, errors: { - 422: "Validation Error", + 422: `Validation Error`, }, }) } @@ -106,7 +106,7 @@ export class LoginService { body: requestBody, mediaType: "application/json", errors: { - 422: "Validation Error", + 422: `Validation Error`, }, }) } @@ -128,7 +128,7 @@ export class LoginService { email, }, errors: { - 422: "Validation Error", + 422: `Validation Error`, }, }) } @@ -180,7 +180,7 @@ export class UsersService { limit, }, errors: { - 422: "Validation Error", + 422: `Validation Error`, }, }) } @@ -201,7 +201,7 @@ export class UsersService { body: requestBody, mediaType: "application/json", errors: { - 422: "Validation Error", + 422: `Validation Error`, }, }) } @@ -248,7 +248,7 @@ export class UsersService { body: requestBody, mediaType: "application/json", errors: { - 422: "Validation Error", + 422: `Validation Error`, }, }) } @@ -269,7 +269,7 @@ export class UsersService { body: requestBody, mediaType: "application/json", errors: { - 422: "Validation Error", + 422: `Validation Error`, }, }) } @@ -290,7 +290,7 @@ export class UsersService { body: requestBody, mediaType: "application/json", errors: { - 422: "Validation Error", + 422: `Validation Error`, }, }) } @@ -312,7 +312,7 @@ export class UsersService { user_id: userId, }, errors: { - 422: "Validation Error", + 422: `Validation Error`, }, }) } @@ -336,7 +336,7 @@ export class UsersService { body: requestBody, mediaType: "application/json", errors: { - 422: "Validation Error", + 422: `Validation Error`, }, }) } @@ -356,7 +356,7 @@ export class UsersService { user_id: userId, }, errors: { - 422: "Validation Error", + 422: `Validation Error`, }, }) } @@ -382,10 +382,22 @@ export class UtilsService { email_to: emailTo, }, errors: { - 422: "Validation Error", + 422: `Validation Error`, }, }) } + + /** + * Health Check + * @returns boolean Successful Response + * @throws ApiError + */ + public static healthCheck(): CancelablePromise { + return __request(OpenAPI, { + method: "GET", + url: "/api/v1/utils/health-check/", + }) + } } export type TDataReadItems = { @@ -425,7 +437,7 @@ export class ItemsService { limit, }, errors: { - 422: "Validation Error", + 422: `Validation Error`, }, }) } @@ -446,7 +458,7 @@ export class ItemsService { body: requestBody, mediaType: "application/json", errors: { - 422: "Validation Error", + 422: `Validation Error`, }, }) } @@ -466,7 +478,7 @@ export class ItemsService { id, }, errors: { - 422: "Validation Error", + 422: `Validation Error`, }, }) } @@ -490,7 +502,7 @@ export class ItemsService { body: requestBody, mediaType: "application/json", errors: { - 422: "Validation Error", + 422: `Validation Error`, }, }) } @@ -510,7 +522,7 @@ export class ItemsService { id, }, errors: { - 422: "Validation Error", + 422: `Validation Error`, }, }) } diff --git a/release-notes.md b/release-notes.md index b3d111b139..c505e9a5ff 100644 --- a/release-notes.md +++ b/release-notes.md @@ -14,6 +14,12 @@ ### Internal +* 👷 Improve playwright CI job. PR [#1335](https://github.com/fastapi/full-stack-fastapi-template/pull/1335) by [@patrick91](https://github.com/patrick91). +* 👷 Update `issue-manager.yml`. PR [#1329](https://github.com/fastapi/full-stack-fastapi-template/pull/1329) by [@tiangolo](https://github.com/tiangolo). +* 💚 Set `include-hidden-files` to `True` when using the `upload-artifact` GH action. PR [#1327](https://github.com/fastapi/full-stack-fastapi-template/pull/1327) by [@svlandeg](https://github.com/svlandeg). +* 👷🏻 Auto-generate frontend client . PR [#1320](https://github.com/fastapi/full-stack-fastapi-template/pull/1320) by [@alejsdev](https://github.com/alejsdev). +* 🐛 Fix in `.github/labeler.yml`. PR [#1322](https://github.com/fastapi/full-stack-fastapi-template/pull/1322) by [@alejsdev](https://github.com/alejsdev). +* 👷 Update `.github/labeler.yml`. PR [#1321](https://github.com/fastapi/full-stack-fastapi-template/pull/1321) by [@alejsdev](https://github.com/alejsdev). * 👷 Update `latest-changes` GitHub Action. PR [#1315](https://github.com/fastapi/full-stack-fastapi-template/pull/1315) by [@tiangolo](https://github.com/tiangolo). * 👷 Update configs for labeler. PR [#1308](https://github.com/fastapi/full-stack-fastapi-template/pull/1308) by [@tiangolo](https://github.com/tiangolo). * 👷 Update GitHub Action labeler to add only one label. PR [#1304](https://github.com/fastapi/full-stack-fastapi-template/pull/1304) by [@tiangolo](https://github.com/tiangolo). diff --git a/scripts/generate-client.sh b/scripts/generate-client.sh new file mode 100644 index 0000000000..1327ee6fd1 --- /dev/null +++ b/scripts/generate-client.sh @@ -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