zrb 1.0.0b9__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/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 +107 -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/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/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 +56 -12
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/task_util.py +10 -4
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/base_service.py +136 -52
- 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 +1 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/migration/versions/3093c7336477_add_auth_tables.py +46 -43
- 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/service/user/repository/user_db_repository.py +5 -2
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/user_service.py +16 -21
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/subroute/auth.py +193 -43
- 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 +6 -1
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/permission.py +1 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/user.py +9 -0
- 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/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.0b9.dist-info → zrb-1.0.0b10.dist-info}/METADATA +1 -1
- {zrb-1.0.0b9.dist-info → zrb-1.0.0b10.dist-info}/RECORD +53 -36
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/migrate.py +0 -3
- 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.0b9.dist-info → zrb-1.0.0b10.dist-info}/WHEEL +0 -0
- {zrb-1.0.0b9.dist-info → zrb-1.0.0b10.dist-info}/entry_points.txt +0 -0
@@ -11,7 +11,7 @@ from my_app_name._zrb.input import (
|
|
11
11
|
from my_app_name._zrb.util import get_existing_schema_names
|
12
12
|
|
13
13
|
from zrb import AnyContext, Task, make_task
|
14
|
-
from zrb.util.codemod.
|
14
|
+
from zrb.util.codemod.modify_class_property import append_property_to_class
|
15
15
|
from zrb.util.file import read_file, write_file
|
16
16
|
from zrb.util.string.conversion import to_pascal_case, to_snake_case
|
17
17
|
|
@@ -45,7 +45,7 @@ def update_my_app_name_schema(ctx: AnyContext):
|
|
45
45
|
snake_column_name = to_snake_case(ctx.input.column)
|
46
46
|
column_type = ctx.input.type
|
47
47
|
# Base
|
48
|
-
new_code =
|
48
|
+
new_code = append_property_to_class(
|
49
49
|
original_code=existing_code,
|
50
50
|
class_name=f"{pascal_entity_name}Base",
|
51
51
|
property_name=snake_column_name,
|
@@ -53,7 +53,7 @@ def update_my_app_name_schema(ctx: AnyContext):
|
|
53
53
|
default_value=_get_default_value(column_type),
|
54
54
|
)
|
55
55
|
# Update
|
56
|
-
new_code =
|
56
|
+
new_code = append_property_to_class(
|
57
57
|
original_code=new_code,
|
58
58
|
class_name=f"{pascal_entity_name}Update",
|
59
59
|
property_name=snake_column_name,
|
@@ -61,7 +61,7 @@ def update_my_app_name_schema(ctx: AnyContext):
|
|
61
61
|
default_value="None",
|
62
62
|
)
|
63
63
|
# Table
|
64
|
-
new_code =
|
64
|
+
new_code = append_property_to_class(
|
65
65
|
original_code=new_code,
|
66
66
|
class_name=f"{pascal_entity_name}",
|
67
67
|
property_name=snake_column_name,
|
@@ -13,6 +13,11 @@ MONOLITH_ENV_VARS = {
|
|
13
13
|
"MY_APP_NAME_MODE": "monolith",
|
14
14
|
"MY_APP_NAME_MODULES": "",
|
15
15
|
}
|
16
|
+
TEST_ENV_VARS = {
|
17
|
+
"MY_APP_NAME_DB_URL": f"sqlite:///{APP_DIR}/test.db",
|
18
|
+
"MY_APP_NAME_AUTH_PRIORITIZE_NEW_SESSION": "1", # Need this because we will launch a new user session for each test
|
19
|
+
"MY_APP_NAME_AUTH_GUEST_USER_PERMISSIONs": "", # Guest user should not has any privilege for testing
|
20
|
+
}
|
16
21
|
|
17
22
|
if platform.system() == "Windows":
|
18
23
|
ACTIVATE_VENV_SCRIPT = "Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser; . .venv\\Scripts\\Activate" # noqa
|
@@ -2,8 +2,14 @@ import os
|
|
2
2
|
|
3
3
|
from my_app_name._zrb.config import ACTIVATE_VENV_SCRIPT, APP_DIR
|
4
4
|
from my_app_name._zrb.entity.add_entity_util import (
|
5
|
+
get_add_permission_migration_script,
|
6
|
+
get_auth_migration_version_dir,
|
7
|
+
get_existing_auth_migration_file_names,
|
8
|
+
get_existing_auth_migration_xcom_key,
|
9
|
+
get_remove_permission_migration_script,
|
5
10
|
is_in_app_schema_dir,
|
6
11
|
is_in_module_entity_dir,
|
12
|
+
is_in_module_entity_test_dir,
|
7
13
|
is_module_api_client_file,
|
8
14
|
is_module_client_file,
|
9
15
|
is_module_direct_client_file,
|
@@ -42,8 +48,11 @@ from zrb import (
|
|
42
48
|
EnvFile,
|
43
49
|
Scaffolder,
|
44
50
|
Task,
|
51
|
+
Xcom,
|
45
52
|
make_task,
|
46
53
|
)
|
54
|
+
from zrb.util.codemod.modify_function import replace_function_code
|
55
|
+
from zrb.util.file import read_file, write_file
|
47
56
|
from zrb.util.string.conversion import to_snake_case
|
48
57
|
|
49
58
|
|
@@ -105,6 +114,18 @@ scaffold_my_app_name_entity = Scaffolder(
|
|
105
114
|
"my-entities": "{to_kebab_case(ctx.input.plural)}",
|
106
115
|
},
|
107
116
|
),
|
117
|
+
# Test transformation (my_app_name/test/my_module/my_entity)
|
118
|
+
ContentTransformer(
|
119
|
+
name="transform-module-entity-test-dir",
|
120
|
+
match=is_in_module_entity_test_dir,
|
121
|
+
transform={
|
122
|
+
"my_module": "{to_snake_case(ctx.input.module)}",
|
123
|
+
"my_entity": "{to_snake_case(ctx.input.entity)}",
|
124
|
+
"my-entity": "{to_kebab_case(ctx.input.entity)}",
|
125
|
+
"my-entities": "{to_kebab_case(ctx.input.plural)}",
|
126
|
+
"my_column": "{to_snake_case(ctx.input.column)}",
|
127
|
+
},
|
128
|
+
),
|
108
129
|
# Add entity to migration metadata
|
109
130
|
# (my_app_name/module/snake_module_name/migration_metadata.py)
|
110
131
|
ContentTransformer(
|
@@ -177,11 +198,96 @@ create_my_app_name_entity_migration = CmdTask(
|
|
177
198
|
],
|
178
199
|
)
|
179
200
|
|
201
|
+
|
202
|
+
@make_task(
|
203
|
+
name="inspect-my-app-name-auth-migration",
|
204
|
+
input=new_entity_input,
|
205
|
+
retries=0,
|
206
|
+
upstream=scaffold_my_app_name_entity,
|
207
|
+
)
|
208
|
+
def inspect_my_app_name_auth_migration(ctx: AnyContext):
|
209
|
+
"""Getting existing migration files in auth module"""
|
210
|
+
migration_file_names = get_existing_auth_migration_file_names()
|
211
|
+
xcom_key = get_existing_auth_migration_xcom_key(ctx)
|
212
|
+
if xcom_key not in ctx.xcom:
|
213
|
+
ctx.xcom[xcom_key] = Xcom([])
|
214
|
+
ctx.xcom[xcom_key].push(migration_file_names)
|
215
|
+
|
216
|
+
|
217
|
+
create_my_app_name_entity_permission = CmdTask(
|
218
|
+
name="create-my-app-name-entity-permission",
|
219
|
+
input=[
|
220
|
+
new_entity_input,
|
221
|
+
],
|
222
|
+
env=EnvFile(path=os.path.join(APP_DIR, "template.env")),
|
223
|
+
cwd=APP_DIR,
|
224
|
+
cmd=[
|
225
|
+
ACTIVATE_VENV_SCRIPT,
|
226
|
+
Cmd(lambda ctx: set_create_migration_db_url_env("auth")),
|
227
|
+
Cmd(lambda ctx: set_env("MY_APP_NAME_MODULES", "auth")),
|
228
|
+
Cmd(lambda ctx: cd_module_script("auth")),
|
229
|
+
"alembic upgrade head",
|
230
|
+
Cmd(
|
231
|
+
'alembic revision --autogenerate -m "create_{to_snake_case(ctx.input.entity)}_permission"', # noqa
|
232
|
+
),
|
233
|
+
],
|
234
|
+
render_cmd=False,
|
235
|
+
retries=0,
|
236
|
+
upstream=[
|
237
|
+
prepare_venv,
|
238
|
+
inspect_my_app_name_auth_migration,
|
239
|
+
],
|
240
|
+
)
|
241
|
+
|
242
|
+
|
243
|
+
@make_task(
|
244
|
+
name="update-my-app-name-entity-permission",
|
245
|
+
input=new_entity_input,
|
246
|
+
retries=0,
|
247
|
+
upstream=create_my_app_name_entity_permission,
|
248
|
+
)
|
249
|
+
def update_my_app_name_entity_permission(ctx: AnyContext):
|
250
|
+
xcom_key = get_existing_auth_migration_xcom_key(ctx)
|
251
|
+
existing_migration_file_names = ctx.xcom[xcom_key].pop()
|
252
|
+
current_migration_file_names = get_existing_auth_migration_file_names()
|
253
|
+
new_migration_file_names = [
|
254
|
+
file_name
|
255
|
+
for file_name in current_migration_file_names
|
256
|
+
if file_name not in existing_migration_file_names
|
257
|
+
]
|
258
|
+
if len(new_migration_file_names) == 0:
|
259
|
+
raise Exception("No migration file created")
|
260
|
+
new_migration_file_path = os.path.join(
|
261
|
+
get_auth_migration_version_dir(), new_migration_file_names[0]
|
262
|
+
)
|
263
|
+
new_migration_code = read_file(
|
264
|
+
new_migration_file_path,
|
265
|
+
{
|
266
|
+
"from alembic import op": "\n".join(
|
267
|
+
[
|
268
|
+
"from alembic import op",
|
269
|
+
"from module.auth.migration_metadata import metadata",
|
270
|
+
]
|
271
|
+
),
|
272
|
+
},
|
273
|
+
)
|
274
|
+
new_migration_code = replace_function_code(
|
275
|
+
new_migration_code, "upgrade", get_add_permission_migration_script(ctx)
|
276
|
+
)
|
277
|
+
new_migration_code = replace_function_code(
|
278
|
+
new_migration_code, "downgrade", get_remove_permission_migration_script(ctx)
|
279
|
+
)
|
280
|
+
write_file(new_migration_file_path, new_migration_code)
|
281
|
+
|
282
|
+
|
180
283
|
add_my_app_name_entity = app_create_group.add_task(
|
181
284
|
Task(
|
182
285
|
name="add-my-app-name-entity",
|
183
286
|
description="🏗️ Create new entity on a module",
|
184
|
-
upstream=
|
287
|
+
upstream=[
|
288
|
+
create_my_app_name_entity_migration,
|
289
|
+
update_my_app_name_entity_permission,
|
290
|
+
],
|
185
291
|
successor=format_my_app_name_code,
|
186
292
|
retries=0,
|
187
293
|
),
|
@@ -3,20 +3,83 @@ import os
|
|
3
3
|
from my_app_name._zrb.config import APP_DIR
|
4
4
|
|
5
5
|
from zrb.context.any_context import AnyContext
|
6
|
-
from zrb.util.codemod.
|
7
|
-
from zrb.util.codemod.
|
8
|
-
from zrb.util.codemod.
|
9
|
-
from zrb.util.codemod.
|
6
|
+
from zrb.util.codemod.modify_class import append_code_to_class
|
7
|
+
from zrb.util.codemod.modify_class_parent import prepend_parent_class
|
8
|
+
from zrb.util.codemod.modify_function import append_code_to_function
|
9
|
+
from zrb.util.codemod.modify_module import prepend_code_to_module
|
10
10
|
from zrb.util.file import read_file, write_file
|
11
11
|
from zrb.util.string.conversion import to_kebab_case, to_pascal_case, to_snake_case
|
12
12
|
|
13
13
|
|
14
|
+
def get_add_permission_migration_script(ctx: AnyContext) -> str:
|
15
|
+
kebab_entity_name = to_kebab_case(ctx.input.entity)
|
16
|
+
return "\n".join(
|
17
|
+
[
|
18
|
+
"op.bulk_insert(",
|
19
|
+
' metadata.tables["permissions"],',
|
20
|
+
" [",
|
21
|
+
f' {{"name": "{kebab_entity_name}:create", "description": "create {kebab_entity_name}"}},', # noqa
|
22
|
+
f' {{"name": "{kebab_entity_name}:read", "description": "read {kebab_entity_name}"}},', # noqa
|
23
|
+
f' {{"name": "{kebab_entity_name}:update", "description": "update {kebab_entity_name}"}},', # noqa
|
24
|
+
f' {{"name": "{kebab_entity_name}:delete", "description": "delete {kebab_entity_name}"}},', # noqa
|
25
|
+
" ]",
|
26
|
+
")",
|
27
|
+
]
|
28
|
+
)
|
29
|
+
|
30
|
+
|
31
|
+
def get_remove_permission_migration_script(ctx: AnyContext) -> str:
|
32
|
+
kebab_entity_name = to_kebab_case(ctx.input.entity)
|
33
|
+
return "\n".join(
|
34
|
+
[
|
35
|
+
"op.execute(",
|
36
|
+
' sa.delete(metadata.tables["permissions"])',
|
37
|
+
' .where(metadata.tables["permissions"].c.name.in_(',
|
38
|
+
f' "{kebab_entity_name}:create",',
|
39
|
+
f' "{kebab_entity_name}:read",',
|
40
|
+
f' "{kebab_entity_name}:update",',
|
41
|
+
f' "{kebab_entity_name}:delete",',
|
42
|
+
" ))",
|
43
|
+
")",
|
44
|
+
]
|
45
|
+
)
|
46
|
+
|
47
|
+
|
48
|
+
def get_auth_migration_version_dir() -> str:
|
49
|
+
return os.path.join(APP_DIR, "module", "auth", "migration", "versions")
|
50
|
+
|
51
|
+
|
52
|
+
def get_existing_auth_migration_file_names() -> list[str]:
|
53
|
+
migration_version_dir = get_auth_migration_version_dir()
|
54
|
+
return [
|
55
|
+
file_name
|
56
|
+
for file_name in os.listdir(migration_version_dir)
|
57
|
+
if file_name.endswith(".py")
|
58
|
+
]
|
59
|
+
|
60
|
+
|
61
|
+
def get_existing_auth_migration_xcom_key(ctx: AnyContext) -> str:
|
62
|
+
snake_entity_name = to_snake_case(ctx.input.entity)
|
63
|
+
return f"existing_my_app_name_auth_{snake_entity_name}_migration"
|
64
|
+
|
65
|
+
|
14
66
|
def is_in_app_schema_dir(ctx: AnyContext, file_path: str) -> bool:
|
15
67
|
return file_path.startswith(
|
16
68
|
os.path.join(APP_DIR, "schema", to_snake_case(ctx.input.entity))
|
17
69
|
)
|
18
70
|
|
19
71
|
|
72
|
+
def is_in_module_entity_test_dir(ctx: AnyContext, file_path: str) -> bool:
|
73
|
+
return file_path.startswith(
|
74
|
+
os.path.join(
|
75
|
+
APP_DIR,
|
76
|
+
"test",
|
77
|
+
to_snake_case(ctx.input.module),
|
78
|
+
to_snake_case(ctx.input.entity),
|
79
|
+
)
|
80
|
+
)
|
81
|
+
|
82
|
+
|
20
83
|
def is_in_module_entity_dir(ctx: AnyContext, file_path: str) -> bool:
|
21
84
|
return file_path.startswith(
|
22
85
|
os.path.join(
|
@@ -0,0 +1,53 @@
|
|
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_my_entity():
|
7
|
+
client = TestClient(app, base_url="http://localhost")
|
8
|
+
access_token = get_admin_access_token()
|
9
|
+
new_my_entity_data = {
|
10
|
+
"my_column": "new-my-entity",
|
11
|
+
}
|
12
|
+
response = client.post(
|
13
|
+
"/api/v1/my-entities",
|
14
|
+
json=new_my_entity_data,
|
15
|
+
headers={"Authorization": f"Bearer {access_token}"},
|
16
|
+
)
|
17
|
+
assert response.status_code == 200
|
18
|
+
response_data = response.json()
|
19
|
+
assert response_data.get("id") is not None
|
20
|
+
assert response_data.get("id") != ""
|
21
|
+
assert response_data.get("my_column") == "new-my-entity"
|
22
|
+
|
23
|
+
|
24
|
+
def test_create_my_entity_bulk():
|
25
|
+
client = TestClient(app, base_url="http://localhost")
|
26
|
+
access_token = get_admin_access_token()
|
27
|
+
new_first_my_entity_data = {
|
28
|
+
"my_column": "new-my-entity-bulk-1",
|
29
|
+
}
|
30
|
+
new_second_my_entity_data = {
|
31
|
+
"my_column": "new-my-entity-bulk-2",
|
32
|
+
}
|
33
|
+
new_my_entity_data = [new_first_my_entity_data, new_second_my_entity_data]
|
34
|
+
response = client.post(
|
35
|
+
"/api/v1/my-entities/bulk",
|
36
|
+
json=new_my_entity_data,
|
37
|
+
headers={"Authorization": f"Bearer {access_token}"},
|
38
|
+
)
|
39
|
+
assert response.status_code == 200
|
40
|
+
response_data = response.json()
|
41
|
+
assert len(response_data) == 2
|
42
|
+
# Id should not be empty
|
43
|
+
assert response_data[0] is not None
|
44
|
+
assert response_data[0] != ""
|
45
|
+
assert response_data[1] is not None
|
46
|
+
assert response_data[1] != ""
|
47
|
+
# Data should match
|
48
|
+
assert new_first_my_entity_data["my_column"] in [
|
49
|
+
row["my_column"] for row in response_data
|
50
|
+
]
|
51
|
+
assert new_second_my_entity_data["my_column"] in [
|
52
|
+
row["my_column"] for row in response_data
|
53
|
+
]
|
@@ -0,0 +1,62 @@
|
|
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_my_entity():
|
7
|
+
client = TestClient(app, base_url="http://localhost")
|
8
|
+
access_token = get_admin_access_token()
|
9
|
+
new_my_entity_data = {
|
10
|
+
"my_column": "to-be-deleted-my-entity",
|
11
|
+
}
|
12
|
+
insert_response = client.post(
|
13
|
+
"/api/v1/my-entities",
|
14
|
+
json=new_my_entity_data,
|
15
|
+
headers={"Authorization": f"Bearer {access_token}"},
|
16
|
+
)
|
17
|
+
assert insert_response.status_code == 200
|
18
|
+
id = insert_response.json().get("id")
|
19
|
+
# deleting
|
20
|
+
response = client.delete(
|
21
|
+
f"/api/v1/my-entities/{id}", headers={"Authorization": f"Bearer {access_token}"}
|
22
|
+
)
|
23
|
+
assert response.status_code == 200
|
24
|
+
response_data = response.json()
|
25
|
+
assert response_data.get("id") == id
|
26
|
+
assert response_data.get("my_column") == "to-be-deleted-my-entity"
|
27
|
+
|
28
|
+
|
29
|
+
def test_delete_my_entity_bulk():
|
30
|
+
client = TestClient(app, base_url="http://localhost")
|
31
|
+
access_token = get_admin_access_token()
|
32
|
+
new_first_my_entity_data = {
|
33
|
+
"my_column": "to-be-deleted-my-entity-bulk-1",
|
34
|
+
}
|
35
|
+
new_second_my_entity_data = {
|
36
|
+
"my_column": "to-be-deleted-my-entity-bulk-2",
|
37
|
+
}
|
38
|
+
new_my_entity_data = [new_first_my_entity_data, new_second_my_entity_data]
|
39
|
+
insert_response = client.post(
|
40
|
+
"/api/v1/my-entities/bulk",
|
41
|
+
json=new_my_entity_data,
|
42
|
+
headers={"Authorization": f"Bearer {access_token}"},
|
43
|
+
)
|
44
|
+
assert insert_response.status_code == 200
|
45
|
+
ids = [row["id"] for row in insert_response.json()]
|
46
|
+
# deleting (use client.request since client.delete doesn't support json param)
|
47
|
+
response = client.request(
|
48
|
+
"DELETE",
|
49
|
+
f"/api/v1/my-entities/bulk",
|
50
|
+
json=ids,
|
51
|
+
headers={"Authorization": f"Bearer {access_token}"},
|
52
|
+
)
|
53
|
+
assert response.status_code == 200
|
54
|
+
response_data = response.json()
|
55
|
+
# Data should match
|
56
|
+
assert len([row["id"] for row in response_data if row["id"] in ids]) == 2
|
57
|
+
assert new_first_my_entity_data["my_column"] in [
|
58
|
+
row["my_column"] for row in response_data
|
59
|
+
]
|
60
|
+
assert new_second_my_entity_data["my_column"] in [
|
61
|
+
row["my_column"] for row in response_data
|
62
|
+
]
|
@@ -0,0 +1,65 @@
|
|
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_my_entity_by_id():
|
7
|
+
client = TestClient(app, base_url="http://localhost")
|
8
|
+
access_token = get_admin_access_token()
|
9
|
+
new_my_entity_data = {
|
10
|
+
"my_column": "to-be-read-my-entity",
|
11
|
+
}
|
12
|
+
insert_response = client.post(
|
13
|
+
"/api/v1/my-entities",
|
14
|
+
json=new_my_entity_data,
|
15
|
+
headers={"Authorization": f"Bearer {access_token}"},
|
16
|
+
)
|
17
|
+
assert insert_response.status_code == 200
|
18
|
+
id = insert_response.json().get("id")
|
19
|
+
# fetching
|
20
|
+
response = client.get(
|
21
|
+
f"/api/v1/my-entities/{id}", headers={"Authorization": f"Bearer {access_token}"}
|
22
|
+
)
|
23
|
+
assert response.status_code == 200
|
24
|
+
response_data = response.json()
|
25
|
+
assert response_data.get("id") == id
|
26
|
+
assert response_data.get("my_column") == "to-be-read-my-entity"
|
27
|
+
|
28
|
+
|
29
|
+
def test_read_my_entity_bulk():
|
30
|
+
client = TestClient(app, base_url="http://localhost")
|
31
|
+
access_token = get_admin_access_token()
|
32
|
+
new_first_my_entity_data = {
|
33
|
+
"my_column": "to-be-read-my-entity-bulk-1",
|
34
|
+
}
|
35
|
+
new_second_my_entity_data = {
|
36
|
+
"my_column": "to-be-read-my-entity-bulk-2",
|
37
|
+
}
|
38
|
+
new_my_entity_data = [new_first_my_entity_data, new_second_my_entity_data]
|
39
|
+
insert_response = client.post(
|
40
|
+
"/api/v1/my-entities/bulk",
|
41
|
+
json=new_my_entity_data,
|
42
|
+
headers={"Authorization": f"Bearer {access_token}"},
|
43
|
+
)
|
44
|
+
assert insert_response.status_code == 200
|
45
|
+
ids = [row["id"] for row in insert_response.json()]
|
46
|
+
# fetching
|
47
|
+
response = client.get(
|
48
|
+
f"/api/v1/my-entities",
|
49
|
+
params={
|
50
|
+
"filter": "my_column:like:to-be-read-my-entity-bulk-%",
|
51
|
+
},
|
52
|
+
headers={"Authorization": f"Bearer {access_token}"},
|
53
|
+
)
|
54
|
+
assert response.status_code == 200
|
55
|
+
response_data_count = response.json()["count"]
|
56
|
+
assert response_data_count == 2
|
57
|
+
response_data = response.json()["data"]
|
58
|
+
# Data should match
|
59
|
+
assert len([row["id"] for row in response_data if row["id"] in ids]) == 2
|
60
|
+
assert new_first_my_entity_data["my_column"] in [
|
61
|
+
row["my_column"] for row in response_data
|
62
|
+
]
|
63
|
+
assert new_second_my_entity_data["my_column"] in [
|
64
|
+
row["my_column"] for row in response_data
|
65
|
+
]
|
@@ -0,0 +1,61 @@
|
|
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_my_entity():
|
7
|
+
client = TestClient(app, base_url="http://localhost")
|
8
|
+
access_token = get_admin_access_token()
|
9
|
+
new_my_entity_data = {
|
10
|
+
"my_column": "to-be-updated-my-entity",
|
11
|
+
}
|
12
|
+
insert_response = client.post(
|
13
|
+
"/api/v1/my-entities",
|
14
|
+
json=new_my_entity_data,
|
15
|
+
headers={"Authorization": f"Bearer {access_token}"},
|
16
|
+
)
|
17
|
+
assert insert_response.status_code == 200
|
18
|
+
id = insert_response.json().get("id")
|
19
|
+
# updating
|
20
|
+
updated_my_entity_data = {
|
21
|
+
"my_column": "updated-my-entity",
|
22
|
+
}
|
23
|
+
response = client.put(
|
24
|
+
f"/api/v1/my-entities/{id}",
|
25
|
+
json=updated_my_entity_data,
|
26
|
+
headers={"Authorization": f"Bearer {access_token}"},
|
27
|
+
)
|
28
|
+
assert response.status_code == 200
|
29
|
+
response_data = response.json()
|
30
|
+
assert response_data.get("id") == id
|
31
|
+
assert response_data.get("my_column") == "updated-my-entity"
|
32
|
+
|
33
|
+
|
34
|
+
def test_update_my_entity_bulk():
|
35
|
+
client = TestClient(app, base_url="http://localhost")
|
36
|
+
access_token = get_admin_access_token()
|
37
|
+
new_first_my_entity_data = {
|
38
|
+
"my_column": "to-be-updated-my-entity-bulk-1",
|
39
|
+
}
|
40
|
+
insert_response = client.post(
|
41
|
+
"/api/v1/my-entities/bulk",
|
42
|
+
json=[new_first_my_entity_data],
|
43
|
+
headers={"Authorization": f"Bearer {access_token}"},
|
44
|
+
)
|
45
|
+
assert insert_response.status_code == 200
|
46
|
+
ids = [row["id"] for row in insert_response.json()]
|
47
|
+
# updating (we only test with one data)
|
48
|
+
updated_my_entity_data = {"my_column": "updated-my-entity-bulk-1"}
|
49
|
+
response = client.put(
|
50
|
+
f"/api/v1/my-entities/bulk",
|
51
|
+
json={
|
52
|
+
"my_entity_ids": ids,
|
53
|
+
"data": updated_my_entity_data,
|
54
|
+
},
|
55
|
+
headers={"Authorization": f"Bearer {access_token}"},
|
56
|
+
)
|
57
|
+
assert response.status_code == 200
|
58
|
+
response_data = response.json()
|
59
|
+
assert len(response_data) == 1
|
60
|
+
response_data[0].get("id") == ids[0]
|
61
|
+
response_data[0].get("my_column") == updated_my_entity_data["my_column"]
|
@@ -3,18 +3,26 @@
|
|
3
3
|
|
4
4
|
@app.get("/api/v1/my-entities", response_model=MultipleMyEntityResponse)
|
5
5
|
async def get_my_entities(
|
6
|
+
current_user: Annotated[AuthUserResponse, Depends(get_current_user)],
|
6
7
|
page: int = 1,
|
7
8
|
page_size: int = 10,
|
8
9
|
sort: str | None = None,
|
9
10
|
filter: str | None = None,
|
10
11
|
) -> MultipleMyEntityResponse:
|
12
|
+
if not current_user.has_permission("my-entity:read"):
|
13
|
+
raise ForbiddenError("Access denied")
|
11
14
|
return await my_module_client.get_my_entities(
|
12
15
|
page=page, page_size=page_size, sort=sort, filter=filter
|
13
16
|
)
|
14
17
|
|
15
18
|
|
16
19
|
@app.get("/api/v1/my-entities/{my_entity_id}", response_model=MyEntityResponse)
|
17
|
-
async def get_my_entity_by_id(
|
20
|
+
async def get_my_entity_by_id(
|
21
|
+
current_user: Annotated[AuthUserResponse, Depends(get_current_user)],
|
22
|
+
my_entity_id: str,
|
23
|
+
) -> MyEntityResponse:
|
24
|
+
if not current_user.has_permission("my-entity:read"):
|
25
|
+
raise ForbiddenError("Access denied")
|
18
26
|
return await my_module_client.get_my_entity_by_id(my_entity_id)
|
19
27
|
|
20
28
|
|
@@ -22,9 +30,14 @@ async def get_my_entity_by_id(my_entity_id: str) -> MyEntityResponse:
|
|
22
30
|
"/api/v1/my-entities/bulk",
|
23
31
|
response_model=list[MyEntityResponse],
|
24
32
|
)
|
25
|
-
async def create_my_entity_bulk(
|
33
|
+
async def create_my_entity_bulk(
|
34
|
+
current_user: Annotated[AuthUserResponse, Depends(get_current_user)],
|
35
|
+
data: list[MyEntityCreate],
|
36
|
+
) -> list[MyEntityResponse]:
|
37
|
+
if not current_user.has_permission("my-entity:create"):
|
38
|
+
raise ForbiddenError("Access denied")
|
26
39
|
return await my_module_client.create_my_entity_bulk(
|
27
|
-
[row.with_audit(created_by=
|
40
|
+
[row.with_audit(created_by=current_user.id) for row in data]
|
28
41
|
)
|
29
42
|
|
30
43
|
|
@@ -32,17 +45,30 @@ async def create_my_entity_bulk(data: list[MyEntityCreate]):
|
|
32
45
|
"/api/v1/my-entities",
|
33
46
|
response_model=MyEntityResponse,
|
34
47
|
)
|
35
|
-
async def create_my_entity(
|
36
|
-
|
48
|
+
async def create_my_entity(
|
49
|
+
current_user: Annotated[AuthUserResponse, Depends(get_current_user)],
|
50
|
+
data: MyEntityCreate,
|
51
|
+
) -> MyEntityResponse:
|
52
|
+
if not current_user.has_permission("my-entity:create"):
|
53
|
+
raise ForbiddenError("Access denied")
|
54
|
+
return await my_module_client.create_my_entity(
|
55
|
+
data.with_audit(created_by=current_user.id)
|
56
|
+
)
|
37
57
|
|
38
58
|
|
39
59
|
@app.put(
|
40
60
|
"/api/v1/my-entities/bulk",
|
41
61
|
response_model=list[MyEntityResponse],
|
42
62
|
)
|
43
|
-
async def update_my_entity_bulk(
|
63
|
+
async def update_my_entity_bulk(
|
64
|
+
current_user: Annotated[AuthUserResponse, Depends(get_current_user)],
|
65
|
+
my_entity_ids: list[str],
|
66
|
+
data: MyEntityUpdate,
|
67
|
+
) -> list[MyEntityResponse]:
|
68
|
+
if not current_user.has_permission("my-entity:update"):
|
69
|
+
raise ForbiddenError("Access denied")
|
44
70
|
return await my_module_client.update_my_entity_bulk(
|
45
|
-
my_entity_ids, data.with_audit(updated_by=
|
71
|
+
my_entity_ids, data.with_audit(updated_by=current_user.id)
|
46
72
|
)
|
47
73
|
|
48
74
|
|
@@ -50,9 +76,15 @@ async def update_my_entity_bulk(my_entity_ids: list[str], data: MyEntityUpdate):
|
|
50
76
|
"/api/v1/my-entities/{my_entity_id}",
|
51
77
|
response_model=MyEntityResponse,
|
52
78
|
)
|
53
|
-
async def update_my_entity(
|
79
|
+
async def update_my_entity(
|
80
|
+
current_user: Annotated[AuthUserResponse, Depends(get_current_user)],
|
81
|
+
my_entity_id: str,
|
82
|
+
data: MyEntityUpdate,
|
83
|
+
) -> MyEntityResponse:
|
84
|
+
if not current_user.has_permission("my-entity:update"):
|
85
|
+
raise ForbiddenError("Access denied")
|
54
86
|
return await my_module_client.update_my_entity(
|
55
|
-
my_entity_id, data.with_audit(updated_by=
|
87
|
+
my_entity_id, data.with_audit(updated_by=current_user.id)
|
56
88
|
)
|
57
89
|
|
58
90
|
|
@@ -60,9 +92,14 @@ async def update_my_entity(my_entity_id: str, data: MyEntityUpdate):
|
|
60
92
|
"/api/v1/my-entities/bulk",
|
61
93
|
response_model=list[MyEntityResponse],
|
62
94
|
)
|
63
|
-
async def delete_my_entity_bulk(
|
95
|
+
async def delete_my_entity_bulk(
|
96
|
+
current_user: Annotated[AuthUserResponse, Depends(get_current_user)],
|
97
|
+
my_entity_ids: list[str],
|
98
|
+
) -> list[MyEntityResponse]:
|
99
|
+
if not current_user.has_permission("my-entity:delete"):
|
100
|
+
raise ForbiddenError("Access denied")
|
64
101
|
return await my_module_client.delete_my_entity_bulk(
|
65
|
-
my_entity_ids, deleted_by=
|
102
|
+
my_entity_ids, deleted_by=current_user.id
|
66
103
|
)
|
67
104
|
|
68
105
|
|
@@ -70,5 +107,12 @@ async def delete_my_entity_bulk(my_entity_ids: list[str]):
|
|
70
107
|
"/api/v1/my-entities/{my_entity_id}",
|
71
108
|
response_model=MyEntityResponse,
|
72
109
|
)
|
73
|
-
async def delete_my_entity(
|
74
|
-
|
110
|
+
async def delete_my_entity(
|
111
|
+
current_user: Annotated[AuthUserResponse, Depends(get_current_user)],
|
112
|
+
my_entity_id: str,
|
113
|
+
) -> MyEntityResponse:
|
114
|
+
if not current_user.has_permission("my-entity:delete"):
|
115
|
+
raise ForbiddenError("Access denied")
|
116
|
+
return await my_module_client.delete_my_entity(
|
117
|
+
my_entity_id, deleted_by=current_user.id
|
118
|
+
)
|