Compare commits

...

788 Commits

Author SHA1 Message Date
e296c0992b Merge pull request 'Added structured and wrapped logger #187' (#193) from #187_structured_logging into dev
All checks were successful
Test before pr merge / test-lint (pull_request) Successful in 6s
Build on push / prepare (push) Successful in 10s
Build on push / query (push) Successful in 17s
Build on push / core (push) Successful in 18s
Build on push / dependency (push) Successful in 18s
Build on push / mail (push) Successful in 15s
Build on push / translation (push) Successful in 15s
Build on push / database (push) Successful in 19s
Build on push / application (push) Successful in 19s
Build on push / auth (push) Successful in 14s
Build on push / api (push) Successful in 17s
Reviewed-on: #193
2025-09-23 23:35:55 +02:00
6639946346 Improved wrapped logging #187
All checks were successful
Test before pr merge / test-lint (pull_request) Successful in 6s
2025-09-23 23:34:45 +02:00
b9ac11e15f Added structured and wrapped logger #187
All checks were successful
Test before pr merge / test-lint (pull_request) Successful in 5s
2025-09-22 23:24:46 +02:00
77d821bb6e Added internal imports
Some checks failed
Test before pr merge / test-lint (pull_request) Failing after 6s
Build on push / prepare (push) Successful in 9s
Build on push / core (push) Successful in 18s
Build on push / query (push) Successful in 18s
Build on push / dependency (push) Successful in 14s
Build on push / application (push) Successful in 15s
Build on push / database (push) Successful in 18s
Build on push / translation (push) Successful in 19s
Build on push / mail (push) Successful in 19s
Build on push / auth (push) Successful in 18s
Build on push / api (push) Successful in 14s
2025-09-22 22:08:59 +02:00
86ad953ff1 Authorization via with_route
All checks were successful
Test before pr merge / test-lint (pull_request) Successful in 6s
Build on push / prepare (push) Successful in 9s
Build on push / query (push) Successful in 18s
Build on push / core (push) Successful in 18s
Build on push / dependency (push) Successful in 17s
Build on push / application (push) Successful in 15s
Build on push / mail (push) Successful in 18s
Build on push / translation (push) Successful in 18s
Build on push / database (push) Successful in 19s
Build on push / auth (push) Successful in 14s
Build on push / api (push) Successful in 14s
2025-09-22 22:04:36 +02:00
d6b7eb9b30 Authorization via decorator 2025-09-22 21:16:47 +02:00
12b7c62b69 Fixed formatting
All checks were successful
Build on push / prepare (push) Successful in 9s
Build on push / core (push) Successful in 17s
Build on push / query (push) Successful in 17s
Build on push / dependency (push) Successful in 17s
Build on push / translation (push) Successful in 14s
Build on push / application (push) Successful in 18s
Build on push / database (push) Successful in 17s
Build on push / mail (push) Successful in 18s
Build on push / auth (push) Successful in 13s
Build on push / api (push) Successful in 17s
Test before pr merge / test-lint (pull_request) Successful in 5s
2025-09-21 23:48:09 +02:00
7fc70747bb Added black test
Some checks failed
Test before pr merge / test-lint (pull_request) Failing after 6s
Build on push / prepare (push) Successful in 10s
Build on push / core (push) Successful in 17s
Build on push / query (push) Successful in 17s
Build on push / dependency (push) Successful in 17s
Build on push / api (push) Has been cancelled
Build on push / auth (push) Has been cancelled
Build on push / mail (push) Has started running
Build on push / translation (push) Has been cancelled
Build on push / application (push) Has been cancelled
Build on push / database (push) Has been cancelled
2025-09-21 23:47:15 +02:00
6de4f3c03a Middleware updated & Fixed mysql pool
All checks were successful
Build on push / prepare (push) Successful in 10s
Build on push / core (push) Successful in 19s
Build on push / query (push) Successful in 18s
Build on push / dependency (push) Successful in 17s
Build on push / database (push) Successful in 15s
Build on push / translation (push) Successful in 18s
Build on push / mail (push) Successful in 19s
Build on push / application (push) Successful in 21s
Build on push / auth (push) Successful in 14s
Build on push / api (push) Successful in 14s
2025-09-21 23:41:25 +02:00
ea3055527c Changed middleware to asgi 2025-09-21 21:22:19 +02:00
7b37748ca6 [WIP] validate token via keycloak 2025-09-21 21:07:09 +02:00
073b35f71a App deps check 2025-09-21 20:11:47 +02:00
eceff6128b [WIP] Authentication
All checks were successful
Build on push / prepare (push) Successful in 9s
Build on push / core (push) Successful in 19s
Build on push / query (push) Successful in 19s
Build on push / dependency (push) Successful in 17s
Build on push / application (push) Successful in 16s
Build on push / mail (push) Successful in 16s
Build on push / translation (push) Successful in 18s
Build on push / database (push) Successful in 22s
Build on push / auth (push) Successful in 15s
Build on push / api (push) Successful in 13s
2025-09-19 23:01:41 +02:00
17dfb245bf Minor cleanup 2025-09-19 21:54:08 +02:00
4f698269b5 Fixed api build
All checks were successful
Build on push / prepare (push) Successful in 9s
Build on push / core (push) Successful in 18s
Build on push / query (push) Successful in 17s
Build on push / dependency (push) Successful in 18s
Build on push / application (push) Successful in 15s
Build on push / database (push) Successful in 18s
Build on push / translation (push) Successful in 18s
Build on push / mail (push) Successful in 19s
Build on push / auth (push) Successful in 15s
Build on push / api (push) Successful in 14s
2025-09-19 21:12:33 +02:00
ddc62dfb9a Added api & route handling
Some checks failed
Build on push / prepare (push) Successful in 9s
Build on push / core (push) Successful in 19s
Build on push / query (push) Successful in 19s
Build on push / dependency (push) Successful in 17s
Build on push / application (push) Successful in 15s
Build on push / database (push) Successful in 18s
Build on push / mail (push) Successful in 19s
Build on push / translation (push) Successful in 23s
Build on push / auth (push) Successful in 16s
Build on push / api (push) Failing after 14s
2025-09-19 21:03:33 +02:00
1a67318091 Config model options handling. Closes #185
All checks were successful
Build on push / prepare (push) Successful in 10s
Build on push / core (push) Successful in 19s
Build on push / query (push) Successful in 19s
Build on push / dependency (push) Successful in 25s
Build on push / translation (push) Successful in 17s
Build on push / database (push) Successful in 20s
Build on push / application (push) Successful in 21s
Build on push / mail (push) Successful in 20s
Build on push / auth (push) Successful in 14s
2025-09-19 17:47:49 +02:00
2be58f6577 Introduced fernet to credential manager. Closes #183
All checks were successful
Build on push / prepare (push) Successful in 10s
Build on push / core (push) Successful in 19s
Build on push / query (push) Successful in 22s
Build on push / dependency (push) Successful in 15s
Build on push / application (push) Successful in 20s
Build on push / database (push) Successful in 21s
Build on push / translation (push) Successful in 21s
Build on push / mail (push) Successful in 22s
Build on push / auth (push) Successful in 18s
2025-09-19 15:01:16 +02:00
9c6078f4fd with_logging & logger level fix
All checks were successful
Build on push / prepare (push) Successful in 8s
Build on push / core (push) Successful in 18s
Build on push / query (push) Successful in 18s
Build on push / dependency (push) Successful in 14s
Build on push / translation (push) Successful in 15s
Build on push / database (push) Successful in 17s
Build on push / application (push) Successful in 18s
Build on push / mail (push) Successful in 20s
Build on push / auth (push) Successful in 18s
2025-09-17 22:18:38 +02:00
dfdc31512d App with extension functions
All checks were successful
Build on push / prepare (push) Successful in 9s
Build on push / core (push) Successful in 17s
Build on push / query (push) Successful in 17s
Build on push / dependency (push) Successful in 14s
Build on push / translation (push) Successful in 14s
Build on push / database (push) Successful in 18s
Build on push / mail (push) Successful in 19s
Build on push / application (push) Successful in 22s
Build on push / auth (push) Successful in 14s
2025-09-17 21:56:47 +02:00
ab7ff7da93 Made startup/app extensions static 2025-09-17 20:54:21 +02:00
41087a838b Removed pass from empty functions
All checks were successful
Build on push / prepare (push) Successful in 9s
Build on push / core (push) Successful in 17s
Build on push / query (push) Successful in 17s
Build on push / dependency (push) Successful in 17s
Build on push / translation (push) Successful in 14s
Build on push / mail (push) Successful in 18s
Build on push / database (push) Successful in 18s
Build on push / application (push) Successful in 24s
Build on push / auth (push) Successful in 14s
2025-09-17 20:49:15 +02:00
836b92ccbf Further console test 2025-09-17 20:44:25 +02:00
8aaba22940 Improved application structure
All checks were successful
Build on push / prepare (push) Successful in 11s
Build on push / core (push) Successful in 22s
Build on push / query (push) Successful in 22s
Build on push / dependency (push) Successful in 20s
Build on push / database (push) Successful in 20s
Build on push / translation (push) Successful in 20s
Build on push / application (push) Successful in 22s
Build on push / mail (push) Successful in 23s
Build on push / auth (push) Successful in 16s
2025-09-17 19:23:14 +02:00
504dc5e188 Added auth & improved database
All checks were successful
Build on push / prepare (push) Successful in 9s
Build on push / query (push) Successful in 18s
Build on push / core (push) Successful in 24s
Build on push / dependency (push) Successful in 17s
Build on push / database (push) Successful in 15s
Build on push / translation (push) Successful in 15s
Build on push / mail (push) Successful in 18s
Build on push / application (push) Successful in 19s
Build on push / auth (push) Successful in 16s
2025-09-17 12:21:32 +02:00
4625b626e6 Added dao base
All checks were successful
Build on push / prepare (push) Successful in 8s
Build on push / query (push) Successful in 16s
Build on push / core (push) Successful in 23s
Build on push / translation (push) Successful in 14s
Build on push / mail (push) Successful in 14s
2025-09-17 00:12:25 +02:00
58dbd3ed1e Cleanup for mysql
All checks were successful
Build on push / prepare (push) Successful in 8s
Build on push / core (push) Successful in 19s
Build on push / query (push) Successful in 18s
Build on push / translation (push) Successful in 15s
Build on push / mail (push) Successful in 17s
2025-09-16 20:21:33 +02:00
cd7dfaf2b4 Fixed spinner thread & remove log thread
All checks were successful
Build on push / prepare (push) Successful in 8s
Build on push / core (push) Successful in 18s
Build on push / query (push) Successful in 19s
Build on push / translation (push) Successful in 18s
Build on push / mail (push) Successful in 18s
2025-09-16 18:41:57 +02:00
8ad3e3bdb4 Removed ConfigModel from_dict
All checks were successful
Build on push / prepare (push) Successful in 8s
Build on push / core (push) Successful in 16s
Build on push / query (push) Successful in 17s
Build on push / mail (push) Successful in 17s
Build on push / translation (push) Successful in 18s
2025-09-16 08:51:56 +02:00
b97bc0a3ed Restructuring
All checks were successful
Build on push / prepare (push) Successful in 8s
Build on push / query (push) Successful in 17s
Build on push / core (push) Successful in 26s
Build on push / translation (push) Successful in 15s
Build on push / mail (push) Successful in 17s
2025-09-16 08:48:07 +02:00
5f25400bcd Updated config & environment 2025-09-16 08:39:00 +02:00
5edabbf8c1 Removed docs from sources
All checks were successful
Build on push / prepare (push) Successful in 10s
Build on push / core (push) Successful in 17s
Build on push / query (push) Successful in 18s
Build on push / translation (push) Successful in 15s
Build on push / mail (push) Successful in 16s
2025-09-15 23:08:17 +02:00
62b6435470 Improved logging closes #180
All checks were successful
Build on push / prepare (push) Successful in 9s
Build on push / query (push) Successful in 17s
Build on push / core (push) Successful in 18s
Build on push / mail (push) Successful in 17s
Build on push / translation (push) Successful in 18s
2025-09-15 23:06:38 +02:00
d9d7802f33 Fixed translation build
All checks were successful
Build on push / prepare (push) Successful in 9s
Build on push / query (push) Successful in 19s
Build on push / core (push) Successful in 20s
Build on push / mail (push) Successful in 15s
Build on push / translation (push) Successful in 18s
2025-09-15 21:08:40 +02:00
25b4ca0696 Added cpl-mail
Some checks failed
Build on push / prepare (push) Successful in 9s
Build on push / core (push) Successful in 18s
Build on push / query (push) Successful in 25s
Build on push / translation (push) Failing after 8s
Build on push / mail (push) Successful in 14s
2025-09-15 20:56:07 +02:00
3b120370b8 Added cpl-mail
All checks were successful
Build on push / prepare (push) Successful in 9s
Build on push / translation (push) Successful in 20s
Build on push / mail (push) Successful in 21s
Build on push / query (push) Successful in 21s
Build on push / core (push) Successful in 21s
2025-09-15 18:38:34 +02:00
aac038ef63 Added cpl-mail
Some checks failed
Build on push / core (push) Failing after 9s
Build on push / mail (push) Has been cancelled
Build on push / translation (push) Has been cancelled
Build on push / prepare (push) Has been cancelled
Build on push / query (push) Has been cancelled
2025-09-15 18:35:36 +02:00
784632a0b4 Updated ci
Some checks failed
Build on push / core (push) Has been cancelled
Build on push / query (push) Has been cancelled
Build on push / translation (push) Has been cancelled
Build on push / prepare (push) Has been cancelled
2025-09-15 18:17:23 +02:00
4719c32457 Updated ci
Some checks failed
Build on push / prepare (push) Successful in 9s
Build on push / query (push) Failing after 18s
Build on push / core (push) Successful in 15s
2025-09-15 16:55:57 +02:00
516fa3fb7e Updated ci
Some checks failed
Build on push / prepare (push) Successful in 8s
Build on push / query (push) Successful in 18s
Build on push / core (push) Failing after 18s
2025-09-15 15:28:09 +02:00
2d2bb86720 First update towards cpl2
Some checks failed
Build on push / prepare (push) Failing after 6s
Build on push / build (push) Has been skipped
2025-09-15 15:02:47 +02:00
c1e65bf0dc Set correct version 2024-07-25 17:56:25 +02:00
688381b106 Merge pull request 'py12' (#177) from py12 into master
Reviewed-on: sh-edraft.de/sh_cpl#177
2024-07-25 17:55:07 +02:00
c3e3367264 Removed bundle project 2024-07-25 17:54:24 +02:00
c3d51e29fc Set version 2024-07-25 17:34:24 +02:00
cae2e6d92e Hopefully fixed db errors 2024-01-13 14:26:36 +01:00
347418bdc5 Added ssl_disabled option 2023-12-06 17:56:13 +01:00
153fc8922e Fixed dc collection 2023-10-13 15:20:26 +02:00
cdbfaeb013 Merge pull request 'Updated deps' (#176) from 2023.10 into master
Reviewed-on: sh-edraft.de/sh_cpl#176
2023-10-13 15:01:06 +02:00
4062a6ebe4 Updated deps 2023-10-13 14:57:46 +02:00
46ef6b3938 Merge pull request '2023.10' (#175) from 2023.10 into master
Reviewed-on: sh-edraft.de/sh_cpl#175
2023-10-13 14:48:39 +02:00
c1b9c0fb4a Added R as return type 2023-10-12 21:20:36 +02:00
8ede2998fe Fixed configuration typing 2023-10-12 21:14:49 +02:00
41f974fe6e Fixed cpl-discord deps 2023-10-12 21:06:10 +02:00
e7af5ce322 Fixed cpl-query typing 2023-10-12 21:05:38 +02:00
7881294554 Fixed types 2023-10-12 20:55:24 +02:00
b3fd122fcd Build cpl-discord new version 2023-10-12 20:42:24 +02:00
1dd48899d7 Improved cpl-discords service loading for commands and events 2023-10-12 20:41:34 +02:00
f357e97ce5 Build and formatted new version 2023-10-12 20:16:16 +02:00
2d8ecd424a Switched mysql package 2023-10-12 20:11:14 +02:00
affbbc822d Removed handled vars from values in JP 2023-07-19 11:34:19 +02:00
43ea732c5f Fixed config loading 2023-07-19 11:28:33 +02:00
708918464f Improved config loading 2023-07-19 11:16:04 +02:00
ed2377c288 Updated discord.py 2023-06-13 16:22:54 +02:00
b819eb439c Fixed translaiton settings 2023-06-13 16:14:38 +02:00
b3ed7101fc Fixed update command 2023-06-13 15:56:46 +02:00
a3504af376 Fixed update command 2023-06-13 15:47:44 +02:00
81c2f1cba9 Fixed discord service config loading 2023-06-13 15:38:04 +02:00
bea5b22251 Fixed db settings default port 2023-06-13 15:08:04 +02:00
e04d0beb68 Fixed circular import from query package 2023-06-13 13:27:37 +02:00
08ba097aaa Merge pull request '#173' (#174) from #173 into master
Reviewed-on: sh-edraft.de/sh_cpl#174
2023-06-12 20:48:34 +02:00
ea968d94e4 Build app 2023-06-12 20:47:14 +02:00
8e12c72b07 Fixed cpl update dev deps bug #170 2023-06-12 20:47:13 +02:00
8dee4d8f70 Fixed config type 2023-04-15 11:47:31 +02:00
315b8e631a Merge pull request '2023.4' (#168) from 2023.4 into master
Reviewed-on: sh-edraft.de/sh_cpl#168
2023-04-12 13:57:50 +02:00
cbb1860f25 Fixed internal settings 2023-04-12 13:47:38 +02:00
9839bcaa14 Removed unused imports 2023-04-12 13:34:43 +02:00
da54337221 Fixed ProjectSettings 2023-04-12 13:31:59 +02:00
a6a1e764d1 Updated deps 2023-04-12 13:30:13 +02:00
eb6aa08c10 Updated deps 2023-04-12 12:57:58 +02:00
14a190a67f Merge pull request '#166' (#167) from #166 into 2023.4
Reviewed-on: sh-edraft.de/sh_cpl#167
Closes #166
2023-04-11 14:49:21 +02:00
cf5ae89884 Fixed start test 2023-04-11 14:26:17 +02:00
558dfb8ced Deactivated test output 2023-04-11 12:51:49 +02:00
2ec8fc22b3 Improved generic type hints 2023-04-11 12:50:05 +02:00
c94700495b Merge pull request '#157' (#165) from #157 into 2023.4
Reviewed-on: sh-edraft.de/sh_cpl#165
Closes #157
2023-04-11 11:48:48 +02:00
69a3bc5e31 Fixed cwd handling 2023-04-10 15:10:39 +02:00
d189f49418 Improved service_provider_tests 2023-04-10 14:53:59 +02:00
60fb416b67 Added service_provider_tests 2023-04-10 14:47:25 +02:00
792429d19d Added console argument tests 2023-04-10 13:35:11 +02:00
106975015e Improved config tests 2023-04-09 12:51:57 +02:00
1117735f2e Added configuration tests & improved json parsing by converting into target types 2023-04-09 12:31:31 +02:00
0378f8944a Added AppEnv tests 2023-04-09 12:05:05 +02:00
75fde0f444 Added pipe tests 2023-04-07 14:52:17 +02:00
04f610c799 Removed obsolete pipes 2023-04-07 14:41:11 +02:00
3178b59147 Added JSONProcessorTestCase 2023-04-07 14:40:03 +02:00
9c7008e179 Improved core test suite 2023-04-07 14:08:06 +02:00
7ff7dbc56b Added core test suite 2023-04-07 14:04:39 +02:00
823d524a81 Added core.utils.string tests & fixed some functions 2023-04-07 14:03:45 +02:00
1b60debba7 Fixed cli schematics & added tests 2023-04-07 13:42:20 +02:00
3fc713cc8d Merge pull request '#154' (#164) from #154 into 2023.4
Reviewed-on: sh-edraft.de/sh_cpl#164
Closes #154
2023-04-05 22:30:38 +02:00
bb26c7e94d Updated docs 2023-04-05 22:01:15 +02:00
52404cf5d3 Updated docs 2023-04-05 21:59:58 +02:00
32223bcfc2 Updated docs 2023-04-05 21:43:21 +02:00
4e2bdb2b59 Fixed translation settings 2023-04-05 18:17:57 +02:00
d72514205c Updated PipSettings 2023-04-05 18:13:29 +02:00
9b4afdbe18 Updated TranslationSettings & formatted code 2023-04-05 18:12:31 +02:00
6751541269 Updated DiscordBotSettings 2023-04-05 18:11:16 +02:00
fe9918af16 Updated discord deps 2023-04-05 18:10:33 +02:00
d2c56f1a3f Fixed some broken references 2023-04-05 18:07:31 +02:00
8624549aa6 Fixed some broken references 2023-04-05 18:03:13 +02:00
9a6ce704b7 Removed from_dict from build and project settings 2023-04-05 16:30:02 +02:00
9af2a1962d Improved internal settings 2023-04-04 23:19:59 +02:00
a8d4a7a362 Added JSONProcessor to utils & Added JSONProcessor as settings parser 2023-04-04 22:52:13 +02:00
943f5ad50b Improved service scope 2023-04-04 21:45:32 +02:00
4cd8fea07a Merge pull request 'Added more version operator support' (#163) from #160 into 2023.4
Reviewed-on: sh-edraft.de/sh_cpl#163
Closes #160
2023-04-04 21:36:54 +02:00
82b3a22ac8 Added more version operator support 2023-04-04 21:36:24 +02:00
2c6367a408 Improved cpl query 2023-04-04 21:05:53 +02:00
af94a0c52d Formatted code 2023-04-04 15:56:37 +02:00
9c374f6bb2 Merge pull request '#161' (#162) from #161 into 2023.4
Reviewed-on: sh-edraft.de/sh_cpl#162
Closes #161
2023-04-04 14:52:30 +02:00
d0a19458c5 Formatted code 2023-04-04 14:50:19 +02:00
5d6e7677de Improved typing in query 2023-04-04 14:42:43 +02:00
01309e3124 Cleaned code 2023-04-04 13:58:37 +02:00
083e1ce4a1 Fixed OrderedQueryable imports 2023-04-04 13:55:50 +02:00
8c012e5902 Set Self type 2023-04-04 13:33:45 +02:00
5e6d17cc86 Updated some stuff 2023-04-04 13:30:10 +02:00
c4a71de83f Formatted code 2023-04-04 13:30:00 +02:00
01bc746092 Merge branch 'master' into 2023.4
# Conflicts:
#	src/cpl_cli/.cpl/__init__.py
#	src/cpl_cli/__init__.py
#	src/cpl_cli/_templates/__init__.py
#	src/cpl_cli/_templates/build/__init__.py
#	src/cpl_cli/_templates/publish/__init__.py
#	src/cpl_cli/abc/__init__.py
#	src/cpl_cli/command/__init__.py
#	src/cpl_cli/configuration/__init__.py
#	src/cpl_cli/cpl-cli.json
#	src/cpl_cli/helper/__init__.py
#	src/cpl_cli/live_server/__init__.py
#	src/cpl_cli/migrations/__init__.py
#	src/cpl_cli/migrations/base/__init__.py
#	src/cpl_cli/migrations/service/__init__.py
#	src/cpl_cli/publish/__init__.py
#	src/cpl_cli/source_creator/__init__.py
#	src/cpl_cli/validators/__init__.py
#	src/cpl_core/__init__.py
#	src/cpl_core/application/__init__.py
#	src/cpl_core/configuration/__init__.py
#	src/cpl_core/console/__init__.py
#	src/cpl_core/cpl-core.json
#	src/cpl_core/database/__init__.py
#	src/cpl_core/database/connection/__init__.py
#	src/cpl_core/database/context/__init__.py
#	src/cpl_core/dependency_injection/__init__.py
#	src/cpl_core/dependency_injection/service_provider.py
#	src/cpl_core/dependency_injection/service_provider_abc.py
#	src/cpl_core/environment/__init__.py
#	src/cpl_core/logging/__init__.py
#	src/cpl_core/mailing/__init__.py
#	src/cpl_core/pipes/__init__.py
#	src/cpl_core/time/__init__.py
#	src/cpl_core/utils/__init__.py
#	src/cpl_discord/.cpl/__init__.py
#	src/cpl_discord/.cpl/schematic_discord_event.py
#	src/cpl_discord/__init__.py
#	src/cpl_discord/application/__init__.py
#	src/cpl_discord/command/__init__.py
#	src/cpl_discord/configuration/__init__.py
#	src/cpl_discord/container/__init__.py
#	src/cpl_discord/cpl-discord.json
#	src/cpl_discord/events/__init__.py
#	src/cpl_discord/helper/__init__.py
#	src/cpl_discord/service/__init__.py
#	src/cpl_discord/service/discord_bot_service.py
#	src/cpl_discord/service/discord_bot_service_abc.py
#	src/cpl_discord/service/discord_service.py
#	src/cpl_query/__init__.py
#	src/cpl_query/base/__init__.py
#	src/cpl_query/base/queryable_abc.py
#	src/cpl_query/cpl-query.json
#	src/cpl_query/enumerable/__init__.py
#	src/cpl_query/extension/__init__.py
#	src/cpl_query/iterable/__init__.py
2023-04-04 13:29:45 +02:00
87d023ef7f Merge pull request 'Updated docs' (#159) from #150 into 2023.4
Reviewed-on: sh-edraft.de/sh_cpl#159
Closes #150
2023-04-04 13:16:38 +02:00
119052f9a2 Updated docs 2023-04-04 13:16:19 +02:00
43710717c9 Fixed cpl-discord deps 2023-03-11 01:33:56 +01:00
7bf5e25c5e Fixed typing 2023-03-06 19:03:35 +01:00
faa030df96 Fixed typing 2023-03-06 19:03:01 +01:00
b2efe285a9 Added line counter 2023-02-20 15:59:33 +01:00
9e28dce5ce Updated docs 2023-02-20 15:55:20 +01:00
8179508248 Added scheduled event support for cpl-discord 2023-02-13 16:58:46 +01:00
2c3664c1b9 Fixed schematic schematic 2023-01-15 10:01:19 +01:00
269f083662 Fixed static inject args order 2023-01-15 02:34:09 +01:00
8b40c65661 Fixed discord event schematic 2023-01-12 13:49:15 +01:00
7be3cf38dc Fixed cpl-cli deps 2023-01-12 09:05:36 +01:00
b18a7297a7 Build fix version 2023-01-12 08:58:05 +01:00
7f46fbe87a Added *args and **kwargs support to discord bot and DI 2023-01-12 08:57:01 +01:00
e0ca7c2ae6 Build fixed version 2023-01-07 15:03:21 +01:00
6389a940bc Fixed static di injection 2023-01-07 15:00:38 +01:00
48d0daabf5 Updated copyright 2022-12-25 12:05:02 +01:00
8fc3c04720 Updated deps 2022-12-25 12:02:47 +01:00
351f953fdb Merge pull request '2022.12' (#133) from 2022.12 into master
Reviewed-on: sh-edraft.de/sh_cpl#133
Reviewed-by: edraft-dev <dev.sven.heidemann@sh-edraft.de>
2022-12-25 11:58:34 +01:00
e1560dfab5 Merge pull request '2022.12.1' (#158) from 2022.12.1 into 2022.12
Reviewed-on: sh-edraft.de/sh_cpl#158
2022-12-25 11:29:56 +01:00
c9bc6a026a Build release 2022.12.1 2022-12-25 11:26:51 +01:00
83f0e8bd42 Set version 2022-12-25 11:25:53 +01:00
7af83b49a5 Merge pull request 'Added multiple instance handling (#152)' (#156) from #152 into 2022.12
Reviewed-on: sh-edraft.de/sh_cpl#156
Closes #152
2022-12-23 17:29:15 +01:00
59263ece6e Added multiple instance handling #152 2022-12-23 17:28:38 +01:00
a507ed9f46 Fixed publish all script 2022-12-23 16:45:14 +01:00
6b451142e2 Merge pull request 'Static dependency injection (#148)' (#155) from #148 into 2022.12
Reviewed-on: sh-edraft.de/sh_cpl#155
Closes #148
2022-12-23 16:36:21 +01:00
12beea05a7 Merge branch '2022.12' into #148 2022-12-23 16:35:24 +01:00
713fa5243c Fixed publisher service 2022-12-23 16:09:28 +01:00
d600852bec Added logic to support global service provider #148 2022-12-22 18:52:17 +01:00
c09f2f8e83 Fixed scoped #148 2022-12-20 14:50:35 +01:00
2fb515195c Improved query performance test 2022-12-20 14:34:55 +01:00
889f51f5c5 Fixed python path in settings 2022-12-20 14:29:43 +01:00
742ba8314a Merge pull request 'cpl n l -> keine Fragen... (#144)' (#149) from #144 into 2022.12
Reviewed-on: sh-edraft.de/sh_cpl#149
Closes #144
2022-12-19 08:39:30 +01:00
b95d90a191 Fixed python path in project settings #144 2022-12-19 08:36:40 +01:00
46fa49a102 Updated project type library logic & fixed tests #144 2022-12-18 14:27:41 +01:00
d5e89d6e2a Fixed cpl cli build #144 2022-12-18 12:03:41 +01:00
086fc75fec Build cpl query 2022-12-12 20:59:43 +01:00
05c33990bb Performance improvements 2022-12-12 20:59:04 +01:00
cbae40ef4d Fixed skip function 2022-12-11 10:44:23 +01:00
002ba6b4e0 Added split function 2022-12-11 09:16:38 +01:00
85f75b4677 Fixed group by 2022-12-11 08:58:55 +01:00
95d8df5bea Merge pull request 'Added new schematics & fixed run command #145' (#147) from #145 into 2022.12
Reviewed-on: sh-edraft.de/sh_cpl#147
Closes #145
2022-12-10 21:44:17 +01:00
ac1cd9bdde Added new schematics & fixed run command #145 2022-12-10 21:43:46 +01:00
2d9bb79af7 Merge pull request 'cli discord unterstützung (#143)' (#146) from #143 into 2022.12
Reviewed-on: sh-edraft.de/sh_cpl#146
Closes #143
2022-12-09 15:07:27 +01:00
120c9e6800 Fixed tests #143 2022-12-08 16:08:48 +01:00
6f3099a8db Updated packages & improved docs #143 2022-12-08 15:29:48 +01:00
e46711dc54 Added unittests #143 2022-12-08 14:23:46 +01:00
59920c59e0 Added cpl g & n discord support #143 2022-12-08 12:14:58 +01:00
604bb8f55c Updated discord test app #143 2022-12-08 11:02:54 +01:00
2f8dc63cdc Added discord command & event to cpl g schematics #143 2022-12-08 10:36:23 +01:00
4fbb3ec838 Updated readme 2022-12-07 20:12:07 +01:00
6818fe9a5c Improved cpl new appsettings template 2022-12-07 20:06:54 +01:00
3854fa5aa6 Merge pull request 'cpl n <project-type> name (#139)' (#140) from #139 into 2022.12
Reviewed-on: sh-edraft.de/sh_cpl#140
Closes #139
2022-12-07 16:16:14 +01:00
1ca459e67a Removed skip test case #139 2022-12-07 15:48:37 +01:00
fe8ffb2839 Removed comments #139 2022-12-07 15:46:58 +01:00
42fb88c35d Removed old new command #139 2022-12-07 15:45:15 +01:00
db7a0786a9 Merge branch '2022.12' into #139 2022-12-07 15:41:56 +01:00
f9f2612356 Merge pull request 'configuration.add_configuration(value: any) type fix (#141)' (#142) from #141 into 2022.12
Reviewed-on: sh-edraft.de/sh_cpl#142
Closes #141
2022-12-07 15:41:18 +01:00
df81bb599f Merge branch '2022.12' into #141 2022-12-07 13:07:38 +01:00
6d2492c1cf Added generic typing to configuration #141 2022-12-07 13:06:50 +01:00
e244535557 Improved cpl n custom templating #139 2022-12-06 17:42:55 +01:00
5f10603fe5 [WIP] Improved cpl new templating #139 2022-12-05 23:08:52 +01:00
6b8491eea2 Merge pull request 'cpl g <schematic> customizable (#137)' (#138) from #137 into 2022.12
Reviewed-on: sh-edraft.de/sh_cpl#138
Closes #137
2022-12-05 20:34:45 +01:00
b63bf41294 Fixed cpl help #137 2022-12-05 20:24:15 +01:00
f6cc3a7ddf Removed cpl g arguments #137 2022-12-05 20:00:32 +01:00
d1c93abe2c Improved cpl g templating & added custom templating #137 2022-12-05 19:57:33 +01:00
d6e3b37f7f Fixed recursion in cpl 2022-12-05 14:37:25 +01:00
9840779af4 build new cpl version 2022-12-05 14:30:33 +01:00
b893ec1fcf Merge pull request 'Removed old file #134' (#136) from #134 into 2022.12
Reviewed-on: sh-edraft.de/sh_cpl#136
2022-12-05 14:16:55 +01:00
d5e5bfe69d Removed old file #134 2022-12-05 14:15:01 +01:00
4afe5c4581 Fixed cpl query for now #134 2022-12-05 14:12:20 +01:00
e054ac364e Updated docs 2022-12-04 00:50:24 +01:00
68e01afb5b Release 2022.12.0 2022-12-04 00:12:40 +01:00
2e0a8bb1ff Minor fixes to cpl query 2022-12-04 00:08:16 +01:00
b02aebef35 Improved set-version tool & set version 2022-12-03 23:42:23 +01:00
099a51ed93 Merge pull request 'Fixed invalid output after console spinner #122' (#132) from #122 into 2022.12
Reviewed-on: sh-edraft.de/sh_cpl#132
Closes #122
2022-12-03 23:31:20 +01:00
e4aedb354b Fixed invalid output after console spinner #122 2022-12-03 23:30:54 +01:00
301768b842 Merge pull request 'cpl start & run - Bessere Einbindung mit den Build-Tools #124' (#131) from #124 into 2022.12
Reviewed-on: sh-edraft.de/sh_cpl#131
Closes #124
2022-12-03 23:25:11 +01:00
856960d799 Added logic to run build code to cpl start #124 2022-12-02 19:38:03 +01:00
d0877a4ea6 Added logic to run build code to cpl run #124 2022-12-02 18:41:58 +01:00
3c20ab296a Added --dev flag to cpl run & start #124 2022-12-02 17:48:35 +01:00
4dc7ee3314 Merge pull request 'cpl query Enumerable quatsch #129' (#130) from #129 into 2022.12
Reviewed-on: sh-edraft.de/sh_cpl#130
Closes #129
2022-12-01 23:43:01 +01:00
05bd5e8593 Improved query performance test #129 2022-12-01 23:41:06 +01:00
2fe3912a07 Fixed version test #129 2022-12-01 23:34:00 +01:00
2840628443 Improved tests #129 2022-12-01 23:23:48 +01:00
f0f79e7e3b Refactored cpl-query Iterable & Enumerable #129 2022-12-01 21:09:39 +01:00
e8ae635c88 Improved tests #129 2022-12-01 17:00:17 +01:00
d8f7e03815 Added group by & refactored Iterable #129 2022-12-01 16:27:29 +01:00
ba1b5e49ae Merge pull request 'Added logic to change base path for cpl n #120' (#128) from #120 into 2022.12
Reviewed-on: sh-edraft.de/sh_cpl#128
Closes #120
2022-12-01 14:00:46 +01:00
703a2c91b5 Added logic to change base path for cpl n #120 2022-12-01 13:55:55 +01:00
4a54bb62de Merge pull request 'cpl g in project directory #93' (#127) from #93 into 2022.12
Reviewed-on: sh-edraft.de/sh_cpl#127
Closes #93
2022-12-01 12:26:01 +01:00
9e84c8359b Removed empty PythonPath #93 2022-12-01 12:25:36 +01:00
5139876d90 Merge branch '#93' of https://git.sh-edraft.de/sh-edraft.de/sh_cpl into #93 2022-12-01 12:22:20 +01:00
6aef49de40 Repaired build & publish #93 2022-12-01 12:22:14 +01:00
186b336bf3 Repaired build & publish #93 2022-12-01 12:22:14 +01:00
1bbec27d1a Fixed cpl g path problems & package names #93 2022-12-01 12:21:57 +01:00
f0a8d69e22 Merge branch '2022.12' of https://git.sh-edraft.de/sh-edraft.de/sh_cpl into 2022.12 2022-12-01 12:21:17 +01:00
8bd237206c Fixed logging #123 2022-12-01 12:20:32 +01:00
6bf7c3a2bc Reverted #125 fix 2022-12-01 12:19:49 +01:00
2e8be741cc Repaired build & publish #93 2022-12-01 12:17:26 +01:00
abd0352750 Repaired build & publish #93 2022-11-30 19:05:11 +01:00
25d91b85f3 Merge branch '2022.12' into #93 2022-11-30 18:06:49 +01:00
f08eb42105 Merge pull request 'Logging Farben #123' (#126) from #123 into 2022.12
Reviewed-on: sh-edraft.de/sh_cpl#126
Closes #123
2022-11-30 18:06:12 +01:00
f450102c93 Fixed cpl g path problems & package names #93 2022-11-30 18:04:40 +01:00
db28645e98 Fixed logging #123 2022-11-30 15:17:08 +01:00
23efb776e7 Call pre & post scripts with args of base command & Build cpl-core@2022.7.0.post9 2022-11-18 09:17:56 +01:00
7f621b282f Build cpl-core@2022.7.0.post8 2022-11-18 08:37:16 +01:00
5dc7fce1e7 Call pre & post scripts with args of base command 2022-11-18 08:36:21 +01:00
879f59b0d5 Fixed #125 2022-10-21 22:34:47 +02:00
511622820a Fixed translation loading 2022-10-19 17:38:21 +02:00
0914f969e5 Resolved deps conflicts 2022-10-18 20:58:19 +02:00
c611045290 Removed german string 'this is a automatic...' in mail client 2022-10-18 20:44:08 +02:00
89ac9e90d6 Fixed take & skip 2022-10-15 13:17:08 +02:00
ae2a98b3bc Fixed unicode problems with windoof 2022-10-12 20:29:18 +02:00
bd242baa0a Removed exit from console warn in logger | WTF im so fckng sorry 2022-10-06 16:47:29 +02:00
8e4caeb2a8 Removed exit from console warn in logger | WTF im so fckng sorry 2022-10-06 16:46:44 +02:00
3b55b3d5a5 Fixed _append_log & create logic in logger_service 2022-10-06 16:45:20 +02:00
cc3b1afaa1 Fixed _append_log & create logic in logger_service 2022-10-06 16:24:01 +02:00
a22b6fdbdb Fixed typo | sry x2 :/ 2022-10-03 17:39:43 +02:00
e3041c4b5b Fixed typo | sry :/ 2022-10-03 17:36:31 +02:00
8ad6d50a04 Added reaction raw events 2022-10-03 17:32:12 +02:00
4db000f6d7 Added protected log and path getter to logger in cpl-core 2022-10-02 23:15:07 +02:00
3821624c3b Removed content from on_message log in cpl-discord 2022-10-02 13:01:09 +02:00
60a722b4ec Improved app builder to return self in use_startup 2022-10-02 02:43:10 +02:00
65132aa107 Improved app builder to return self in use_extension 2022-10-02 02:39:03 +02:00
48eac124a5 Fixed settings handling in cpl-discord 2022-10-01 22:38:01 +02:00
be23b18d33 Merge pull request 'Release 2022.10' (#112) from 2022.10 into master
Reviewed-on: sh-edraft.de/sh_cpl#112
Reviewed-by: edraft-dev <dev.sven.heidemann@sh-edraft.de>
2022-09-30 15:48:19 +02:00
748cc66946 Removed print 2022-09-30 15:47:54 +02:00
304b2e4255 Set new version & build projects 2022-09-30 15:39:41 +02:00
9fa183f143 Merge pull request '2022.10.11 - Added migrations to update command #114' (#117) from 2022.10.11-#114 into 2022.10
Reviewed-on: sh-edraft.de/sh_cpl#117
Closes 117
2022-09-30 14:10:50 +02:00
82df528395 Merge pull request '2022.10.12 - Fixed cpl g existing __init__.py overwrite #116' (#118) from 2022.10.12-#116 into 2022.10
Reviewed-on: sh-edraft.de/sh_cpl#118
Closes #118
2022-09-30 14:07:07 +02:00
49e3bddc91 Fixed cpl g existing __init__.py overwrite #116 2022-09-19 23:10:10 +02:00
c52bf961e1 Added migrations to update command #114 2022-09-19 23:00:09 +02:00
df3f9f0306 Merge pull request 'Fixed update & install commands #113' (#115) from 2022.10.10-#113 into 2022.10
Reviewed-on: sh-edraft.de/sh_cpl#115
Closes #113
2022-09-19 21:48:56 +02:00
ef2d73aadc Fixed update & install commands #113 2022-09-19 21:09:41 +02:00
1ed721cccb Updated packages (Build RC2) 2022-09-18 11:37:50 +02:00
a724216563 Added dockerfile to test 2022-09-18 11:27:26 +02:00
2bcf4d7a97 Re-build correct version 2022-09-18 11:09:38 +02:00
2dfa2dd68f Improved project file structure 2022-09-18 10:59:29 +02:00
e94ed0b7f0 Build rc1 2022-09-17 13:03:37 +02:00
c7e2f8609c Merge pull request '2022.10.3 - Removed duplicated schematic name in generate command #92' (#111) from 2022.10.3 into 2022.10
Reviewed-on: sh-edraft.de/sh_cpl#111
Closes #92
2022-09-17 12:12:52 +02:00
9d2321cc9c Merge branch '2022.10' into 2022.10.3 2022-09-17 12:12:40 +02:00
61d9912950 Removed duplicated schematic name in generate command #92 2022-09-17 12:09:05 +02:00
f23d32a92a Merge pull request '2022.10.4 - Removed async from startup functions (#94)' (#110) from 2022.10.4 into 2022.10
Reviewed-on: sh-edraft.de/sh_cpl#110
Closes #94
2022-09-17 11:59:38 +02:00
dc489cbf7a Removed async from startup functions 2022-09-17 11:58:43 +02:00
41d24aae99 Merge pull request '2022.10.6 - Added error message when logging configuration not exists (#95)' (#109) from 2022.10.6 into 2022.10
Reviewed-on: sh-edraft.de/sh_cpl#109
Closes #95
2022-09-17 11:40:46 +02:00
6b50bba139 Merge branch '2022.10' into 2022.10.6 2022-09-17 11:40:30 +02:00
139422fd32 Added error message when logging configuration not exists 2022-09-17 11:39:19 +02:00
409e11df42 Merge pull request '2022.10.5 Improved version command & fixed unittests (#97)' (#107) from 2022.10.5 into 2022.10
Reviewed-on: sh-edraft.de/sh_cpl#107
Closes #97
2022-09-16 10:43:17 +02:00
f3a11f9791 Improved version command & fixed unittests 2022-09-15 19:16:52 +02:00
8dcc5b4011 Merge pull request '2022.10.9 - Enumerable & List (#105)' (#106) from 2022.10.9 into 2022.10
Reviewed-on: sh-edraft.de/sh_cpl#106
Closes #105
2022-09-15 18:21:25 +02:00
d0f65834f1 Added performance test case 2022-09-15 18:14:40 +02:00
47ed0a705d Improved query 2022-09-15 17:00:22 +02:00
ae3192b63c Removed SequenceABC 2022-09-15 12:48:05 +02:00
bb461f5fba Improved query 2022-09-15 00:30:44 +02:00
52069b7bb3 Improved Sequences 2022-09-14 23:01:52 +02:00
f0ed0bd2e1 Added sequence converters 2022-09-14 13:23:23 +02:00
e868a120f0 Build cpl-query 2022-09-13 19:38:16 +02:00
70652aeb4c Changed structure of cpl-query 2022-09-13 19:33:26 +02:00
28adcc4e49 Implemented enumerable 2022-09-13 12:07:04 +02:00
affbb1ee7b Merge pull request '2022.10.8 - Moved query functions to static query class (#102)' (#104) from 2022.10.8 into 2022.10
Reviewed-on: sh-edraft.de/sh_cpl#104
Closes #102
2022-09-12 22:55:14 +02:00
d8a4210c9b Moved query functions to static query class 2022-09-12 22:53:52 +02:00
b16d555e33 Merge pull request '2022.10.2 - Code refactoring' (#103) from 2022.10.2 into 2022.10
Reviewed-on: sh-edraft.de/sh_cpl#103
2022-09-12 22:31:04 +02:00
4380142ccd Merge branch '2022.10' into 2022.10.2 2022-09-12 22:30:41 +02:00
91f2d6cb21 Code refactoring 2022-09-12 22:29:46 +02:00
249b071ee9 Merge pull request '2022.10.2' (#101) from 2022.10.2 into 2022.10
Reviewed-on: sh-edraft.de/sh_cpl#101
2022-09-12 22:29:22 +02:00
53fe437100 Added more data containers 2022-09-12 22:18:54 +02:00
fc63f3616e Added tests for select & select many 2022-09-12 18:07:37 +02:00
a528511732 Added tests for select & select many 2022-09-12 17:58:48 +02:00
dbe1d37046 Build version 2022-09-11 22:02:10 +02:00
988965ed52 Improved cpl-query & added first types from discord 2022-09-11 21:57:27 +02:00
3c5ca594d5 Merge pull request '2022.10.1' (#100) from 2022.10.1 into 2022.10
Reviewed-on: sh-edraft.de/sh_cpl#100
2022-09-11 19:30:39 +02:00
452b30c899 Build version 2022-09-11 19:30:19 +02:00
85c5b26fdf Added slash command support 2022-09-11 10:21:28 +02:00
92ed684866 First migrations 2022-09-10 10:27:31 +02:00
88e83db330 Improved scope handling 2022-07-31 13:32:02 +02:00
55609fc691 Renamed sh_cpl to cpl in docs 2022-07-31 11:33:45 +02:00
e0d89f967f Improved docs 2022-07-31 11:30:05 +02:00
b2ce8a6924 Added on_error to event types enum 2022-07-19 21:42:45 +02:00
71efbd1b42 Fixed database connection handling by reconnecting 2022-07-19 07:37:03 +02:00
e36939a108 Fixed database port handling 2022-07-18 12:15:28 +02:00
721461fc1b Deployed cpl-discord==2022.7.0.post2 2022-07-17 16:11:33 +02:00
72ee59b2b4 Deployed cpl-core==2022.7.0.post3 2022-07-17 15:52:53 +02:00
95159da0fc Fixed pynput imports 2022-07-17 15:52:17 +02:00
330d702347 Fixed base class of translation service 2022-07-17 15:49:50 +02:00
cbf669d3bd Fixed base class of translation service 2022-07-16 22:06:38 +02:00
7498fe3255 Fixed settings error 2022-07-16 19:37:40 +02:00
880998e8ca Published cpl-discord.post1 2022-07-16 19:23:00 +02:00
ecc9657eaa Merge pull request '2022.7 - cpl-discord' (#96) from 2022.7 into master
Reviewed-on: sh-edraft.de/sh_cpl#96
Closes #91
2022-07-16 16:16:49 +02:00
125f65cf1b Published cpl-discord 2022-07-16 16:15:52 +02:00
1cd9c7d669 Fixed set-version 2022-07-16 16:07:39 +02:00
5165f31066 Build cpl-discord 2022-07-16 15:53:02 +02:00
10d33e5d1d Added logic to handle commands 2022-07-16 15:45:34 +02:00
fb35e8b8b2 Added logic to create a bot 2022-07-16 14:53:54 +02:00
4402bcce73 Added test project 2022-07-16 12:22:10 +02:00
8839b26f00 Added event bases 2022-07-16 12:21:59 +02:00
a79358725c Added cpl-discord project 2022-07-16 12:12:52 +02:00
2ab1576230 Fixed async init error & Fixed pip install 2022-07-14 16:29:08 +02:00
c0be380f69 Removed vscode files 2022-07-14 15:22:10 +02:00
c0ae0c8389 Merge pull request '2022.7 - cpl-translation' (#90) from 2022.7 into master
Reviewed-on: sh-edraft.de/sh_cpl#90
Closes #89
Closes #63
Closes #62
2022-07-10 18:10:30 +02:00
4a766e8284 Release 2022.7 2022-07-10 18:08:44 +02:00
2772ea8df2 Added translation unittests 2022-07-10 17:56:38 +02:00
a604f0e821 Build package 2022-07-10 17:23:50 +02:00
6995b25518 Injected add_translation function from translation package 2022-07-10 17:17:45 +02:00
e4f843829f Added logic to load translations and to translate texts 2022-06-29 20:03:13 +02:00
39b6ca790c Added test project 2022-06-29 19:05:12 +02:00
4c8d7a8031 Added translation project 2022-06-29 19:04:01 +02:00
6d75bc433e Merge pull request '2022.6' (#88) from 2022.6 into master
Reviewed-on: sh-edraft.de/sh_cpl#88
2022-06-29 17:50:06 +02:00
8bb7fbd1c4 Merge remote-tracking branch 'origin/2022.6' into 2022.6 2022-06-29 17:47:04 +02:00
ad03e655e9 Build Release 2022.6.0 2022-06-29 17:46:59 +02:00
61d3974a47 Build Release 2022.6.0 2022-06-29 17:46:01 +02:00
3d852c87e2 Improved docs 2022-06-29 17:44:10 +02:00
0e46528f32 Merge pull request '2022.6.8 - Dokumentation' (#87) from 2022.6.8 into 2022.6
Reviewed-on: sh-edraft.de/sh_cpl#87
Closes #56
2022-06-29 17:42:33 +02:00
bb8fa1f6a8 Improved readme 2022-06-29 17:40:05 +02:00
9fdaa810c0 Build 2022.6.0rc2 2022-06-27 21:48:15 +02:00
4083ac0c14 Build docs and added tutorial files 2022-06-27 21:42:34 +02:00
b9e0b7ccd0 Build 2022.6.0.rc1 2022-06-27 20:55:44 +02:00
4b9e428abb Updated tools 2022-06-27 20:52:27 +02:00
63ad13e09a Improved version handling of set-version 2022-06-27 20:46:01 +02:00
cc06a308a1 Merge pull request '2022.6.10 - Dev dependencies' (#86) from 2022.6.10 into 2022.6
Reviewed-on: sh-edraft.de/sh_cpl#86
Closes #71
2022-06-27 20:18:24 +02:00
e9fdd20181 Updated unittests 2022-06-27 20:16:37 +02:00
8692686cb5 Added dev-deps support to new command 2022-06-27 20:07:52 +02:00
db29ad36fe Added dev-deps support to cpl install without package 2022-06-27 20:05:34 +02:00
0710b25317 Added dev-dependencies to uninstall command 2022-06-27 20:04:19 +02:00
cbd2964f42 Added dev-dependencies to install command 2022-06-27 20:02:01 +02:00
f51118f13c Merge pull request '2022.6.2 - Venv support' (#85) from 2022.6.2 into 2022.6
Reviewed-on: sh-edraft.de/sh_cpl#85
Closes #51
2022-06-27 11:52:17 +02:00
323e363b42 Improved venv support to all related commands 2022-06-27 11:50:22 +02:00
e8a4fe6142 Added venv support to install command 2022-06-27 10:52:26 +02:00
587d9b359c Merge pull request '2022.6.17 - Unittests' (#84) from 2022.6.17 into 2022.6
Reviewed-on: sh-edraft.de/sh_cpl#84
2022-06-27 10:03:54 +02:00
c2632253ac Added tests for update command 2022-06-27 09:10:21 +02:00
45be77dd10 Added tests for version command 2022-06-26 19:51:18 +02:00
78ffbcc33d Readded all test cases 2022-06-26 01:07:10 +02:00
d83d1a244e Fixed for start command [WIP] output is needed 2022-06-26 01:05:45 +02:00
fa002a50ac Fixed for start command 2022-06-26 00:41:25 +02:00
32507afae9 Added tests for start command 2022-06-26 00:39:16 +02:00
6de3bbdaf4 Added tests for run command 2022-06-26 00:15:05 +02:00
6e50d0cb22 Added tests for publish command 2022-06-23 23:47:29 +02:00
9e978f3ece Fixed tests for un/install commands 2022-06-23 23:39:41 +02:00
32478926c8 Added tests for build command 2022-06-23 21:49:44 +02:00
2925788d01 Added tests for uninstall command 2022-06-21 09:24:56 +02:00
c0789cf4f7 Added tests for install command 2022-06-21 08:56:26 +02:00
89d591ce0a Added project reference handling in cpl remove 2022-06-04 00:36:31 +02:00
77c560b40c Improved cpl remove test 2022-06-03 23:46:03 +02:00
dad4913bcd Added cpl remove test 2022-06-03 23:44:26 +02:00
9b56650d4b Added cpl add test 2022-06-03 23:35:07 +02:00
9fca2018e5 Build dev10 2022-05-27 18:57:12 +02:00
e5dd890931 Improved unittest for cpl new & Fixed subdirectory handling with cpl new 2022-05-27 18:57:00 +02:00
beadbf8e91 Improved set-version to ignore skipped deps 2022-05-27 18:26:32 +02:00
ff5f9b833d Improved unittest for cpl new 2022-05-27 18:07:12 +02:00
162f06001a Build dev8 & improved cpl new unittests 2022-05-26 23:27:32 +02:00
348fad24c7 Build dev8 & improved set-version change handling 2022-05-26 23:27:20 +02:00
f9748cdb76 Improved gitignore 2022-05-26 23:03:18 +02:00
56380763a9 Improved unittests 2022-05-26 23:01:50 +02:00
3a1630b687 Added force option to set-version 2022-05-26 23:01:23 +02:00
a593f34cde Build dev7 2022-05-26 23:00:55 +02:00
49cce5fa1a Extended cpl new options 2022-05-26 22:52:03 +02:00
df7bd06629 Fixed additional argument & flag bug 2022-05-26 22:51:29 +02:00
e0da77fd4b Added new test condition for cpl new 2022-05-26 22:24:19 +02:00
833dd83008 Improved unittests & added tests for cpl new 2022-05-26 22:21:16 +02:00
ce0ad4013f [WIP] Improved unittest 2022-05-26 21:42:43 +02:00
698f80adfe Build dev6 2022-05-26 21:42:28 +02:00
d0b0f4bfcb Fixed set-version 2022-05-26 21:41:42 +02:00
d23ea86ccf Added logic to answer questions of new command dialog by flag arguments 2022-05-26 21:18:57 +02:00
15d938b172 Fixed handing of unregistered console arguments 2022-05-26 21:01:41 +02:00
6c1d7a3b29 Removed prints from CLITestSuite 2022-05-26 16:29:07 +02:00
c28f551682 Added skip message to set-version 2022-05-26 16:26:55 +02:00
def92c78c8 Added logic to change version only for projects with changes 2022-05-26 16:25:15 +02:00
ddff9dec11 Fixed install command 2022-05-26 16:08:37 +02:00
9e0e85d646 Fixed install command 2022-05-26 16:06:56 +02:00
b05ca7e980 Set dev version 2022-05-26 16:03:14 +02:00
8f8a9f7dd7 Moved query unittests 2022-05-26 15:53:23 +02:00
df32978a31 Moved unittests 2022-05-26 15:49:52 +02:00
79b51cf8c5 Build dev4 & added unittest support to console 2022-05-26 15:41:47 +02:00
a72dd0dc2e Improved tests 2022-05-26 15:29:49 +02:00
d937c4c0e6 Added tests for generate command 2022-05-26 14:47:36 +02:00
46cceb0910 Registered CLI test cases 2022-05-26 13:59:46 +02:00
e74e39c189 Added CLI command test cases 2022-05-26 13:49:31 +02:00
c2706c7737 Added unittest projects 2022-05-26 13:37:15 +02:00
d0e66d24bd Added logic to generate test cases 2022-05-26 13:17:58 +02:00
c1a1849ad2 Added logic to create new unittest project 2022-05-26 13:12:35 +02:00
0675105731 Merge pull request '2022.6.16 - Build-Tools' (#83) from 2022.6.16 into 2022.6
Reviewed-on: sh-edraft.de/sh_cpl#83
Closes #82
2022-05-26 12:13:45 +02:00
7ee0b951a8 Rebuild & improved workspace scripts 2022-05-25 19:59:38 +02:00
d8168f024c Added logic to write PipPath & build dev2 2022-05-25 19:53:05 +02:00
2654dc7c69 Merge remote-tracking branch 'origin/2022.6.16' into 2022.6.16 2022-05-25 19:38:56 +02:00
4224960837 First dev build 2022-05-25 19:38:49 +02:00
907c3a8fff First dev build 2022-05-25 19:38:38 +02:00
28505479ba Added new project 2022-05-25 19:30:11 +02:00
84d3abce3f Merge pull request '2022.6.15 - set-version' (#81) from 2022.6.15 into 2022.6
Reviewed-on: sh-edraft.de/sh_cpl#81
Closes #80
2022-05-25 19:10:35 +02:00
9050dffc53 Renamed projects && build dev6 2022-05-25 19:07:29 +02:00
e0f69c9bdf Fixed old naming of cpl_core 2022-05-25 18:56:08 +02:00
bbd0a60f98 Added functionality to set dependencies for all projects in workspace 2022-05-25 18:54:21 +02:00
e23f4187fe Added logic to add internal pipes in ServiceCollection 2022-05-25 18:42:47 +02:00
15f200df3a Added version pipe 2022-05-25 18:40:01 +02:00
a1a3ad6fb5 Build dev4 2022-05-25 17:09:31 +02:00
d6677d53a3 Added empty lines to generate output 2022-05-25 17:08:03 +02:00
07e43b50d1 Fixed generate pipe argument 2022-05-25 17:07:22 +02:00
ac83555b24 Added logic to handle cpl deps 2022-05-25 17:04:18 +02:00
1ab358059c Added more pre-build scripts 2022-05-25 16:57:21 +02:00
5bf83c0d77 Build dev2 2022-05-25 16:56:24 +02:00
03ac00c6e3 Bugfixes for set-version & CustomScriptService 2022-05-25 16:51:52 +02:00
0122d8fc78 Merge remote-tracking branch 'origin/2022.6.15' into 2022.6.15 2022-05-25 16:43:52 +02:00
a66ce96dbf Added set-version logic && Fixed CustomScriptService to handle arguments 2022-05-25 16:43:43 +02:00
6da8e30446 Added set-version logic && Fixed CustomScriptService to handle arguments 2022-05-25 16:43:28 +02:00
539ed13be4 Bugfixes for run service 2022-05-25 16:06:44 +02:00
036a553f64 Dev build 2022.6.15.dev1 2022-05-25 15:12:36 +02:00
976a14584b Dev build 2022.6.15.dev1 2022-05-25 15:12:30 +02:00
ed69f52927 Fixed generate service 2022-05-25 15:11:42 +02:00
98847495e6 Merge pull request '2022.6.14 - Pre & Post build scripts' (#79) from 2022.6.14 into 2022.6
Reviewed-on: sh-edraft.de/sh_cpl#79
Closes #77
2022-05-25 14:15:24 +02:00
40f132da33 Added workspace scripts 2022-05-25 14:14:26 +02:00
5feb44595a Fixed cpl run command 2022-05-25 14:00:38 +02:00
48c6941f1a Added run command & tools/set-version project 2022-05-25 13:43:16 +02:00
a8756d493a Fixed error with RemoveService 2022-05-25 13:19:12 +02:00
9f4077a073 Improved configuration 2022-05-25 13:11:41 +02:00
3e7a3d75c5 Merge pull request 'cpl@2022.6.13 - cpl run command' (#78) from 2022.6.13 into 2022.6
Reviewed-on: sh-edraft.de/sh_cpl#78
Closes #76
2022-05-25 08:42:41 +02:00
477d2d87d3 Added run command 2022-05-25 08:41:35 +02:00
7a65dbe39b Readded logic to handle pre and post scripts from workspace 2022-05-24 22:28:38 +02:00
f83755ac77 Removed console output & build new dev version 2022-05-24 20:20:42 +02:00
a590cfd066 Bugfixes for start service 2022-05-24 20:12:59 +02:00
f317035342 Bugfixes for test application & added validators to cli 2022-05-24 19:20:55 +02:00
8900bdd446 Removed invalid import && build for rc1 2022-05-24 18:20:31 +02:00
35ecf158a2 Fixed loading by base_type 2022-05-24 18:13:39 +02:00
353c1d30ec Merge pull request '2022.6.9 - Singleton initialisierung' (#75) from 2022.6.9 into 2022.6
Reviewed-on: sh-edraft.de/sh_cpl#75
Closes #70
2022-05-24 17:52:13 +02:00
1c94fb5bd1 Added logic to load singletons when requested not when registered 2022-05-24 17:51:17 +02:00
cf305aeebb Merge pull request '2022.6.12 - Alle Umgebungsvariablen' (#74) from 2022.6.12 into 2022.6
Reviewed-on: sh-edraft.de/sh_cpl#74
Closes #73
2022-05-24 17:39:20 +02:00
90e97433e0 Added logic to load all environment variables by prefix 2022-05-24 17:37:31 +02:00
125a85a0af Merge pull request '2022.6.7 - Pipes' (#69) from 2022.6.7 into 2022.6
Reviewed-on: sh-edraft.de/sh_cpl#69
Closes #60
2022-05-22 20:28:46 +02:00
cfb8838c73 Added default pipes 2022-05-22 20:27:58 +02:00
824d491ebc Dev build projects 2022-05-22 18:49:31 +02:00
d694c408c0 Added CLI logic to generate pipes 2022-05-22 18:48:33 +02:00
d1bb266d49 Added Pipes 2022-05-22 18:43:10 +02:00
419fcc3d29 Merge pull request '2022.6.5 - CLI Erweiterung' (#68) from 2022.6.5 into 2022.6
Reviewed-on: sh-edraft.de/sh_cpl#68
Closes #54
2022-05-22 18:34:44 +02:00
24ab6a51c1 Refactored executable arguments 2022-05-22 18:33:07 +02:00
dec4a45d98 Added logic to add CLI commands from external packages 2022-05-22 18:32:34 +02:00
d11c56db03 Merge pull request 'cpl@2022.6.3 - Verbesserung der Parameter' (#67) from 2022.6.3 into 2022.6
Reviewed-on: sh-edraft.de/sh_cpl#67
2022-05-22 17:38:21 +02:00
fc23315cc7 Build version 2022.6.3 2022-05-22 17:35:38 +02:00
1f503fe20d Build new version 2022-05-20 10:37:43 +02:00
4564f7ac0e Improved output & imports 2022-05-20 10:35:47 +02:00
7b823e1141 Added validator generation 2022-05-20 10:34:09 +02:00
dac3d9c6bb Added validators (closes #59) 2022-05-20 10:27:55 +02:00
ccca904cb8 Smaller output fixes 2022-05-20 09:17:51 +02:00
c66a81c0ec Dev build & migrated custom scripts 2022-05-20 09:12:28 +02:00
fb810e2943 Dev build & migrated custom scripts & add logic to prevent next executable 2022-05-20 09:10:29 +02:00
5cc663dedc Migrated generate command 2022-05-20 08:50:50 +02:00
593372699b BUild dev version 2022-05-20 08:44:32 +02:00
1899cb5b86 Added scripts to workspace 2022-05-19 20:49:48 +02:00
0a285f2d28 Improved help command 2022-05-19 20:23:21 +02:00
94c82e6701 Improved help command 2022-05-19 20:21:46 +02:00
0d37c86061 Removed console output 2022-05-19 20:05:22 +02:00
6c8e9ccd89 Migrated StartService to new argument parsing 2022-05-19 20:04:29 +02:00
e7cf545502 Migrated NewService to new argument parsing 2022-05-19 19:52:36 +02:00
44182eec86 Fixed value handling for variable arguments 2022-05-19 19:41:02 +02:00
0f5b1b7586 Improved argument handling 2022-05-19 19:37:32 +02:00
4fe073580a Migrated first commands to new argument parser 2022-05-19 18:12:47 +02:00
2fed654c16 Improved argument handling 2022-05-19 18:09:25 +02:00
8ebd4864d3 Improved & added commands 2022-05-19 08:25:32 +02:00
a495532a4d Removed console debug outputs 2022-05-18 22:29:44 +02:00
d87ce72f07 Improved argument parsing 2022-05-18 22:26:11 +02:00
50201346e2 Added functions to define console arguments 2022-05-18 18:21:05 +02:00
773b154371 Improved service collection 2022-05-18 18:20:25 +02:00
5b59d1d384 Merge pull request '2022.6.4' (#58) from 2022.6.4 into 2022.6
Reviewed-on: sh-edraft.de/sh_cpl#58
2022-04-30 18:33:54 +02:00
dcd8583183 Build packages 2022-04-30 17:44:15 +02:00
97e52b39c0 Added logic to handle StartupExtension classes 2022-04-30 17:43:34 +02:00
581013b61a Merge pull request '2022.6.1' (#57) from 2022.6.1 into 2022.6
Reviewed-on: sh-edraft.de/sh_cpl#57
2022-04-30 17:16:49 +02:00
fd68d25a1d Updated to python3.10 2022-04-30 17:15:26 +02:00
588f93f7b1 Updated packages 2022-04-30 16:41:52 +02:00
96f402dcdd Updated packages 2022-04-30 16:33:30 +02:00
c1b7fca8d5 Moved from pyfiglet to art 2022-01-19 22:06:59 +01:00
2e4e7ccc57 Fixed minor issues with console & updated packages 2022-01-19 15:06:26 +01:00
ada3d133df Changed exit() to sys.exit() 2022-01-19 12:44:23 +01:00
61e7b9d684 Fixed cpl new command 2022-01-15 23:28:22 +01:00
24172c658c Fixed cpl new command 2022-01-15 22:16:17 +01:00
62ac976b99 Fixed where query typing 2022-01-15 11:11:14 +01:00
1f445a99b2 Fixed typing in count function of cpl_query 2022-01-08 18:09:03 +01:00
025286052b Fixed typing in service collection abc 2022-01-08 17:58:22 +01:00
32a856f8e8 Published new version 2021-12-07 16:07:21 +01:00
21f50f3b24 Fixed table_abc 2021-12-07 16:06:20 +01:00
ce77d41246 Build new docs 2021-12-02 08:20:38 +01:00
dec137220c Improved TableABC 2021-11-30 17:48:08 +01:00
01c7e155c3 Fixed typo 2021-11-30 15:24:01 +01:00
dcbcb67ce8 Fixed db connection 2021-11-30 15:01:28 +01:00
d57fd10718 Fixed add_db_context function 2021-11-30 14:14:34 +01:00
2b91db5d81 Removed unsued parameter 2021-11-30 14:05:52 +01:00
dffa3de1ec Merge pull request '2021.11' (#50) from 2021.11 into master
Reviewed-on: sh-edraft.de/sh_cpl#50
2021-11-30 12:03:03 +01:00
403837901b Added logic to simulate uninstall command 2021-11-30 11:55:11 +01:00
0de725c907 Added logic to simulate install command 2021-11-30 11:51:45 +01:00
e811e08c52 Changed version of cli 2021-11-30 11:22:02 +01:00
6518ae7516 Changed version of query 2021-11-30 11:21:19 +01:00
95b5f99f1f Changed core version 2021-11-30 11:20:15 +01:00
de22e79b71 Removed console from db connection 2021-11-30 11:15:41 +01:00
127c67e566 Improved imports 2021-11-30 11:14:40 +01:00
3523dbd5c5 Improved typing 2021-11-30 11:12:13 +01:00
b526a07e19 Added demo for SQL Support 2021-11-30 11:10:36 +01:00
d9f10edbb7 [WIP] Added native mysql support 2021-11-29 20:55:43 +01:00
7616cd4c69 Changed version 2021-11-29 17:49:10 +01:00
afe75f6848 Fixed custom scripts 2021-11-29 17:47:44 +01:00
c0e2db8281 Build new CLI version 2021-11-29 09:30:46 +01:00
3727af11c4 Minor bugfixes with custom scripts 2021-11-29 09:30:10 +01:00
450295cfcf Build new version 2021-11-29 09:22:33 +01:00
457bce8aa9 Minor bugfixes 2021-11-29 09:21:55 +01:00
0f81497ea3 Changed version 2021-11-24 17:48:39 +01:00
fa7ee800e1 Fixed cpl start reading full path 2021-11-24 17:48:19 +01:00
56b7452e55 Changed version 2021-11-23 19:02:15 +01:00
9d73566453 Fixed custom command handling 2021-11-23 19:01:00 +01:00
28ba0c174a Changed version 2021-11-22 20:54:07 +01:00
5d0444bcc0 Fixed type problems with ordered List 2021-11-22 20:52:10 +01:00
40876005d5 Changed vs code settings 2021-11-22 20:49:39 +01:00
ac6d941822 Improved gitignore 2021-11-19 09:16:11 +01:00
113b365a47 Removed outdating line 2021-11-15 01:16:37 +01:00
2f785536e1 Improved path handling for new projects 2021-11-15 01:03:50 +01:00
9ab4013b4b Fixed async handling for new command 2021-11-15 00:20:39 +01:00
b3b04f03ce Merge pull request '2021.10.2' (#48) from 2021.10.2 into master
Reviewed-on: sh-edraft.de/sh_cpl#48
Closed #47
2021-11-14 12:56:41 +01:00
57040f85f0 Changed versions 2021-11-14 12:53:09 +01:00
589ef4fd4c Added logic to generate new projects with async 2021-11-14 12:32:11 +01:00
4b829cc484 Added logic to handle async applicationBase 2021-11-14 12:16:50 +01:00
98e343a108 Added logic to handle scoped services 2021-11-14 11:56:42 +01:00
7749d5a789 Moved tests to src & added add scoped logic 2021-11-14 10:04:32 +01:00
c3b48bfa41 Changed version 2021-11-14 09:55:27 +01:00
470e7ab2f4 Changed versions to new scheme 2021-11-14 09:52:07 +01:00
4ceea36926 Changed version 2021-11-02 18:42:13 +01:00
b3f8174663 Added cpl_query to docs 2021-11-02 18:41:30 +01:00
58dccea28b Improved docs 2021-11-02 17:45:45 +01:00
ca086e01d7 Improved docs 2021-11-02 17:30:10 +01:00
c5b1a95661 Moved implementations to extensions 2021-11-02 17:28:36 +01:00
d7881774a8 Improved docs by adding cpl_query.exceptions 2021-11-02 17:23:42 +01:00
6eec92243e Improved docs 2021-11-01 21:17:56 +01:00
124b236182 Improved docs 2021-11-01 20:43:25 +01:00
2dc96cdf3d Improved workspace 2021-11-01 18:49:22 +01:00
6f7923debc Moved tests 2021-11-01 18:47:23 +01:00
90d148b4a5 Added launch settings 2021-11-01 18:42:12 +01:00
73e2c5ec10 Added vs code 2021-11-01 18:24:41 +01:00
f31522dcd9 Changed version 2021-10-27 14:25:33 +02:00
a83607fd81 Fixed subclass support for Iterable 2021-10-27 14:24:43 +02:00
707df3f27d Fixed command handling 2021-10-26 23:23:56 +02:00
0f43e57831 Removed console.write_line 2021-10-26 13:09:44 +02:00
c08ab33060 Changed deps 2021-10-26 13:06:14 +02:00
ee2abd98b3 Changed version 2021-10-26 13:00:27 +02:00
e39661a42a Fixed command handling 2021-10-26 13:00:16 +02:00
9518d19580 Fixed invalid lines in startup template 2021-10-08 08:22:36 +02:00
adf95faaf8 Improved typo 2021-10-08 08:16:46 +02:00
2f92e9909d Improved env 2021-10-04 10:21:50 +02:00
b18f2c4bcd Removed debug changes 2021-10-04 09:59:20 +02:00
784ba049ad Fixed new library problems 2021-10-04 09:56:33 +02:00
4d6d19d2e5 Fixed new command problems 2021-10-04 09:56:08 +02:00
6fa47f19af Merge pull request '2021.10' (#41) from 2021.10 into master
Reviewed-on: sh-edraft.de/sh_cpl#41
2021-10-04 09:32:41 +02:00
fb2872422e Added upload script 2021-10-04 09:30:27 +02:00
8460c0fa33 Published 2021.10 2021-10-04 09:23:08 +02:00
18f95341f4 Merge pull request '2021.10.6' (#39) from 2021.10.6 into 2021.10 | closed #38
Reviewed-on: sh-edraft.de/sh_cpl#39
2021-10-03 11:26:48 +02:00
a8011267d9 Improved version command 2021-10-03 11:23:17 +02:00
f8ebb4f1d7 Improved imports in test app 2021-10-03 11:02:08 +02:00
3903f85368 Added logic to handle extensions before running the app 2021-10-02 10:57:54 +02:00
05a9bdbf4a Improved docstrings 2021-08-07 11:29:00 +02:00
2809a9cd82 Improved imports 2021-08-07 11:24:17 +02:00
1c6794dc40 Renamed cpl to cpl_core & added config and services as arguments to startup 2021-08-07 11:21:30 +02:00
cac80657df Renamed cpl to cpl_core 2021-08-05 14:21:42 +02:00
11241d8f99 Renamed project 2021-08-05 14:17:38 +02:00
308e5c9b0c Merge pull request '2021.10.4' (#37) from 2021.10.4 into 2021.10
Reviewed-on: sh-edraft.de/sh_cpl#37
Closed #32
2021-08-05 14:04:45 +02:00
c12ff4f9e3 Added logic to migrate attributes from workspace and project settings 2021-08-05 14:02:00 +02:00
091c89df2c Changed version of CLI 2021-08-05 13:37:40 +02:00
63a67be0ee Improved new command 2021-08-05 13:27:08 +02:00
c68224b31a Improved deps of cpl_query 2021-08-02 14:48:46 +02:00
9f219757c3 Merge pull request '2021.10.3' (#35) from 2021.10.3 into 2021.10
Reviewed-on: sh-edraft.de/sh_cpl#35
2021-08-02 14:36:45 +02:00
e09a4402b3 Published sh_cpl-query to version 2021.10.0rc1 2021-08-02 14:36:00 +02:00
6867eb0e65 Improved scripts 2021-08-02 14:14:50 +02:00
ffaa5c4a2b Refactoring 2021-08-02 14:09:42 +02:00
b9499108d8 Build cpl_query 2021-08-02 14:03:51 +02:00
37175b7227 Added queries sum, skip & take 2021-07-27 13:57:42 +02:00
2b5831c5fe Improved numbered queries 2021-07-27 13:00:06 +02:00
62c8b9c68d Added reverse query 2021-07-27 12:54:49 +02:00
a080119c44 Improved unittests 2021-07-27 12:48:22 +02:00
a26c6f1bd9 Improved avg, max and min queries type handling 2021-07-27 12:42:33 +02:00
daac14e099 Improved avg query 2021-07-27 12:35:14 +02:00
d4b5c32a21 Added min and max queries 2021-07-27 11:57:42 +02:00
c115afd736 Moved last queries 2021-07-27 11:47:13 +02:00
e7863a92e0 Added last query 2021-07-27 11:43:45 +02:00
82a9e3a23a Improved index out of range exception 2021-07-27 11:40:24 +02:00
a3a0a150a8 Added element at query 2021-07-27 11:29:52 +02:00
6951a19670 Renamed files 2021-07-27 11:22:29 +02:00
669133d491 Added distinct query 2021-07-27 11:20:35 +02:00
c60598b3f6 Added count query 2021-07-27 11:01:43 +02:00
45733b30ef Improved error handling 2021-07-27 10:59:24 +02:00
f8bd86692e Added contains query 2021-07-27 10:55:23 +02:00
5e5d86cf94 Added better exceptions 2021-07-27 09:41:51 +02:00
e0b7728719 Added avg query 2021-07-27 09:26:30 +02:00
0f85d4b9bc Refactoring & added all query 2021-07-27 09:15:19 +02:00
a3fff9c7d7 Build package 2021-07-26 15:55:08 +02:00
8dcf3a6768 Refactoring 2021-07-26 15:52:27 +02:00
94949394de Improved list to handle typing 2021-07-26 15:48:42 +02:00
b7be439381 Changed func type from str to Callable 2021-07-26 15:32:28 +02:00
0cae3428b9 Added order by functions 2021-07-26 15:21:57 +02:00
cc7755bafc Improved cli project builders 2021-07-25 19:18:06 +02:00
4e78b9c12b Added different queries & unittests 2021-07-25 19:15:02 +02:00
5c80cf2222 Added CPL Query 2021-07-25 12:24:58 +02:00
d02548e1a8 Merge pull request '2021.10.2' (#33) from 2021.10.2 into 2021.10
Reviewed-on: sh-edraft.de/sh_cpl#33
2021-07-25 12:05:12 +02:00
51efe973e8 Improved logic to handle pre and post scripts 2021-07-25 12:00:12 +02:00
0bb272d89b Added logic to handle pre and post scripts 2021-07-25 11:42:46 +02:00
59f6843ea9 Changed version 2021-07-24 17:25:06 +02:00
be6838f97b Added logic to handle custom scripts from workspaces 2021-07-24 17:24:01 +02:00
d5b2d3605b CPL Release 2021.4.0.post2 2021-05-23 12:41:59 +02:00
7a48917fb4 CLI Release 2021.4.0.post3 2021-05-22 15:10:50 +02:00
59832742b6 CLI Release 2021.4.0.post2 2021-05-22 12:55:17 +02:00
f9f04901c0 Bugfixes 2021-05-22 12:53:04 +02:00
593ffbb068 Updated cli project file 2021-05-19 09:03:31 +02:00
3d35f2ac63 Merge pull request '2021.4.post1' (#31) from 2021.4.post1 into master
Reviewed-on: sh-edraft.de/sh_cpl#31
2021-05-19 09:00:39 +02:00
5bfba581cb Release 2021.4.0.post1 2021-05-19 08:35:02 +02:00
8fb570468c More bugfixes & added generated test projects 2021-05-19 08:28:59 +02:00
128fda60b0 Release 2021.4.1.rc2 2021-05-19 08:19:21 +02:00
ab539c15dd Fixed source creation bug 2021-05-19 08:03:04 +02:00
b931ba6e17 Fixed source creation bug 2021-05-19 08:01:02 +02:00
f611aa73db Publish 2021.4.post1.rc1 2021-05-17 19:43:09 +02:00
69f697e36f Merge pull request '2021.4.4' (#30) from 2021.4.4 into 2021.4.post1
Reviewed-on: sh-edraft.de/sh_cpl#30
2021-05-17 18:34:58 +02:00
654fb69295 Added cli docs 2021-05-17 18:32:09 +02:00
850d44a105 Added docs to cpl.utils 2021-05-17 18:07:08 +02:00
8d4597705d Build docs 2021-05-17 17:55:20 +02:00
fa5655414a Added docs for cpl.mailing 2021-05-17 17:54:57 +02:00
6fc4cebbfe Added docs for cpl.logging 2021-05-17 17:45:22 +02:00
89f3c5833f Added docs for cpl.time 2021-05-17 17:30:26 +02:00
adf9a33e83 Added docs for cpl.environment 2021-05-17 17:28:52 +02:00
4adf6bc834 Added docs for cpl.dependency_injection 2021-05-17 17:22:41 +02:00
9026f021ed Improved docs for cpl.database 2021-05-17 17:13:36 +02:00
e562c31033 Added docs for cpl.database 2021-05-17 17:05:43 +02:00
7b90b5af57 Added docs for cpl.console 2021-05-16 17:42:09 +02:00
cda1fabd4f Added docs for cpl.configuration 2021-05-16 16:57:59 +02:00
4209055d62 Improved documentation for cpl.application 2021-04-14 22:20:18 +02:00
ed1a4eb88c Added cpl_cli docs & moved cpl_cli/templates -> cpl_cli/_templates 2021-04-14 10:58:47 +02:00
478f1ad0ff Added first sphinx generated doc 2021-04-14 10:45:39 +02:00
80eb1da0e1 Merge pull request '2021.4.7' (#29) from 2021.4.7 into 2021.4.post1
Reviewed-on: sh-edraft.de/sh_cpl#29
2021-04-13 15:47:24 +02:00
ae39d43ea9 Added useful deployment scripts & closed #22 2021-04-13 15:43:05 +02:00
602c48bc06 Bugfixes for help command handling 2021-04-13 15:27:19 +02:00
d34d501520 Bugfixes for help command 2021-04-12 20:22:28 +02:00
8f3ee53915 Fixed typos 2021-04-12 20:05:55 +02:00
7eca382cb3 Added help messages to cli commands 2021-04-12 20:02:45 +02:00
f8f5d46491 Added help message to generate command 2021-04-12 19:44:29 +02:00
696c62292b Added logic to cli to use --help option for help command 2021-04-12 19:43:55 +02:00
d94b25d9d8 Improved configuration console argument handling 2021-04-12 19:43:30 +02:00
93829027af Added help message to all cli commands 2021-04-11 16:36:59 +02:00
cce4b6b191 Merge pull request '2021.4.6' (#25) from 2021.4.6 into 2021.4.post1
Reviewed-on: sh-edraft.de/sh_cpl#25
2021-04-11 15:54:37 +02:00
0876f06282 Improved cpl-project related files & closed #20 2021-04-11 15:50:13 +02:00
c970412a66 Fixed relative path handling in add command 2021-04-11 15:42:34 +02:00
6af9328e14 Added cli command add 2021-04-11 15:36:04 +02:00
5987b71c80 Improved configuration to allow multiple arguments 2021-04-11 15:09:11 +02:00
79fdc0c60b Improved configuration to allow multiple arguments 2021-04-11 15:00:54 +02:00
cfb6a84799 Fixed console argument handling 2021-04-11 14:44:46 +02:00
faa7e701fd Fixed workspace loading error for update command 2021-04-11 14:21:59 +02:00
d5b8286f68 Fixed workspace loading error for update command 2021-04-11 13:23:02 +02:00
a3d5737eaf Fixed workspace loading error for update command 2021-04-11 13:20:32 +02:00
314bb1b722 Fixed workspace loading error for update command 2021-04-11 13:15:19 +02:00
1c353bbcdf Improved cli workspace handling 2021-04-11 13:00:26 +02:00
036b9dcc2d Added more functions to string 2021-04-11 13:00:05 +02:00
0fec5719bc Added remove to help command 2021-04-11 12:34:02 +02:00
3572081e43 Added remove command 2021-04-11 12:33:05 +02:00
8cf7f494b3 Improved configuration 2021-04-11 12:18:36 +02:00
b1165e169a Improved console argument value handling in configuration 2021-04-11 11:42:48 +02:00
284ebe1b11 Added logic to cpl cli to change cwd only when wanted 2021-04-11 11:12:39 +02:00
d51659db3e Improved cli build and publish to use project references 2021-04-11 11:01:57 +02:00
3aa3a874c9 Improved default cwd in ApplicationEnvironment 2021-04-11 10:04:55 +02:00
113d3199b1 Improved main.py templates for new command to handle local imports 2021-04-10 12:04:47 +02:00
5712a9ad67 Improved file creation new command 2021-04-10 11:50:35 +02:00
1f8d420749 Improved workspace handling in new command 2021-04-10 11:46:36 +02:00
9065ef901c Improved new command to handle existing projects better 2021-04-10 11:24:39 +02:00
b2c5d304f8 Improved path and name handling in new command 2021-04-10 11:14:37 +02:00
70ae7dd9e6 Fixed workspace creation error 2021-04-10 11:06:58 +02:00
94b122d677 Removed unused import 2021-04-10 11:05:31 +02:00
f6fc5d09e7 Improved path and name handling in new command 2021-04-10 11:01:56 +02:00
2b5c75cc67 Improved workspace handling to new command for console type 2021-04-09 22:41:04 +02:00
167f813bcf Removed dev outputs 2021-04-09 22:26:55 +02:00
7b9765e5b1 Improved readme 2021-04-09 22:16:43 +02:00
7ad01d4bdd Improved new command to use workspace when found 2021-04-09 22:09:55 +02:00
d19c47e571 Added cpl-env to gitignore 2021-04-09 20:52:50 +02:00
4bfc647b91 Improved live dev server to use venv 2021-04-09 20:42:15 +02:00
98117ed7ad Moved templates/[..]/src to templates/[..]/source to prevent 'src' exclusion 2021-04-09 20:26:31 +02:00
cc9b25356c Improved workspace and venv handling for uninstall and install command 2021-04-09 20:07:38 +02:00
a95d6e7f8a Improved configuration to handle absolute paths in add_json_file 2021-04-08 21:40:50 +02:00
fe5c41ac09 Added workspace handling to update command 2021-04-08 21:18:29 +02:00
b672174960 Added workspace handling to uninstall command 2021-04-08 21:14:38 +02:00
a8691ced5b Added workspace handling to install command 2021-04-08 21:12:00 +02:00
fc53f90ecd Added workspace handling for new command 2021-04-08 20:58:55 +02:00
0f836b5c7d Published after file move 2021-04-08 20:28:34 +02:00
34ed7123d1 Improved workspace handling in publish and build command 2021-04-08 20:28:19 +02:00
5f668e17c9 Improved workspace handling in start command 2021-04-08 19:30:00 +02:00
806888bf49 Removed content_root_path 2021-04-08 17:50:54 +02:00
e8756a09ca Improved general test project file 2021-04-08 16:46:46 +02:00
e38988be1b Changed project files of general test project 2021-04-07 22:11:22 +02:00
4009c93353 Added workspace handling to live dev server 2021-04-07 22:00:56 +02:00
3ba5c10fad Publish CLI 2021.4 2021-04-07 19:19:20 +02:00
2552f1d43c Added workspace handling to publish command 2021-04-07 19:18:55 +02:00
5be2e83798 Improved configuration 2021-04-07 18:57:00 +02:00
d4919e027c Removed console output 2021-04-07 18:37:57 +02:00
7ef238b64d Added workspace handling to build command 2021-04-07 18:36:08 +02:00
703d20f43f Improved version handling 2021-04-07 18:35:14 +02:00
d9bd7911ef Improved start command to use workspace 2021-04-07 18:19:55 +02:00
6a5c832288 Added new file structure 2021-04-07 18:19:34 +02:00
dc849c4f71 Improved application environment 2021-04-07 18:06:07 +02:00
76a6ba11fb Publish 2021.4.post1 for local dev 2021-04-07 17:01:39 +02:00
305a19730f Merge pull request 'Fixed #21 - Added logic to automatically add 'src' to path in generate command' (#24) from 2021.4.5 into 2021.4.post1
Reviewed-on: sh-edraft.de/sh_cpl#24
2021-04-07 16:59:24 +02:00
de15e81703 Fixed #21 - Added logic to automatically add 'src' to path in generate command 2021-04-07 16:55:44 +02:00
b91544a0da Edited LICENSE 2021-04-01 11:09:50 +02:00
caca34168e Merge pull request '2021.4' (#19) from 2021.4 into master
Reviewed-on: sh-edraft.de/sh_common_py_lib#19
2021-04-01 10:13:32 +02:00
18b8ac589d Publish 2021.4.0 2021-04-01 10:04:13 +02:00
d85e628784 Improved readme 2021-03-31 15:51:14 +02:00
1e150cf10c Improved readme 2021-03-31 15:49:52 +02:00
9b0f0b7559 Improved readme 2021-03-31 15:49:00 +02:00
cc118b5030 Merge pull request '2021.04.03 - Readme' (#18) from 2021.04.03 into 2021.4
Reviewed-on: sh-edraft.de/sh_common_py_lib#18
2021-03-31 15:42:40 +02:00
3e7ff78861 Merge branch '2021.4' into 2021.04.03 2021-03-31 15:40:18 +02:00
448d538d21 Updated reame 2021-03-31 15:38:26 +02:00
d368161eb3 Publish 2021.4.0rc7 2021-03-31 12:11:55 +02:00
eef8488ca2 Bugfixes for new lib 2021-03-31 12:11:19 +02:00
b22d04b145 Publish 2021.4.0rc6 2021-03-31 12:03:15 +02:00
6579e54c2b Improved windows support 2021-03-31 12:02:51 +02:00
88c0153ca2 Improved windows support for publisher 2021-03-31 11:55:46 +02:00
f28c6ab309 Removed prints 2021-03-31 11:51:20 +02:00
976041f812 Publish 2021.4.0rc5 2021-03-31 11:48:26 +02:00
7693dd462f Bugfixes for publisher windows support 2021-03-31 11:47:47 +02:00
df5e61cf98 Bugfixes for publisher windows support 2021-03-31 11:47:28 +02:00
df87d08be3 Bugfix for live server windows support 2021-03-31 11:41:16 +02:00
33cb35e26f Publish 2021.4.0rc4.post1 2021-03-31 11:25:07 +02:00
18b2cd8f2f Publish 2021.4.0rc4 2021-03-31 11:01:08 +02:00
7ab2a881e8 Bugfixes 2021-03-31 10:47:14 +02:00
960de880ee Bugfixes 2021-03-31 10:44:54 +02:00
c0d8904782 Bugfixes for the command new 2021-03-31 10:19:54 +02:00
b6d508526a Bugfixes for the command new 2021-03-31 09:46:41 +02:00
31307485f0 Bugfixes in new command template 2021-03-31 09:13:04 +02:00
612c3583f1 Removed ununsed files 2021-03-30 21:02:36 +02:00
6cdd3376f1 Publish 2021.4.0rc3 2021-03-30 21:01:10 +02:00
b947789779 Bugfixes 2021-03-30 20:59:09 +02:00
0da83935fc Bugfixes 2021-03-30 12:44:31 +02:00
3428c70b69 Bugfixes 2021-03-30 11:54:44 +02:00
3d511ff727 Bugfixes 2021-03-30 11:53:07 +02:00
cf2f293290 Publish 2021.4.0rc2 2021-03-30 11:03:16 +02:00
c39601a35d Improved publisher 2021-03-30 11:02:58 +02:00
5f36e0a43e Improved publisher 2021-03-30 10:43:39 +02:00
31fe1c5cd7 Improved service collection 2021-03-30 10:29:50 +02:00
0cdecd893a Improved database context 2021-03-30 10:06:07 +02:00
260fe06be8 Improved new command 2021-03-30 09:54:04 +02:00
8ddfd35a82 Improved generate command 2021-03-30 09:47:47 +02:00
099bae3d7c Removed usage of ServiceABC 2021-03-30 09:27:45 +02:00
9758e1fae2 Improved update command 2021-03-29 16:16:27 +02:00
211143d6ab Package update 2021-03-29 16:06:54 +02:00
70b49a098a Publish 2021.4.0rc1 2021-03-29 15:59:05 +02:00
e2544f90fe Merge pull request '2021.4.2' (#12) from 2021.4.2 into 2021.4
Reviewed-on: sh-edraft.de/sh_common_py_lib#12
2021-03-29 15:48:58 +02:00
c223524d1b Removed direct import 2021-03-29 15:47:41 +02:00
1789e2671a Publish 2021.4.2 2021-03-29 15:26:45 +02:00
d0812bcf5c Added database table test 2021-03-29 14:29:17 +02:00
4d5368e5fc Bugfixes & improved database context handling 2021-03-29 14:18:58 +02:00
f3be927440 Removed service abc 2021-03-29 13:29:26 +02:00
d0e3e1792f Added db context handling 2021-03-29 13:25:25 +02:00
31793fb290 Added db context handling 2021-03-29 13:25:15 +02:00
41ddc86961 Bugfixes 2021-03-29 12:16:12 +02:00
30ef74dd18 Added generated simple di test app 2021-03-29 11:48:25 +02:00
bc69476bb2 Improved new project templates & publish 2021.4.2.dev1 2021-03-29 11:47:49 +02:00
05b9fd801b Bugfixes 2021-03-29 09:21:58 +02:00
7af7f866c1 Removed application runtime | Code refactoring p.2 2021-03-29 08:56:18 +02:00
069e44bee6 Code refactoring p.1 2021-03-27 08:41:33 +01:00
3c0dd00a99 Bugfixes 2021-03-26 13:16:40 +01:00
afb91c8f1f Improved service provider 2021-03-23 21:42:08 +01:00
ca51f88d2b Improved service provider 2021-03-23 21:39:29 +01:00
a683b70c5f [WIP] Improved service collection 2021-03-23 20:23:30 +01:00
c650e87443 [WIP] Added first changes for the DI 2021-03-23 20:21:24 +01:00
36dfedf918 Bugfixes cpl cli output 2021-03-23 17:31:24 +01:00
252f2b55fe Bugfixes in templates 2021-03-23 17:29:13 +01:00
99091a2540 Publish for 2021.4.2 2021-03-21 20:22:04 +01:00
c3fdb7a049 Changed version 2021-03-21 20:05:51 +01:00
483 changed files with 14028 additions and 6822 deletions

View File

@@ -0,0 +1,76 @@
name: Build on push
run-name: Build on push
on:
push:
branches:
- dev
jobs:
prepare:
uses: ./.gitea/workflows/prepare.yaml
with:
version_suffix: 'dev'
secrets: inherit
api:
uses: ./.gitea/workflows/package.yaml
needs: [ prepare, application, auth, core, dependency ]
with:
working_directory: src/cpl-api
secrets: inherit
application:
uses: ./.gitea/workflows/package.yaml
needs: [ prepare, core, dependency ]
with:
working_directory: src/cpl-application
secrets: inherit
auth:
uses: ./.gitea/workflows/package.yaml
needs: [ prepare, core, dependency, database ]
with:
working_directory: src/cpl-auth
secrets: inherit
core:
uses: ./.gitea/workflows/package.yaml
needs: [prepare]
with:
working_directory: src/cpl-core
secrets: inherit
database:
uses: ./.gitea/workflows/package.yaml
needs: [ prepare, core, dependency ]
with:
working_directory: src/cpl-database
secrets: inherit
dependency:
uses: ./.gitea/workflows/package.yaml
needs: [ prepare, core ]
with:
working_directory: src/cpl-dependency
secrets: inherit
mail:
uses: ./.gitea/workflows/package.yaml
needs: [ prepare, core, dependency ]
with:
working_directory: src/cpl-mail
secrets: inherit
query:
uses: ./.gitea/workflows/package.yaml
needs: [prepare]
with:
working_directory: src/cpl-query
secrets: inherit
translation:
uses: ./.gitea/workflows/package.yaml
needs: [ prepare, core, dependency ]
with:
working_directory: src/cpl-translation
secrets: inherit

View File

@@ -0,0 +1,39 @@
name: Build on push
run-name: Build on push
on:
push:
branches:
- master
jobs:
prepare:
uses: ./.gitea/workflows/prepare.yaml
secrets: inherit
core:
uses: ./.gitea/workflows/package.yaml
needs: [prepare]
with:
working_directory: src/cpl-core
secrets: inherit
query:
uses: ./.gitea/workflows/package.yaml
needs: [prepare]
with:
working_directory: src/cpl-query
secrets: inherit
translation:
uses: ./.gitea/workflows/package.yaml
needs: [ prepare, core ]
with:
working_directory: src/cpl-translation
secrets: inherit
mail:
uses: ./.gitea/workflows/package.yaml
needs: [ prepare, core ]
with:
working_directory: src/cpl-mail
secrets: inherit

View File

@@ -0,0 +1,65 @@
name: Build Package
run-name: Build Python Package
on:
workflow_call:
inputs:
version_suffix:
description: 'Suffix for version (z.B. "dev", "alpha", "beta")'
required: false
type: string
working_directory:
required: true
type: string
jobs:
build:
runs-on: [ runner ]
container: git.sh-edraft.de/sh-edraft.de/act-runner:latest
defaults:
run:
working-directory: ${{ inputs.working_directory }}
steps:
- name: Clone Repository
uses: https://github.com/actions/checkout@v3
with:
token: ${{ secrets.CI_ACCESS_TOKEN }}
- name: Download build version artifact
uses: actions/download-artifact@v3
with:
name: version
- name: Set version
run: |
sed -i -E "s/^version = \".*\"/version = \"$(cat /workspace/sh-edraft.de/cpl/version.txt)\"/" pyproject.toml
echo "Set version to $(cat /workspace/sh-edraft.de/cpl/version.txt)"
cat pyproject.toml
- name: Set pip conf
run: |
cat > .pip.conf <<'EOF'
[global]
extra-index-url = https://git.sh-edraft.de/api/packages/sh-edraft.de/pypi/simple/
EOF
- name: Install Dependencies
run: |
export PIP_CONFIG_FILE=".pip.conf"
pip install build
- name: Build Package
run: |
python -m build --outdir dist
- name: Login to registry git.sh-edraft.de
uses: https://github.com/docker/login-action@v1
with:
registry: git.sh-edraft.de
username: ${{ secrets.CI_USERNAME }}
password: ${{ secrets.CI_ACCESS_TOKEN }}
- name: Push image
run: |
pip install twine
python -m twine upload --repository-url https://git.sh-edraft.de/api/packages/sh-edraft.de/pypi -u ${{ secrets.CI_USERNAME }} -p ${{ secrets.CI_ACCESS_TOKEN }} ./dist/*

View File

@@ -0,0 +1,54 @@
name: Prepare Build
run-name: Prepare Build Version
on:
workflow_call:
inputs:
version_suffix:
description: 'Suffix for version (z.B. "dev", "alpha", "beta")'
required: false
type: string
jobs:
prepare:
runs-on: [ runner ]
container: git.sh-edraft.de/sh-edraft.de/act-runner:latest
steps:
- name: Clone Repository
uses: https://github.com/actions/checkout@v3
with:
token: ${{ secrets.CI_ACCESS_TOKEN }}
- name: Get Date and Build Number
run: |
git fetch --tags
git tag
DATE=$(date +'%Y.%m.%d')
TAG_COUNT=$(git tag -l "${DATE}.*" | wc -l)
BUILD_NUMBER=$(($TAG_COUNT + 1))
VERSION_SUFFIX=${{ inputs.version_suffix }}
if [ -n "$VERSION_SUFFIX" ] && [ "$VERSION_SUFFIX" = "dev" ]; then
BUILD_VERSION="${DATE}.dev${BUILD_NUMBER}"
elif [ -n "$VERSION_SUFFIX" ]; then
BUILD_VERSION="${DATE}.${BUILD_NUMBER}${VERSION_SUFFIX}"
else
BUILD_VERSION="${DATE}.${BUILD_NUMBER}"
fi
echo "$BUILD_VERSION" > version.txt
echo "VERSION $BUILD_VERSION"
- name: Create Git Tag for Build
run: |
git config user.name "ci"
git config user.email "dev@sh-edraft.de"
echo "tag $(cat version.txt)"
git tag $(cat version.txt)
git push origin --tags
- name: Upload build version artifact
uses: actions/upload-artifact@v3
with:
name: version
path: version.txt

View File

@@ -0,0 +1,26 @@
name: Test before pr merge
run-name: Test before pr merge
on:
pull_request:
types:
- opened
- edited
- reopened
- synchronize
- ready_for_review
jobs:
test-lint:
runs-on: [ runner ]
container: git.sh-edraft.de/sh-edraft.de/act-runner:latest
steps:
- name: Clone Repository
uses: https://github.com/actions/checkout@v3
with:
token: ${{ secrets.CI_ACCESS_TOKEN }}
- name: Installing black
run: python3.12 -m pip install black
- name: Checking black
run: python3.12 -m black src --check

13
.gitignore vendored
View File

@@ -106,10 +106,15 @@ celerybeat.pid
.venv
env/
venv/
venv_*/
ENV/
env.bak/
venv.bak/
# Custom Environments
cpl-env/
.secret
# Spyder project settings
.spyderproject
.spyproject
@@ -129,5 +134,11 @@ dmypy.json
.pyre/
# IDE
.vscode/
.idea/
PythonImportHelper-v2-Completion.json
# cpl unittest stuff
unittests/test_*_playground
# cpl logs
**/logs/*.jsonl

2
.pip.conf Normal file
View File

@@ -0,0 +1,2 @@
[global]
extra-index-url = https://git.sh-edraft.de/api/packages/sh-edraft.de/pypi/simple/

View File

@@ -1,4 +1,4 @@
MIT License Copyright (c) <year> <copyright holders>
MIT License Copyright (c) 2020 - 2023 sh-edraft.de
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -1,2 +0,0 @@
# sh_common_py_lib

View File

@@ -1,56 +0,0 @@
{
"ProjectSettings": {
"Name": "sh_cpl",
"Version": {
"Major": "2021",
"Minor": "04",
"Micro": "01-15"
},
"Author": "Sven Heidemann",
"AuthorEmail": "sven.heidemann@sh-edraft.de",
"Description": "sh-edraft Common Python library",
"LongDescription": "sh-edraft Common Python library",
"URL": "https://www.sh-edraft.de",
"CopyrightDate": "2020 - 2021",
"CopyrightName": "sh-edraft.de",
"LicenseName": "MIT",
"LicenseDescription": "MIT, see LICENSE for more details.",
"Dependencies": [
"colorama==0.4.4",
"mysql-connector==2.2.9",
"psutil==5.8.0",
"packaging==20.9",
"pyfiglet==0.8.post1",
"pynput==1.7.3",
"SQLAlchemy==1.4.0",
"setuptools==54.1.2",
"tabulate==0.8.9",
"termcolor==1.1.0",
"watchdog==2.0.2",
"wheel==0.36.2"
],
"PythonVersion": ">=3.8",
"PythonPath": {},
"Classifiers": []
},
"BuildSettings": {
"SourcePath": "src",
"OutputPath": "dist",
"Main": "cpl_cli.main",
"EntryPoint": "cpl",
"IncludePackageData": true,
"Included": [
"*/templates"
],
"Excluded": [
"*/__pycache__",
"*/logs",
"*/tests"
],
"PackageData": {
"cpl_cli": [
"*.json"
]
}
}
}

View File

@@ -1,19 +0,0 @@
prefix: cpl
commands:
build
generate:
abc | a
class | c
configmodel | cm
enum | e
service | s
help
new
console
start
publish
update
version

View File

@@ -1,2 +0,0 @@
python setup.py install # for install
python setup.py sdist bdist_wheel # for build

View File

@@ -1,11 +0,0 @@
upload:
prod:
twine upload --repository-url https://pip.sh-edraft.de dist/publish/setup/*
twine upload -r pip.sh-edraft.de dist/publish/setup/*
dev:
twine upload --repository-url https://pip-dev.sh-edraft.de dist/publish/setup/*
twine upload -r pip-dev.sh-edraft.de dist/publish/setup/*
install:
pip install --extra-index-url https://pip.sh-edraft.de/ sh_cpl

61
install.sh Normal file
View File

@@ -0,0 +1,61 @@
#!/usr/bin/env bash
set -euo pipefail
# Find and combine requirements from src/cpl-*/requirements.txt,
# filtering out lines whose *package name* starts with "cpl-".
# Works with pinned versions, extras, markers, editable installs, and VCS refs.
shopt -s nullglob
req_files=(src/cpl-*/requirements.txt)
if ((${#req_files[@]} == 0)); then
echo "No requirements files found at src/cpl-*/requirements.txt" >&2
exit 1
fi
tmp_combined="$(mktemp)"
trap 'rm -f "$tmp_combined"' EXIT
# Concatenate, trim comments/whitespace, filter out cpl-* packages, dedupe.
# We keep non-package options/flags/constraints as-is.
awk '
function trim(s){ sub(/^[[:space:]]+/,"",s); sub(/[[:space:]]+$/,"",s); return s }
{
line=$0
# drop full-line comments and strip inline comments
if (line ~ /^[[:space:]]*#/) next
sub(/#[^!].*$/,"",line) # strip trailing comment (simple heuristic)
line=trim(line)
if (line == "") next
# Determine the package *name* even for "-e", extras, pins, markers, or VCS "@"
e = line
sub(/^-e[[:space:]]+/,"",e) # remove editable prefix
# Tokenize up to the first of these separators: space, [ < > = ! ~ ; @
token = e
sub(/\[.*/,"",token) # remove extras quickly
n = split(token, a, /[<>=!~;@[:space:]]/)
name = tolower(a[1])
# If the first token (name) starts with "cpl-", skip this requirement
if (name ~ /^cpl-/) next
print line
}
' "${req_files[@]}" | sort -u > "$tmp_combined"
if ! [ -s "$tmp_combined" ]; then
echo "Nothing to install after filtering out cpl-* packages." >&2
exit 0
fi
echo "Installing dependencies (excluding cpl-*) from:"
printf ' - %s\n' "${req_files[@]}"
echo
echo "Final set to install:"
cat "$tmp_combined"
echo
# Use python -m pip for reliability; change to python3 if needed.
python -m pip install -r "$tmp_combined"

2
pyproject.toml Normal file
View File

@@ -0,0 +1,2 @@
[tool.black]
line-length = 120

View File

@@ -0,0 +1,36 @@
from cpl.dependency.service_collection import ServiceCollection as _ServiceCollection
from .error import APIError, AlreadyExists, EndpointNotImplemented, Forbidden, NotFound, Unauthorized
from .logger import APILogger
from .settings import ApiSettings
def add_api(collection: _ServiceCollection):
try:
from cpl.database import mysql
collection.add_module(mysql)
except ImportError as e:
from cpl.core.errors import dependency_error
dependency_error("cpl-database", e)
try:
from cpl import auth
from cpl.auth import permission
collection.add_module(auth)
collection.add_module(permission)
except ImportError as e:
from cpl.core.errors import dependency_error
dependency_error("cpl-auth", e)
from cpl.api.registry.policy import PolicyRegistry
from cpl.api.registry.route import RouteRegistry
collection.add_singleton(PolicyRegistry)
collection.add_singleton(RouteRegistry)
_ServiceCollection.with_module(add_api, __name__)

View File

@@ -0,0 +1 @@
from .asgi_middleware_abc import ASGIMiddleware

View File

@@ -0,0 +1,15 @@
from abc import ABC, abstractmethod
from starlette.types import Scope, Receive, Send
class ASGIMiddleware(ABC):
@abstractmethod
def __init__(self, app):
self._app = app
def _call_next(self, scope: Scope, receive: Receive, send: Send):
return self._app(scope, receive, send)
@abstractmethod
async def __call__(self, scope: Scope, receive: Receive, send: Send): ...

View File

@@ -0,0 +1 @@
from .web_app import WebApp

View File

@@ -0,0 +1,249 @@
import os
from enum import Enum
from typing import Mapping, Any, Callable, Self, Union
import uvicorn
from starlette.applications import Starlette
from starlette.middleware import Middleware
from starlette.middleware.cors import CORSMiddleware
from starlette.requests import Request
from starlette.responses import JSONResponse
from starlette.types import ExceptionHandler
from cpl import api, auth
from cpl.api.error import APIError
from cpl.api.logger import APILogger
from cpl.api.middleware.authentication import AuthenticationMiddleware
from cpl.api.middleware.authorization import AuthorizationMiddleware
from cpl.api.middleware.logging import LoggingMiddleware
from cpl.api.middleware.request import RequestMiddleware
from cpl.api.model.api_route import ApiRoute
from cpl.api.model.policy import Policy
from cpl.api.model.validation_match import ValidationMatch
from cpl.api.registry.policy import PolicyRegistry
from cpl.api.registry.route import RouteRegistry
from cpl.api.router import Router
from cpl.api.settings import ApiSettings
from cpl.api.typing import HTTPMethods, PartialMiddleware, PolicyResolver
from cpl.application.abc.application_abc import ApplicationABC
from cpl.core.configuration import Configuration
from cpl.dependency.service_provider_abc import ServiceProviderABC
PolicyInput = Union[dict[str, PolicyResolver], Policy]
class WebApp(ApplicationABC):
def __init__(self, services: ServiceProviderABC):
super().__init__(services, [auth, api])
self._app: Starlette | None = None
self._logger = services.get_service(APILogger)
self._api_settings = Configuration.get(ApiSettings)
self._policies = services.get_service(PolicyRegistry)
self._routes = services.get_service(RouteRegistry)
self._middleware: list[Middleware] = [
Middleware(RequestMiddleware),
Middleware(LoggingMiddleware),
]
self._exception_handlers: Mapping[Any, ExceptionHandler] = {
Exception: self._handle_exception,
APIError: self._handle_exception,
}
async def _handle_exception(self, request: Request, exc: Exception):
if isinstance(exc, APIError):
self._logger.error(exc)
return JSONResponse({"error": str(exc)}, status_code=exc.status_code)
if hasattr(request.state, "request_id"):
self._logger.error(f"Request {request.state.request_id}", exc)
else:
self._logger.error("Request unknown", exc)
return JSONResponse({"error": str(exc)}, status_code=500)
def _get_allowed_origins(self):
origins = self._api_settings.allowed_origins
if origins is None or origins == "":
self._logger.warning("No allowed origins specified, allowing all origins")
return ["*"]
self._logger.debug(f"Allowed origins: {origins}")
return origins.split(",")
def with_database(self) -> Self:
self.with_migrations()
self.with_seeders()
return self
def with_app(self, app: Starlette) -> Self:
assert app is not None, "app must not be None"
assert isinstance(app, Starlette), "app must be an instance of Starlette"
self._app = app
return self
def _check_for_app(self):
if self._app is not None:
raise ValueError("App is already set, cannot add routes or middleware")
def with_routes_directory(self, directory: str) -> Self:
self._check_for_app()
assert directory is not None, "directory must not be None"
base = directory.replace("/", ".").replace("\\", ".")
for filename in os.listdir(directory):
if not filename.endswith(".py") or filename == "__init__.py":
continue
__import__(f"{base}.{filename[:-3]}")
return self
def with_routes(
self,
routes: list[ApiRoute],
method: HTTPMethods,
authentication: bool = False,
roles: list[str | Enum] = None,
permissions: list[str | Enum] = None,
policies: list[str] = None,
match: ValidationMatch = None,
) -> Self:
self._check_for_app()
assert self._routes is not None, "routes must not be None"
assert all(isinstance(route, ApiRoute) for route in routes), "all routes must be of type ApiRoute"
for route in routes:
self.with_route(
route.path,
route.fn,
method,
authentication,
roles,
permissions,
policies,
match,
)
return self
def with_route(
self,
path: str,
fn: Callable[[Request], Any],
method: HTTPMethods,
authentication: bool = False,
roles: list[str | Enum] = None,
permissions: list[str | Enum] = None,
policies: list[str] = None,
match: ValidationMatch = None,
) -> Self:
self._check_for_app()
assert path is not None, "path must not be None"
assert fn is not None, "fn must not be None"
assert method in [
"GET",
"HEAD",
"POST",
"PUT",
"PATCH",
"DELETE",
"OPTIONS",
], "method must be a valid HTTP method"
Router.route(path, method, registry=self._routes)(fn)
if authentication:
Router.authenticate()(fn)
if roles or permissions or policies:
Router.authorize(roles, permissions, policies, match)(fn)
return self
def with_middleware(self, middleware: PartialMiddleware) -> Self:
self._check_for_app()
if isinstance(middleware, Middleware):
self._middleware.append(middleware)
elif callable(middleware):
self._middleware.append(Middleware(middleware))
else:
raise ValueError("middleware must be of type starlette.middleware.Middleware or a callable")
return self
def with_authentication(self) -> Self:
self.with_middleware(AuthenticationMiddleware)
return self
def with_authorization(self, *policies: list[PolicyInput] | PolicyInput) -> Self:
if policies:
_policies = []
if not isinstance(policies, list):
policies = list(policies)
for i, policy in enumerate(policies):
if isinstance(policy, dict):
for name, resolver in policy.items():
if not isinstance(name, str):
self._logger.warning(f"Skipping policy at index {i}, name must be a string")
continue
if not callable(resolver):
self._logger.warning(f"Skipping policy {name}, resolver must be callable")
continue
_policies.append(Policy(name, resolver))
continue
_policies.append(policy)
self._policies.extend(_policies)
self.with_middleware(AuthorizationMiddleware)
return self
def _validate_policies(self):
for rule in Router.get_authorization_rules():
for policy_name in rule["policies"]:
policy = self._policies.get(policy_name)
if not policy:
self._logger.fatal(f"Authorization policy '{policy_name}' not found")
async def main(self):
self._logger.debug(f"Preparing API")
self._validate_policies()
if self._app is None:
routes = [route.to_starlette(self._services.inject) for route in self._routes.all()]
app = Starlette(
routes=routes,
middleware=[
*self._middleware,
Middleware(
CORSMiddleware,
allow_origins=self._get_allowed_origins(),
allow_methods=["*"],
allow_headers=["*"],
),
],
exception_handlers=self._exception_handlers,
)
else:
app = self._app
self._logger.info(f"Start API on {self._api_settings.host}:{self._api_settings.port}")
config = uvicorn.Config(
app, host=self._api_settings.host, port=self._api_settings.port, log_config=None, loop="asyncio"
)
server = uvicorn.Server(config)
await server.serve()
self._logger.info("Shutdown API")

View File

@@ -0,0 +1,46 @@
from http.client import HTTPException
from starlette.responses import JSONResponse
from starlette.types import Scope, Receive, Send
class APIError(HTTPException):
status_code = 500
def __init__(self, message: str = ""):
super().__init__(self.status_code, message)
self._message = message
@property
def error_message(self) -> str:
if self._message:
return f"{type(self).__name__}: {self._message}"
return f"{type(self).__name__}"
async def asgi_response(self, scope: Scope, receive: Receive, send: Send):
r = JSONResponse({"error": self.error_message}, status_code=self.status_code)
return await r(scope, receive, send)
def response(self):
return JSONResponse({"error": self.error_message}, status_code=self.status_code)
class Unauthorized(APIError):
status_code = 401
class Forbidden(APIError):
status_code = 403
class NotFound(APIError):
status_code = 404
class AlreadyExists(APIError):
status_code = 409
class EndpointNotImplemented(APIError):
status_code = 501

View File

@@ -0,0 +1,7 @@
from cpl.core.log.wrapped_logger import WrappedLogger
class APILogger(WrappedLogger):
def __init__(self):
WrappedLogger.__init__(self, "api")

View File

@@ -0,0 +1,4 @@
from .authentication import AuthenticationMiddleware
from .authorization import AuthorizationMiddleware
from .logging import LoggingMiddleware
from .request import RequestMiddleware

View File

@@ -0,0 +1,80 @@
from keycloak import KeycloakAuthenticationError
from starlette.types import Scope, Receive, Send
from cpl.api.abc.asgi_middleware_abc import ASGIMiddleware
from cpl.api.error import Unauthorized
from cpl.api.logger import APILogger
from cpl.api.middleware.request import get_request
from cpl.api.router import Router
from cpl.auth.keycloak import KeycloakClient
from cpl.auth.schema import AuthUserDao, AuthUser
from cpl.core.ctx import set_user
from cpl.dependency import ServiceProviderABC
class AuthenticationMiddleware(ASGIMiddleware):
@ServiceProviderABC.inject
def __init__(self, app, logger: APILogger, keycloak: KeycloakClient, user_dao: AuthUserDao):
ASGIMiddleware.__init__(self, app)
self._logger = logger
self._keycloak = keycloak
self._user_dao = user_dao
async def __call__(self, scope: Scope, receive: Receive, send: Send):
request = get_request()
url = request.url.path
if url not in Router.get_auth_required_routes():
self._logger.trace(f"No authentication required for {url}")
return await self._app(scope, receive, send)
if not request.headers.get("Authorization"):
self._logger.debug(f"Unauthorized access to {url}, missing Authorization header")
return await Unauthorized(f"Missing header Authorization").asgi_response(scope, receive, send)
auth_header = request.headers.get("Authorization", None)
if not auth_header or not auth_header.startswith("Bearer "):
return await Unauthorized("Invalid Authorization header").asgi_response(scope, receive, send)
token = auth_header.split("Bearer ")[1]
if not await self._verify_login(token):
self._logger.debug(f"Unauthorized access to {url}, invalid token")
return await Unauthorized("Invalid token").asgi_response(scope, receive, send)
# check user exists in db, if not create
keycloak_id = self._keycloak.get_user_id(token)
if keycloak_id is None:
return await Unauthorized("Failed to get user id from token").asgi_response(scope, receive, send)
user = await self._get_or_crate_user(keycloak_id)
if user.deleted:
self._logger.debug(f"Unauthorized access to {url}, user is deleted")
return await Unauthorized("User is deleted").asgi_response(scope, receive, send)
request.state.user = user
set_user(user)
return await self._call_next(scope, receive, send)
async def _get_or_crate_user(self, keycloak_id: str) -> AuthUser:
existing = await self._user_dao.find_by_keycloak_id(keycloak_id)
if existing is not None:
return existing
user = AuthUser(0, keycloak_id)
uid = await self._user_dao.create(user)
return await self._user_dao.get_by_id(uid)
async def _verify_login(self, token: str) -> bool:
try:
token_info = self._keycloak.introspect(token)
return token_info.get("active", False)
except KeycloakAuthenticationError as e:
self._logger.debug(f"Keycloak authentication error: {e}")
return False
except Exception as e:
self._logger.error(f"Unexpected error during token verification: {e}")
return False

View File

@@ -0,0 +1,73 @@
from starlette.types import Scope, Receive, Send
from cpl.api.abc.asgi_middleware_abc import ASGIMiddleware
from cpl.api.error import Unauthorized, Forbidden
from cpl.api.logger import APILogger
from cpl.api.middleware.request import get_request
from cpl.api.model.validation_match import ValidationMatch
from cpl.api.registry.policy import PolicyRegistry
from cpl.api.router import Router
from cpl.auth.schema._administration.auth_user_dao import AuthUserDao
from cpl.core.ctx.user_context import get_user
from cpl.dependency.service_provider_abc import ServiceProviderABC
class AuthorizationMiddleware(ASGIMiddleware):
@ServiceProviderABC.inject
def __init__(self, app, logger: APILogger, policies: PolicyRegistry, user_dao: AuthUserDao):
ASGIMiddleware.__init__(self, app)
self._logger = logger
self._policies = policies
self._user_dao = user_dao
async def __call__(self, scope: Scope, receive: Receive, send: Send):
request = get_request()
url = request.url.path
if url not in Router.get_authorization_rules_paths():
self._logger.trace(f"No authorization required for {url}")
return await self._app(scope, receive, send)
user = get_user()
if not user:
return await Unauthorized(f"Unknown user").asgi_response(scope, receive, send)
roles = await user.roles
request.state.roles = roles
role_names = [r.name for r in roles]
perms = await user.permissions
request.state.permissions = perms
perm_names = [p.name for p in perms]
for rule in Router.get_authorization_rules():
match = rule["match"]
if rule["roles"]:
if match == ValidationMatch.all and not all(r in role_names for r in rule["roles"]):
return await Forbidden(f"missing roles: {rule["roles"]}").asgi_response(scope, receive, send)
if match == ValidationMatch.any and not any(r in role_names for r in rule["roles"]):
return await Forbidden(f"missing roles: {rule["roles"]}").asgi_response(scope, receive, send)
if rule["permissions"]:
if match == ValidationMatch.all and not all(p in perm_names for p in rule["permissions"]):
return await Forbidden(f"missing permissions: {rule["permissions"]}").asgi_response(
scope, receive, send
)
if match == ValidationMatch.any and not any(p in perm_names for p in rule["permissions"]):
return await Forbidden(f"missing permissions: {rule["permissions"]}").asgi_response(
scope, receive, send
)
for policy_name in rule["policies"]:
policy = self._policies.get(policy_name)
if not policy:
self._logger.warning(f"Authorization policy '{policy_name}' not found")
continue
if not await policy.resolve(user):
return await Forbidden(f"policy {policy.name} failed").asgi_response(scope, receive, send)
return await self._call_next(scope, receive, send)

View File

@@ -0,0 +1,87 @@
import time
from starlette.requests import Request
from starlette.types import Receive, Scope, Send
from cpl.api.abc.asgi_middleware_abc import ASGIMiddleware
from cpl.api.logger import APILogger
from cpl.api.middleware.request import get_request
from cpl.dependency import ServiceProviderABC
class LoggingMiddleware(ASGIMiddleware):
@ServiceProviderABC.inject
def __init__(self, app, logger: APILogger):
ASGIMiddleware.__init__(self, app)
self._logger = logger
async def __call__(self, scope: Scope, receive: Receive, send: Send):
if scope["type"] != "http":
await self._call_next(scope, receive, send)
return
request = get_request()
await self._log_request(request)
start_time = time.time()
response_body = b""
status_code = 500
async def send_wrapper(message):
nonlocal response_body, status_code
if message["type"] == "http.response.start":
status_code = message["status"]
if message["type"] == "http.response.body":
response_body += message.get("body", b"")
await send(message)
await self._call_next(scope, receive, send_wrapper)
duration = (time.time() - start_time) * 1000
await self._log_after_request(request, status_code, duration)
@staticmethod
def _filter_relevant_headers(headers: dict) -> dict:
relevant_keys = {
"content-type",
"host",
"connection",
"user-agent",
"origin",
"referer",
"accept",
}
return {key: value for key, value in headers.items() if key in relevant_keys}
async def _log_request(self, request: Request):
self._logger.debug(
f"Request {getattr(request.state, 'request_id', '-')}: {request.method}@{request.url.path} from {request.client.host}"
)
from cpl.core.ctx.user_context import get_user
user = get_user()
request_info = {
"headers": self._filter_relevant_headers(dict(request.headers)),
"args": dict(request.query_params),
"form-data": (
await request.form()
if request.headers.get("content-type") == "application/x-www-form-urlencoded"
else None
),
"payload": (await request.json() if request.headers.get("content-length") == "0" else None),
"user": f"{user.id}-{user.keycloak_id}" if user else None,
"files": (
{key: file.filename for key, file in (await request.form()).items()} if await request.form() else None
),
}
self._logger.trace(f"Request {getattr(request.state, 'request_id', '-')}: {request_info}")
async def _log_after_request(self, request: Request, status_code: int, duration: float):
self._logger.info(
f"Request finished {getattr(request.state, 'request_id', '-')}: {status_code}-{request.method}@{request.url.path} from {request.client.host} in {duration:.2f}ms"
)

View File

@@ -0,0 +1,56 @@
import time
from contextvars import ContextVar
from typing import Optional, Union
from uuid import uuid4
from starlette.requests import Request
from starlette.types import Scope, Receive, Send
from cpl.api.abc.asgi_middleware_abc import ASGIMiddleware
from cpl.api.logger import APILogger
from cpl.api.typing import TRequest
from cpl.dependency import ServiceProviderABC
_request_context: ContextVar[Union[TRequest, None]] = ContextVar("request", default=None)
class RequestMiddleware(ASGIMiddleware):
@ServiceProviderABC.inject
def __init__(self, app, logger: APILogger):
ASGIMiddleware.__init__(self, app)
self._logger = logger
self._ctx_token = None
async def __call__(self, scope: Scope, receive: Receive, send: Send):
request = Request(scope, receive, send)
await self.set_request_data(request)
try:
await self._app(scope, receive, send)
finally:
await self.clean_request_data()
async def set_request_data(self, request: TRequest):
request.state.request_id = uuid4()
request.state.start_time = time.time()
self._logger.trace(f"Set new current request: {request.state.request_id}")
self._ctx_token = _request_context.set(request)
async def clean_request_data(self):
request = get_request()
if request is None:
return
if self._ctx_token is None:
return
self._logger.trace(f"Clearing current request: {request.state.request_id}")
_request_context.reset(self._ctx_token)
def get_request() -> Optional[TRequest]:
return _request_context.get()

View File

@@ -0,0 +1,3 @@
from .api_route import ApiRoute
from .policy import Policy
from .validation_match import ValidationMatch

View File

@@ -0,0 +1,43 @@
from typing import Callable
from starlette.routing import Route
from cpl.api.typing import HTTPMethods
class ApiRoute:
def __init__(self, path: str, fn: Callable, method: HTTPMethods, **kwargs):
self._path = path
self._fn = fn
self._method = method
self._kwargs = kwargs
@property
def name(self) -> str:
return self._fn.__name__
@property
def fn(self) -> Callable:
return self._fn
@property
def path(self) -> str:
return self._path
@property
def method(self) -> HTTPMethods:
return self._method
@property
def kwargs(self) -> dict:
return self._kwargs
def to_starlette(self, wrap_endpoint: Callable = None) -> Route:
return Route(
self._path,
self._fn if not wrap_endpoint else wrap_endpoint(self._fn),
methods=[self._method],
**self._kwargs,
)

View File

@@ -0,0 +1,34 @@
from asyncio import iscoroutinefunction
from typing import Optional
from cpl.api.typing import PolicyResolver
from cpl.core.ctx import get_user
class Policy:
def __init__(
self,
name: str,
resolver: PolicyResolver = None,
):
self._name = name
self._resolver: Optional[PolicyResolver] = resolver
@property
def name(self) -> str:
return self._name
@property
def resolvers(self) -> PolicyResolver:
return self._resolver
async def resolve(self, *args, **kwargs) -> bool:
if not self._resolver:
return True
if callable(self._resolver):
if iscoroutinefunction(self._resolver):
return await self._resolver(get_user())
return self._resolver(get_user())
return False

View File

@@ -0,0 +1,6 @@
from enum import Enum
class ValidationMatch(Enum):
any = "any"
all = "all"

View File

@@ -0,0 +1,2 @@
from .policy import PolicyRegistry
from .route import RouteRegistry

View File

@@ -0,0 +1,28 @@
from typing import Optional
from cpl.api.model.policy import Policy
from cpl.core.abc.registry_abc import RegistryABC
class PolicyRegistry(RegistryABC):
def __init__(self):
RegistryABC.__init__(self)
def extend(self, items: list[Policy]):
for policy in items:
self.add(policy)
def add(self, item: Policy):
assert isinstance(item, Policy), "policy must be an instance of Policy"
if item.name in self._items:
raise ValueError(f"Policy {item.name} is already registered")
self._items[item.name] = item
def get(self, key: str) -> Optional[Policy]:
return self._items.get(key)
def all(self) -> list[Policy]:
return list(self._items.values())

View File

@@ -0,0 +1,32 @@
from typing import Optional
from cpl.api.model.api_route import ApiRoute
from cpl.core.abc.registry_abc import RegistryABC
class RouteRegistry(RegistryABC):
def __init__(self):
RegistryABC.__init__(self)
def extend(self, items: list[ApiRoute]):
for policy in items:
self.add(policy)
def add(self, item: ApiRoute):
assert isinstance(item, ApiRoute), "route must be an instance of ApiRoute"
if item.path in self._items:
raise ValueError(f"ApiRoute {item.path} is already registered")
self._items[item.path] = item
def set(self, item: ApiRoute):
assert isinstance(item, ApiRoute), "route must be an instance of ApiRoute"
self._items[item.path] = item
def get(self, key: str) -> Optional[ApiRoute]:
return self._items.get(key)
def all(self) -> list[ApiRoute]:
return list(self._items.values())

View File

@@ -0,0 +1,164 @@
from enum import Enum
from cpl.api.model.validation_match import ValidationMatch
from cpl.api.registry.route import RouteRegistry
from cpl.api.typing import HTTPMethods
class Router:
_auth_required: list[str] = []
_authorization_rules: dict[str, dict] = {}
@classmethod
def get_auth_required_routes(cls) -> list[str]:
return cls._auth_required
@classmethod
def get_authorization_rules_paths(cls) -> list[str]:
return list(cls._authorization_rules.keys())
@classmethod
def get_authorization_rules(cls) -> list[dict]:
return list(cls._authorization_rules.values())
@classmethod
def authenticate(cls):
"""
Decorator to mark a route as requiring authentication.
Usage:
@Route.authenticate()
@Route.get("/example")
async def example_endpoint(request: TRequest):
...
"""
def inner(fn):
route_path = getattr(fn, "_route_path", None)
if route_path and route_path not in cls._auth_required:
cls._auth_required.append(route_path)
return fn
return inner
@classmethod
def authorize(
cls,
roles: list[str | Enum] = None,
permissions: list[str | Enum] = None,
policies: list[str] = None,
match: ValidationMatch = None,
):
"""
Decorator to mark a route as requiring authorization.
Usage:
@Route.authorize()
@Route.get("/example")
async def example_endpoint(request: TRequest):
...
"""
assert roles is None or isinstance(roles, list), "roles must be a list of strings"
assert permissions is None or isinstance(permissions, list), "permissions must be a list of strings"
assert policies is None or isinstance(policies, list), "policies must be a list of strings"
assert match is None or isinstance(match, ValidationMatch), "match must be an instance of ValidationMatch"
if roles is not None:
for role in roles:
if isinstance(role, Enum):
roles[roles.index(role)] = role.value
if permissions is not None:
for perm in permissions:
if isinstance(perm, Enum):
permissions[permissions.index(perm)] = perm.value
def inner(fn):
path = getattr(fn, "_route_path", None)
if not path:
return fn
if path in cls._authorization_rules:
raise ValueError(f"Route {path} is already registered for authorization")
cls._authorization_rules[path] = {
"roles": roles or [],
"permissions": permissions or [],
"policies": policies or [],
"match": match or ValidationMatch.all,
}
return fn
return inner
@classmethod
def route(cls, path: str, method: HTTPMethods, registry: RouteRegistry = None, **kwargs):
from cpl.api.model.api_route import ApiRoute
if not registry:
from cpl.dependency.service_provider_abc import ServiceProviderABC
routes = ServiceProviderABC.get_global_service(RouteRegistry)
else:
routes = registry
def inner(fn):
routes.add(ApiRoute(path, fn, method, **kwargs))
setattr(fn, "_route_path", path)
return fn
return inner
@classmethod
def get(cls, path: str, **kwargs):
return cls.route(path, "GET", **kwargs)
@classmethod
def head(cls, path: str, **kwargs):
return cls.route(path, "HEAD", **kwargs)
@classmethod
def post(cls, path: str, **kwargs):
return cls.route(path, "POST", **kwargs)
@classmethod
def put(cls, path: str, **kwargs):
return cls.route(path, "PUT", **kwargs)
@classmethod
def patch(cls, path: str, **kwargs):
return cls.route(path, "PATCH", **kwargs)
@classmethod
def delete(cls, path: str, **kwargs):
return cls.route(path, "DELETE", **kwargs)
@classmethod
def override(cls):
"""
Decorator to override an existing route with the same path.
Usage:
@Route.override()
@Route.get("/example")
async def example_endpoint(request: TRequest):
...
"""
from cpl.api.model.api_route import ApiRoute
from cpl.dependency.service_provider_abc import ServiceProviderABC
routes = ServiceProviderABC.get_global_service(RouteRegistry)
def inner(fn):
path = getattr(fn, "_route_path", None)
if path is None:
raise ValueError("Cannot override a route that has not been registered yet")
route = routes.get(path)
if route is None:
raise ValueError(f"Cannot override a route that does not exist: {path}")
routes.add(ApiRoute(path, fn, route.method, **route.kwargs))
setattr(fn, "_route_path", path)
return fn
return inner

View File

@@ -0,0 +1,13 @@
from typing import Optional
from cpl.core.configuration import ConfigurationModelABC
class ApiSettings(ConfigurationModelABC):
def __init__(self, src: Optional[dict] = None):
super().__init__(src)
self.option("host", str, "0.0.0.0")
self.option("port", int, 5000)
self.option("allowed_origins", list[str])

View File

@@ -0,0 +1,19 @@
from typing import Union, Literal, Callable, Type, Awaitable
from urllib.request import Request
from starlette.middleware import Middleware
from starlette.types import ASGIApp
from starlette.websockets import WebSocket
from cpl.api.abc.asgi_middleware_abc import ASGIMiddleware
from cpl.auth.schema import AuthUser
TRequest = Union[Request, WebSocket]
HTTPMethods = Literal["GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"]
PartialMiddleware = Union[
ASGIMiddleware,
Type[ASGIMiddleware],
Middleware,
Callable[[ASGIApp], ASGIApp],
]
PolicyResolver = Callable[[AuthUser], bool | Awaitable[bool]]

View File

@@ -0,0 +1,30 @@
[build-system]
requires = ["setuptools>=70.1.0", "wheel>=0.43.0"]
build-backend = "setuptools.build_meta"
[project]
name = "cpl-api"
version = "2024.7.0"
description = "CPL api"
readme ="CPL api package"
requires-python = ">=3.12"
license = { text = "MIT" }
authors = [
{ name = "Sven Heidemann", email = "sven.heidemann@sh-edraft.de" }
]
keywords = ["cpl", "api", "backend", "shared", "library"]
dynamic = ["dependencies", "optional-dependencies"]
[project.urls]
Homepage = "https://www.sh-edraft.de"
[tool.setuptools.packages.find]
where = ["."]
include = ["cpl*"]
[tool.setuptools.dynamic]
dependencies = { file = ["requirements.txt"] }
optional-dependencies.dev = { file = ["requirements.dev.txt"] }

View File

@@ -0,0 +1 @@
black==25.1.0

View File

@@ -0,0 +1,7 @@
cpl-auth
cpl-application
cpl-core
cpl-dependency
starlette==0.48.0
python-multipart==0.0.20
uvicorn==0.35.0

View File

@@ -0,0 +1 @@
from .application_builder import ApplicationBuilder

View File

@@ -0,0 +1,4 @@
from .application_abc import ApplicationABC
from .application_extension_abc import ApplicationExtensionABC
from .startup_abc import StartupABC
from .startup_extension_abc import StartupExtensionABC

View File

@@ -0,0 +1,92 @@
from abc import ABC, abstractmethod
from typing import Callable, Self
from cpl.application.host import Host
from cpl.core.log.log_level import LogLevel
from cpl.core.log.log_settings import LogSettings
from cpl.core.log.logger_abc import LoggerABC
from cpl.dependency.service_provider_abc import ServiceProviderABC
def __not_implemented__(package: str, func: Callable):
raise NotImplementedError(f"Package {package} is required to use {func.__name__} method")
class ApplicationABC(ABC):
r"""ABC for the Application class
Parameters:
services: :class:`cpl.dependency.service_provider_abc.ServiceProviderABC`
Contains instances of prepared objects
"""
@abstractmethod
def __init__(self, services: ServiceProviderABC, required_modules: list[str | object] = None):
self._services = services
self._required_modules = (
[x.__name__ if not isinstance(x, str) else x for x in required_modules] if required_modules else []
)
@property
def required_modules(self) -> list[str]:
return self._required_modules
@classmethod
def extend(cls, name: str | Callable, func: Callable[[Self], Self]):
r"""Extend the Application with a custom method
Parameters:
name: :class:`str`
Name of the method
func: :class:`Callable[[Self], Self]`
Function that takes the Application as a parameter and returns it
"""
if callable(name):
name = name.__name__
setattr(cls, name, func)
return cls
def with_logging(self, level: LogLevel = None):
if level is None:
from cpl.core.configuration.configuration import Configuration
settings = Configuration.get(LogSettings)
level = settings.level if settings else LogLevel.info
logger = self._services.get_service(LoggerABC)
logger.set_level(level)
def with_permissions(self, *args, **kwargs):
__not_implemented__("cpl-auth", self.with_permissions)
def with_migrations(self, *args, **kwargs):
__not_implemented__("cpl-database", self.with_migrations)
def with_seeders(self, *args, **kwargs):
__not_implemented__("cpl-database", self.with_seeders)
def with_extension(self, func: Callable[[Self, ...], None], *args, **kwargs):
r"""Extend the Application with a custom method
Parameters:
func: :class:`Callable[[Self], Self]`
Function that takes the Application as a parameter and returns it
"""
assert func is not None, "func must not be None"
assert callable(func), "func must be callable"
func(self, *args, **kwargs)
def run(self):
r"""Entry point
Called by custom Application.main
"""
try:
Host.run(self.main)
except KeyboardInterrupt:
pass
@abstractmethod
def main(self): ...

View File

@@ -0,0 +1,10 @@
from abc import ABC, abstractmethod
from cpl.dependency.service_provider_abc import ServiceProviderABC
class ApplicationExtensionABC(ABC):
@staticmethod
@abstractmethod
def run(services: ServiceProviderABC): ...

View File

@@ -0,0 +1,21 @@
from abc import ABC, abstractmethod
from cpl.dependency.service_collection import ServiceCollection
class StartupABC(ABC):
r"""ABC for the startup class"""
@staticmethod
@abstractmethod
def configure_configuration():
r"""Creates configuration of application"""
@staticmethod
@abstractmethod
def configure_services(service: ServiceCollection):
r"""Creates service provider
Parameter:
services: :class:`cpl.dependency.service_collection`
"""

View File

@@ -0,0 +1,20 @@
from abc import ABC, abstractmethod
from cpl.dependency import ServiceCollection
class StartupExtensionABC(ABC):
r"""ABC for startup extension classes"""
@staticmethod
@abstractmethod
def configure_configuration():
r"""Creates configuration of application"""
@staticmethod
@abstractmethod
def configure_services(services: ServiceCollection):
r"""Creates service provider
Parameter:
services: :class:`cpl.dependency.service_collection`
"""

View File

@@ -0,0 +1,80 @@
import asyncio
from typing import Type, Optional, TypeVar, Generic
from cpl.application.abc.application_abc import ApplicationABC
from cpl.application.abc.application_extension_abc import ApplicationExtensionABC
from cpl.application.abc.startup_abc import StartupABC
from cpl.application.abc.startup_extension_abc import StartupExtensionABC
from cpl.application.host import Host
from cpl.core.errors import dependency_error
from cpl.dependency.service_collection import ServiceCollection
TApp = TypeVar("TApp", bound=ApplicationABC)
class ApplicationBuilder(Generic[TApp]):
def __init__(self, app: Type[ApplicationABC]):
assert app is not None, "app must not be None"
assert issubclass(app, ApplicationABC), "app must be an subclass of ApplicationABC or its subclass"
self._app = app if app is not None else ApplicationABC
self._services = ServiceCollection()
self._startup: Optional[StartupABC] = None
self._app_extensions: list[Type[ApplicationExtensionABC]] = []
self._startup_extensions: list[Type[StartupExtensionABC]] = []
self._async_loop = asyncio.get_event_loop()
@property
def services(self) -> ServiceCollection:
return self._services
@property
def service_provider(self):
return self._services.build()
def validate_app_required_modules(self, app: ApplicationABC):
for module in app.required_modules:
if module in self._services.loaded_modules:
continue
dependency_error(
module,
ImportError(
f"Required module '{module}' for application '{app.__class__.__name__}' is not loaded. Load using 'add_module({module})' method."
),
)
def with_startup(self, startup: Type[StartupABC]) -> "ApplicationBuilder":
self._startup = startup
return self
def with_extension(
self,
extension: Type[ApplicationExtensionABC | StartupExtensionABC],
) -> "ApplicationBuilder":
if (issubclass(extension, ApplicationExtensionABC)) and extension not in self._app_extensions:
self._app_extensions.append(extension)
elif (issubclass(extension, StartupExtensionABC)) and extension not in self._startup_extensions:
self._startup_extensions.append(extension)
return self
def build(self) -> TApp:
for extension in self._startup_extensions:
Host.run(extension.configure_configuration)
Host.run(extension.configure_services, self._services)
if self._startup is not None:
Host.run(self._startup.configure_configuration)
Host.run(self._startup.configure_services, self._services)
for extension in self._app_extensions:
Host.run(extension.run, self.service_provider)
app = self._app(self.service_provider)
self.validate_app_required_modules(app)
return app

View File

@@ -0,0 +1,17 @@
import asyncio
from typing import Callable
class Host:
_loop = asyncio.get_event_loop()
@classmethod
def get_loop(cls):
return cls._loop
@classmethod
def run(cls, func: Callable, *args, **kwargs):
if asyncio.iscoroutinefunction(func):
return cls._loop.run_until_complete(func(*args, **kwargs))
return func(*args, **kwargs)

View File

@@ -0,0 +1,30 @@
[build-system]
requires = ["setuptools>=70.1.0", "wheel>=0.43.0"]
build-backend = "setuptools.build_meta"
[project]
name = "cpl-application"
version = "2024.7.0"
description = "CPL application"
readme ="CPL application package"
requires-python = ">=3.12"
license = { text = "MIT" }
authors = [
{ name = "Sven Heidemann", email = "sven.heidemann@sh-edraft.de" }
]
keywords = ["cpl", "application", "backend", "shared", "library"]
dynamic = ["dependencies", "optional-dependencies"]
[project.urls]
Homepage = "https://www.sh-edraft.de"
[tool.setuptools.packages.find]
where = ["."]
include = ["cpl*"]
[tool.setuptools.dynamic]
dependencies = { file = ["requirements.txt"] }
optional-dependencies.dev = { file = ["requirements.dev.txt"] }

View File

@@ -0,0 +1 @@
black==25.1.0

View File

@@ -0,0 +1,2 @@
cpl-core
cpl-dependency

View File

@@ -0,0 +1,84 @@
from enum import Enum
from typing import Type
from cpl.application.abc import ApplicationABC as _ApplicationABC
from cpl.auth import permission as _permission
from cpl.auth.keycloak.keycloak_admin import KeycloakAdmin as _KeycloakAdmin
from cpl.auth.keycloak.keycloak_client import KeycloakClient as _KeycloakClient
from cpl.dependency.service_collection import ServiceCollection as _ServiceCollection
from .logger import AuthLogger
from .keycloak_settings import KeycloakSettings
from .permission_seeder import PermissionSeeder
def _with_permissions(self: _ApplicationABC, *permissions: Type[Enum]) -> _ApplicationABC:
from cpl.auth.permission.permissions_registry import PermissionsRegistry
for perm in permissions:
PermissionsRegistry.with_enum(perm)
return self
def _add_daos(collection: _ServiceCollection):
from .schema._administration.auth_user_dao import AuthUserDao
from .schema._administration.api_key_dao import ApiKeyDao
from .schema._permission.api_key_permission_dao import ApiKeyPermissionDao
from .schema._permission.permission_dao import PermissionDao
from .schema._permission.role_dao import RoleDao
from .schema._permission.role_permission_dao import RolePermissionDao
from .schema._permission.role_user_dao import RoleUserDao
collection.add_singleton(AuthUserDao)
collection.add_singleton(ApiKeyDao)
collection.add_singleton(ApiKeyPermissionDao)
collection.add_singleton(PermissionDao)
collection.add_singleton(RoleDao)
collection.add_singleton(RolePermissionDao)
collection.add_singleton(RoleUserDao)
def add_auth(collection: _ServiceCollection):
import os
try:
from cpl.database.service.migration_service import MigrationService
from cpl.database.model.server_type import ServerType, ServerTypes
collection.add_singleton(_KeycloakClient)
collection.add_singleton(_KeycloakAdmin)
_add_daos(collection)
provider = collection.build()
migration_service: MigrationService = provider.get_service(MigrationService)
if ServerType.server_type == ServerTypes.POSTGRES:
migration_service.with_directory(
os.path.join(os.path.dirname(os.path.realpath(__file__)), "scripts/postgres")
)
elif ServerType.server_type == ServerTypes.MYSQL:
migration_service.with_directory(os.path.join(os.path.dirname(os.path.realpath(__file__)), "scripts/mysql"))
except ImportError as e:
from cpl.core.console import Console
Console.error("cpl-database is not installed", str(e))
def add_permission(collection: _ServiceCollection):
from .permission_seeder import PermissionSeeder
from .permission.permissions_registry import PermissionsRegistry
from .permission.permissions import Permissions
try:
from cpl.database.abc.data_seeder_abc import DataSeederABC
collection.add_singleton(DataSeederABC, PermissionSeeder)
PermissionsRegistry.with_enum(Permissions)
except ImportError as e:
from cpl.core.console import Console
Console.error("cpl-database is not installed", str(e))
_ServiceCollection.with_module(add_auth, __name__)
_ServiceCollection.with_module(add_permission, _permission.__name__)
_ApplicationABC.extend(_ApplicationABC.with_permissions, _with_permissions)

View File

@@ -0,0 +1,3 @@
from .keycloak_admin import KeycloakAdmin
from .keycloak_client import KeycloakClient
from .keycloak_user import KeycloakUser

View File

@@ -0,0 +1,22 @@
from keycloak import KeycloakAdmin as _KeycloakAdmin, KeycloakOpenIDConnection
from cpl.auth.keycloak_settings import KeycloakSettings
from cpl.auth.logger import AuthLogger
class KeycloakAdmin(_KeycloakAdmin):
def __init__(self, logger: AuthLogger, settings: KeycloakSettings):
# logger.info("Initializing Keycloak admin")
_connection = KeycloakOpenIDConnection(
server_url=settings.url,
client_id=settings.client_id,
realm_name=settings.realm,
client_secret_key=settings.client_secret,
)
_KeycloakAdmin.__init__(
self,
connection=_connection,
)
self.__connection = _connection

View File

@@ -0,0 +1,23 @@
from typing import Optional
from keycloak import KeycloakOpenID
from cpl.auth.logger import AuthLogger
from cpl.auth.keycloak_settings import KeycloakSettings
class KeycloakClient(KeycloakOpenID):
def __init__(self, logger: AuthLogger, settings: KeycloakSettings):
KeycloakOpenID.__init__(
self,
server_url=settings.url,
client_id=settings.client_id,
realm_name=settings.realm,
client_secret_key=settings.client_secret,
)
logger.info("Initializing Keycloak client")
def get_user_id(self, token: str) -> Optional[str]:
info = self.introspect(token)
return info.get("sub", None)

View File

@@ -0,0 +1,36 @@
from cpl.core.utils.get_value import get_value
from cpl.dependency import ServiceProviderABC
class KeycloakUser:
def __init__(self, source: dict):
self._username = get_value(source, "preferred_username", str)
self._email = get_value(source, "email", str)
self._email_verified = get_value(source, "email_verified", bool)
self._name = get_value(source, "name", str)
@property
def username(self) -> str:
return self._username
@property
def email(self) -> str:
return self._email
@property
def email_verified(self) -> bool:
return self._email_verified
@property
def name(self) -> str:
return self._name
# Attrs from keycloak
@property
def id(self) -> str:
from cpl.auth import KeycloakAdmin
keycloak_admin: KeycloakAdmin = ServiceProviderABC.get_global_service(KeycloakAdmin)
return keycloak_admin.get_user_id(self._username)

View File

@@ -0,0 +1,17 @@
from typing import Optional
from cpl.core.configuration.configuration_model_abc import ConfigurationModelABC
class KeycloakSettings(ConfigurationModelABC):
def __init__(
self,
src: Optional[dict] = None,
):
ConfigurationModelABC.__init__(self, src, "KEYCLOAK")
self.option("url", str, required=True)
self.option("client_id", str, required=True)
self.option("realm", str, required=True)
self.option("client_secret", str, required=True)

View File

@@ -0,0 +1,7 @@
from cpl.core.log.wrapped_logger import WrappedLogger
class AuthLogger(WrappedLogger):
def __init__(self):
WrappedLogger.__init__(self, "auth")

View File

@@ -0,0 +1,36 @@
from enum import Enum
class Permissions(Enum):
""" """
"""
Administration
"""
# administrator
administrator = "administrator"
# api keys
api_keys = "api_keys"
api_keys_create = "api_keys.create"
api_keys_update = "api_keys.update"
api_keys_delete = "api_keys.delete"
# users
users = "users"
users_create = "users.create"
users_update = "users.update"
users_delete = "users.delete"
# settings
settings = "settings"
settings_update = "settings.update"
"""
Permissions
"""
# roles
roles = "roles"
roles_create = "roles.create"
roles_update = "roles.update"
roles_delete = "roles.delete"

View File

@@ -0,0 +1,24 @@
from enum import Enum
from typing import Type
class PermissionsRegistry:
_permissions: dict[str, str] = {}
@classmethod
def get(cls):
return cls._permissions.keys()
@classmethod
def descriptions(cls):
return {x: cls._permissions[x] for x in cls._permissions if cls._permissions[x] is not None}
@classmethod
def set(cls, permission: str, description: str = None):
cls._permissions[permission] = description
@classmethod
def with_enum(cls, e: Type[Enum]):
perms = [x.value for x in e]
for perm in perms:
cls.set(str(perm))

View File

@@ -0,0 +1,120 @@
from cpl.auth.permission.permissions import Permissions
from cpl.auth.permission.permissions_registry import PermissionsRegistry
from cpl.auth.schema import (
Permission,
Role,
RolePermission,
ApiKey,
ApiKeyPermission,
PermissionDao,
RoleDao,
RolePermissionDao,
ApiKeyDao,
ApiKeyPermissionDao,
)
from cpl.core.utils.get_value import get_value
from cpl.database.abc.data_seeder_abc import DataSeederABC
from cpl.database.logger import DBLogger
class PermissionSeeder(DataSeederABC):
def __init__(
self,
logger: DBLogger,
permission_dao: PermissionDao,
role_dao: RoleDao,
role_permission_dao: RolePermissionDao,
api_key_dao: ApiKeyDao,
api_key_permission_dao: ApiKeyPermissionDao,
):
DataSeederABC.__init__(self)
self._logger = logger
self._permission_dao = permission_dao
self._role_dao = role_dao
self._role_permission_dao = role_permission_dao
self._api_key_dao = api_key_dao
self._api_key_permission_dao = api_key_permission_dao
async def seed(self):
permissions = await self._permission_dao.get_all()
possible_permissions = [permission for permission in PermissionsRegistry.get()]
if len(permissions) == len(possible_permissions):
self._logger.info("Permissions already existing")
await self._update_missing_descriptions()
return
to_delete = []
for permission in permissions:
if permission.name in possible_permissions:
continue
to_delete.append(permission)
await self._permission_dao.delete_many(to_delete, hard_delete=True)
self._logger.warning("Permissions incomplete")
permission_names = [permission.name for permission in permissions]
await self._permission_dao.create_many(
[
Permission(
0,
permission,
get_value(PermissionsRegistry.descriptions(), permission, str),
)
for permission in possible_permissions
if permission not in permission_names
]
)
await self._update_missing_descriptions()
await self._add_missing_to_role()
await self._add_missing_to_api_key()
async def _add_missing_to_role(self):
admin_role = await self._role_dao.find_single_by([{Role.id: 1}, {Role.name: "admin"}])
if admin_role is None:
return
admin_permissions = await self._role_permission_dao.get_by_role_id(admin_role.id, with_deleted=True)
to_assign = [
RolePermission(0, admin_role.id, permission.id)
for permission in await self._permission_dao.get_all()
if permission.id not in [x.permission_id for x in admin_permissions]
]
await self._role_permission_dao.create_many(to_assign)
async def _add_missing_to_api_key(self):
admin_api_key = await self._api_key_dao.find_single_by([{ApiKey.id: 1}, {ApiKey.identifier: "admin"}])
if admin_api_key is None:
return
admin_permissions = await self._api_key_permission_dao.find_by_api_key_id(admin_api_key.id, with_deleted=True)
to_assign = [
ApiKeyPermission(0, admin_api_key.id, permission.id)
for permission in await self._permission_dao.get_all()
if permission.id not in [x.permission_id for x in admin_permissions]
]
await self._api_key_permission_dao.create_many(to_assign)
async def _update_missing_descriptions(self):
permissions = {
permission.name: permission
for permission in await self._permission_dao.find_by([{Permission.description: None}])
}
to_update = []
if len(permissions) == 0:
return
for key in PermissionsRegistry.descriptions():
if key.value not in permissions:
continue
permissions[key.value].description = PermissionsRegistry.descriptions()[key]
to_update.append(permissions[key.value])
if len(to_update) == 0:
return
await self._permission_dao.update_many(to_update)

View File

@@ -0,0 +1,15 @@
from ._administration.api_key import ApiKey
from ._administration.api_key_dao import ApiKeyDao
from ._administration.auth_user import AuthUser
from ._administration.auth_user_dao import AuthUserDao
from ._permission.api_key_permission import ApiKeyPermission
from ._permission.api_key_permission_dao import ApiKeyPermissionDao
from ._permission.permission import Permission
from ._permission.permission_dao import PermissionDao
from ._permission.role import Role
from ._permission.role_dao import RoleDao
from ._permission.role_permission import RolePermission
from ._permission.role_permission_dao import RolePermissionDao
from ._permission.role_user import RoleUser
from ._permission.role_user_dao import RoleUserDao

View File

@@ -0,0 +1,66 @@
import secrets
from datetime import datetime
from typing import Optional, Union
from async_property import async_property
from cpl.auth.permission.permissions import Permissions
from cpl.core.environment.environment import Environment
from cpl.core.log.logger import Logger
from cpl.core.typing import Id, SerialId
from cpl.core.utils.credential_manager import CredentialManager
from cpl.database.abc.db_model_abc import DbModelABC
from cpl.dependency.service_provider_abc import ServiceProviderABC
_logger = Logger(__name__)
class ApiKey(DbModelABC):
def __init__(
self,
id: SerialId,
identifier: str,
key: Union[str, bytes],
deleted: bool = False,
editor_id: Optional[Id] = None,
created: Optional[datetime] = None,
updated: Optional[datetime] = None,
):
DbModelABC.__init__(self, id, deleted, editor_id, created, updated)
self._identifier = identifier
self._key = key
@property
def identifier(self) -> str:
return self._identifier
@property
def key(self) -> str:
return self._key
@property
def plain_key(self) -> str:
return CredentialManager.decrypt(self.key)
@async_property
async def permissions(self):
from cpl.auth.schema._permission.api_key_permission_dao import ApiKeyPermissionDao
apiKeyPermissionDao = ServiceProviderABC.get_global_provider().get_service(ApiKeyPermissionDao)
return [await x.permission for x in await apiKeyPermissionDao.find_by_api_key_id(self.id)]
async def has_permission(self, permission: Permissions) -> bool:
return permission.value in [x.name for x in await self.permissions]
def set_new_api_key(self):
self._key = self.new_key()
@staticmethod
def new_key() -> str:
return CredentialManager.encrypt(f"api_{secrets.token_urlsafe(Environment.get("API_KEY_LENGTH", int, 64))}")
@classmethod
def new(cls, identifier: str) -> "ApiKey":
return ApiKey(0, identifier, cls.new_key())

View File

@@ -0,0 +1,29 @@
from typing import Optional
from cpl.auth.schema._administration.api_key import ApiKey
from cpl.database import TableManager
from cpl.database.abc import DbModelDaoABC
class ApiKeyDao(DbModelDaoABC[ApiKey]):
def __init__(self):
DbModelDaoABC.__init__(self, ApiKey, TableManager.get("api_keys"))
self.attribute(ApiKey.identifier, str)
self.attribute(ApiKey.key, str, "keystring")
async def get_by_identifier(self, ident: str) -> ApiKey:
result = await self._db.select_map(f"SELECT * FROM {self._table_name} WHERE Identifier = '{ident}'")
return self.to_object(result[0])
async def get_by_key(self, key: str) -> ApiKey:
result = await self._db.select_map(f"SELECT * FROM {self._table_name} WHERE Keystring = '{key}'")
return self.to_object(result[0])
async def find_by_key(self, key: str) -> Optional[ApiKey]:
result = await self._db.select_map(f"SELECT * FROM {self._table_name} WHERE Keystring = '{key}'")
if not result or len(result) == 0:
return None
return self.to_object(result[0])

View File

@@ -0,0 +1,89 @@
import uuid
from datetime import datetime
from typing import Optional
from async_property import async_property
from keycloak import KeycloakGetError
from cpl.auth.keycloak import KeycloakAdmin
from cpl.auth.permission.permissions import Permissions
from cpl.core.typing import SerialId
from cpl.database.abc import DbModelABC
from cpl.database.logger import DBLogger
from cpl.dependency import ServiceProviderABC
class AuthUser(DbModelABC):
def __init__(
self,
id: SerialId,
keycloak_id: str,
deleted: bool = False,
editor_id: Optional[SerialId] = None,
created: Optional[datetime] = None,
updated: Optional[datetime] = None,
):
DbModelABC.__init__(self, id, deleted, editor_id, created, updated)
self._keycloak_id = keycloak_id
@property
def keycloak_id(self) -> str:
return self._keycloak_id
@property
def username(self):
if self._keycloak_id == str(uuid.UUID(int=0)):
return "ANONYMOUS"
try:
keycloak = ServiceProviderABC.get_global_service(KeycloakAdmin)
return keycloak.get_user(self._keycloak_id).get("username")
except KeycloakGetError as e:
return "UNKNOWN"
except Exception as e:
logger = ServiceProviderABC.get_global_service(DBLogger)
logger.error(f"Failed to get user {self._keycloak_id} from Keycloak", e)
return "UNKNOWN"
@property
def email(self):
if self._keycloak_id == str(uuid.UUID(int=0)):
return "ANONYMOUS"
try:
keycloak = ServiceProviderABC.get_global_service(KeycloakAdmin)
return keycloak.get_user(self._keycloak_id).get("email")
except KeycloakGetError as e:
return "UNKNOWN"
except Exception as e:
logger = ServiceProviderABC.get_global_service(DBLogger)
logger.error(f"Failed to get user {self._keycloak_id} from Keycloak", e)
return "UNKNOWN"
@async_property
async def roles(self):
from cpl.auth.schema._permission.role_user_dao import RoleUserDao
role_user_dao: RoleUserDao = ServiceProviderABC.get_global_service(RoleUserDao)
return [await x.role for x in await role_user_dao.get_by_user_id(self.id)]
@async_property
async def permissions(self):
from cpl.auth.schema._administration.auth_user_dao import AuthUserDao
auth_user_dao: AuthUserDao = ServiceProviderABC.get_global_service(AuthUserDao)
return await auth_user_dao.get_permissions(self.id)
async def has_permission(self, permission: Permissions) -> bool:
from cpl.auth.schema._administration.auth_user_dao import AuthUserDao
auth_user_dao: AuthUserDao = ServiceProviderABC.get_global_service(AuthUserDao)
return await auth_user_dao.has_permission(self.id, permission)
async def anonymize(self):
from cpl.auth.schema._administration.auth_user_dao import AuthUserDao
auth_user_dao: AuthUserDao = ServiceProviderABC.get_global_service(AuthUserDao)
self._keycloak_id = str(uuid.UUID(int=0))
await auth_user_dao.update(self)

View File

@@ -0,0 +1,69 @@
from typing import Optional, Union
from cpl.auth.permission.permissions import Permissions
from cpl.auth.schema._administration.auth_user import AuthUser
from cpl.database import TableManager
from cpl.database.abc import DbModelDaoABC
from cpl.database.external_data_temp_table_builder import ExternalDataTempTableBuilder
from cpl.dependency import ServiceProviderABC
class AuthUserDao(DbModelDaoABC[AuthUser]):
def __init__(self):
DbModelDaoABC.__init__(self, AuthUser, TableManager.get("auth_users"))
self.attribute(AuthUser.keycloak_id, str, db_name="keycloakId")
async def get_users():
return [(x.id, x.username, x.email) for x in await self.get_all()]
self.use_external_fields(
ExternalDataTempTableBuilder()
.with_table_name(self._table_name)
.with_field("id", "int", True)
.with_field("username", "text")
.with_field("email", "text")
.with_value_getter(get_users)
)
async def get_by_keycloak_id(self, keycloak_id: str) -> AuthUser:
return await self.get_single_by({AuthUser.keycloak_id: keycloak_id})
async def find_by_keycloak_id(self, keycloak_id: str) -> Optional[AuthUser]:
return await self.find_single_by({AuthUser.keycloak_id: keycloak_id})
async def has_permission(self, user_id: int, permission: Union[Permissions, str]) -> bool:
from cpl.auth.schema._permission.permission_dao import PermissionDao
permission_dao: PermissionDao = ServiceProviderABC.get_global_service(PermissionDao)
p = await permission_dao.get_by_name(permission if isinstance(permission, str) else permission.value)
result = await self._db.select_map(
f"""
SELECT COUNT(*) as count
FROM {TableManager.get("role_users")} ru
JOIN {TableManager.get("role_permissions")} rp ON ru.roleId = rp.roleId
WHERE ru.userId = {user_id}
AND rp.permissionId = {p.id}
AND ru.deleted = FALSE
AND rp.deleted = FALSE;
"""
)
if result is None or len(result) == 0:
return False
return result[0]["count"] > 0
async def get_permissions(self, user_id: int) -> list[Permissions]:
result = await self._db.select_map(
f"""
SELECT p.*
FROM {TableManager.get("permissions")} p
JOIN {TableManager.get("role_permissions")} rp ON p.id = rp.permissionId
JOIN {TableManager.get("role_users")} ru ON rp.roleId = ru.roleId
WHERE ru.userId = {user_id}
AND rp.deleted = FALSE
AND ru.deleted = FALSE;
"""
)
return [Permissions(p["name"]) for p in result]

View File

@@ -0,0 +1,46 @@
from datetime import datetime
from typing import Optional
from async_property import async_property
from cpl.core.typing import SerialId
from cpl.database.abc import DbJoinModelABC
from cpl.dependency import ServiceProviderABC
class ApiKeyPermission(DbJoinModelABC):
def __init__(
self,
id: SerialId,
api_key_id: SerialId,
permission_id: SerialId,
deleted: bool = False,
editor_id: Optional[SerialId] = None,
created: Optional[datetime] = None,
updated: Optional[datetime] = None,
):
DbJoinModelABC.__init__(self, api_key_id, permission_id, id, deleted, editor_id, created, updated)
self._api_key_id = api_key_id
self._permission_id = permission_id
@property
def api_key_id(self) -> int:
return self._api_key_id
@async_property
async def api_key(self):
from cpl.auth.schema._administration.api_key_dao import ApiKeyDao
api_key_dao: ApiKeyDao = ServiceProviderABC.get_global_service(ApiKeyDao)
return await api_key_dao.get_by_id(self._api_key_id)
@property
def permission_id(self) -> int:
return self._permission_id
@async_property
async def permission(self):
from cpl.auth.schema._permission.permission_dao import PermissionDao
permission_dao: PermissionDao = ServiceProviderABC.get_global_service(PermissionDao)
return await permission_dao.get_by_id(self._permission_id)

View File

@@ -0,0 +1,26 @@
from cpl.auth.schema._permission.api_key_permission import ApiKeyPermission
from cpl.database import TableManager
from cpl.database.abc import DbModelDaoABC
class ApiKeyPermissionDao(DbModelDaoABC[ApiKeyPermission]):
def __init__(self):
DbModelDaoABC.__init__(self, ApiKeyPermission, TableManager.get("api_key_permissions"))
self.attribute(ApiKeyPermission.api_key_id, int)
self.attribute(ApiKeyPermission.permission_id, int)
async def find_by_api_key_id(self, api_key_id: int, with_deleted=False) -> list[ApiKeyPermission]:
f = [{ApiKeyPermission.api_key_id: api_key_id}]
if not with_deleted:
f.append({ApiKeyPermission.deleted: False})
return await self.find_by(f)
async def find_by_permission_id(self, permission_id: int, with_deleted=False) -> list[ApiKeyPermission]:
f = [{ApiKeyPermission.permission_id: permission_id}]
if not with_deleted:
f.append({ApiKeyPermission.deleted: False})
return await self.find_by(f)

View File

@@ -0,0 +1,37 @@
from datetime import datetime
from typing import Optional
from cpl.core.typing import SerialId
from cpl.database.abc import DbModelABC
class Permission(DbModelABC):
def __init__(
self,
id: SerialId,
name: str,
description: str,
deleted: bool = False,
editor_id: Optional[SerialId] = None,
created: Optional[datetime] = None,
updated: Optional[datetime] = None,
):
DbModelABC.__init__(self, id, deleted, editor_id, created, updated)
self._name = name
self._description = description
@property
def name(self) -> str:
return self._name
@name.setter
def name(self, value: str):
self._name = value
@property
def description(self) -> str:
return self._description
@description.setter
def description(self, value: str):
self._description = value

View File

@@ -0,0 +1,18 @@
from typing import Optional
from cpl.auth.schema._permission.permission import Permission
from cpl.database import TableManager
from cpl.database.abc import DbModelDaoABC
class PermissionDao(DbModelDaoABC[Permission]):
def __init__(self):
DbModelDaoABC.__init__(self, Permission, TableManager.get("permissions"))
self.attribute(Permission.name, str)
self.attribute(Permission.description, Optional[str])
async def get_by_name(self, name: str) -> Permission:
result = await self._db.select_map(f"SELECT * FROM {self._table_name} WHERE Name = '{name}'")
return self.to_object(result[0])

View File

@@ -0,0 +1,66 @@
from datetime import datetime
from typing import Optional
from async_property import async_property
from cpl.auth.permission.permissions import Permissions
from cpl.core.typing import SerialId
from cpl.database.abc import DbModelABC
from cpl.dependency import ServiceProviderABC
class Role(DbModelABC):
def __init__(
self,
id: SerialId,
name: str,
description: str,
deleted: bool = False,
editor_id: Optional[SerialId] = None,
created: Optional[datetime] = None,
updated: Optional[datetime] = None,
):
DbModelABC.__init__(self, id, deleted, editor_id, created, updated)
self._name = name
self._description = description
@property
def name(self) -> str:
return self._name
@name.setter
def name(self, value: str):
self._name = value
@property
def description(self) -> str:
return self._description
@description.setter
def description(self, value: str):
self._description = value
@async_property
async def permissions(self):
from cpl.auth.schema._permission.role_permission_dao import RolePermissionDao
role_permission_dao: RolePermissionDao = ServiceProviderABC.get_global_service(RolePermissionDao)
return [await x.permission for x in await role_permission_dao.get_by_role_id(self.id)]
@async_property
async def users(self):
from cpl.auth.schema._permission.role_user_dao import RoleUserDao
role_user_dao: RoleUserDao = ServiceProviderABC.get_global_service(RoleUserDao)
return [await x.user for x in await role_user_dao.get_by_role_id(self.id)]
async def has_permission(self, permission: Permissions) -> bool:
from cpl.auth.schema._permission.permission_dao import PermissionDao
from cpl.auth.schema._permission.role_permission_dao import RolePermissionDao
permission_dao: PermissionDao = ServiceProviderABC.get_global_service(PermissionDao)
role_permission_dao: RolePermissionDao = ServiceProviderABC.get_global_service(RolePermissionDao)
p = await permission_dao.get_by_name(permission.value)
return p.id in [x.id for x in await role_permission_dao.get_by_role_id(self.id)]

View File

@@ -0,0 +1,14 @@
from cpl.auth.schema._permission.role import Role
from cpl.database import TableManager
from cpl.database.abc import DbModelDaoABC
class RoleDao(DbModelDaoABC[Role]):
def __init__(self):
DbModelDaoABC.__init__(self, Role, TableManager.get("roles"))
self.attribute(Role.name, str)
self.attribute(Role.description, str)
async def get_by_name(self, name: str) -> Role:
result = await self._db.select_map(f"SELECT * FROM {self._table_name} WHERE Name = '{name}'")
return self.to_object(result[0])

View File

@@ -0,0 +1,46 @@
from datetime import datetime
from typing import Optional
from async_property import async_property
from cpl.core.typing import SerialId
from cpl.database.abc import DbModelABC
from cpl.dependency import ServiceProviderABC
class RolePermission(DbModelABC):
def __init__(
self,
id: SerialId,
role_id: SerialId,
permission_id: SerialId,
deleted: bool = False,
editor_id: Optional[SerialId] = None,
created: Optional[datetime] = None,
updated: Optional[datetime] = None,
):
DbModelABC.__init__(self, id, deleted, editor_id, created, updated)
self._role_id = role_id
self._permission_id = permission_id
@property
def role_id(self) -> int:
return self._role_id
@async_property
async def role(self):
from cpl.auth.schema._permission.role_dao import RoleDao
role_dao: RoleDao = ServiceProviderABC.get_global_service(RoleDao)
return await role_dao.get_by_id(self._role_id)
@property
def permission_id(self) -> int:
return self._permission_id
@async_property
async def permission(self):
from cpl.auth.schema._permission.permission_dao import PermissionDao
permission_dao: PermissionDao = ServiceProviderABC.get_global_service(PermissionDao)
return await permission_dao.get_by_id(self._permission_id)

View File

@@ -0,0 +1,26 @@
from cpl.auth.schema._permission.role_permission import RolePermission
from cpl.database import TableManager
from cpl.database.abc import DbModelDaoABC
class RolePermissionDao(DbModelDaoABC[RolePermission]):
def __init__(self):
DbModelDaoABC.__init__(self, RolePermission, TableManager.get("role_permissions"))
self.attribute(RolePermission.role_id, int)
self.attribute(RolePermission.permission_id, int)
async def get_by_role_id(self, role_id: int, with_deleted=False) -> list[RolePermission]:
f = [{RolePermission.role_id: role_id}]
if not with_deleted:
f.append({RolePermission.deleted: False})
return await self.find_by(f)
async def get_by_permission_id(self, permission_id: int, with_deleted=False) -> list[RolePermission]:
f = [{RolePermission.permission_id: permission_id}]
if not with_deleted:
f.append({RolePermission.deleted: False})
return await self.find_by(f)

View File

@@ -0,0 +1,46 @@
from datetime import datetime
from typing import Optional
from async_property import async_property
from cpl.core.typing import SerialId
from cpl.database.abc import DbJoinModelABC
from cpl.dependency import ServiceProviderABC
class RoleUser(DbJoinModelABC):
def __init__(
self,
id: SerialId,
user_id: SerialId,
role_id: SerialId,
deleted: bool = False,
editor_id: Optional[SerialId] = None,
created: Optional[datetime] = None,
updated: Optional[datetime] = None,
):
DbJoinModelABC.__init__(self, id, user_id, role_id, deleted, editor_id, created, updated)
self._user_id = user_id
self._role_id = role_id
@property
def user_id(self) -> int:
return self._user_id
@async_property
async def user(self):
from cpl.auth.schema._administration.auth_user_dao import AuthUserDao
auth_user_dao: AuthUserDao = ServiceProviderABC.get_global_service(AuthUserDao)
return await auth_user_dao.get_by_id(self._user_id)
@property
def role_id(self) -> int:
return self._role_id
@async_property
async def role(self):
from cpl.auth.schema._permission.role_dao import RoleDao
role_dao: RoleDao = ServiceProviderABC.get_global_service(RoleDao)
return await role_dao.get_by_id(self._role_id)

View File

@@ -0,0 +1,26 @@
from cpl.auth.schema._permission.role_user import RoleUser
from cpl.database import TableManager
from cpl.database.abc import DbModelDaoABC
class RoleUserDao(DbModelDaoABC[RoleUser]):
def __init__(self):
DbModelDaoABC.__init__(self, RoleUser, TableManager.get("role_users"))
self.attribute(RoleUser.role_id, int)
self.attribute(RoleUser.user_id, int)
async def get_by_role_id(self, rid: int, with_deleted=False) -> list[RoleUser]:
f = [{RoleUser.role_id: rid}]
if not with_deleted:
f.append({RoleUser.deleted: False})
return await self.find_by(f)
async def get_by_user_id(self, uid: int, with_deleted=False) -> list[RoleUser]:
f = [{RoleUser.user_id: uid}]
if not with_deleted:
f.append({RoleUser.deleted: False})
return await self.find_by(f)

View File

@@ -0,0 +1,44 @@
CREATE TABLE IF NOT EXISTS administration_auth_users
(
id INT AUTO_INCREMENT PRIMARY KEY,
keycloakId CHAR(36) NOT NULL,
-- for history
deleted BOOL NOT NULL DEFAULT FALSE,
editorId INT NULL,
created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT UC_KeycloakId UNIQUE (keycloakId),
CONSTRAINT FK_EditorId FOREIGN KEY (editorId) REFERENCES administration_auth_users (id)
);
CREATE TABLE IF NOT EXISTS administration_auth_users_history
(
id INT NOT NULL,
keycloakId CHAR(36) NOT NULL,
-- for history
deleted BOOL NOT NULL,
editorId INT NULL,
created TIMESTAMP NOT NULL,
updated TIMESTAMP NOT NULL
);
CREATE TRIGGER TR_administration_auth_usersUpdate
AFTER UPDATE
ON administration_auth_users
FOR EACH ROW
BEGIN
INSERT INTO administration_auth_users_history
(id, keycloakId, deleted, editorId, created, updated)
VALUES (OLD.id, OLD.keycloakId, OLD.deleted, OLD.editorId, OLD.created, NOW());
END;
CREATE TRIGGER TR_administration_auth_usersDelete
AFTER DELETE
ON administration_auth_users
FOR EACH ROW
BEGIN
INSERT INTO administration_auth_users_history
(id, keycloakId, deleted, editorId, created, updated)
VALUES (OLD.id, OLD.keycloakId, 1, OLD.editorId, OLD.created, NOW());
END;

View File

@@ -0,0 +1,46 @@
CREATE TABLE IF NOT EXISTS administration_api_keys
(
id INT AUTO_INCREMENT PRIMARY KEY,
identifier VARCHAR(255) NOT NULL,
keyString VARCHAR(255) NOT NULL,
deleted BOOL NOT NULL DEFAULT FALSE,
editorId INT NULL,
created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT UC_Identifier_Key UNIQUE (identifier, keyString),
CONSTRAINT UC_Key UNIQUE (keyString),
CONSTRAINT FK_ApiKeys_Editor FOREIGN KEY (editorId) REFERENCES administration_auth_users (id)
);
CREATE TABLE IF NOT EXISTS administration_api_keys_history
(
id INT NOT NULL,
identifier VARCHAR(255) NOT NULL,
keyString VARCHAR(255) NOT NULL,
deleted BOOL NOT NULL,
editorId INT NULL,
created TIMESTAMP NOT NULL,
updated TIMESTAMP NOT NULL
);
CREATE TRIGGER TR_ApiKeysUpdate
AFTER UPDATE
ON administration_api_keys
FOR EACH ROW
BEGIN
INSERT INTO administration_api_keys_history
(id, identifier, keyString, deleted, editorId, created, updated)
VALUES (OLD.id, OLD.identifier, OLD.keyString, OLD.deleted, OLD.editorId, OLD.created, NOW());
END;
CREATE TRIGGER TR_ApiKeysDelete
AFTER DELETE
ON administration_api_keys
FOR EACH ROW
BEGIN
INSERT INTO administration_api_keys_history
(id, identifier, keyString, deleted, editorId, created, updated)
VALUES (OLD.id, OLD.identifier, OLD.keyString, 1, OLD.editorId, OLD.created, NOW());
END;

View File

@@ -0,0 +1,179 @@
CREATE TABLE IF NOT EXISTS permission_permissions
(
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
description TEXT NULL,
deleted BOOL NOT NULL DEFAULT FALSE,
editorId INT NULL,
created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT UQ_PermissionName UNIQUE (name),
CONSTRAINT FK_Permissions_Editor FOREIGN KEY (editorId) REFERENCES administration_auth_users (id)
);
CREATE TABLE IF NOT EXISTS permission_permissions_history
(
id INT NOT NULL,
name VARCHAR(255) NOT NULL,
description TEXT NULL,
deleted BOOL NOT NULL,
editorId INT NULL,
created TIMESTAMP NOT NULL,
updated TIMESTAMP NOT NULL
);
CREATE TRIGGER TR_PermissionsUpdate
AFTER UPDATE
ON permission_permissions
FOR EACH ROW
BEGIN
INSERT INTO permission_permissions_history
(id, name, description, deleted, editorId, created, updated)
VALUES (OLD.id, OLD.name, OLD.description, OLD.deleted, OLD.editorId, OLD.created, NOW());
END;
CREATE TRIGGER TR_PermissionsDelete
AFTER DELETE
ON permission_permissions
FOR EACH ROW
BEGIN
INSERT INTO permission_permissions_history
(id, name, description, deleted, editorId, created, updated)
VALUES (OLD.id, OLD.name, OLD.description, 1, OLD.editorId, OLD.created, NOW());
END;
CREATE TABLE IF NOT EXISTS permission_roles
(
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
description TEXT NULL,
deleted BOOL NOT NULL DEFAULT FALSE,
editorId INT NULL,
created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT UQ_RoleName UNIQUE (name),
CONSTRAINT FK_Roles_Editor FOREIGN KEY (editorId) REFERENCES administration_auth_users (id)
);
CREATE TABLE IF NOT EXISTS permission_roles_history
(
id INT NOT NULL,
name VARCHAR(255) NOT NULL,
description TEXT NULL,
deleted BOOL NOT NULL,
editorId INT NULL,
created TIMESTAMP NOT NULL,
updated TIMESTAMP NOT NULL
);
CREATE TRIGGER TR_RolesUpdate
AFTER UPDATE
ON permission_roles
FOR EACH ROW
BEGIN
INSERT INTO permission_roles_history
(id, name, description, deleted, editorId, created, updated)
VALUES (OLD.id, OLD.name, OLD.description, OLD.deleted, OLD.editorId, OLD.created, NOW());
END;
CREATE TRIGGER TR_RolesDelete
AFTER DELETE
ON permission_roles
FOR EACH ROW
BEGIN
INSERT INTO permission_roles_history
(id, name, description, deleted, editorId, created, updated)
VALUES (OLD.id, OLD.name, OLD.description, 1, OLD.editorId, OLD.created, NOW());
END;
CREATE TABLE IF NOT EXISTS permission_role_permissions
(
id INT AUTO_INCREMENT PRIMARY KEY,
RoleId INT NOT NULL,
permissionId INT NOT NULL,
deleted BOOL NOT NULL DEFAULT FALSE,
editorId INT NULL,
created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT UQ_RolePermission UNIQUE (RoleId, permissionId),
CONSTRAINT FK_RolePermissions_Role FOREIGN KEY (RoleId) REFERENCES permission_roles (id) ON DELETE CASCADE,
CONSTRAINT FK_RolePermissions_Permission FOREIGN KEY (permissionId) REFERENCES permission_permissions (id) ON DELETE CASCADE,
CONSTRAINT FK_RolePermissions_Editor FOREIGN KEY (editorId) REFERENCES administration_auth_users (id)
);
CREATE TABLE IF NOT EXISTS permission_role_permissions_history
(
id INT NOT NULL,
RoleId INT NOT NULL,
permissionId INT NOT NULL,
deleted BOOL NOT NULL,
editorId INT NULL,
created TIMESTAMP NOT NULL,
updated TIMESTAMP NOT NULL
);
CREATE TRIGGER TR_RolePermissionsUpdate
AFTER UPDATE
ON permission_role_permissions
FOR EACH ROW
BEGIN
INSERT INTO permission_role_permissions_history
(id, RoleId, permissionId, deleted, editorId, created, updated)
VALUES (OLD.id, OLD.RoleId, OLD.permissionId, OLD.deleted, OLD.editorId, OLD.created, NOW());
END;
CREATE TRIGGER TR_RolePermissionsDelete
AFTER DELETE
ON permission_role_permissions
FOR EACH ROW
BEGIN
INSERT INTO permission_role_permissions_history
(id, RoleId, permissionId, deleted, editorId, created, updated)
VALUES (OLD.id, OLD.RoleId, OLD.permissionId, 1, OLD.editorId, OLD.created, NOW());
END;
CREATE TABLE IF NOT EXISTS permission_role_auth_users
(
id INT AUTO_INCREMENT PRIMARY KEY,
RoleId INT NOT NULL,
UserId INT NOT NULL,
deleted BOOL NOT NULL DEFAULT FALSE,
editorId INT NULL,
created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT UQ_RoleUser UNIQUE (RoleId, UserId),
CONSTRAINT FK_Roleauth_users_Role FOREIGN KEY (RoleId) REFERENCES permission_roles (id) ON DELETE CASCADE,
CONSTRAINT FK_Roleauth_users_User FOREIGN KEY (UserId) REFERENCES administration_auth_users (id) ON DELETE CASCADE,
CONSTRAINT FK_Roleauth_users_Editor FOREIGN KEY (editorId) REFERENCES administration_auth_users (id)
);
CREATE TABLE IF NOT EXISTS permission_role_auth_users_history
(
id INT NOT NULL,
RoleId INT NOT NULL,
UserId INT NOT NULL,
deleted BOOL NOT NULL,
editorId INT NULL,
created TIMESTAMP NOT NULL,
updated TIMESTAMP NOT NULL
);
CREATE TRIGGER TR_Roleauth_usersUpdate
AFTER UPDATE
ON permission_role_auth_users
FOR EACH ROW
BEGIN
INSERT INTO permission_role_auth_users_history
(id, RoleId, UserId, deleted, editorId, created, updated)
VALUES (OLD.id, OLD.RoleId, OLD.UserId, OLD.deleted, OLD.editorId, OLD.created, NOW());
END;
CREATE TRIGGER TR_Roleauth_usersDelete
AFTER DELETE
ON permission_role_auth_users
FOR EACH ROW
BEGIN
INSERT INTO permission_role_auth_users_history
(id, RoleId, UserId, deleted, editorId, created, updated)
VALUES (OLD.id, OLD.RoleId, OLD.UserId, 1, OLD.editorId, OLD.created, NOW());
END;

View File

@@ -0,0 +1,46 @@
CREATE TABLE IF NOT EXISTS permission_api_key_permissions
(
id INT AUTO_INCREMENT PRIMARY KEY,
apiKeyId INT NOT NULL,
permissionId INT NOT NULL,
deleted BOOL NOT NULL DEFAULT FALSE,
editorId INT NULL,
created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT UQ_ApiKeyPermission UNIQUE (apiKeyId, permissionId),
CONSTRAINT FK_ApiKeyPermissions_ApiKey FOREIGN KEY (apiKeyId) REFERENCES administration_api_keys (id) ON DELETE CASCADE,
CONSTRAINT FK_ApiKeyPermissions_Permission FOREIGN KEY (permissionId) REFERENCES permission_permissions (id) ON DELETE CASCADE,
CONSTRAINT FK_ApiKeyPermissions_Editor FOREIGN KEY (editorId) REFERENCES administration_auth_users (id)
);
CREATE TABLE IF NOT EXISTS permission_api_key_permissions_history
(
id INT NOT NULL,
apiKeyId INT NOT NULL,
permissionId INT NOT NULL,
deleted BOOL NOT NULL,
editorId INT NULL,
created TIMESTAMP NOT NULL,
updated TIMESTAMP NOT NULL
);
CREATE TRIGGER TR_ApiKeyPermissionsUpdate
AFTER UPDATE
ON permission_api_key_permissions
FOR EACH ROW
BEGIN
INSERT INTO permission_api_key_permissions_history
(id, apiKeyId, permissionId, deleted, editorId, created, updated)
VALUES (OLD.id, OLD.apiKeyId, OLD.permissionId, OLD.deleted, OLD.editorId, OLD.created, NOW());
END;
CREATE TRIGGER TR_ApiKeyPermissionsDelete
AFTER DELETE
ON permission_api_key_permissions
FOR EACH ROW
BEGIN
INSERT INTO permission_api_key_permissions_history
(id, apiKeyId, permissionId, deleted, editorId, created, updated)
VALUES (OLD.id, OLD.apiKeyId, OLD.permissionId, 1, OLD.editorId, OLD.created, NOW());
END;

View File

@@ -0,0 +1,26 @@
CREATE SCHEMA IF NOT EXISTS administration;
CREATE TABLE IF NOT EXISTS administration.auth_users
(
id SERIAL PRIMARY KEY,
keycloakId UUID NOT NULL,
-- for history
deleted BOOLEAN NOT NULL DEFAULT FALSE,
editorId INT NULL REFERENCES administration.auth_users (id),
created timestamptz NOT NULL DEFAULT NOW(),
updated timestamptz NOT NULL DEFAULT NOW(),
CONSTRAINT UC_KeycloakId UNIQUE (keycloakId)
);
CREATE TABLE IF NOT EXISTS administration.auth_users_history
(
LIKE administration.auth_users
);
CREATE TRIGGER users_history_trigger
BEFORE INSERT OR UPDATE OR DELETE
ON administration.auth_users
FOR EACH ROW
EXECUTE FUNCTION public.history_trigger_function();

View File

@@ -0,0 +1,28 @@
CREATE SCHEMA IF NOT EXISTS administration;
CREATE TABLE IF NOT EXISTS administration.api_keys
(
id SERIAL PRIMARY KEY,
identifier VARCHAR(255) NOT NULL,
keyString VARCHAR(255) NOT NULL,
-- for history
deleted BOOLEAN NOT NULL DEFAULT FALSE,
editorId INT NULL REFERENCES administration.auth_users (id),
created timestamptz NOT NULL DEFAULT NOW(),
updated timestamptz NOT NULL DEFAULT NOW(),
CONSTRAINT UC_Identifier_Key UNIQUE (identifier, keyString),
CONSTRAINT UC_Key UNIQUE (keyString)
);
CREATE TABLE IF NOT EXISTS administration.api_keys_history
(
LIKE administration.api_keys
);
CREATE TRIGGER api_keys_history_trigger
BEFORE INSERT OR UPDATE OR DELETE
ON administration.api_keys
FOR EACH ROW
EXECUTE FUNCTION public.history_trigger_function();

View File

@@ -0,0 +1,105 @@
CREATE SCHEMA IF NOT EXISTS permission;
-- Permissions
CREATE TABLE permission.permissions
(
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
description TEXT NULL,
-- for history
deleted BOOLEAN NOT NULL DEFAULT FALSE,
editorId INT NULL REFERENCES administration.auth_users (id),
created timestamptz NOT NULL DEFAULT NOW(),
updated timestamptz NOT NULL DEFAULT NOW(),
CONSTRAINT UQ_PermissionName UNIQUE (name)
);
CREATE TABLE permission.permissions_history
(
LIKE permission.permissions
);
CREATE TRIGGER versioning_trigger
BEFORE INSERT OR UPDATE OR DELETE
ON permission.permissions
FOR EACH ROW
EXECUTE PROCEDURE public.history_trigger_function();
-- Roles
CREATE TABLE permission.roles
(
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
description TEXT NULL,
-- for history
deleted BOOLEAN NOT NULL DEFAULT FALSE,
editorId INT NULL REFERENCES administration.auth_users (id),
created timestamptz NOT NULL DEFAULT NOW(),
updated timestamptz NOT NULL DEFAULT NOW(),
CONSTRAINT UQ_RoleName UNIQUE (name)
);
CREATE TABLE permission.roles_history
(
LIKE permission.roles
);
CREATE TRIGGER versioning_trigger
BEFORE INSERT OR UPDATE OR DELETE
ON permission.roles
FOR EACH ROW
EXECUTE PROCEDURE public.history_trigger_function();
-- Role permissions
CREATE TABLE permission.role_permissions
(
id SERIAL PRIMARY KEY,
RoleId INT NOT NULL REFERENCES permission.roles (id) ON DELETE CASCADE,
permissionId INT NOT NULL REFERENCES permission.permissions (id) ON DELETE CASCADE,
-- for history
deleted BOOLEAN NOT NULL DEFAULT FALSE,
editorId INT NULL REFERENCES administration.auth_users (id),
created timestamptz NOT NULL DEFAULT NOW(),
updated timestamptz NOT NULL DEFAULT NOW(),
CONSTRAINT UQ_RolePermission UNIQUE (RoleId, permissionId)
);
CREATE TABLE permission.role_permissions_history
(
LIKE permission.role_permissions
);
CREATE TRIGGER versioning_trigger
BEFORE INSERT OR UPDATE OR DELETE
ON permission.role_permissions
FOR EACH ROW
EXECUTE PROCEDURE public.history_trigger_function();
-- Role user
CREATE TABLE permission.role_users
(
id SERIAL PRIMARY KEY,
RoleId INT NOT NULL REFERENCES permission.roles (id) ON DELETE CASCADE,
UserId INT NOT NULL REFERENCES administration.auth_users (id) ON DELETE CASCADE,
-- for history
deleted BOOLEAN NOT NULL DEFAULT FALSE,
editorId INT NULL REFERENCES administration.auth_users (id),
created timestamptz NOT NULL DEFAULT NOW(),
updated timestamptz NOT NULL DEFAULT NOW(),
CONSTRAINT UQ_RoleUser UNIQUE (RoleId, UserId)
);
CREATE TABLE permission.role_users_history
(
LIKE permission.role_users
);
CREATE TRIGGER versioning_trigger
BEFORE INSERT OR UPDATE OR DELETE
ON permission.role_users
FOR EACH ROW
EXECUTE PROCEDURE public.history_trigger_function();

View File

@@ -0,0 +1,24 @@
CREATE TABLE permission.api_key_permissions
(
id SERIAL PRIMARY KEY,
apiKeyId INT NOT NULL REFERENCES administration.api_keys (id) ON DELETE CASCADE,
permissionId INT NOT NULL REFERENCES permission.permissions (id) ON DELETE CASCADE,
-- for history
deleted BOOLEAN NOT NULL DEFAULT FALSE,
editorId INT NULL REFERENCES administration.auth_users (id),
created timestamptz NOT NULL DEFAULT NOW(),
updated timestamptz NOT NULL DEFAULT NOW(),
CONSTRAINT UQ_ApiKeyPermission UNIQUE (apiKeyId, permissionId)
);
CREATE TABLE permission.api_key_permissions_history
(
LIKE permission.api_key_permissions
);
CREATE TRIGGER versioning_trigger
BEFORE INSERT OR UPDATE OR DELETE
ON permission.api_key_permissions
FOR EACH ROW
EXECUTE PROCEDURE public.history_trigger_function();

View File

@@ -0,0 +1,30 @@
[build-system]
requires = ["setuptools>=70.1.0", "wheel>=0.43.0"]
build-backend = "setuptools.build_meta"
[project]
name = "cpl-auth"
version = "2024.7.0"
description = "CPL auth"
readme ="CPL auth package"
requires-python = ">=3.12"
license = { text = "MIT" }
authors = [
{ name = "Sven Heidemann", email = "sven.heidemann@sh-edraft.de" }
]
keywords = ["cpl", "auth", "backend", "shared", "library"]
dynamic = ["dependencies", "optional-dependencies"]
[project.urls]
Homepage = "https://www.sh-edraft.de"
[tool.setuptools.packages.find]
where = ["."]
include = ["cpl*"]
[tool.setuptools.dynamic]
dependencies = { file = ["requirements.txt"] }
optional-dependencies.dev = { file = ["requirements.dev.txt"] }

View File

@@ -0,0 +1 @@
black==25.1.0

View File

@@ -0,0 +1,4 @@
cpl-core
cpl-dependency
cpl-database
python-keycloak==5.8.1

View File

@@ -0,0 +1,23 @@
from abc import abstractmethod, ABC
from typing import Generic
from cpl.core.typing import T
class RegistryABC(ABC, Generic[T]):
@abstractmethod
def __init__(self):
self._items: dict[str, T] = {}
@abstractmethod
def extend(self, items: list[T]) -> None: ...
@abstractmethod
def add(self, item: T) -> None: ...
@abstractmethod
def get(self, key: str) -> T | None: ...
@abstractmethod
def all(self) -> list[T]: ...

View File

@@ -0,0 +1,2 @@
from .configuration import Configuration
from .configuration_model_abc import ConfigurationModelABC

View File

@@ -0,0 +1,137 @@
import inspect
import json
import os
import sys
from inspect import isclass
from typing import Any
from cpl.core.configuration.configuration_model_abc import ConfigurationModelABC
from cpl.core.console.console import Console
from cpl.core.console.foreground_color_enum import ForegroundColorEnum
from cpl.core.typing import D, T
class Configuration:
_config = {}
@staticmethod
def _print_info(message: str):
r"""Prints an info message
Parameter:
name: :class:`str`
Info name
message: :class:`str`
Info message
"""
Console.set_foreground_color(ForegroundColorEnum.green)
Console.write_line(f"[CONFIG] {message}")
Console.set_foreground_color(ForegroundColorEnum.default)
@staticmethod
def _print_warn(message: str):
r"""Prints a warning
Parameter:
name: :class:`str`
Warning name
message: :class:`str`
Warning message
"""
Console.set_foreground_color(ForegroundColorEnum.yellow)
Console.write_line(f"[CONFIG] {message}")
Console.set_foreground_color(ForegroundColorEnum.default)
@staticmethod
def _print_error(message: str):
r"""Prints an error
Parameter:
name: :class:`str`
Error name
message: :class:`str`
Error message
"""
Console.set_foreground_color(ForegroundColorEnum.red)
Console.write_line(f"[CONFIG] {message}")
Console.set_foreground_color(ForegroundColorEnum.default)
@classmethod
def _load_json_file(cls, file: str, output: bool) -> dict:
r"""Reads the json file
Parameter:
file: :class:`str`
Name of the file
output: :class:`bool`
Specifies whether an output should take place
Returns:
Object of :class:`dict`
"""
try:
# open config file, create if not exists
with open(file, encoding="utf-8") as cfg:
# load json
json_cfg = json.load(cfg)
if output:
cls._print_info(f"Loaded config file: {file}")
return json_cfg
except Exception as e:
cls._print_error(f"Cannot load config file: {file}! -> {e}")
return {}
@classmethod
def add_json_file(cls, name: str, optional: bool = None, output: bool = True, path: str = None):
if os.path.isabs(name):
file_path = name
else:
from cpl.core.environment import Environment
path_root = Environment.get_cwd()
if path is not None:
path_root = path
if str(path_root).endswith("/") and not name.startswith("/"):
file_path = f"{path_root}{name}"
else:
file_path = f"{path_root}/{name}"
if not os.path.isfile(file_path):
if optional is not True:
if output:
cls._print_error(f"File not found: {file_path}")
sys.exit()
if output:
cls._print_warn(f"Not Loaded config file: {file_path}")
return None
config_from_file = cls._load_json_file(file_path, output)
for sub in ConfigurationModelABC.__subclasses__():
for key, value in config_from_file.items():
if sub.__name__ != key and sub.__name__.replace("Settings", "") != key:
continue
cls.set(sub, sub(value))
@classmethod
def set(cls, key: Any, value: T):
if inspect.isclass(key):
key = key.__name__
cls._config[key] = value
@classmethod
def get(cls, key: Any, default: D = None) -> T | D:
key_name = key.__name__ if inspect.isclass(key) else key
result = cls._config.get(key_name, default)
if isclass(key) and issubclass(key, ConfigurationModelABC) and result == default:
result = key()
cls.set(key, result)
return result

View File

@@ -0,0 +1,82 @@
from abc import ABC, abstractmethod
from typing import Optional, Type, Any
from cpl.core.typing import T
from cpl.core.utils.cast import cast
from cpl.core.utils.get_value import get_value
from cpl.core.utils.string import String
class ConfigurationModelABC(ABC):
r"""
ABC for configuration model classes
"""
@abstractmethod
def __init__(
self,
src: Optional[dict] = None,
env_prefix: Optional[str] = None,
readonly: bool = True,
):
ABC.__init__(self)
self._src = src or {}
self._options: dict[str, Any] = {}
self._env_prefix = env_prefix
self._readonly = readonly
def __setattr__(self, attr: str, value: Any):
if hasattr(self, "_readonly") and self._readonly:
raise AttributeError(f"Cannot set attribute: {attr}. {type(self).__name__} is read-only")
super().__setattr__(attr, value)
def __getattr__(self, attr: str) -> Any:
options = super().__getattribute__("_options")
if attr in options:
return options[attr]
return super().__getattribute__(attr)
def option(self, field: str, cast_type: Type[T], default=None, required=False, from_env=True):
value = None
field_variants = [
field,
String.first_to_upper(field),
String.first_to_lower(field),
String.to_camel_case(field),
String.to_snake_case(field),
String.to_pascal_case(field),
]
value = None
for variant in field_variants:
if variant in self._src:
value = self._src[variant]
break
if value is None and from_env:
from cpl.core.environment import Environment
env_field = field.upper()
if self._env_prefix:
env_field = f"{self._env_prefix}_{env_field}"
value = cast(Environment.get(env_field, str), cast_type)
if value is None and required:
raise ValueError(f"{type(self).__name__}.{field} is required")
elif value is None:
self._options[field] = default
return
self._options[field] = cast(value, cast_type)
def get(self, field: str, default=None) -> Optional[T]:
return get_value(self._src, field, self._options[field].type, default)
def to_dict(self) -> dict:
return {field: self.get(field) for field in self._options.keys()}

View File

@@ -0,0 +1,4 @@
from .background_color_enum import BackgroundColorEnum
from .console import Console
from ._call import ConsoleCall
from .foreground_color_enum import ForegroundColorEnum

View File

@@ -1,14 +1,17 @@
from collections import Callable
from collections.abc import Callable
class ConsoleCall:
r"""Represents a console call, for hold back when spinner is active
Parameter:
function: :class:`Callable`
Function to call
args: :class:`list`
List of arguments
"""
def __init__(self, function: Callable, *args):
"""
Represents a console call, for hold back when spinner is active
:param function:
:param args:
"""
self._func = function
self._args = args

View File

@@ -0,0 +1,95 @@
import os
import sys
import multiprocessing
import time
from multiprocessing import Process
from termcolor import colored
from cpl.core.console.background_color_enum import BackgroundColorEnum
from cpl.core.console.foreground_color_enum import ForegroundColorEnum
class Spinner(Process):
r"""Process to show spinner in terminal
Parameter:
msg_len: :class:`int`
Length of the message
foreground_color: :class:`cpl.core.console.foreground_color.ForegroundColorEnum`
Foreground color of the spinner
background_color: :class:`cpl.core.console.background_color.BackgroundColorEnum`
Background color of the spinner
"""
def __init__(self, msg_len: int, foreground_color: ForegroundColorEnum, background_color: BackgroundColorEnum):
Process.__init__(self)
self._msg_len = msg_len
self._foreground_color = foreground_color
self._background_color = background_color
self._is_spinning = True
self._exit = False
@staticmethod
def _spinner():
r"""Selects active spinner char"""
while True:
for cursor in "|/-\\":
yield cursor
def _get_color_args(self) -> list[str]:
r"""Creates color arguments"""
color_args = []
if self._foreground_color is not None:
color_args.append(str(self._foreground_color.value))
if self._background_color is not None:
color_args.append(str(self._background_color.value))
return color_args
def run(self) -> None:
r"""Entry point of process, shows the spinner"""
columns = 0
if sys.platform == "win32":
columns = os.get_terminal_size().columns
else:
values = os.popen("stty size", "r").read().split()
term_rows, term_columns = values if len(values) == 2 else (0, 0)
columns = int(term_columns)
end_msg = "done"
padding = columns - self._msg_len - len(end_msg)
if padding > 0:
print(f'{"" : >{padding}}', end="")
else:
print("", end="")
spinner = self._spinner()
while self._is_spinning:
print(colored(f"{next(spinner): >{len(end_msg)}}", *self._get_color_args()), end="")
time.sleep(0.1)
back = ""
for i in range(0, len(end_msg)):
back += "\b"
print(back, end="")
sys.stdout.flush()
if not self._exit:
print(colored(end_msg, *self._get_color_args()), end="")
def stop(self):
r"""Stops the spinner"""
self._is_spinning = False
super().terminate()
time.sleep(0.1)
def exit(self):
r"""Stops the spinner"""
self._is_spinning = False
self._exit = True
time.sleep(0.1)

View File

@@ -0,0 +1,13 @@
from enum import Enum
class BackgroundColorEnum(Enum):
default = "on_default"
grey = "on_grey"
red = "on_red"
green = "on_green"
yellow = "on_yellow"
blue = "on_blue"
magenta = "on_magenta"
cyan = "on_cyan"
white = "on_white"

Some files were not shown because too many files have changed in this diff Show More