From aaa8a0373bdd3b8de081751443d28655a6724261 Mon Sep 17 00:00:00 2001
From: Pamella Bezerra
Date: Tue, 14 May 2024 16:18:57 -0300
Subject: [PATCH 01/10] Set up openapi-ts
---
.eslintignore | 1 +
.eslintrc.js | 11 +++++++
.pre-commit-config.yaml | 6 ++++
Makefile | 4 +++
backend/common/serializers.py | 5 +++
backend/common/views.py | 40 +++++++++++------------
frontend/js/pages/Home.tsx | 22 ++++++-------
frontend/js/pages/__tests__/Home.spec.tsx | 23 +++++--------
openapi-ts.config.ts | 11 +++++++
package.json | 4 ++-
10 files changed, 81 insertions(+), 46 deletions(-)
create mode 100644 backend/common/serializers.py
create mode 100644 openapi-ts.config.ts
diff --git a/.eslintignore b/.eslintignore
index 575c8b05..176153ce 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -1,2 +1,3 @@
frontend/bundles/
frontend/webpack_bundles/
+frontend/js/api/
diff --git a/.eslintrc.js b/.eslintrc.js
index 7b440389..117861ac 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -46,4 +46,15 @@ module.exports = {
version: "detect",
},
},
+ overrides: [
+ {
+ files: ["openapi-ts.config.ts"],
+ rules: {
+ "import/no-extraneous-dependencies": [
+ "error",
+ { devDependencies: true },
+ ],
+ },
+ },
+ ],
};
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 27e7c8b5..3e650eff 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -64,3 +64,9 @@ repos:
language: system
files: ^backend/
pass_filenames: false
+ - id: frontend-api
+ name: frontend-api-local
+ entry: npm run openapi-ts
+ language: system
+ files: backend/schema\.yml$
+ pass_filenames: false
diff --git a/Makefile b/Makefile
index 1cc07e67..2e44504d 100644
--- a/Makefile
+++ b/Makefile
@@ -20,6 +20,7 @@ docker_setup:
docker compose build --no-cache backend
docker compose run --rm backend python manage.py spectacular --color --file schema.yml
docker compose run frontend npm install
+ docker compose run --rm frontend npm run openapi-ts
docker_test:
docker compose run backend python manage.py test $(ARG) --parallel --keepdb
@@ -51,3 +52,6 @@ docker_backend_shell:
docker_backend_update_schema:
docker compose run --rm backend python manage.py spectacular --color --file schema.yml
+
+docker_frontend_update_api:
+ docker compose run --rm frontend npm run openapi-ts
diff --git a/backend/common/serializers.py b/backend/common/serializers.py
new file mode 100644
index 00000000..ea75a475
--- /dev/null
+++ b/backend/common/serializers.py
@@ -0,0 +1,5 @@
+from rest_framework import serializers
+
+
+class MessageSerializer(serializers.Serializer):
+ message = serializers.CharField()
diff --git a/backend/common/views.py b/backend/common/views.py
index 6e7b23f8..d8793769 100644
--- a/backend/common/views.py
+++ b/backend/common/views.py
@@ -1,35 +1,34 @@
from django.views import generic
-from drf_spectacular.utils import OpenApiExample, OpenApiResponse, extend_schema
+from drf_spectacular.utils import OpenApiExample, extend_schema
from rest_framework import status, viewsets
from rest_framework.decorators import action
from rest_framework.permissions import AllowAny
from rest_framework.response import Response
+from .serializers import MessageSerializer
+
class IndexView(generic.TemplateView):
template_name = "common/index.html"
class RestViewSet(viewsets.ViewSet):
+ serializer_class = MessageSerializer
+
@extend_schema(
summary="Check REST API",
description="This endpoint checks if the REST API is working.",
- responses={
- 200: OpenApiResponse(
- description="Successful Response",
- examples=[
- OpenApiExample(
- "Example Response",
- value={
- "result": "This message comes from the backend. "
- "If you're seeing this, the REST API is working!"
- },
- response_only=True,
- )
- ],
+ examples=[
+ OpenApiExample(
+ "Successful Response",
+ value={
+ "message": "This message comes from the backend. "
+ "If you're seeing this, the REST API is working!"
+ },
+ response_only=True,
)
- },
+ ],
methods=["GET"],
)
@action(
@@ -39,10 +38,11 @@ class RestViewSet(viewsets.ViewSet):
url_path="rest-check",
)
def rest_check(self, request):
- return Response(
- {
- "result": "This message comes from the backend. "
+ serializer = self.serializer_class(
+ data={
+ "message": "This message comes from the backend. "
"If you're seeing this, the REST API is working!"
- },
- status=status.HTTP_200_OK,
+ }
)
+ serializer.is_valid(raise_exception=True)
+ return Response(serializer.data, status=status.HTTP_200_OK)
diff --git a/frontend/js/pages/Home.tsx b/frontend/js/pages/Home.tsx
index 6a7da7d7..095ac7f7 100644
--- a/frontend/js/pages/Home.tsx
+++ b/frontend/js/pages/Home.tsx
@@ -1,20 +1,20 @@
import { useState, useEffect } from "react";
import Button from "react-bootstrap/Button";
-import { useDispatch, useSelector } from "react-redux";
import DjangoImgSrc from "../../assets/images/django-logo-negative.png";
-import { AppDispatch, RootState } from "../store";
-import { fetchRestCheck } from "../store/rest_check";
+import { RestService } from "../api";
const Home = () => {
- const dispatch: AppDispatch = useDispatch();
- const restCheck = useSelector((state: RootState) => state.restCheck);
- useEffect(() => {
- const action = fetchRestCheck();
- dispatch(action);
- }, [dispatch]);
-
const [showBugComponent, setShowBugComponent] = useState(false);
+ const [restCheck, setRestCheck] =
+ useState>>();
+
+ useEffect(() => {
+ async function onFetchRestCheck() {
+ setRestCheck(await RestService.restRestCheckRetrieve());
+ }
+ onFetchRestCheck();
+ }, []);
return (
<>
@@ -31,7 +31,7 @@ const Home = () => {
Rest API
- {restCheck?.data?.payload?.result}
+ {restCheck?.message}
}>
-
-
-
+
);
diff --git a/frontend/js/store/api.ts b/frontend/js/store/api.ts
deleted file mode 100644
index c8d303c2..00000000
--- a/frontend/js/store/api.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import axios from "axios";
-import cookie from "cookie";
-
-const api = axios.create();
-api.interceptors.request.use((config) => {
- const { csrftoken } = cookie.parse(document.cookie);
- config.headers = config.headers || {};
- if (csrftoken) {
- config.headers["X-CSRFTOKEN"] = csrftoken;
- }
- return config;
-});
-
-export default api;
diff --git a/frontend/js/store/index.ts b/frontend/js/store/index.ts
deleted file mode 100644
index 3bdf6aec..00000000
--- a/frontend/js/store/index.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import { configureStore } from "@reduxjs/toolkit";
-
-import { rootReducer } from "./reducers";
-
-const configureReduxStore = (preloadedState: object) => {
- const store = configureStore({
- reducer: rootReducer,
- preloadedState,
- });
- return store;
-};
-
-export default configureReduxStore;
-
-const store = configureReduxStore({});
-export type RootState = ReturnType;
-export type AppDispatch = typeof store.dispatch;
diff --git a/frontend/js/store/reducers.ts b/frontend/js/store/reducers.ts
deleted file mode 100644
index 5ee5d9a3..00000000
--- a/frontend/js/store/reducers.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import { combineReducers } from "@reduxjs/toolkit";
-
-import { restCheckReducer as restCheck } from "./rest_check";
-
-export const rootReducer = combineReducers({
- restCheck,
-});
diff --git a/frontend/js/store/rest_check.ts b/frontend/js/store/rest_check.ts
deleted file mode 100644
index 511741c6..00000000
--- a/frontend/js/store/rest_check.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
-
-import api from "./api";
-
-// Thunks
-export const fetchRestCheck = createAsyncThunk("restCheck/fetch", async () => {
- const res = await api.get("/api/rest/rest-check/");
- return res.data;
-});
-
-// Reducer
-interface Payload {
- result: string;
-}
-interface RestCheckState {
- data: {
- isLoading: boolean;
- payload?: Payload;
- error?: unknown;
- };
-}
-export const restCheckReducer = createSlice({
- name: "restCheck",
- initialState: { data: { isLoading: false } } as RestCheckState,
- reducers: {},
- extraReducers: (builder) => {
- builder.addCase(fetchRestCheck.pending, (state) => {
- state.data = {
- isLoading: true,
- };
- });
- builder.addCase(fetchRestCheck.fulfilled, (state, action) => {
- state.data = {
- isLoading: false,
- payload: action.payload,
- };
- });
- builder.addCase(fetchRestCheck.rejected, (state, action) => {
- state.data = {
- isLoading: false,
- error: action.error,
- };
- });
- },
-}).reducer;
diff --git a/package.json b/package.json
index 3b882c0b..ef2f6b25 100644
--- a/package.json
+++ b/package.json
@@ -21,7 +21,6 @@
},
"dependencies": {
"@hey-api/openapi-ts": "^0.44.0",
- "@reduxjs/toolkit": "~2.2.4",
"@sentry/browser": "~8.0.0",
"@sentry/react": "~8.0.0",
"axios": "~1.6.8",
@@ -33,7 +32,6 @@
"react": "~18.3.1",
"react-bootstrap": "~2.10.2",
"react-dom": "~18.3.1",
- "react-redux": "~9.1.2",
"react-router": "~6.23.1"
},
"devDependencies": {
@@ -76,7 +74,6 @@
"postcss-loader": "~8.1.1",
"prettier": "~3.2.5",
"react-refresh": "^0.14.2",
- "redux-mock-store": "~1.5.4",
"sass": "~1.77.1",
"sass-loader": "~14.2.1",
"style-loader": "~4.0.0",
From c6f89ad34b3dff5325eb57de990565fb940e467b Mon Sep 17 00:00:00 2001
From: Pamella Bezerra
Date: Wed, 15 May 2024 12:04:31 -0300
Subject: [PATCH 09/10] Upgrade @hey-api/openapi-ts
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index ef2f6b25..abcd9394 100644
--- a/package.json
+++ b/package.json
@@ -20,7 +20,7 @@
"openapi-ts": "openapi-ts"
},
"dependencies": {
- "@hey-api/openapi-ts": "^0.44.0",
+ "@hey-api/openapi-ts": "^0.45.0",
"@sentry/browser": "~8.0.0",
"@sentry/react": "~8.0.0",
"axios": "~1.6.8",
From 37d8ba0d5205ca087b53fe124d9b33c55e9038ae Mon Sep 17 00:00:00 2001
From: Pamella Bezerra
Date: Wed, 15 May 2024 12:30:29 -0300
Subject: [PATCH 10/10] Remove redux mentions
---
.eslintrc.js | 1 -
README.md | 3 ---
2 files changed, 4 deletions(-)
diff --git a/.eslintrc.js b/.eslintrc.js
index 117861ac..ea0758da 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -5,7 +5,6 @@ module.exports = {
parser: "@typescript-eslint/parser",
extends: ["vinta/recommended-typescript"],
rules: {
- "default-param-last": "off", // due to initialState in Redux
"import/extensions": [
"error",
"ignorePackages",
diff --git a/README.md b/README.md
index 677bb452..a0830742 100644
--- a/README.md
+++ b/README.md
@@ -41,10 +41,7 @@ Also, includes a Render.com `render.yaml` and a working Django `production.py` s
- `axios` for performing asynchronous calls
- `cookie` for easy integration with Django using the `csrftoken` cookie
- `openapi-ts` for generating TypeScript client API code from the backend OpenAPI schema
- - `@reduxjs/toolkit` for easy state management across the application with the whole toolkit including devtools for inspecting and debugging Redux via browser and ability to run thunks for interacting with the Redux store through asynchronous logic
- - `connected-react-router` for integrating Redux with React Router
- `history` for providing browser history to Connected React Router
- - `react-redux` for integrating React with Redux
- Utilities
- `lodash` for general utility functions
- `classnames` for easy working with complex CSS class names on components