Skip to content

Commit 6f8c7d6

Browse files
committed
Enhanced coverage
1 parent 179c448 commit 6f8c7d6

31 files changed

Lines changed: 359 additions & 146 deletions

Makefile

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,16 @@ dev:
1111

1212
.PHONY: fix
1313
fix:
14-
poetry run pyupgrade --exit-zero-even-if-changed --py39-plus fastadmin/**/*.py
15-
poetry run isort --settings-path pyproject.toml fastadmin
16-
poetry run black --config pyproject.toml fastadmin
14+
poetry run pyupgrade --exit-zero-even-if-changed --py39-plus fastadmin/**/*.py tests/**/*.py
15+
poetry run isort --settings-path pyproject.toml fastadmin tests
16+
poetry run black --config pyproject.toml fastadmin tests
1717
make -C frontend fix
1818

1919
.PHONY: lint
2020
lint:
21-
poetry run isort --diff --check-only --settings-path pyproject.toml fastadmin
22-
poetry run black --diff --check --config pyproject.toml fastadmin
23-
poetry run flake8 --show-source --config .flake8 fastadmin
21+
poetry run isort --diff --check-only --settings-path pyproject.toml fastadmin tests
22+
poetry run black --diff --check --config pyproject.toml fastadmin tests
23+
poetry run flake8 --show-source --config .flake8 fastadmin tests
2424
poetry run mypy --show-error-code --install-types --non-interactive --namespace-packages --show-traceback --config-file pyproject.toml fastadmin
2525
make -C frontend lint
2626

fastadmin/api/api.py

Lines changed: 26 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import logging
22
from datetime import datetime, timedelta
3+
from typing import Any, List
4+
from uuid import UUID
35

46
import jwt
57
from fastapi import APIRouter, Depends, HTTPException, Request, status
@@ -29,7 +31,7 @@
2931
async def sign_in(
3032
response: Response,
3133
payload: SignInInputSchema,
32-
):
34+
) -> None:
3335
"""This method is used to sign in.
3436
3537
:params response: a response object.
@@ -62,8 +64,8 @@ async def sign_in(
6264
@router.post("/sign-out")
6365
async def sign_out(
6466
response: Response,
65-
_: str = Depends(get_user_id),
66-
):
67+
_: UUID | int = Depends(get_user_id),
68+
) -> None:
6769
"""This method is used to sign out.
6870
6971
:params response: a response object.
@@ -75,15 +77,15 @@ async def sign_out(
7577

7678
@router.get("/me")
7779
async def me(
78-
user_id: str = Depends(get_user_id),
79-
):
80+
user_id: UUID | int = Depends(get_user_id),
81+
) -> Any:
8082
"""This method is used to get current user.
8183
8284
:params user_id: a user id.
8385
:return: A user object.
8486
"""
8587
model = settings.ADMIN_USER_MODEL
86-
admin_model = get_admin_model(model)
88+
admin_model: Any = get_admin_model(model)
8789
return await admin_model.get_obj(user_id)
8890

8991

