maleo-managers 0.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- maleo_managers-0.0.1/LICENSE +57 -0
- maleo_managers-0.0.1/PKG-INFO +137 -0
- maleo_managers-0.0.1/README.md +29 -0
- maleo_managers-0.0.1/maleo_managers.egg-info/PKG-INFO +137 -0
- maleo_managers-0.0.1/maleo_managers.egg-info/SOURCES.txt +18 -0
- maleo_managers-0.0.1/maleo_managers.egg-info/dependency_links.txt +1 -0
- maleo_managers-0.0.1/maleo_managers.egg-info/requires.txt +97 -0
- maleo_managers-0.0.1/maleo_managers.egg-info/top_level.txt +1 -0
- maleo_managers-0.0.1/pyproject.toml +130 -0
- maleo_managers-0.0.1/setup.cfg +4 -0
- maleo_managers-0.0.1/src/__init__.py +0 -0
- maleo_managers-0.0.1/src/client/__init__.py +0 -0
- maleo_managers-0.0.1/src/client/config.py +17 -0
- maleo_managers-0.0.1/src/client/http.py +24 -0
- maleo_managers-0.0.1/src/client/maleo/__init__.py +176 -0
- maleo_managers-0.0.1/src/client/maleo/config.py +92 -0
- maleo_managers-0.0.1/src/config.py +49 -0
- maleo_managers-0.0.1/src/credential.py +119 -0
- maleo_managers-0.0.1/src/key.py +40 -0
- maleo_managers-0.0.1/src/service.py +123 -0
@@ -0,0 +1,57 @@
|
|
1
|
+
# Proprietary Software License
|
2
|
+
|
3
|
+
**Copyright (c) 2025 Agra Bima Yuda / Nexmedis**
|
4
|
+
|
5
|
+
## License Grant
|
6
|
+
|
7
|
+
This software and associated documentation files (the "Software") are proprietary and confidential to Agra Bima Yuda and/or Nexmedis ("Licensor"). All rights reserved.
|
8
|
+
|
9
|
+
## Restrictions
|
10
|
+
|
11
|
+
**NO PERMISSION** is granted to any person to:
|
12
|
+
|
13
|
+
1. **Use** the Software for any purpose without explicit written permission from the Licensor
|
14
|
+
2. **Copy, modify, merge, publish, distribute, sublicense, or sell** copies of the Software
|
15
|
+
3. **Reverse engineer, decompile, or disassemble** the Software
|
16
|
+
4. **Create derivative works** based upon the Software
|
17
|
+
5. **Remove or alter** any proprietary notices, labels, or marks on the Software
|
18
|
+
|
19
|
+
## Permitted Use
|
20
|
+
|
21
|
+
Use of this Software is permitted only under the following conditions:
|
22
|
+
|
23
|
+
1. **Authorized Users**: Only individuals or entities explicitly authorized by the Licensor in writing
|
24
|
+
2. **Internal Use Only**: The Software may only be used for internal business purposes of the authorized entity
|
25
|
+
3. **No Redistribution**: The Software may not be shared, distributed, or made available to any third party
|
26
|
+
|
27
|
+
## Ownership
|
28
|
+
|
29
|
+
The Software is and remains the exclusive property of the Licensor. This license does not grant any ownership rights in the Software.
|
30
|
+
|
31
|
+
## Confidentiality
|
32
|
+
|
33
|
+
The Software contains proprietary and confidential information. Recipients agree to:
|
34
|
+
|
35
|
+
1. Maintain the confidentiality of the Software
|
36
|
+
2. Use the same degree of care to protect the Software as they use for their own confidential information, but no less than reasonable care
|
37
|
+
3. Not disclose the Software to any third party without prior written consent
|
38
|
+
|
39
|
+
## Termination
|
40
|
+
|
41
|
+
This license is effective until terminated. The Licensor may terminate this license at any time without notice. Upon termination, all rights granted herein cease immediately, and the recipient must destroy all copies of the Software.
|
42
|
+
|
43
|
+
## Disclaimer of Warranty
|
44
|
+
|
45
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
46
|
+
|
47
|
+
## Limitation of Liability
|
48
|
+
|
49
|
+
IN NO EVENT SHALL THE LICENSOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
50
|
+
|
51
|
+
## Governing Law
|
52
|
+
|
53
|
+
This license shall be governed by and construed in accordance with the laws of Indonesia, without regard to its conflict of law provisions.
|
54
|
+
|
55
|
+
---
|
56
|
+
|
57
|
+
For licensing inquiries, contact: agra@nexmedis.com
|
@@ -0,0 +1,137 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: maleo-managers
|
3
|
+
Version: 0.0.1
|
4
|
+
Summary: Managers package for MaleoSuite
|
5
|
+
Author-email: Agra Bima Yuda <agra@nexmedis.com>
|
6
|
+
License: Proprietary
|
7
|
+
Requires-Python: >=3.12
|
8
|
+
Description-Content-Type: text/markdown
|
9
|
+
License-File: LICENSE
|
10
|
+
Requires-Dist: aioredis>=2.0.1
|
11
|
+
Requires-Dist: annotated-types>=0.7.0
|
12
|
+
Requires-Dist: anyio>=4.10.0
|
13
|
+
Requires-Dist: async-timeout>=5.0.1
|
14
|
+
Requires-Dist: bcrypt>=4.3.0
|
15
|
+
Requires-Dist: black>=25.1.0
|
16
|
+
Requires-Dist: cachetools>=5.5.2
|
17
|
+
Requires-Dist: certifi>=2025.8.3
|
18
|
+
Requires-Dist: cffi>=2.0.0
|
19
|
+
Requires-Dist: cfgv>=3.4.0
|
20
|
+
Requires-Dist: charset-normalizer>=3.4.3
|
21
|
+
Requires-Dist: click>=8.2.1
|
22
|
+
Requires-Dist: cryptography>=45.0.7
|
23
|
+
Requires-Dist: distlib>=0.4.0
|
24
|
+
Requires-Dist: dnspython>=2.8.0
|
25
|
+
Requires-Dist: elastic-transport>=9.1.0
|
26
|
+
Requires-Dist: elasticsearch>=9.1.0
|
27
|
+
Requires-Dist: fastapi>=0.116.1
|
28
|
+
Requires-Dist: filelock>=3.19.1
|
29
|
+
Requires-Dist: google-api-core>=2.25.1
|
30
|
+
Requires-Dist: google-auth>=2.40.3
|
31
|
+
Requires-Dist: google-cloud-appengine-logging>=1.6.2
|
32
|
+
Requires-Dist: google-cloud-audit-log>=0.3.2
|
33
|
+
Requires-Dist: google-cloud-core>=2.4.3
|
34
|
+
Requires-Dist: google-cloud-logging>=3.12.1
|
35
|
+
Requires-Dist: google-cloud-pubsub>=2.31.1
|
36
|
+
Requires-Dist: google-cloud-secret-manager>=2.24.0
|
37
|
+
Requires-Dist: google-cloud-storage>=3.3.1
|
38
|
+
Requires-Dist: google-crc32c>=1.7.1
|
39
|
+
Requires-Dist: google-resumable-media>=2.7.2
|
40
|
+
Requires-Dist: googleapis-common-protos>=1.70.0
|
41
|
+
Requires-Dist: greenlet>=3.2.4
|
42
|
+
Requires-Dist: grpc-google-iam-v1>=0.14.2
|
43
|
+
Requires-Dist: grpcio>=1.74.0
|
44
|
+
Requires-Dist: grpcio-status>=1.74.0
|
45
|
+
Requires-Dist: h11>=0.16.0
|
46
|
+
Requires-Dist: httpcore>=1.0.9
|
47
|
+
Requires-Dist: httpx>=0.28.1
|
48
|
+
Requires-Dist: identify>=2.6.14
|
49
|
+
Requires-Dist: idna>=3.10
|
50
|
+
Requires-Dist: importlib_metadata>=8.7.0
|
51
|
+
Requires-Dist: maleo-constants>=0.0.6
|
52
|
+
Requires-Dist: maleo-crypto>=0.0.2
|
53
|
+
Requires-Dist: maleo-database>=0.0.18
|
54
|
+
Requires-Dist: maleo-dtos>=0.0.18
|
55
|
+
Requires-Dist: maleo-enums>=0.0.6
|
56
|
+
Requires-Dist: maleo-exceptions>=0.0.24
|
57
|
+
Requires-Dist: maleo-google>=0.0.12
|
58
|
+
Requires-Dist: maleo-infra>=0.0.4
|
59
|
+
Requires-Dist: maleo-logging>=0.0.8
|
60
|
+
Requires-Dist: maleo-middlewares>=0.0.12
|
61
|
+
Requires-Dist: maleo-mixins>=0.0.13
|
62
|
+
Requires-Dist: maleo-schemas>=0.0.21
|
63
|
+
Requires-Dist: maleo-types-base>=0.0.2
|
64
|
+
Requires-Dist: maleo-types-controllers>=0.0.1
|
65
|
+
Requires-Dist: maleo-types-enums>=0.0.4
|
66
|
+
Requires-Dist: maleo-utils>=0.0.5
|
67
|
+
Requires-Dist: motor>=3.7.1
|
68
|
+
Requires-Dist: mypy_extensions>=1.1.0
|
69
|
+
Requires-Dist: nodeenv>=1.9.1
|
70
|
+
Requires-Dist: opentelemetry-api>=1.36.0
|
71
|
+
Requires-Dist: opentelemetry-sdk>=1.36.0
|
72
|
+
Requires-Dist: opentelemetry-semantic-conventions>=0.57b0
|
73
|
+
Requires-Dist: packaging>=25.0
|
74
|
+
Requires-Dist: pathspec>=0.12.1
|
75
|
+
Requires-Dist: platformdirs>=4.4.0
|
76
|
+
Requires-Dist: pre_commit>=4.3.0
|
77
|
+
Requires-Dist: proto-plus>=1.26.1
|
78
|
+
Requires-Dist: protobuf>=6.32.0
|
79
|
+
Requires-Dist: psutil>=7.0.0
|
80
|
+
Requires-Dist: pyasn1>=0.6.1
|
81
|
+
Requires-Dist: pyasn1_modules>=0.4.2
|
82
|
+
Requires-Dist: pycparser>=2.22
|
83
|
+
Requires-Dist: pycryptodome>=3.23.0
|
84
|
+
Requires-Dist: pydantic>=2.11.7
|
85
|
+
Requires-Dist: pydantic-settings>=2.10.1
|
86
|
+
Requires-Dist: pydantic_core>=2.33.2
|
87
|
+
Requires-Dist: PyJWT>=2.10.1
|
88
|
+
Requires-Dist: pymongo>=4.14.1
|
89
|
+
Requires-Dist: python-dateutil>=2.9.0.post0
|
90
|
+
Requires-Dist: python-dotenv>=1.1.1
|
91
|
+
Requires-Dist: PyYAML>=6.0.2
|
92
|
+
Requires-Dist: redis>=6.4.0
|
93
|
+
Requires-Dist: requests>=2.32.5
|
94
|
+
Requires-Dist: rsa>=4.9.1
|
95
|
+
Requires-Dist: six>=1.17.0
|
96
|
+
Requires-Dist: sniffio>=1.3.1
|
97
|
+
Requires-Dist: SQLAlchemy>=2.0.43
|
98
|
+
Requires-Dist: starlette>=0.47.3
|
99
|
+
Requires-Dist: typing-inspection>=0.4.1
|
100
|
+
Requires-Dist: typing_extensions>=4.15.0
|
101
|
+
Requires-Dist: ua-parser>=1.0.1
|
102
|
+
Requires-Dist: ua-parser-builtins>=0.18.0.post1
|
103
|
+
Requires-Dist: urllib3>=2.5.0
|
104
|
+
Requires-Dist: user-agents>=2.2.0
|
105
|
+
Requires-Dist: virtualenv>=20.34.0
|
106
|
+
Requires-Dist: zipp>=3.23.0
|
107
|
+
Dynamic: license-file
|
108
|
+
|
109
|
+
# README #
|
110
|
+
|
111
|
+
This README would normally document whatever steps are necessary to get your application up and running.
|
112
|
+
|
113
|
+
### What is this repository for? ###
|
114
|
+
|
115
|
+
* Quick summary
|
116
|
+
* Version
|
117
|
+
* [Learn Markdown](https://bitbucket.org/tutorials/markdowndemo)
|
118
|
+
|
119
|
+
### How do I get set up? ###
|
120
|
+
|
121
|
+
* Summary of set up
|
122
|
+
* Configuration
|
123
|
+
* Dependencies
|
124
|
+
* Database configuration
|
125
|
+
* How to run tests
|
126
|
+
* Deployment instructions
|
127
|
+
|
128
|
+
### Contribution guidelines ###
|
129
|
+
|
130
|
+
* Writing tests
|
131
|
+
* Code review
|
132
|
+
* Other guidelines
|
133
|
+
|
134
|
+
### Who do I talk to? ###
|
135
|
+
|
136
|
+
* Repo owner or admin
|
137
|
+
* Other community or team contact
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# README #
|
2
|
+
|
3
|
+
This README would normally document whatever steps are necessary to get your application up and running.
|
4
|
+
|
5
|
+
### What is this repository for? ###
|
6
|
+
|
7
|
+
* Quick summary
|
8
|
+
* Version
|
9
|
+
* [Learn Markdown](https://bitbucket.org/tutorials/markdowndemo)
|
10
|
+
|
11
|
+
### How do I get set up? ###
|
12
|
+
|
13
|
+
* Summary of set up
|
14
|
+
* Configuration
|
15
|
+
* Dependencies
|
16
|
+
* Database configuration
|
17
|
+
* How to run tests
|
18
|
+
* Deployment instructions
|
19
|
+
|
20
|
+
### Contribution guidelines ###
|
21
|
+
|
22
|
+
* Writing tests
|
23
|
+
* Code review
|
24
|
+
* Other guidelines
|
25
|
+
|
26
|
+
### Who do I talk to? ###
|
27
|
+
|
28
|
+
* Repo owner or admin
|
29
|
+
* Other community or team contact
|
@@ -0,0 +1,137 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: maleo-managers
|
3
|
+
Version: 0.0.1
|
4
|
+
Summary: Managers package for MaleoSuite
|
5
|
+
Author-email: Agra Bima Yuda <agra@nexmedis.com>
|
6
|
+
License: Proprietary
|
7
|
+
Requires-Python: >=3.12
|
8
|
+
Description-Content-Type: text/markdown
|
9
|
+
License-File: LICENSE
|
10
|
+
Requires-Dist: aioredis>=2.0.1
|
11
|
+
Requires-Dist: annotated-types>=0.7.0
|
12
|
+
Requires-Dist: anyio>=4.10.0
|
13
|
+
Requires-Dist: async-timeout>=5.0.1
|
14
|
+
Requires-Dist: bcrypt>=4.3.0
|
15
|
+
Requires-Dist: black>=25.1.0
|
16
|
+
Requires-Dist: cachetools>=5.5.2
|
17
|
+
Requires-Dist: certifi>=2025.8.3
|
18
|
+
Requires-Dist: cffi>=2.0.0
|
19
|
+
Requires-Dist: cfgv>=3.4.0
|
20
|
+
Requires-Dist: charset-normalizer>=3.4.3
|
21
|
+
Requires-Dist: click>=8.2.1
|
22
|
+
Requires-Dist: cryptography>=45.0.7
|
23
|
+
Requires-Dist: distlib>=0.4.0
|
24
|
+
Requires-Dist: dnspython>=2.8.0
|
25
|
+
Requires-Dist: elastic-transport>=9.1.0
|
26
|
+
Requires-Dist: elasticsearch>=9.1.0
|
27
|
+
Requires-Dist: fastapi>=0.116.1
|
28
|
+
Requires-Dist: filelock>=3.19.1
|
29
|
+
Requires-Dist: google-api-core>=2.25.1
|
30
|
+
Requires-Dist: google-auth>=2.40.3
|
31
|
+
Requires-Dist: google-cloud-appengine-logging>=1.6.2
|
32
|
+
Requires-Dist: google-cloud-audit-log>=0.3.2
|
33
|
+
Requires-Dist: google-cloud-core>=2.4.3
|
34
|
+
Requires-Dist: google-cloud-logging>=3.12.1
|
35
|
+
Requires-Dist: google-cloud-pubsub>=2.31.1
|
36
|
+
Requires-Dist: google-cloud-secret-manager>=2.24.0
|
37
|
+
Requires-Dist: google-cloud-storage>=3.3.1
|
38
|
+
Requires-Dist: google-crc32c>=1.7.1
|
39
|
+
Requires-Dist: google-resumable-media>=2.7.2
|
40
|
+
Requires-Dist: googleapis-common-protos>=1.70.0
|
41
|
+
Requires-Dist: greenlet>=3.2.4
|
42
|
+
Requires-Dist: grpc-google-iam-v1>=0.14.2
|
43
|
+
Requires-Dist: grpcio>=1.74.0
|
44
|
+
Requires-Dist: grpcio-status>=1.74.0
|
45
|
+
Requires-Dist: h11>=0.16.0
|
46
|
+
Requires-Dist: httpcore>=1.0.9
|
47
|
+
Requires-Dist: httpx>=0.28.1
|
48
|
+
Requires-Dist: identify>=2.6.14
|
49
|
+
Requires-Dist: idna>=3.10
|
50
|
+
Requires-Dist: importlib_metadata>=8.7.0
|
51
|
+
Requires-Dist: maleo-constants>=0.0.6
|
52
|
+
Requires-Dist: maleo-crypto>=0.0.2
|
53
|
+
Requires-Dist: maleo-database>=0.0.18
|
54
|
+
Requires-Dist: maleo-dtos>=0.0.18
|
55
|
+
Requires-Dist: maleo-enums>=0.0.6
|
56
|
+
Requires-Dist: maleo-exceptions>=0.0.24
|
57
|
+
Requires-Dist: maleo-google>=0.0.12
|
58
|
+
Requires-Dist: maleo-infra>=0.0.4
|
59
|
+
Requires-Dist: maleo-logging>=0.0.8
|
60
|
+
Requires-Dist: maleo-middlewares>=0.0.12
|
61
|
+
Requires-Dist: maleo-mixins>=0.0.13
|
62
|
+
Requires-Dist: maleo-schemas>=0.0.21
|
63
|
+
Requires-Dist: maleo-types-base>=0.0.2
|
64
|
+
Requires-Dist: maleo-types-controllers>=0.0.1
|
65
|
+
Requires-Dist: maleo-types-enums>=0.0.4
|
66
|
+
Requires-Dist: maleo-utils>=0.0.5
|
67
|
+
Requires-Dist: motor>=3.7.1
|
68
|
+
Requires-Dist: mypy_extensions>=1.1.0
|
69
|
+
Requires-Dist: nodeenv>=1.9.1
|
70
|
+
Requires-Dist: opentelemetry-api>=1.36.0
|
71
|
+
Requires-Dist: opentelemetry-sdk>=1.36.0
|
72
|
+
Requires-Dist: opentelemetry-semantic-conventions>=0.57b0
|
73
|
+
Requires-Dist: packaging>=25.0
|
74
|
+
Requires-Dist: pathspec>=0.12.1
|
75
|
+
Requires-Dist: platformdirs>=4.4.0
|
76
|
+
Requires-Dist: pre_commit>=4.3.0
|
77
|
+
Requires-Dist: proto-plus>=1.26.1
|
78
|
+
Requires-Dist: protobuf>=6.32.0
|
79
|
+
Requires-Dist: psutil>=7.0.0
|
80
|
+
Requires-Dist: pyasn1>=0.6.1
|
81
|
+
Requires-Dist: pyasn1_modules>=0.4.2
|
82
|
+
Requires-Dist: pycparser>=2.22
|
83
|
+
Requires-Dist: pycryptodome>=3.23.0
|
84
|
+
Requires-Dist: pydantic>=2.11.7
|
85
|
+
Requires-Dist: pydantic-settings>=2.10.1
|
86
|
+
Requires-Dist: pydantic_core>=2.33.2
|
87
|
+
Requires-Dist: PyJWT>=2.10.1
|
88
|
+
Requires-Dist: pymongo>=4.14.1
|
89
|
+
Requires-Dist: python-dateutil>=2.9.0.post0
|
90
|
+
Requires-Dist: python-dotenv>=1.1.1
|
91
|
+
Requires-Dist: PyYAML>=6.0.2
|
92
|
+
Requires-Dist: redis>=6.4.0
|
93
|
+
Requires-Dist: requests>=2.32.5
|
94
|
+
Requires-Dist: rsa>=4.9.1
|
95
|
+
Requires-Dist: six>=1.17.0
|
96
|
+
Requires-Dist: sniffio>=1.3.1
|
97
|
+
Requires-Dist: SQLAlchemy>=2.0.43
|
98
|
+
Requires-Dist: starlette>=0.47.3
|
99
|
+
Requires-Dist: typing-inspection>=0.4.1
|
100
|
+
Requires-Dist: typing_extensions>=4.15.0
|
101
|
+
Requires-Dist: ua-parser>=1.0.1
|
102
|
+
Requires-Dist: ua-parser-builtins>=0.18.0.post1
|
103
|
+
Requires-Dist: urllib3>=2.5.0
|
104
|
+
Requires-Dist: user-agents>=2.2.0
|
105
|
+
Requires-Dist: virtualenv>=20.34.0
|
106
|
+
Requires-Dist: zipp>=3.23.0
|
107
|
+
Dynamic: license-file
|
108
|
+
|
109
|
+
# README #
|
110
|
+
|
111
|
+
This README would normally document whatever steps are necessary to get your application up and running.
|
112
|
+
|
113
|
+
### What is this repository for? ###
|
114
|
+
|
115
|
+
* Quick summary
|
116
|
+
* Version
|
117
|
+
* [Learn Markdown](https://bitbucket.org/tutorials/markdowndemo)
|
118
|
+
|
119
|
+
### How do I get set up? ###
|
120
|
+
|
121
|
+
* Summary of set up
|
122
|
+
* Configuration
|
123
|
+
* Dependencies
|
124
|
+
* Database configuration
|
125
|
+
* How to run tests
|
126
|
+
* Deployment instructions
|
127
|
+
|
128
|
+
### Contribution guidelines ###
|
129
|
+
|
130
|
+
* Writing tests
|
131
|
+
* Code review
|
132
|
+
* Other guidelines
|
133
|
+
|
134
|
+
### Who do I talk to? ###
|
135
|
+
|
136
|
+
* Repo owner or admin
|
137
|
+
* Other community or team contact
|
@@ -0,0 +1,18 @@
|
|
1
|
+
LICENSE
|
2
|
+
README.md
|
3
|
+
pyproject.toml
|
4
|
+
maleo_managers.egg-info/PKG-INFO
|
5
|
+
maleo_managers.egg-info/SOURCES.txt
|
6
|
+
maleo_managers.egg-info/dependency_links.txt
|
7
|
+
maleo_managers.egg-info/requires.txt
|
8
|
+
maleo_managers.egg-info/top_level.txt
|
9
|
+
src/__init__.py
|
10
|
+
src/config.py
|
11
|
+
src/credential.py
|
12
|
+
src/key.py
|
13
|
+
src/service.py
|
14
|
+
src/client/__init__.py
|
15
|
+
src/client/config.py
|
16
|
+
src/client/http.py
|
17
|
+
src/client/maleo/__init__.py
|
18
|
+
src/client/maleo/config.py
|
@@ -0,0 +1 @@
|
|
1
|
+
|
@@ -0,0 +1,97 @@
|
|
1
|
+
aioredis>=2.0.1
|
2
|
+
annotated-types>=0.7.0
|
3
|
+
anyio>=4.10.0
|
4
|
+
async-timeout>=5.0.1
|
5
|
+
bcrypt>=4.3.0
|
6
|
+
black>=25.1.0
|
7
|
+
cachetools>=5.5.2
|
8
|
+
certifi>=2025.8.3
|
9
|
+
cffi>=2.0.0
|
10
|
+
cfgv>=3.4.0
|
11
|
+
charset-normalizer>=3.4.3
|
12
|
+
click>=8.2.1
|
13
|
+
cryptography>=45.0.7
|
14
|
+
distlib>=0.4.0
|
15
|
+
dnspython>=2.8.0
|
16
|
+
elastic-transport>=9.1.0
|
17
|
+
elasticsearch>=9.1.0
|
18
|
+
fastapi>=0.116.1
|
19
|
+
filelock>=3.19.1
|
20
|
+
google-api-core>=2.25.1
|
21
|
+
google-auth>=2.40.3
|
22
|
+
google-cloud-appengine-logging>=1.6.2
|
23
|
+
google-cloud-audit-log>=0.3.2
|
24
|
+
google-cloud-core>=2.4.3
|
25
|
+
google-cloud-logging>=3.12.1
|
26
|
+
google-cloud-pubsub>=2.31.1
|
27
|
+
google-cloud-secret-manager>=2.24.0
|
28
|
+
google-cloud-storage>=3.3.1
|
29
|
+
google-crc32c>=1.7.1
|
30
|
+
google-resumable-media>=2.7.2
|
31
|
+
googleapis-common-protos>=1.70.0
|
32
|
+
greenlet>=3.2.4
|
33
|
+
grpc-google-iam-v1>=0.14.2
|
34
|
+
grpcio>=1.74.0
|
35
|
+
grpcio-status>=1.74.0
|
36
|
+
h11>=0.16.0
|
37
|
+
httpcore>=1.0.9
|
38
|
+
httpx>=0.28.1
|
39
|
+
identify>=2.6.14
|
40
|
+
idna>=3.10
|
41
|
+
importlib_metadata>=8.7.0
|
42
|
+
maleo-constants>=0.0.6
|
43
|
+
maleo-crypto>=0.0.2
|
44
|
+
maleo-database>=0.0.18
|
45
|
+
maleo-dtos>=0.0.18
|
46
|
+
maleo-enums>=0.0.6
|
47
|
+
maleo-exceptions>=0.0.24
|
48
|
+
maleo-google>=0.0.12
|
49
|
+
maleo-infra>=0.0.4
|
50
|
+
maleo-logging>=0.0.8
|
51
|
+
maleo-middlewares>=0.0.12
|
52
|
+
maleo-mixins>=0.0.13
|
53
|
+
maleo-schemas>=0.0.21
|
54
|
+
maleo-types-base>=0.0.2
|
55
|
+
maleo-types-controllers>=0.0.1
|
56
|
+
maleo-types-enums>=0.0.4
|
57
|
+
maleo-utils>=0.0.5
|
58
|
+
motor>=3.7.1
|
59
|
+
mypy_extensions>=1.1.0
|
60
|
+
nodeenv>=1.9.1
|
61
|
+
opentelemetry-api>=1.36.0
|
62
|
+
opentelemetry-sdk>=1.36.0
|
63
|
+
opentelemetry-semantic-conventions>=0.57b0
|
64
|
+
packaging>=25.0
|
65
|
+
pathspec>=0.12.1
|
66
|
+
platformdirs>=4.4.0
|
67
|
+
pre_commit>=4.3.0
|
68
|
+
proto-plus>=1.26.1
|
69
|
+
protobuf>=6.32.0
|
70
|
+
psutil>=7.0.0
|
71
|
+
pyasn1>=0.6.1
|
72
|
+
pyasn1_modules>=0.4.2
|
73
|
+
pycparser>=2.22
|
74
|
+
pycryptodome>=3.23.0
|
75
|
+
pydantic>=2.11.7
|
76
|
+
pydantic-settings>=2.10.1
|
77
|
+
pydantic_core>=2.33.2
|
78
|
+
PyJWT>=2.10.1
|
79
|
+
pymongo>=4.14.1
|
80
|
+
python-dateutil>=2.9.0.post0
|
81
|
+
python-dotenv>=1.1.1
|
82
|
+
PyYAML>=6.0.2
|
83
|
+
redis>=6.4.0
|
84
|
+
requests>=2.32.5
|
85
|
+
rsa>=4.9.1
|
86
|
+
six>=1.17.0
|
87
|
+
sniffio>=1.3.1
|
88
|
+
SQLAlchemy>=2.0.43
|
89
|
+
starlette>=0.47.3
|
90
|
+
typing-inspection>=0.4.1
|
91
|
+
typing_extensions>=4.15.0
|
92
|
+
ua-parser>=1.0.1
|
93
|
+
ua-parser-builtins>=0.18.0.post1
|
94
|
+
urllib3>=2.5.0
|
95
|
+
user-agents>=2.2.0
|
96
|
+
virtualenv>=20.34.0
|
97
|
+
zipp>=3.23.0
|
@@ -0,0 +1 @@
|
|
1
|
+
maleo
|
@@ -0,0 +1,130 @@
|
|
1
|
+
[build-system]
|
2
|
+
requires = ["setuptools", "wheel"]
|
3
|
+
build-backend = "setuptools.build_meta"
|
4
|
+
|
5
|
+
[project]
|
6
|
+
name = "maleo-managers"
|
7
|
+
version = "0.0.1"
|
8
|
+
description = "Managers package for MaleoSuite"
|
9
|
+
authors = [
|
10
|
+
{ name = "Agra Bima Yuda", email = "agra@nexmedis.com" }
|
11
|
+
]
|
12
|
+
license = { text = "Proprietary"}
|
13
|
+
readme = "README.md"
|
14
|
+
requires-python = ">=3.12"
|
15
|
+
dependencies = [
|
16
|
+
"aioredis>=2.0.1",
|
17
|
+
"annotated-types>=0.7.0",
|
18
|
+
"anyio>=4.10.0",
|
19
|
+
"async-timeout>=5.0.1",
|
20
|
+
"bcrypt>=4.3.0",
|
21
|
+
"black>=25.1.0",
|
22
|
+
"cachetools>=5.5.2",
|
23
|
+
"certifi>=2025.8.3",
|
24
|
+
"cffi>=2.0.0",
|
25
|
+
"cfgv>=3.4.0",
|
26
|
+
"charset-normalizer>=3.4.3",
|
27
|
+
"click>=8.2.1",
|
28
|
+
"cryptography>=45.0.7",
|
29
|
+
"distlib>=0.4.0",
|
30
|
+
"dnspython>=2.8.0",
|
31
|
+
"elastic-transport>=9.1.0",
|
32
|
+
"elasticsearch>=9.1.0",
|
33
|
+
"fastapi>=0.116.1",
|
34
|
+
"filelock>=3.19.1",
|
35
|
+
"google-api-core>=2.25.1",
|
36
|
+
"google-auth>=2.40.3",
|
37
|
+
"google-cloud-appengine-logging>=1.6.2",
|
38
|
+
"google-cloud-audit-log>=0.3.2",
|
39
|
+
"google-cloud-core>=2.4.3",
|
40
|
+
"google-cloud-logging>=3.12.1",
|
41
|
+
"google-cloud-pubsub>=2.31.1",
|
42
|
+
"google-cloud-secret-manager>=2.24.0",
|
43
|
+
"google-cloud-storage>=3.3.1",
|
44
|
+
"google-crc32c>=1.7.1",
|
45
|
+
"google-resumable-media>=2.7.2",
|
46
|
+
"googleapis-common-protos>=1.70.0",
|
47
|
+
"greenlet>=3.2.4",
|
48
|
+
"grpc-google-iam-v1>=0.14.2",
|
49
|
+
"grpcio>=1.74.0",
|
50
|
+
"grpcio-status>=1.74.0",
|
51
|
+
"h11>=0.16.0",
|
52
|
+
"httpcore>=1.0.9",
|
53
|
+
"httpx>=0.28.1",
|
54
|
+
"identify>=2.6.14",
|
55
|
+
"idna>=3.10",
|
56
|
+
"importlib_metadata>=8.7.0",
|
57
|
+
"maleo-constants>=0.0.6",
|
58
|
+
"maleo-crypto>=0.0.2",
|
59
|
+
"maleo-database>=0.0.18",
|
60
|
+
"maleo-dtos>=0.0.18",
|
61
|
+
"maleo-enums>=0.0.6",
|
62
|
+
"maleo-exceptions>=0.0.24",
|
63
|
+
"maleo-google>=0.0.12",
|
64
|
+
"maleo-infra>=0.0.4",
|
65
|
+
"maleo-logging>=0.0.8",
|
66
|
+
"maleo-middlewares>=0.0.12",
|
67
|
+
"maleo-mixins>=0.0.13",
|
68
|
+
"maleo-schemas>=0.0.21",
|
69
|
+
"maleo-types-base>=0.0.2",
|
70
|
+
"maleo-types-controllers>=0.0.1",
|
71
|
+
"maleo-types-enums>=0.0.4",
|
72
|
+
"maleo-utils>=0.0.5",
|
73
|
+
"motor>=3.7.1",
|
74
|
+
"mypy_extensions>=1.1.0",
|
75
|
+
"nodeenv>=1.9.1",
|
76
|
+
"opentelemetry-api>=1.36.0",
|
77
|
+
"opentelemetry-sdk>=1.36.0",
|
78
|
+
"opentelemetry-semantic-conventions>=0.57b0",
|
79
|
+
"packaging>=25.0",
|
80
|
+
"pathspec>=0.12.1",
|
81
|
+
"platformdirs>=4.4.0",
|
82
|
+
"pre_commit>=4.3.0",
|
83
|
+
"proto-plus>=1.26.1",
|
84
|
+
"protobuf>=6.32.0",
|
85
|
+
"psutil>=7.0.0",
|
86
|
+
"pyasn1>=0.6.1",
|
87
|
+
"pyasn1_modules>=0.4.2",
|
88
|
+
"pycparser>=2.22",
|
89
|
+
"pycryptodome>=3.23.0",
|
90
|
+
"pydantic>=2.11.7",
|
91
|
+
"pydantic-settings>=2.10.1",
|
92
|
+
"pydantic_core>=2.33.2",
|
93
|
+
"PyJWT>=2.10.1",
|
94
|
+
"pymongo>=4.14.1",
|
95
|
+
"python-dateutil>=2.9.0.post0",
|
96
|
+
"python-dotenv>=1.1.1",
|
97
|
+
"PyYAML>=6.0.2",
|
98
|
+
"redis>=6.4.0",
|
99
|
+
"requests>=2.32.5",
|
100
|
+
"rsa>=4.9.1",
|
101
|
+
"six>=1.17.0",
|
102
|
+
"sniffio>=1.3.1",
|
103
|
+
"SQLAlchemy>=2.0.43",
|
104
|
+
"starlette>=0.47.3",
|
105
|
+
"typing-inspection>=0.4.1",
|
106
|
+
"typing_extensions>=4.15.0",
|
107
|
+
"ua-parser>=1.0.1",
|
108
|
+
"ua-parser-builtins>=0.18.0.post1",
|
109
|
+
"urllib3>=2.5.0",
|
110
|
+
"user-agents>=2.2.0",
|
111
|
+
"virtualenv>=20.34.0",
|
112
|
+
"zipp>=3.23.0",
|
113
|
+
]
|
114
|
+
|
115
|
+
[tool.setuptools]
|
116
|
+
packages = [
|
117
|
+
"maleo.managers",
|
118
|
+
"maleo.managers.client",
|
119
|
+
"maleo.managers.client.maleo"
|
120
|
+
]
|
121
|
+
|
122
|
+
[tool.setuptools.package-data]
|
123
|
+
"maleo.managers" = ["*.json", "*.yaml"]
|
124
|
+
"maleo.managers.client" = ["*.json", "*.yaml"]
|
125
|
+
"maleo.managers.client.maleo" = ["*.json", "*.yaml"]
|
126
|
+
|
127
|
+
[tool.setuptools.package-dir]
|
128
|
+
"maleo.managers" = "src"
|
129
|
+
"maleo.managers.client" = "src/client"
|
130
|
+
"maleo.managers.client.maleo" = "src/client/maleo"
|
File without changes
|
File without changes
|
@@ -0,0 +1,17 @@
|
|
1
|
+
from pydantic import BaseModel, Field
|
2
|
+
from typing import Generic, Optional, TypeVar
|
3
|
+
from .maleo.config import MaleoClientsConfigT
|
4
|
+
|
5
|
+
|
6
|
+
class Config(BaseModel, Generic[MaleoClientsConfigT]):
|
7
|
+
maleo: MaleoClientsConfigT = Field(
|
8
|
+
...,
|
9
|
+
description="Maleo client's configurations",
|
10
|
+
)
|
11
|
+
|
12
|
+
|
13
|
+
ConfigT = TypeVar("ConfigT", bound=Optional[Config])
|
14
|
+
|
15
|
+
|
16
|
+
class ConfigMixin(BaseModel, Generic[ConfigT]):
|
17
|
+
client: ConfigT = Field(..., description="Client config")
|
@@ -0,0 +1,24 @@
|
|
1
|
+
import httpx
|
2
|
+
from contextlib import asynccontextmanager
|
3
|
+
from typing import AsyncGenerator
|
4
|
+
|
5
|
+
|
6
|
+
class HTTPClientManager:
|
7
|
+
def __init__(self) -> None:
|
8
|
+
self._client = httpx.AsyncClient()
|
9
|
+
|
10
|
+
async def _client_handler(self) -> AsyncGenerator[httpx.AsyncClient, None]:
|
11
|
+
"""Async generator for client handling"""
|
12
|
+
if self._client is None or (
|
13
|
+
self._client is not None and self._client.is_closed
|
14
|
+
):
|
15
|
+
self._client = httpx.AsyncClient()
|
16
|
+
yield self._client
|
17
|
+
|
18
|
+
async def inject(self) -> AsyncGenerator[httpx.AsyncClient, None]:
|
19
|
+
return self._client_handler()
|
20
|
+
|
21
|
+
@asynccontextmanager
|
22
|
+
async def get(self) -> AsyncGenerator[httpx.AsyncClient, None]:
|
23
|
+
async for client in self._client_handler():
|
24
|
+
yield client
|
@@ -0,0 +1,176 @@
|
|
1
|
+
from abc import ABC, abstractmethod
|
2
|
+
from uuid import UUID
|
3
|
+
from Crypto.PublicKey.RSA import RsaKey
|
4
|
+
from datetime import datetime
|
5
|
+
from httpx import Response
|
6
|
+
from pydantic import ValidationError
|
7
|
+
from typing import Optional
|
8
|
+
from maleo.database.managers import RedisManager
|
9
|
+
from maleo.dtos.authentication import GenericAuthentication
|
10
|
+
from maleo.dtos.contexts.operation import OperationContext, generate_operation_context
|
11
|
+
from maleo.dtos.contexts.request import RequestContext
|
12
|
+
from maleo.dtos.contexts.response import ResponseContext
|
13
|
+
from maleo.dtos.contexts.service import ServiceContext
|
14
|
+
from maleo.dtos.resource import Resource
|
15
|
+
from maleo.enums.cache import Origin as CacheOrigin, Layer as CacheLayer
|
16
|
+
from maleo.enums.operation import OperationType, Origin, Layer, Target
|
17
|
+
from maleo.exceptions import (
|
18
|
+
UnprocessableEntity,
|
19
|
+
InternalServerError,
|
20
|
+
from_resource_http_request,
|
21
|
+
)
|
22
|
+
from maleo.logging.config import Config as LogConfig
|
23
|
+
from maleo.logging.logger import Client
|
24
|
+
from maleo.mixins.timestamp import OperationTimestamp
|
25
|
+
from maleo.schemas.operation.resource import AllResourceOperationAction
|
26
|
+
from maleo.schemas.response import ErrorResponse
|
27
|
+
from maleo.utils.cache import build_namespace
|
28
|
+
from ...credential import CredentialManager
|
29
|
+
from ..http import HTTPClientManager
|
30
|
+
from .config import MaleoClientConfig
|
31
|
+
|
32
|
+
|
33
|
+
class MaleoClientService:
|
34
|
+
def __init__(
|
35
|
+
self,
|
36
|
+
config: MaleoClientConfig,
|
37
|
+
logger: Client,
|
38
|
+
credential_manager: CredentialManager,
|
39
|
+
http_client_manager: HTTPClientManager,
|
40
|
+
private_key: RsaKey,
|
41
|
+
redis: RedisManager,
|
42
|
+
service_context: ServiceContext,
|
43
|
+
):
|
44
|
+
self._config = config
|
45
|
+
self._logger = logger
|
46
|
+
self._credential_manager = credential_manager
|
47
|
+
self._http_client_manager = http_client_manager
|
48
|
+
self._private_key = private_key
|
49
|
+
self._redis = redis
|
50
|
+
self._service_context = service_context
|
51
|
+
|
52
|
+
self._namespace = build_namespace(
|
53
|
+
base=self._service_context.key,
|
54
|
+
client=self._config.key,
|
55
|
+
origin=CacheOrigin.CLIENT,
|
56
|
+
layer=CacheLayer.SERVICE,
|
57
|
+
)
|
58
|
+
|
59
|
+
self._operation_context = generate_operation_context(
|
60
|
+
origin=Origin.CLIENT, layer=Layer.SERVICE, target=Target.INTERNAL
|
61
|
+
)
|
62
|
+
|
63
|
+
def raise_resource_http_request_error(
|
64
|
+
self,
|
65
|
+
*,
|
66
|
+
executed_at: datetime,
|
67
|
+
response: Response,
|
68
|
+
operation_id: UUID,
|
69
|
+
operation_context: OperationContext,
|
70
|
+
operation_action: AllResourceOperationAction,
|
71
|
+
request_context: Optional[RequestContext],
|
72
|
+
authentication: Optional[GenericAuthentication],
|
73
|
+
resource: Resource,
|
74
|
+
):
|
75
|
+
"""Handle HTTP error response and raise appropriate exception"""
|
76
|
+
operation_timestamp = OperationTimestamp.completed_now(executed_at)
|
77
|
+
|
78
|
+
response_context = ResponseContext(
|
79
|
+
status_code=response.status_code,
|
80
|
+
media_type=response.headers.get("content-type"),
|
81
|
+
headers=response.headers.multi_items(),
|
82
|
+
body=response.content,
|
83
|
+
)
|
84
|
+
|
85
|
+
try:
|
86
|
+
error_response = ErrorResponse.model_validate(response.json())
|
87
|
+
except ValidationError as ve:
|
88
|
+
exception = UnprocessableEntity(
|
89
|
+
OperationType.RESOURCE,
|
90
|
+
service_context=self._service_context,
|
91
|
+
operation_id=operation_id,
|
92
|
+
operation_context=operation_context,
|
93
|
+
operation_timestamp=operation_timestamp,
|
94
|
+
operation_summary="Validation error occured while validating resource http request's response",
|
95
|
+
operation_action=operation_action,
|
96
|
+
request_context=request_context,
|
97
|
+
authentication=authentication,
|
98
|
+
resource=resource,
|
99
|
+
details=ve.errors(),
|
100
|
+
)
|
101
|
+
raise exception from ve
|
102
|
+
except Exception as e:
|
103
|
+
exception = InternalServerError(
|
104
|
+
OperationType.RESOURCE,
|
105
|
+
service_context=self._service_context,
|
106
|
+
operation_id=operation_id,
|
107
|
+
operation_context=self._operation_context,
|
108
|
+
operation_timestamp=operation_timestamp,
|
109
|
+
operation_summary="Unexpected error occured while validating resource http request's response",
|
110
|
+
operation_action=operation_action,
|
111
|
+
request_context=request_context,
|
112
|
+
authentication=authentication,
|
113
|
+
resource=resource,
|
114
|
+
details={
|
115
|
+
"exc_type": type(e).__name__,
|
116
|
+
"exc_data": {
|
117
|
+
"message": str(e),
|
118
|
+
"args": e.args,
|
119
|
+
},
|
120
|
+
},
|
121
|
+
)
|
122
|
+
raise exception from e
|
123
|
+
|
124
|
+
exception = from_resource_http_request(
|
125
|
+
logger=self._logger,
|
126
|
+
service_context=self._service_context,
|
127
|
+
operation_id=operation_id,
|
128
|
+
operation_context=self._operation_context,
|
129
|
+
operation_timestamp=operation_timestamp,
|
130
|
+
operation_action=operation_action,
|
131
|
+
request_context=request_context,
|
132
|
+
authentication=authentication,
|
133
|
+
resource=resource,
|
134
|
+
response_context=response_context,
|
135
|
+
response=error_response,
|
136
|
+
)
|
137
|
+
raise exception
|
138
|
+
|
139
|
+
|
140
|
+
class MaleoClientManager(ABC):
|
141
|
+
def __init__(
|
142
|
+
self,
|
143
|
+
config: MaleoClientConfig,
|
144
|
+
log_config: LogConfig,
|
145
|
+
credential_manager: CredentialManager,
|
146
|
+
private_key: RsaKey,
|
147
|
+
redis: RedisManager,
|
148
|
+
service_context: Optional[ServiceContext] = None,
|
149
|
+
):
|
150
|
+
self._config = config
|
151
|
+
self._log_config = log_config
|
152
|
+
|
153
|
+
self._key = self._config.key
|
154
|
+
self._name = self._config.name
|
155
|
+
|
156
|
+
self._logger = Client(
|
157
|
+
environment=self._service_context.environment,
|
158
|
+
service_key=self._service_context.key,
|
159
|
+
client_key=self._key,
|
160
|
+
config=log_config,
|
161
|
+
)
|
162
|
+
|
163
|
+
self._credential_manager = credential_manager
|
164
|
+
self._http_client_manager = HTTPClientManager()
|
165
|
+
self._private_key = private_key
|
166
|
+
self._redis = redis
|
167
|
+
|
168
|
+
self._service_context = (
|
169
|
+
service_context
|
170
|
+
if service_context is not None
|
171
|
+
else ServiceContext.from_env()
|
172
|
+
)
|
173
|
+
|
174
|
+
@abstractmethod
|
175
|
+
def initalize_services(self):
|
176
|
+
pass
|
@@ -0,0 +1,92 @@
|
|
1
|
+
from pydantic import BaseModel, Field
|
2
|
+
from typing import Optional, TypeVar
|
3
|
+
from maleo.enums.environment import Environment
|
4
|
+
from maleo.enums.service import Key
|
5
|
+
|
6
|
+
|
7
|
+
class MaleoClientConfig(BaseModel):
|
8
|
+
environment: Environment = Field(..., description="Client's environment")
|
9
|
+
key: Key = Field(..., description="Client's key")
|
10
|
+
name: str = Field(..., description="Client's name")
|
11
|
+
url: str = Field(..., description="Client's URL")
|
12
|
+
|
13
|
+
|
14
|
+
class MaleoTelemetryClientConfigMixin(BaseModel):
|
15
|
+
telemetry: MaleoClientConfig = Field(
|
16
|
+
..., description="MaleoTelemetry client's configuration"
|
17
|
+
)
|
18
|
+
|
19
|
+
|
20
|
+
class MaleoMetadataClientConfigMixin(BaseModel):
|
21
|
+
metadata: MaleoClientConfig = Field(
|
22
|
+
..., description="MaleoMetadata client's configuration"
|
23
|
+
)
|
24
|
+
|
25
|
+
|
26
|
+
class MaleoIdentityClientConfigMixin(BaseModel):
|
27
|
+
identity: MaleoClientConfig = Field(
|
28
|
+
..., description="MaleoIdentity client's configuration"
|
29
|
+
)
|
30
|
+
|
31
|
+
|
32
|
+
class MaleoAccessClientConfigMixin(BaseModel):
|
33
|
+
access: MaleoClientConfig = Field(
|
34
|
+
..., description="MaleoAccess client's configuration"
|
35
|
+
)
|
36
|
+
|
37
|
+
|
38
|
+
class MaleoWorkshopClientConfigMixin(BaseModel):
|
39
|
+
workshop: MaleoClientConfig = Field(
|
40
|
+
..., description="MaleoWorkshop client's configuration"
|
41
|
+
)
|
42
|
+
|
43
|
+
|
44
|
+
class MaleoResearchClientConfigMixin(BaseModel):
|
45
|
+
research: MaleoClientConfig = Field(
|
46
|
+
..., description="MaleoResearch client's configuration"
|
47
|
+
)
|
48
|
+
|
49
|
+
|
50
|
+
class MaleoSOAPIEClientConfigMixin(BaseModel):
|
51
|
+
soapie: MaleoClientConfig = Field(
|
52
|
+
..., description="MaleoSOAPIE client's configuration"
|
53
|
+
)
|
54
|
+
|
55
|
+
|
56
|
+
class MaleoMedixClientConfigMixin(BaseModel):
|
57
|
+
medix: MaleoClientConfig = Field(
|
58
|
+
..., description="MaleoMedix client's configuration"
|
59
|
+
)
|
60
|
+
|
61
|
+
|
62
|
+
class MaleoDICOMClientConfigMixin(BaseModel):
|
63
|
+
dicom: MaleoClientConfig = Field(
|
64
|
+
..., description="MaleoDICOM client's configuration"
|
65
|
+
)
|
66
|
+
|
67
|
+
|
68
|
+
class MaleoScribeClientConfigMixin(BaseModel):
|
69
|
+
scribe: MaleoClientConfig = Field(
|
70
|
+
..., description="MaleoScribe client's configuration"
|
71
|
+
)
|
72
|
+
|
73
|
+
|
74
|
+
class MaleoCDSClientConfigMixin(BaseModel):
|
75
|
+
cds: MaleoClientConfig = Field(..., description="MaleoCDS client's configuration")
|
76
|
+
|
77
|
+
|
78
|
+
class MaleoImagingClientConfigMixin(BaseModel):
|
79
|
+
imaging: MaleoClientConfig = Field(
|
80
|
+
..., description="MaleoImaging client's configuration"
|
81
|
+
)
|
82
|
+
|
83
|
+
|
84
|
+
class MaleoMCUClientConfigMixin(BaseModel):
|
85
|
+
mcu: MaleoClientConfig = Field(..., description="MaleoMCU client's configuration")
|
86
|
+
|
87
|
+
|
88
|
+
class MaleoClientsConfig(BaseModel):
|
89
|
+
pass
|
90
|
+
|
91
|
+
|
92
|
+
MaleoClientsConfigT = TypeVar("MaleoClientsConfigT", bound=Optional[MaleoClientsConfig])
|
@@ -0,0 +1,49 @@
|
|
1
|
+
from pathlib import Path
|
2
|
+
from pydantic import BaseModel, Field
|
3
|
+
from typing import Generic, Type, TypeVar
|
4
|
+
from maleo.database.config import ConfigsT as DatabaseConfigsT
|
5
|
+
from maleo.dtos.settings import ServiceSettings
|
6
|
+
from maleo.google.pubsub import ConfigT as PubSubConfigT
|
7
|
+
from maleo.google.secret import Format, GoogleSecretManager
|
8
|
+
from maleo.infra.config import Config as InfraConfig
|
9
|
+
from maleo.middlewares.config import Config as MiddlewareConfig
|
10
|
+
from maleo.types.base.uuid import OptionalUUID
|
11
|
+
from maleo.utils.loaders.yaml import from_path, from_string
|
12
|
+
from .client.config import ConfigT as ClientConfigT
|
13
|
+
|
14
|
+
|
15
|
+
class Config(BaseModel, Generic[ClientConfigT, DatabaseConfigsT]):
|
16
|
+
cient: ClientConfigT = Field(..., description="Client config")
|
17
|
+
database: DatabaseConfigsT = Field(..., description="Database configs")
|
18
|
+
infra: InfraConfig = Field(..., description="Infra config")
|
19
|
+
middleware: MiddlewareConfig = Field(..., description="Middleware config")
|
20
|
+
pubsub: PubSubConfigT = Field(..., description="PubSub config")
|
21
|
+
|
22
|
+
|
23
|
+
ConfigT = TypeVar("ConfigT", bound=Config)
|
24
|
+
|
25
|
+
|
26
|
+
class ConfigManager(Generic[ConfigT]):
|
27
|
+
def __init__(
|
28
|
+
self,
|
29
|
+
settings: ServiceSettings,
|
30
|
+
secret_manager: GoogleSecretManager,
|
31
|
+
config_cls: Type[ConfigT],
|
32
|
+
operation_id: OptionalUUID = None,
|
33
|
+
) -> None:
|
34
|
+
use_local = settings.USE_LOCAL_CONFIG
|
35
|
+
config_path = settings.CONFIG_PATH
|
36
|
+
|
37
|
+
if use_local and config_path is not None and isinstance(config_path, str):
|
38
|
+
config_path = Path(config_path)
|
39
|
+
if config_path.exists() and config_path.is_file():
|
40
|
+
data = from_path(config_path)
|
41
|
+
self.config: ConfigT = config_cls.model_validate(data)
|
42
|
+
return
|
43
|
+
|
44
|
+
name = f"{settings.SERVICE_KEY}-config-{settings.ENVIRONMENT}"
|
45
|
+
read_secret = secret_manager.read(
|
46
|
+
Format.STRING, name=name, operation_id=operation_id
|
47
|
+
)
|
48
|
+
data = from_string(read_secret.data.value)
|
49
|
+
self.config: ConfigT = config_cls.model_validate(data)
|
@@ -0,0 +1,119 @@
|
|
1
|
+
from functools import cached_property
|
2
|
+
from Crypto.PublicKey.RSA import RsaKey
|
3
|
+
from google.oauth2.service_account import Credentials
|
4
|
+
from maleo.crypto.token import encode
|
5
|
+
from maleo.dtos.credential import Credential, MaleoCredential
|
6
|
+
from maleo.enums.environment import Environment
|
7
|
+
from maleo.enums.token import TokenType
|
8
|
+
from maleo.google.secret import Format, GoogleSecretManager
|
9
|
+
from maleo.dtos.authentication import (
|
10
|
+
PrivilegedCredentials,
|
11
|
+
AuthenticatedUser,
|
12
|
+
PrivilegedAuthentication,
|
13
|
+
)
|
14
|
+
from maleo.dtos.token import (
|
15
|
+
PrivilegedCredentialPayload,
|
16
|
+
TimestampPayload,
|
17
|
+
PrivilegedPayload,
|
18
|
+
PrivilegedAuthenticationToken,
|
19
|
+
)
|
20
|
+
from maleo.types.base.string import OptionalString
|
21
|
+
from maleo.types.base.uuid import OptionalUUID
|
22
|
+
from maleo.utils.loaders.yaml import from_string
|
23
|
+
|
24
|
+
|
25
|
+
class CredentialManager:
|
26
|
+
def __init__(
|
27
|
+
self,
|
28
|
+
environment: Environment,
|
29
|
+
private_key: RsaKey,
|
30
|
+
secret_manager: GoogleSecretManager,
|
31
|
+
operation_id: OptionalUUID = None,
|
32
|
+
) -> None:
|
33
|
+
self._private_key = private_key
|
34
|
+
self._secret_manager = secret_manager
|
35
|
+
|
36
|
+
name = f"maleo-internal-credentials-{environment}"
|
37
|
+
read_secret = secret_manager.read(
|
38
|
+
Format.STRING, name=name, operation_id=operation_id
|
39
|
+
)
|
40
|
+
data = from_string(read_secret.data.value)
|
41
|
+
self.maleo_credentials = MaleoCredential.model_validate(data)
|
42
|
+
|
43
|
+
def assign_google_credentials(self, google_credentials: Credentials) -> None:
|
44
|
+
self.google_credentials = google_credentials
|
45
|
+
|
46
|
+
@property
|
47
|
+
def credentials(self) -> Credential:
|
48
|
+
if not hasattr(self, "google_credentials") or not isinstance(
|
49
|
+
self.google_credentials, Credentials
|
50
|
+
):
|
51
|
+
raise ValueError("Google credential not initialized")
|
52
|
+
if not hasattr(self, "maleo_credentials") or not isinstance(
|
53
|
+
self.maleo_credentials, MaleoCredential
|
54
|
+
):
|
55
|
+
raise ValueError("Maleo credential not initialized")
|
56
|
+
return Credential(google=self.google_credentials, maleo=self.maleo_credentials)
|
57
|
+
|
58
|
+
@cached_property
|
59
|
+
def token_credential_payload(self) -> PrivilegedCredentialPayload:
|
60
|
+
return PrivilegedCredentialPayload(
|
61
|
+
iss=None,
|
62
|
+
sub=str(self.maleo_credentials.id),
|
63
|
+
sr=["administrator"],
|
64
|
+
u_i=self.maleo_credentials.id,
|
65
|
+
u_uu=self.maleo_credentials.uuid,
|
66
|
+
u_u=self.maleo_credentials.username,
|
67
|
+
u_e=self.maleo_credentials.email,
|
68
|
+
u_ut="service",
|
69
|
+
o_i=None,
|
70
|
+
o_uu=None,
|
71
|
+
o_k=None,
|
72
|
+
o_ot=None,
|
73
|
+
uor=None,
|
74
|
+
)
|
75
|
+
|
76
|
+
@property
|
77
|
+
def token(self) -> OptionalString:
|
78
|
+
payload = PrivilegedPayload.model_validate(
|
79
|
+
{
|
80
|
+
**self.token_credential_payload.model_dump(),
|
81
|
+
**TimestampPayload.new_timestamp().model_dump(),
|
82
|
+
}
|
83
|
+
)
|
84
|
+
try:
|
85
|
+
return encode(
|
86
|
+
payload=payload.model_dump(mode="json"), key=self._private_key
|
87
|
+
)
|
88
|
+
except Exception:
|
89
|
+
return None
|
90
|
+
|
91
|
+
@property
|
92
|
+
def request_credentials(
|
93
|
+
self,
|
94
|
+
) -> PrivilegedCredentials:
|
95
|
+
return PrivilegedCredentials(
|
96
|
+
token=PrivilegedAuthenticationToken(
|
97
|
+
type=TokenType.ACCESS,
|
98
|
+
payload=PrivilegedPayload.model_validate(
|
99
|
+
{
|
100
|
+
**self.token_credential_payload.model_dump(),
|
101
|
+
**TimestampPayload.new_timestamp().model_dump(),
|
102
|
+
}
|
103
|
+
),
|
104
|
+
),
|
105
|
+
scopes=["authenticated", "administrator"],
|
106
|
+
)
|
107
|
+
|
108
|
+
@cached_property
|
109
|
+
def request_user(self) -> AuthenticatedUser:
|
110
|
+
return AuthenticatedUser(
|
111
|
+
display_name=self.maleo_credentials.username,
|
112
|
+
identity=self.maleo_credentials.email,
|
113
|
+
)
|
114
|
+
|
115
|
+
@property
|
116
|
+
def authentication(self) -> PrivilegedAuthentication:
|
117
|
+
return PrivilegedAuthentication(
|
118
|
+
credentials=self.request_credentials, user=self.request_user
|
119
|
+
)
|
@@ -0,0 +1,40 @@
|
|
1
|
+
from maleo.dtos.settings import ServiceSettings
|
2
|
+
from maleo.google.secret import Format, GoogleSecretManager
|
3
|
+
from maleo.dtos.key.rsa import Complete
|
4
|
+
from maleo.types.base.uuid import OptionalUUID
|
5
|
+
|
6
|
+
|
7
|
+
class RSAKeyManager:
|
8
|
+
def __init__(
|
9
|
+
self,
|
10
|
+
settings: ServiceSettings,
|
11
|
+
secret_manager: GoogleSecretManager,
|
12
|
+
operation_id: OptionalUUID = None,
|
13
|
+
) -> None:
|
14
|
+
if settings.KEY_PASSWORD is not None:
|
15
|
+
password = settings.KEY_PASSWORD
|
16
|
+
else:
|
17
|
+
read_key_password = secret_manager.read(
|
18
|
+
Format.STRING,
|
19
|
+
name="maleo-key-password",
|
20
|
+
operation_id=operation_id,
|
21
|
+
)
|
22
|
+
password = read_key_password.data.value
|
23
|
+
|
24
|
+
if settings.PRIVATE_KEY is not None:
|
25
|
+
private = settings.PRIVATE_KEY
|
26
|
+
else:
|
27
|
+
read_private_key = secret_manager.read(
|
28
|
+
Format.STRING, name="maleo-private-key", operation_id=operation_id
|
29
|
+
)
|
30
|
+
private = read_private_key.data.value
|
31
|
+
|
32
|
+
if settings.PUBLIC_KEY is not None:
|
33
|
+
public = settings.PUBLIC_KEY
|
34
|
+
else:
|
35
|
+
read_public_key = secret_manager.read(
|
36
|
+
Format.STRING, name="maleo-public-key", operation_id=operation_id
|
37
|
+
)
|
38
|
+
public = read_public_key.data.value
|
39
|
+
|
40
|
+
self.keys = Complete(password=password, private=private, public=public)
|
@@ -0,0 +1,123 @@
|
|
1
|
+
from abc import ABC, abstractmethod
|
2
|
+
from fastapi import FastAPI, APIRouter
|
3
|
+
from fastapi.exceptions import RequestValidationError
|
4
|
+
from google.cloud import pubsub_v1
|
5
|
+
from google.oauth2.service_account import Credentials
|
6
|
+
from pydantic import ValidationError
|
7
|
+
from starlette.exceptions import HTTPException
|
8
|
+
from starlette.types import Lifespan, AppType
|
9
|
+
from sqlalchemy import MetaData
|
10
|
+
from typing import Generic, Optional
|
11
|
+
from uuid import UUID
|
12
|
+
from maleo.dtos.settings import ServiceSettingsT
|
13
|
+
from maleo.logging.config import Config as LogConfig
|
14
|
+
from maleo.logging.logger import ServiceLoggers
|
15
|
+
from maleo.middlewares.manager import MiddlewareManager
|
16
|
+
from maleo.dtos.key.rsa import Complete
|
17
|
+
from maleo.dtos.contexts.service import ServiceContext
|
18
|
+
from maleo.exceptions.handlers.request import (
|
19
|
+
http_exception_handler,
|
20
|
+
maleo_exception_handler,
|
21
|
+
pydantic_validation_exception_handler,
|
22
|
+
request_validation_exception_handler,
|
23
|
+
general_exception_handler,
|
24
|
+
)
|
25
|
+
from maleo.exceptions import MaleoException
|
26
|
+
from .config import ConfigT
|
27
|
+
|
28
|
+
|
29
|
+
class ServiceManager(ABC, Generic[ServiceSettingsT, ConfigT]):
|
30
|
+
"""ServiceManager class"""
|
31
|
+
|
32
|
+
key = "service_manager"
|
33
|
+
name = "ServiceManager"
|
34
|
+
|
35
|
+
def __init__(
|
36
|
+
self,
|
37
|
+
db_metadata: MetaData,
|
38
|
+
google_credentials: Credentials,
|
39
|
+
log_config: LogConfig,
|
40
|
+
settings: ServiceSettingsT,
|
41
|
+
config: ConfigT,
|
42
|
+
keys: Complete,
|
43
|
+
):
|
44
|
+
self._db_metadata = db_metadata
|
45
|
+
self._google_credentials = google_credentials
|
46
|
+
self._log_config = log_config
|
47
|
+
self._settings = settings
|
48
|
+
self._config = config
|
49
|
+
self._keys = keys
|
50
|
+
|
51
|
+
self._service_context = ServiceContext(
|
52
|
+
environment=self._settings.ENVIRONMENT, key=self._settings.SERVICE_KEY
|
53
|
+
)
|
54
|
+
|
55
|
+
self._initialize_loggers()
|
56
|
+
self._initialize_publisher()
|
57
|
+
|
58
|
+
def _initialize_loggers(self) -> None:
|
59
|
+
self.loggers = ServiceLoggers.new(
|
60
|
+
environment=self._settings.ENVIRONMENT,
|
61
|
+
service_key=self._settings.SERVICE_KEY,
|
62
|
+
config=self._log_config,
|
63
|
+
)
|
64
|
+
|
65
|
+
def _initialize_publisher(self) -> None:
|
66
|
+
self.publisher = pubsub_v1.PublisherClient()
|
67
|
+
|
68
|
+
@abstractmethod
|
69
|
+
def _initialize_database(self):
|
70
|
+
"""Initialize all given databases"""
|
71
|
+
|
72
|
+
def create_app(
|
73
|
+
self,
|
74
|
+
operation_id: UUID,
|
75
|
+
router: APIRouter,
|
76
|
+
lifespan: Optional[Lifespan[AppType]] = None,
|
77
|
+
version: str = "unknown",
|
78
|
+
) -> FastAPI:
|
79
|
+
root_path = self._settings.ROOT_PATH
|
80
|
+
self.app = FastAPI(
|
81
|
+
title=self._settings.SERVICE_NAME,
|
82
|
+
version=version,
|
83
|
+
lifespan=lifespan, # type: ignore
|
84
|
+
root_path=root_path,
|
85
|
+
)
|
86
|
+
|
87
|
+
# Add middleware(s)
|
88
|
+
self.middleware_manager = MiddlewareManager(
|
89
|
+
self.app,
|
90
|
+
operation_id=operation_id,
|
91
|
+
config=self._config.middleware,
|
92
|
+
keys=self._keys,
|
93
|
+
logger=self.loggers.middleware,
|
94
|
+
service_context=self._service_context,
|
95
|
+
)
|
96
|
+
self.middleware_manager.add(operation_id=operation_id)
|
97
|
+
|
98
|
+
# Add exception handler(s)
|
99
|
+
self.app.add_exception_handler(
|
100
|
+
exc_class_or_status_code=ValidationError,
|
101
|
+
handler=pydantic_validation_exception_handler, # type: ignore
|
102
|
+
)
|
103
|
+
self.app.add_exception_handler(
|
104
|
+
exc_class_or_status_code=RequestValidationError,
|
105
|
+
handler=request_validation_exception_handler, # type: ignore
|
106
|
+
)
|
107
|
+
self.app.add_exception_handler(
|
108
|
+
exc_class_or_status_code=HTTPException,
|
109
|
+
handler=http_exception_handler, # type: ignore
|
110
|
+
)
|
111
|
+
self.app.add_exception_handler(
|
112
|
+
exc_class_or_status_code=MaleoException,
|
113
|
+
handler=maleo_exception_handler, # type: ignore
|
114
|
+
)
|
115
|
+
self.app.add_exception_handler(
|
116
|
+
exc_class_or_status_code=Exception,
|
117
|
+
handler=general_exception_handler, # type: ignore
|
118
|
+
)
|
119
|
+
|
120
|
+
# Include router
|
121
|
+
self.app.include_router(router)
|
122
|
+
|
123
|
+
return self.app
|