zrb 1.0.0b8__py3-none-any.whl → 1.0.0b10__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- zrb/__main__.py +3 -0
- zrb/builtin/project/add/fastapp/fastapp_task.py +1 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/.coveragerc +11 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/.gitignore +4 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/column/add_column_task.py +4 -4
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/config.py +5 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/add_entity_task.py +108 -1
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/add_entity_util.py +67 -4
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/module/my_module/service/my_entity/my_entity_service.py +5 -5
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/schema/my_entity.py +1 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/test/my_module/my_entity/test_create_my_entity.py +53 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/test/my_module/my_entity/test_delete_my_entity.py +62 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/test/my_module/my_entity/test_read_my_entity.py +65 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/test/my_module/my_entity/test_update_my_entity.py +61 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/gateway_subroute.py +57 -13
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/input.py +8 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/add_module_util.py +2 -2
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/gateway/subroute/my_module.py +6 -1
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/module_task_definition.py +10 -6
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/task.py +65 -14
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/task_util.py +106 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/util.py +6 -86
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/base_db_repository.py +27 -11
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/base_service.py +140 -51
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/error.py +15 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/util/parser.py +1 -1
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/config.py +22 -4
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/client/auth_client.py +21 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/migration/versions/3093c7336477_add_auth_tables.py +106 -61
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/migration/versions/8ed025bcc845_create_permissions.py +69 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/migration_metadata.py +3 -4
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/route.py +15 -14
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/permission/permission_service.py +4 -4
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/role/repository/role_db_repository.py +24 -5
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/role/role_service.py +14 -12
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/repository/user_db_repository.py +134 -97
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/repository/user_repository.py +28 -11
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/user_service.py +215 -13
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/user_service_factory.py +30 -2
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/subroute/auth.py +216 -41
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/util/auth.py +57 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/requirements.txt +7 -1
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/permission.py +2 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/role.py +13 -12
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/user.py +64 -12
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/_util/access_token.py +19 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/auth/permission/test_create_permission.py +59 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/auth/permission/test_delete_permission.py +68 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/auth/permission/test_read_permission.py +71 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/auth/permission/test_update_permission.py +66 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/auth/test_user_session.py +195 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/test_health_and_readiness.py +28 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/test_homepage.py +17 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/test_not_found_error.py +16 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test.sh +7 -0
- zrb/task/base_task.py +10 -10
- zrb/task/cmd_task.py +2 -5
- zrb/util/cmd/command.py +39 -48
- zrb/util/codemod/modification_mode.py +3 -0
- zrb/util/codemod/modify_class.py +58 -0
- zrb/util/codemod/modify_class_parent.py +68 -0
- zrb/util/codemod/modify_class_property.py +128 -0
- zrb/util/codemod/modify_dict.py +75 -0
- zrb/util/codemod/modify_function.py +65 -0
- zrb/util/codemod/modify_function_call.py +68 -0
- zrb/util/codemod/modify_method.py +88 -0
- zrb/util/codemod/{prepend_code_to_module.py → modify_module.py} +2 -3
- zrb/util/file.py +3 -2
- {zrb-1.0.0b8.dist-info → zrb-1.0.0b10.dist-info}/METADATA +2 -1
- {zrb-1.0.0b8.dist-info → zrb-1.0.0b10.dist-info}/RECORD +72 -55
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/migrate.py +0 -3
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/session.py +0 -48
- zrb/util/codemod/append_code_to_class.py +0 -35
- zrb/util/codemod/append_code_to_function.py +0 -38
- zrb/util/codemod/append_code_to_method.py +0 -55
- zrb/util/codemod/append_key_to_dict.py +0 -51
- zrb/util/codemod/append_param_to_function_call.py +0 -39
- zrb/util/codemod/prepend_parent_to_class.py +0 -38
- zrb/util/codemod/prepend_property_to_class.py +0 -55
- {zrb-1.0.0b8.dist-info → zrb-1.0.0b10.dist-info}/WHEEL +0 -0
- {zrb-1.0.0b8.dist-info → zrb-1.0.0b10.dist-info}/entry_points.txt +0 -0
@@ -9,6 +9,7 @@ from sqlmodel import Field, SQLModel
|
|
9
9
|
|
10
10
|
class UserBase(SQLModel):
|
11
11
|
username: str
|
12
|
+
active: bool
|
12
13
|
|
13
14
|
|
14
15
|
class UserCreate(UserBase):
|
@@ -23,7 +24,7 @@ class UserCreateWithAudit(UserCreate):
|
|
23
24
|
|
24
25
|
|
25
26
|
class UserCreateWithRoles(UserCreate):
|
26
|
-
|
27
|
+
role_names: list[str] | None = None
|
27
28
|
|
28
29
|
def with_audit(self, created_by: str) -> "UserCreateWithRolesAndAudit":
|
29
30
|
return UserCreateWithRolesAndAudit(**self.model_dump(), created_by=created_by)
|
@@ -33,18 +34,21 @@ class UserCreateWithRolesAndAudit(UserCreateWithRoles):
|
|
33
34
|
created_by: str
|
34
35
|
|
35
36
|
def get_user_create_with_audit(self) -> UserCreateWithAudit:
|
36
|
-
data = {
|
37
|
+
data = {
|
38
|
+
key: val for key, val in self.model_dump().items() if key != "role_names"
|
39
|
+
}
|
37
40
|
return UserCreateWithAudit(**data)
|
38
41
|
|
39
|
-
def
|
40
|
-
if self.
|
42
|
+
def get_role_names(self) -> list[str]:
|
43
|
+
if self.role_names is None:
|
41
44
|
return []
|
42
|
-
return self.
|
45
|
+
return self.role_names
|
43
46
|
|
44
47
|
|
45
48
|
class UserUpdate(SQLModel):
|
46
49
|
username: str | None = None
|
47
50
|
password: str | None = None
|
51
|
+
active: bool | None = None
|
48
52
|
|
49
53
|
def with_audit(self, updated_by: str) -> "UserUpdateWithAudit":
|
50
54
|
return UserUpdateWithAudit(**self.model_dump(), updated_by=updated_by)
|
@@ -55,7 +59,7 @@ class UserUpdateWithAudit(UserUpdate):
|
|
55
59
|
|
56
60
|
|
57
61
|
class UserUpdateWithRoles(UserUpdate):
|
58
|
-
|
62
|
+
role_names: list[str] | None = None
|
59
63
|
|
60
64
|
def with_audit(self, updated_by: str) -> "UserUpdateWithRolesAndAudit":
|
61
65
|
return UserUpdateWithRolesAndAudit(**self.model_dump(), updated_by=updated_by)
|
@@ -65,19 +69,32 @@ class UserUpdateWithRolesAndAudit(UserUpdateWithRoles):
|
|
65
69
|
updated_by: str
|
66
70
|
|
67
71
|
def get_user_update_with_audit(self) -> UserUpdateWithAudit:
|
68
|
-
data = {
|
72
|
+
data = {
|
73
|
+
key: val for key, val in self.model_dump().items() if key != "role_names"
|
74
|
+
}
|
69
75
|
return UserUpdateWithAudit(**data)
|
70
76
|
|
71
|
-
def
|
72
|
-
if self.
|
77
|
+
def get_role_names(self) -> list[str]:
|
78
|
+
if self.role_names is None:
|
73
79
|
return []
|
74
|
-
return self.
|
80
|
+
return self.role_names
|
75
81
|
|
76
82
|
|
77
83
|
class UserResponse(UserBase):
|
78
84
|
id: str
|
79
|
-
|
80
|
-
|
85
|
+
role_names: list[str]
|
86
|
+
permission_names: list[str]
|
87
|
+
|
88
|
+
|
89
|
+
class AuthUserResponse(UserResponse):
|
90
|
+
is_super_user: bool
|
91
|
+
is_guest: bool
|
92
|
+
|
93
|
+
def has_permission(self, permission_name: str):
|
94
|
+
return self.is_super_user or permission_name in self.permission_names
|
95
|
+
|
96
|
+
def has_role(self, role_name: str):
|
97
|
+
return self.is_super_user or role_name in self.role_names
|
81
98
|
|
82
99
|
|
83
100
|
class MultipleUserResponse(BaseModel):
|
@@ -85,7 +102,30 @@ class MultipleUserResponse(BaseModel):
|
|
85
102
|
count: int
|
86
103
|
|
87
104
|
|
105
|
+
class UserCredentials(SQLModel):
|
106
|
+
username: str
|
107
|
+
password: str
|
108
|
+
|
109
|
+
|
110
|
+
class UserTokenData(SQLModel):
|
111
|
+
access_token: str
|
112
|
+
refresh_token: str
|
113
|
+
access_token_expired_at: datetime.datetime
|
114
|
+
refresh_token_expired_at: datetime.datetime
|
115
|
+
|
116
|
+
|
117
|
+
class UserSessionResponse(SQLModel):
|
118
|
+
id: str
|
119
|
+
user_id: str
|
120
|
+
access_token: str
|
121
|
+
refresh_token: str
|
122
|
+
token_type: str
|
123
|
+
access_token_expired_at: datetime.datetime
|
124
|
+
refresh_token_expired_at: datetime.datetime
|
125
|
+
|
126
|
+
|
88
127
|
class User(SQLModel, table=True):
|
128
|
+
__tablename__ = "users"
|
89
129
|
id: str = Field(default_factory=lambda: ulid.new().str, primary_key=True)
|
90
130
|
created_at: datetime.datetime = Field(index=True)
|
91
131
|
created_by: str = Field(index=True)
|
@@ -93,11 +133,23 @@ class User(SQLModel, table=True):
|
|
93
133
|
updated_by: str | None = Field(index=True)
|
94
134
|
username: str = Field(index=True, unique=True)
|
95
135
|
password: str
|
136
|
+
active: bool = Field(index=True)
|
96
137
|
|
97
138
|
|
98
139
|
class UserRole(SQLModel, table=True):
|
140
|
+
__tablename__ = "user_roles"
|
99
141
|
id: str = Field(default_factory=lambda: ulid.new().str, primary_key=True)
|
100
142
|
user_id: str = Field(index=True)
|
101
143
|
role_id: str = Field(index=True)
|
102
144
|
created_at: datetime.datetime | None
|
103
145
|
created_by: str | None
|
146
|
+
|
147
|
+
|
148
|
+
class UserSession(SQLModel, table=True):
|
149
|
+
__tablename__ = "user_sessions"
|
150
|
+
id: str = Field(default_factory=lambda: ulid.new().str, primary_key=True)
|
151
|
+
user_id: str = Field(index=True)
|
152
|
+
access_token: str = Field(index=True)
|
153
|
+
refresh_token: str = Field(index=True)
|
154
|
+
access_token_expired_at: datetime.datetime = Field(index=True)
|
155
|
+
refresh_token_expired_at: datetime.datetime = Field(index=True)
|
@@ -0,0 +1,19 @@
|
|
1
|
+
import time
|
2
|
+
|
3
|
+
from fastapi.testclient import TestClient
|
4
|
+
from my_app_name.config import APP_AUTH_SUPER_USER, APP_AUTH_SUPER_USER_PASSWORD
|
5
|
+
from my_app_name.main import app
|
6
|
+
|
7
|
+
|
8
|
+
def get_admin_access_token():
|
9
|
+
client = TestClient(app, base_url="http://localhost")
|
10
|
+
# Create new admin user session and check the response
|
11
|
+
session_response = client.post(
|
12
|
+
"/api/v1/user-sessions",
|
13
|
+
data={
|
14
|
+
"username": APP_AUTH_SUPER_USER,
|
15
|
+
"password": APP_AUTH_SUPER_USER_PASSWORD,
|
16
|
+
},
|
17
|
+
)
|
18
|
+
session_data = session_response.json()
|
19
|
+
return session_data.get("access_token")
|
@@ -0,0 +1,59 @@
|
|
1
|
+
from fastapi.testclient import TestClient
|
2
|
+
from my_app_name.main import app
|
3
|
+
from my_app_name.test._util.access_token import get_admin_access_token
|
4
|
+
|
5
|
+
|
6
|
+
def test_create_permission():
|
7
|
+
client = TestClient(app, base_url="http://localhost")
|
8
|
+
access_token = get_admin_access_token()
|
9
|
+
new_permission_data = {
|
10
|
+
"name": "new-permission",
|
11
|
+
"description": "new-permission-description",
|
12
|
+
}
|
13
|
+
response = client.post(
|
14
|
+
"/api/v1/permissions",
|
15
|
+
json=new_permission_data,
|
16
|
+
headers={"Authorization": f"Bearer {access_token}"},
|
17
|
+
)
|
18
|
+
assert response.status_code == 200
|
19
|
+
response_data = response.json()
|
20
|
+
assert response_data.get("id") is not None
|
21
|
+
assert response_data.get("id") != ""
|
22
|
+
assert response_data.get("name") == "new-permission"
|
23
|
+
assert response_data.get("description") == "new-permission-description"
|
24
|
+
|
25
|
+
|
26
|
+
def test_create_permission_bulk():
|
27
|
+
client = TestClient(app, base_url="http://localhost")
|
28
|
+
access_token = get_admin_access_token()
|
29
|
+
new_first_permission_data = {
|
30
|
+
"name": "new-permission-bulk-1",
|
31
|
+
"description": "new-permission-bulk-description-1",
|
32
|
+
}
|
33
|
+
new_second_permission_data = {
|
34
|
+
"name": "new-permission-bulk-2",
|
35
|
+
"description": "new-permission-bulk-description-2",
|
36
|
+
}
|
37
|
+
new_permission_data = [new_first_permission_data, new_second_permission_data]
|
38
|
+
response = client.post(
|
39
|
+
"/api/v1/permissions/bulk",
|
40
|
+
json=new_permission_data,
|
41
|
+
headers={"Authorization": f"Bearer {access_token}"},
|
42
|
+
)
|
43
|
+
assert response.status_code == 200
|
44
|
+
response_data = response.json()
|
45
|
+
assert len(response_data) == 2
|
46
|
+
# Id should not be empty
|
47
|
+
assert response_data[0] is not None
|
48
|
+
assert response_data[0] != ""
|
49
|
+
assert response_data[1] is not None
|
50
|
+
assert response_data[1] != ""
|
51
|
+
# Data should match
|
52
|
+
assert new_first_permission_data["name"] in [row["name"] for row in response_data]
|
53
|
+
assert new_second_permission_data["name"] in [row["name"] for row in response_data]
|
54
|
+
assert new_first_permission_data["description"] in [
|
55
|
+
row["description"] for row in response_data
|
56
|
+
]
|
57
|
+
assert new_second_permission_data["description"] in [
|
58
|
+
row["description"] for row in response_data
|
59
|
+
]
|
@@ -0,0 +1,68 @@
|
|
1
|
+
from fastapi.testclient import TestClient
|
2
|
+
from my_app_name.main import app
|
3
|
+
from my_app_name.test._util.access_token import get_admin_access_token
|
4
|
+
|
5
|
+
|
6
|
+
def test_delete_permission():
|
7
|
+
client = TestClient(app, base_url="http://localhost")
|
8
|
+
access_token = get_admin_access_token()
|
9
|
+
new_permission_data = {
|
10
|
+
"name": "to-be-deleted-permission",
|
11
|
+
"description": "to-be-deleted-permission-description",
|
12
|
+
}
|
13
|
+
insert_response = client.post(
|
14
|
+
"/api/v1/permissions",
|
15
|
+
json=new_permission_data,
|
16
|
+
headers={"Authorization": f"Bearer {access_token}"},
|
17
|
+
)
|
18
|
+
assert insert_response.status_code == 200
|
19
|
+
id = insert_response.json().get("id")
|
20
|
+
# deleting
|
21
|
+
response = client.delete(
|
22
|
+
f"/api/v1/permissions/{id}", headers={"Authorization": f"Bearer {access_token}"}
|
23
|
+
)
|
24
|
+
assert response.status_code == 200
|
25
|
+
response_data = response.json()
|
26
|
+
assert response_data.get("id") == id
|
27
|
+
assert response_data.get("name") == "to-be-deleted-permission"
|
28
|
+
assert response_data.get("description") == "to-be-deleted-permission-description"
|
29
|
+
|
30
|
+
|
31
|
+
def test_delete_permission_bulk():
|
32
|
+
client = TestClient(app, base_url="http://localhost")
|
33
|
+
access_token = get_admin_access_token()
|
34
|
+
new_first_permission_data = {
|
35
|
+
"name": "to-be-deleted-permission-bulk-1",
|
36
|
+
"description": "to-be-deleted-permission-bulk-description-1",
|
37
|
+
}
|
38
|
+
new_second_permission_data = {
|
39
|
+
"name": "to-be-deleted-permission-bulk-2",
|
40
|
+
"description": "to-be-deleted-permission-bulk-description-2",
|
41
|
+
}
|
42
|
+
new_permission_data = [new_first_permission_data, new_second_permission_data]
|
43
|
+
insert_response = client.post(
|
44
|
+
"/api/v1/permissions/bulk",
|
45
|
+
json=new_permission_data,
|
46
|
+
headers={"Authorization": f"Bearer {access_token}"},
|
47
|
+
)
|
48
|
+
assert insert_response.status_code == 200
|
49
|
+
ids = [row["id"] for row in insert_response.json()]
|
50
|
+
# deleting (use client.request since client.delete doesn't support json param)
|
51
|
+
response = client.request(
|
52
|
+
"DELETE",
|
53
|
+
f"/api/v1/permissions/bulk",
|
54
|
+
json=ids,
|
55
|
+
headers={"Authorization": f"Bearer {access_token}"},
|
56
|
+
)
|
57
|
+
assert response.status_code == 200
|
58
|
+
response_data = response.json()
|
59
|
+
# Data should match
|
60
|
+
assert len([row["id"] for row in response_data if row["id"] in ids]) == 2
|
61
|
+
assert new_first_permission_data["name"] in [row["name"] for row in response_data]
|
62
|
+
assert new_second_permission_data["name"] in [row["name"] for row in response_data]
|
63
|
+
assert new_first_permission_data["description"] in [
|
64
|
+
row["description"] for row in response_data
|
65
|
+
]
|
66
|
+
assert new_second_permission_data["description"] in [
|
67
|
+
row["description"] for row in response_data
|
68
|
+
]
|
@@ -0,0 +1,71 @@
|
|
1
|
+
from fastapi.testclient import TestClient
|
2
|
+
from my_app_name.main import app
|
3
|
+
from my_app_name.test._util.access_token import get_admin_access_token
|
4
|
+
|
5
|
+
|
6
|
+
def test_read_permission_by_id():
|
7
|
+
client = TestClient(app, base_url="http://localhost")
|
8
|
+
access_token = get_admin_access_token()
|
9
|
+
new_permission_data = {
|
10
|
+
"name": "to-be-read-permission",
|
11
|
+
"description": "to-be-read-permission-description",
|
12
|
+
}
|
13
|
+
insert_response = client.post(
|
14
|
+
"/api/v1/permissions",
|
15
|
+
json=new_permission_data,
|
16
|
+
headers={"Authorization": f"Bearer {access_token}"},
|
17
|
+
)
|
18
|
+
assert insert_response.status_code == 200
|
19
|
+
id = insert_response.json().get("id")
|
20
|
+
# fetching
|
21
|
+
response = client.get(
|
22
|
+
f"/api/v1/permissions/{id}", headers={"Authorization": f"Bearer {access_token}"}
|
23
|
+
)
|
24
|
+
assert response.status_code == 200
|
25
|
+
response_data = response.json()
|
26
|
+
assert response_data.get("id") == id
|
27
|
+
assert response_data.get("name") == "to-be-read-permission"
|
28
|
+
assert response_data.get("description") == "to-be-read-permission-description"
|
29
|
+
|
30
|
+
|
31
|
+
def test_read_permission_bulk():
|
32
|
+
client = TestClient(app, base_url="http://localhost")
|
33
|
+
access_token = get_admin_access_token()
|
34
|
+
new_first_permission_data = {
|
35
|
+
"name": "to-be-read-permission-bulk-1",
|
36
|
+
"description": "to-be-read-permission-bulk-description-1",
|
37
|
+
}
|
38
|
+
new_second_permission_data = {
|
39
|
+
"name": "to-be-read-permission-bulk-2",
|
40
|
+
"description": "to-be-read-permission-bulk-description-2",
|
41
|
+
}
|
42
|
+
new_permission_data = [new_first_permission_data, new_second_permission_data]
|
43
|
+
insert_response = client.post(
|
44
|
+
"/api/v1/permissions/bulk",
|
45
|
+
json=new_permission_data,
|
46
|
+
headers={"Authorization": f"Bearer {access_token}"},
|
47
|
+
)
|
48
|
+
assert insert_response.status_code == 200
|
49
|
+
ids = [row["id"] for row in insert_response.json()]
|
50
|
+
# fetching
|
51
|
+
response = client.get(
|
52
|
+
f"/api/v1/permissions",
|
53
|
+
params={
|
54
|
+
"filter": "name:like:to-be-read-permission-bulk-%",
|
55
|
+
},
|
56
|
+
headers={"Authorization": f"Bearer {access_token}"},
|
57
|
+
)
|
58
|
+
assert response.status_code == 200
|
59
|
+
response_data_count = response.json()["count"]
|
60
|
+
assert response_data_count == 2
|
61
|
+
response_data = response.json()["data"]
|
62
|
+
# Data should match
|
63
|
+
assert len([row["id"] for row in response_data if row["id"] in ids]) == 2
|
64
|
+
assert new_first_permission_data["name"] in [row["name"] for row in response_data]
|
65
|
+
assert new_second_permission_data["name"] in [row["name"] for row in response_data]
|
66
|
+
assert new_first_permission_data["description"] in [
|
67
|
+
row["description"] for row in response_data
|
68
|
+
]
|
69
|
+
assert new_second_permission_data["description"] in [
|
70
|
+
row["description"] for row in response_data
|
71
|
+
]
|
@@ -0,0 +1,66 @@
|
|
1
|
+
from fastapi.testclient import TestClient
|
2
|
+
from my_app_name.main import app
|
3
|
+
from my_app_name.test._util.access_token import get_admin_access_token
|
4
|
+
|
5
|
+
|
6
|
+
def test_update_permission():
|
7
|
+
client = TestClient(app, base_url="http://localhost")
|
8
|
+
access_token = get_admin_access_token()
|
9
|
+
new_permission_data = {
|
10
|
+
"name": "to-be-updated-permission",
|
11
|
+
"description": "to-be-updated-permission-description",
|
12
|
+
}
|
13
|
+
insert_response = client.post(
|
14
|
+
"/api/v1/permissions",
|
15
|
+
json=new_permission_data,
|
16
|
+
headers={"Authorization": f"Bearer {access_token}"},
|
17
|
+
)
|
18
|
+
assert insert_response.status_code == 200
|
19
|
+
id = insert_response.json().get("id")
|
20
|
+
# updating
|
21
|
+
updated_permission_data = {
|
22
|
+
"name": "updated-permission",
|
23
|
+
"description": "updated-permission-description",
|
24
|
+
}
|
25
|
+
response = client.put(
|
26
|
+
f"/api/v1/permissions/{id}",
|
27
|
+
json=updated_permission_data,
|
28
|
+
headers={"Authorization": f"Bearer {access_token}"},
|
29
|
+
)
|
30
|
+
assert response.status_code == 200
|
31
|
+
response_data = response.json()
|
32
|
+
assert response_data.get("id") == id
|
33
|
+
assert response_data.get("name") == "updated-permission"
|
34
|
+
assert response_data.get("description") == "updated-permission-description"
|
35
|
+
|
36
|
+
|
37
|
+
def test_update_permission_bulk():
|
38
|
+
client = TestClient(app, base_url="http://localhost")
|
39
|
+
access_token = get_admin_access_token()
|
40
|
+
new_first_permission_data = {
|
41
|
+
"name": "to-be-updated-permission-bulk-1",
|
42
|
+
"description": "to-be-updated-permission-bulk-description-1",
|
43
|
+
}
|
44
|
+
insert_response = client.post(
|
45
|
+
"/api/v1/permissions/bulk",
|
46
|
+
json=[new_first_permission_data],
|
47
|
+
headers={"Authorization": f"Bearer {access_token}"},
|
48
|
+
)
|
49
|
+
assert insert_response.status_code == 200
|
50
|
+
ids = [row["id"] for row in insert_response.json()]
|
51
|
+
# updating (we only test with one data)
|
52
|
+
updated_permission_data = {"description": "updated-permission-description"}
|
53
|
+
response = client.put(
|
54
|
+
f"/api/v1/permissions/bulk",
|
55
|
+
json={
|
56
|
+
"permission_ids": ids,
|
57
|
+
"data": updated_permission_data,
|
58
|
+
},
|
59
|
+
headers={"Authorization": f"Bearer {access_token}"},
|
60
|
+
)
|
61
|
+
assert response.status_code == 200
|
62
|
+
response_data = response.json()
|
63
|
+
assert len(response_data) == 1
|
64
|
+
response_data[0].get("id") == ids[0]
|
65
|
+
response_data[0].get("name") == new_first_permission_data["name"]
|
66
|
+
response_data[0].get("description") == new_first_permission_data["description"]
|
@@ -0,0 +1,195 @@
|
|
1
|
+
import time
|
2
|
+
from typing import Annotated
|
3
|
+
|
4
|
+
from fastapi import Depends
|
5
|
+
from fastapi.testclient import TestClient
|
6
|
+
from my_app_name.config import (
|
7
|
+
APP_AUTH_ACCESS_TOKEN_COOKIE_NAME,
|
8
|
+
APP_AUTH_GUEST_USER,
|
9
|
+
APP_AUTH_GUEST_USER_PERMISSIONS,
|
10
|
+
APP_AUTH_REFRESH_TOKEN_COOKIE_NAME,
|
11
|
+
APP_AUTH_SUPER_USER,
|
12
|
+
APP_AUTH_SUPER_USER_PASSWORD,
|
13
|
+
)
|
14
|
+
from my_app_name.main import app
|
15
|
+
from my_app_name.module.gateway.util.auth import get_current_user
|
16
|
+
from my_app_name.schema.user import AuthUserResponse
|
17
|
+
|
18
|
+
|
19
|
+
@app.get("/test/current-user", response_model=AuthUserResponse)
|
20
|
+
async def serve_get_current_user(
|
21
|
+
current_user: Annotated[AuthUserResponse, Depends(get_current_user)],
|
22
|
+
) -> AuthUserResponse:
|
23
|
+
return current_user
|
24
|
+
|
25
|
+
|
26
|
+
def test_create_invalid_user_session():
|
27
|
+
client = TestClient(app, base_url="http://localhost")
|
28
|
+
response = client.post(
|
29
|
+
"/api/v1/user-sessions",
|
30
|
+
data={
|
31
|
+
"username": "nonExistingUserFromSevenKingdom",
|
32
|
+
"password": "nonExistingSecurity",
|
33
|
+
},
|
34
|
+
)
|
35
|
+
assert response.status_code == 401
|
36
|
+
|
37
|
+
|
38
|
+
def test_get_guest_user():
|
39
|
+
client = TestClient(app, base_url="http://localhost")
|
40
|
+
# Fetch current user
|
41
|
+
response = client.get("/test/current-user")
|
42
|
+
assert response.status_code == 200
|
43
|
+
user_data = response.json()
|
44
|
+
assert user_data.get("username") == APP_AUTH_GUEST_USER
|
45
|
+
assert not user_data.get("is_super_user")
|
46
|
+
assert user_data.get("is_guest")
|
47
|
+
assert user_data.get("permission_names") == APP_AUTH_GUEST_USER_PERMISSIONS
|
48
|
+
|
49
|
+
|
50
|
+
def test_create_admin_user_session():
|
51
|
+
client = TestClient(app, base_url="http://localhost")
|
52
|
+
# Create new admin user session and check the response
|
53
|
+
session_response = client.post(
|
54
|
+
"/api/v1/user-sessions",
|
55
|
+
data={
|
56
|
+
"username": APP_AUTH_SUPER_USER,
|
57
|
+
"password": APP_AUTH_SUPER_USER_PASSWORD,
|
58
|
+
},
|
59
|
+
)
|
60
|
+
assert session_response.status_code == 200
|
61
|
+
session_data = session_response.json()
|
62
|
+
assert session_data.get("user_id") == APP_AUTH_SUPER_USER
|
63
|
+
assert session_data.get("token_type") == "bearer"
|
64
|
+
assert "access_token" in session_data
|
65
|
+
assert "access_token_expired_at" in session_data
|
66
|
+
assert "refresh_token" in session_data
|
67
|
+
assert "refresh_token_expired_at" in session_data
|
68
|
+
assert session_response.cookies.get(
|
69
|
+
APP_AUTH_ACCESS_TOKEN_COOKIE_NAME
|
70
|
+
) == session_data.get("access_token")
|
71
|
+
assert session_response.cookies.get(
|
72
|
+
APP_AUTH_REFRESH_TOKEN_COOKIE_NAME
|
73
|
+
) == session_data.get("refresh_token")
|
74
|
+
|
75
|
+
|
76
|
+
def test_get_admin_user_with_bearer():
|
77
|
+
client = TestClient(app, base_url="http://localhost")
|
78
|
+
session_response = client.post(
|
79
|
+
"/api/v1/user-sessions",
|
80
|
+
data={
|
81
|
+
"username": APP_AUTH_SUPER_USER,
|
82
|
+
"password": APP_AUTH_SUPER_USER_PASSWORD,
|
83
|
+
},
|
84
|
+
)
|
85
|
+
assert session_response.status_code == 200
|
86
|
+
access_token = session_response.json()["access_token"]
|
87
|
+
# Fetch current user with bearer token
|
88
|
+
response = client.get(
|
89
|
+
"/test/current-user", headers={"Authorization": f"Bearer {access_token}"}
|
90
|
+
)
|
91
|
+
assert response.status_code == 200
|
92
|
+
user_data = response.json()
|
93
|
+
assert user_data.get("username") == APP_AUTH_SUPER_USER
|
94
|
+
assert user_data.get("is_super_user")
|
95
|
+
assert not user_data.get("is_guest")
|
96
|
+
|
97
|
+
|
98
|
+
def test_get_admin_user_with_cookie():
|
99
|
+
login_client = TestClient(app, base_url="http://localhost")
|
100
|
+
session_response = login_client.post(
|
101
|
+
"/api/v1/user-sessions",
|
102
|
+
data={
|
103
|
+
"username": APP_AUTH_SUPER_USER,
|
104
|
+
"password": APP_AUTH_SUPER_USER_PASSWORD,
|
105
|
+
},
|
106
|
+
)
|
107
|
+
assert session_response.status_code == 200
|
108
|
+
# Fetch current user with cookies
|
109
|
+
cookies = {
|
110
|
+
APP_AUTH_ACCESS_TOKEN_COOKIE_NAME: session_response.cookies.get(
|
111
|
+
APP_AUTH_ACCESS_TOKEN_COOKIE_NAME
|
112
|
+
)
|
113
|
+
}
|
114
|
+
# re-initiate client with cookies
|
115
|
+
client = TestClient(app, base_url="http://localhost", cookies=cookies)
|
116
|
+
response = client.get("/test/current-user")
|
117
|
+
assert response.status_code == 200
|
118
|
+
user_data = response.json()
|
119
|
+
assert user_data.get("username") == APP_AUTH_SUPER_USER
|
120
|
+
assert user_data.get("is_super_user")
|
121
|
+
assert not user_data.get("is_guest")
|
122
|
+
|
123
|
+
|
124
|
+
def test_update_user_session():
|
125
|
+
login_client = TestClient(app, base_url="http://localhost")
|
126
|
+
old_session_response = login_client.post(
|
127
|
+
"/api/v1/user-sessions",
|
128
|
+
data={
|
129
|
+
"username": APP_AUTH_SUPER_USER,
|
130
|
+
"password": APP_AUTH_SUPER_USER_PASSWORD,
|
131
|
+
},
|
132
|
+
)
|
133
|
+
assert old_session_response.status_code == 200
|
134
|
+
old_session_data = old_session_response.json()
|
135
|
+
# re-initiate client with cookies and delete user session
|
136
|
+
client = TestClient(app, base_url="http://localhost")
|
137
|
+
time.sleep(1)
|
138
|
+
new_session_response = client.put(
|
139
|
+
"/api/v1/user-sessions",
|
140
|
+
params={"refresh_token": old_session_data.get("refresh_token")},
|
141
|
+
)
|
142
|
+
assert new_session_response.status_code == 200
|
143
|
+
new_session_data = new_session_response.json()
|
144
|
+
# Cookies and response should match
|
145
|
+
assert new_session_response.cookies.get(
|
146
|
+
APP_AUTH_ACCESS_TOKEN_COOKIE_NAME
|
147
|
+
) == new_session_data.get("access_token")
|
148
|
+
assert new_session_response.cookies.get(
|
149
|
+
APP_AUTH_REFRESH_TOKEN_COOKIE_NAME
|
150
|
+
) == new_session_data.get("refresh_token")
|
151
|
+
# New session should has longer TTL than old session
|
152
|
+
assert old_session_data.get("access_token") != new_session_data.get("access_token")
|
153
|
+
assert old_session_data.get("access_token_expired_at") < new_session_data.get(
|
154
|
+
"access_token_expired_at"
|
155
|
+
)
|
156
|
+
assert old_session_data.get("refresh_token") != new_session_data.get(
|
157
|
+
"refresh_token"
|
158
|
+
)
|
159
|
+
assert old_session_data.get("refresh_token_expired_at") < new_session_data.get(
|
160
|
+
"refresh_token_expired_at"
|
161
|
+
)
|
162
|
+
|
163
|
+
|
164
|
+
def test_delete_user_session():
|
165
|
+
login_client = TestClient(app, base_url="http://localhost")
|
166
|
+
session_response = login_client.post(
|
167
|
+
"/api/v1/user-sessions",
|
168
|
+
data={
|
169
|
+
"username": APP_AUTH_SUPER_USER,
|
170
|
+
"password": APP_AUTH_SUPER_USER_PASSWORD,
|
171
|
+
},
|
172
|
+
)
|
173
|
+
assert session_response.status_code == 200
|
174
|
+
# Initiate cookies that should be deleted when user session is deleted.
|
175
|
+
cookies = {
|
176
|
+
APP_AUTH_ACCESS_TOKEN_COOKIE_NAME: session_response.cookies.get(
|
177
|
+
APP_AUTH_ACCESS_TOKEN_COOKIE_NAME
|
178
|
+
),
|
179
|
+
APP_AUTH_REFRESH_TOKEN_COOKIE_NAME: session_response.cookies.get(
|
180
|
+
APP_AUTH_REFRESH_TOKEN_COOKIE_NAME
|
181
|
+
),
|
182
|
+
}
|
183
|
+
# re-initiate client with cookies and delete user session
|
184
|
+
client = TestClient(app, base_url="http://localhost", cookies=cookies)
|
185
|
+
response = client.delete(
|
186
|
+
"/api/v1/user-sessions",
|
187
|
+
params={
|
188
|
+
"refresh_token": session_response.cookies.get(
|
189
|
+
APP_AUTH_REFRESH_TOKEN_COOKIE_NAME
|
190
|
+
),
|
191
|
+
},
|
192
|
+
)
|
193
|
+
assert response.status_code == 200
|
194
|
+
assert response.cookies.get(APP_AUTH_ACCESS_TOKEN_COOKIE_NAME, None) is None
|
195
|
+
assert response.cookies.get(APP_AUTH_REFRESH_TOKEN_COOKIE_NAME, None) is None
|
zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/test_health_and_readiness.py
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
from fastapi.testclient import TestClient
|
2
|
+
from my_app_name.main import app
|
3
|
+
|
4
|
+
|
5
|
+
def test_get_health():
|
6
|
+
client = TestClient(app, base_url="http://localhost")
|
7
|
+
response = client.get("/health")
|
8
|
+
assert response.status_code == 200
|
9
|
+
assert response.json() == {"message": "ok"}
|
10
|
+
|
11
|
+
|
12
|
+
def test_head_health():
|
13
|
+
client = TestClient(app, base_url="http://localhost")
|
14
|
+
response = client.head("/health")
|
15
|
+
assert response.status_code == 200
|
16
|
+
|
17
|
+
|
18
|
+
def test_get_readiness():
|
19
|
+
client = TestClient(app, base_url="http://localhost")
|
20
|
+
response = client.get("/readiness")
|
21
|
+
assert response.status_code == 200
|
22
|
+
assert response.json() == {"message": "ok"}
|
23
|
+
|
24
|
+
|
25
|
+
def test_head_readiness():
|
26
|
+
client = TestClient(app, base_url="http://localhost")
|
27
|
+
response = client.head("/readiness")
|
28
|
+
assert response.status_code == 200
|
@@ -0,0 +1,17 @@
|
|
1
|
+
import os
|
2
|
+
|
3
|
+
from fastapi.testclient import TestClient
|
4
|
+
from my_app_name.main import app
|
5
|
+
from my_app_name.module.gateway.util.view import render
|
6
|
+
|
7
|
+
|
8
|
+
def test_homepage():
|
9
|
+
client = TestClient(app, base_url="http://localhost")
|
10
|
+
response = client.get("/")
|
11
|
+
assert response.status_code == 200
|
12
|
+
view_path = os.path.join(
|
13
|
+
os.path.dirname(os.path.dirname(__file__)), "module", "gateway", "view"
|
14
|
+
)
|
15
|
+
assert response.text == render(
|
16
|
+
os.path.join(view_path, "content", "homepage.html")
|
17
|
+
).body.decode("utf-8")
|