@@ -95,8 +97,8 @@ async def list(
9597
sort_by: str = "-created_at",
9698
offset: int | None = 0,
9799
limit: int | None = 10,
98-
_: str = Depends(get_user_id),
99-
):
100+
_: UUID | int = Depends(get_user_id),
101+
) -> dict[str, int | List[Any]]:
100102
"""This method is used to get a list of objects.
101103
102104
:params request: a request object.
@@ -132,9 +134,9 @@ async def list(
132134
@router.get("/retrieve/{model}/{id}")
133135
async def get(
134136
model: str,
135-
id: str,
136-
_: str = Depends(get_user_id),
137-
):
137+
id: UUID | int,
138+
_: UUID | int = Depends(get_user_id),
139+
) -> Any:
138140
"""This method is used to get an object.
139141
140142
:params model: a name of model.
@@ -154,8 +156,8 @@ async def get(
154156
async def add(
155157
model: str,
156158
payload: dict,
157-
_: str = Depends(get_user_id),
158-
):
159+
_: UUID | int = Depends(get_user_id),
160+
) -> Any:
159161
"""This method is used to add an object.
160162
161163
:params model: a name of model.
@@ -165,19 +167,16 @@ async def add(
165167
admin_model = get_admin_model(model)
166168
if not admin_model:
167169
raise HTTPException(status.HTTP_404_NOT_FOUND, detail=f"{model} model is not registered.")
168-
obj = await admin_model.save_model(None, payload)
169-
if not obj:
170-
raise HTTPException(status.HTTP_404_NOT_FOUND, detail=f"{model} not found.")
171-
return obj
170+
return await admin_model.save_model(None, payload)
172171

173172

174173
@router.patch("/change/{model}/{id}")
175174
async def change(
176175
model: str,
177-
id: str,
176+
id: UUID | int,
178177
payload: dict,
179-
_: str = Depends(get_user_id),
180-
):
178+
_: UUID | int = Depends(get_user_id),
179+
) -> Any:
181180
"""This method is used to change an object.
182181
183182
:params model: a name of model.
@@ -201,7 +200,7 @@ async def export(
201200
payload: ExportSchema,
202201
search: str | None = None,
203202
sort_by: str = "-created_at",
204-
_: str = Depends(get_user_id),
203+
_: UUID | int = Depends(get_user_id),
205204
):
206205
"""This method is used to export a list of objects.
207206
@@ -237,9 +236,9 @@ async def export(
237236
@router.delete("/delete/{model}/{id}")
238237
async def delete(
239238
model: str,
240-
id: str,
241-
user_id: str = Depends(get_user_id),
242-
):
239+
id: UUID | int,
240+
user_id: UUID | int = Depends(get_user_id),
241+
) -> UUID | int:
243242
"""This method is used to delete an object.
244243
245244
:params model: a name of model.
@@ -249,16 +248,16 @@ async def delete(
249248
admin_model = get_admin_model(model)
250249
if not admin_model:
251250
raise HTTPException(status.HTTP_404_NOT_FOUND, detail=f"{model} model is not registered.")
252-
if user_id == id:
251+
if str(user_id) == str(id) and model == settings.ADMIN_USER_MODEL:
253252
raise HTTPException(status.HTTP_403_FORBIDDEN, detail="You cannot delete yourself.")
254253
await admin_model.delete_model(id)
255254
return id
256255

257256

258257
@router.get("/configuration")
259258
async def configuration(
260-
user_id: str | None = Depends(get_user_id_or_none),
261-
):
259+
user_id: UUID | int | None = Depends(get_user_id_or_none),
260+
) -> ConfigurationSchema:
262261
"""This method is used to get a configuration.
263262
264263
:params user_id: an id of user.

fastadmin/api/depends.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from datetime import datetime
2+
from uuid import UUID
23

34
import jwt
45
from fastapi import Request, status
@@ -8,7 +9,7 @@
89
from fastadmin.settings import settings
910

1011

11-
async def get_user_id_or_none(request: Request) -> str | None:
12+
async def get_user_id_or_none(request: Request) -> UUID | int | None:
1213
"""This method is used to get user id from request or None.
1314
1415
:params request: a request object.
@@ -42,7 +43,7 @@ async def get_user_id_or_none(request: Request) -> str | None:
4243
return user_id
4344

4445

45-
async def get_user_id(request: Request) -> str:
46+
async def get_user_id(request: Request) -> UUID | int:
4647
"""This method is used to get user id from request.
4748
4849
:params request: a request object.

fastadmin/models/base.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from collections.abc import Sequence
44
from io import BytesIO, StringIO
55
from typing import Any
6+
from uuid import UUID
67

78
from fastadmin.schemas.api import ExportFormat
89
from fastadmin.schemas.configuration import WidgetType
@@ -167,7 +168,7 @@ async def authenticate(self, username: str, password: str) -> str | None:
167168
"""
168169
raise NotImplementedError
169170

170-
async def save_model(self, id: str | None, payload: dict) -> dict | None:
171+
async def save_model(self, id: UUID | int | None, payload: dict) -> dict | None:
171172
"""This method is used to save orm/db model object.
172173
173174
:params id: an id of object.
@@ -176,15 +177,15 @@ async def save_model(self, id: str | None, payload: dict) -> dict | None:
176177
"""
177178
raise NotImplementedError
178179

179-
async def delete_model(self, id: str) -> None:
180+
async def delete_model(self, id: UUID | int) -> None:
180181
"""This method is used to delete orm/db model object.
181182
182183
:params id: an id of object.
183184
:return: None.
184185
"""
185186
raise NotImplementedError
186187

187-
async def get_obj(self, id: str) -> dict | None:
188+
async def get_obj(self, id: UUID | int) -> dict | None:
188189
"""This method is used to get orm/db model object by id.
189190
190191
:params id: an id of object.

fastadmin/models/orm/tortoise.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import asyncio
22
from collections import OrderedDict
33
from typing import Any
4+
from uuid import UUID
45

56
from fastapi import HTTPException, status
67

@@ -29,7 +30,7 @@ async def obj_to_dict(obj: Any, with_m2m: bool = True) -> dict:
2930

3031

3132
class TortoiseModelAdmin(BaseModelAdmin):
32-
async def save_model(self, id: str | None, payload: dict) -> dict | None:
33+
async def save_model(self, id: UUID | int | None, payload: dict) -> dict | None:
3334
"""This method is used to save orm/db model object.
3435
3536
:params id: an id of object.
@@ -71,15 +72,15 @@ async def save_model(self, id: str | None, payload: dict) -> dict | None:
7172
await m2m_rel.add(*remote_model_objs)
7273
return await obj_to_dict(obj)
7374

74-
async def delete_model(self, id: str) -> None:
75+
async def delete_model(self, id: UUID | int) -> None:
7576
"""This method is used to delete orm/db model object.
7677
7778
:params id: an id of object.
7879
:return: None.
7980
"""
8081
await self.model_cls.filter(id=id).delete()
8182

82-
async def get_obj(self, id: str) -> dict | None:
83+
async def get_obj(self, id: UUID | int) -> dict | None:
8384
"""This method is used to get orm/db model object by id.
8485
8586
:params id: an id of object.

frontend/src/components/async-transfer/index.test.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,14 @@ import { AsyncTransfer } from 'components/async-transfer';
77

88
test('Renders AsyncTransfer', () => {
99
const queryClient = new QueryClient();
10+
const onChange = (data: any) => undefined;
1011
render(
1112
<TestProviders client={queryClient}>
1213
<AsyncTransfer
1314
idField="id"
1415
labelField="id"
1516
parentModel="test"
16-
onChange={(data: any) => {}}
17+
onChange={onChange}
1718
value={undefined}
1819
/>
1920
</TestProviders>

frontend/src/components/async-transfer/index.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,23 +46,25 @@ export const AsyncTransfer: React.FC<IAsyncTransfer> = ({
4646
);
4747
};
4848

49-
const onSearch = (direction: TransferDirection, value: string) => {
49+
const onSearch = (direction: TransferDirection, v: string) => {
5050
if (direction === 'left') {
51-
setSearch(value);
51+
setSearch(v);
5252
}
5353
};
5454

5555
const dataSource = (data?.results || []).map((item: any) => {
5656
return { key: item[idField], title: item[labelField] };
5757
});
5858

59+
const render = (item: any) => item.title;
60+
5961
return (
6062
<Transfer
6163
dataSource={dataSource}
6264
showSearch={true}
6365
filterOption={onFilter}
6466
onSearch={debounce(onSearch, 500)}
65-
render={(item) => item.title}
67+
render={render}
6668
onChange={onChange}
6769
targetKeys={value}
6870
listStyle={

frontend/src/components/crud-container/index.tsx

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -100,17 +100,18 @@ export const CrudContainer: React.FC<ICrudContainer> = ({
100100
},
101101
...configuration.models
102102
.filter(
103-
(model: IModel) =>
104-
!search || model.name.toLocaleLowerCase().includes(search?.toLocaleLowerCase())
103+
(m: IModel) => !search || m.name.toLocaleLowerCase().includes(search?.toLocaleLowerCase())
105104
)
106-
.map((model: IModel) => {
105+
.map((m: IModel) => {
107106
return {
108-
key: model.name,
109-
label: model.name,
107+
key: m.name,
108+
label: m.name,
110109
};
111110
}),
112111
];
113112

113+
const onSearch = (e: any) => setSearch(e.target.value);
114+
114115
return (
115116
<>
116117
<Helmet defaultTitle={title}>
@@ -193,7 +194,7 @@ export const CrudContainer: React.FC<ICrudContainer> = ({
193194
<div style={{ padding: 10 }}>
194195
<Input
195196
value={search}
196-
onChange={(e) => setSearch(e.target.value)}
197+
onChange={onSearch}
197198
placeholder={_t('Search By Menu') as string}
198199
prefix={<SearchOutlined />}
199200
/>
@@ -236,7 +237,7 @@ export const CrudContainer: React.FC<ICrudContainer> = ({
236237
}
237238
style={{ marginTop: 16 }}
238239
>
239-
<Skeleton loading={isLoading} active>
240+
<Skeleton loading={isLoading} active={true}>
240241
{children}
241242
</Skeleton>
242243
</Card>

frontend/src/components/form-container/index.test.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@ import { FormContainer } from 'components/form-container';
77

88
test('Renders FormContainer', () => {
99
const queryClient = new QueryClient();
10+
const onFinish = (data: any) => undefined;
1011
render(
1112
<TestProviders client={queryClient}>
12-
<FormContainer form="test" onFinish={(data: any) => {}} mode="add">
13+
<FormContainer form="test" onFinish={onFinish} mode="add">
1314
<div />
1415
</FormContainer>
1516
</TestProviders>

frontend/src/containers/add/index.test.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import { QueryClient } from '@tanstack/react-query';
33

44
import { TestProviders } from 'providers';
55

6-
76
import { Add } from 'containers/add';
87

98
test('Renders Add', () => {

0 commit comments

Comments
 (0)