maleo-foundation 0.3.46__py3-none-any.whl → 0.3.48__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. maleo_foundation/authentication.py +24 -13
  2. maleo_foundation/authorization.py +2 -1
  3. maleo_foundation/client/manager.py +22 -21
  4. maleo_foundation/client/services/__init__.py +16 -7
  5. maleo_foundation/client/services/encryption/__init__.py +13 -4
  6. maleo_foundation/client/services/encryption/aes.py +41 -36
  7. maleo_foundation/client/services/encryption/rsa.py +50 -50
  8. maleo_foundation/client/services/hash/__init__.py +19 -6
  9. maleo_foundation/client/services/hash/bcrypt.py +20 -18
  10. maleo_foundation/client/services/hash/hmac.py +20 -17
  11. maleo_foundation/client/services/hash/sha256.py +18 -15
  12. maleo_foundation/client/services/key.py +50 -42
  13. maleo_foundation/client/services/signature.py +46 -42
  14. maleo_foundation/client/services/token.py +49 -58
  15. maleo_foundation/constants.py +12 -19
  16. maleo_foundation/enums.py +14 -13
  17. maleo_foundation/expanded_types/__init__.py +2 -3
  18. maleo_foundation/expanded_types/client.py +30 -34
  19. maleo_foundation/expanded_types/encryption/__init__.py +2 -1
  20. maleo_foundation/expanded_types/encryption/aes.py +7 -5
  21. maleo_foundation/expanded_types/encryption/rsa.py +7 -5
  22. maleo_foundation/expanded_types/general.py +13 -11
  23. maleo_foundation/expanded_types/hash.py +7 -5
  24. maleo_foundation/expanded_types/key.py +8 -6
  25. maleo_foundation/expanded_types/service.py +30 -34
  26. maleo_foundation/expanded_types/signature.py +7 -5
  27. maleo_foundation/expanded_types/token.py +7 -5
  28. maleo_foundation/extended_types.py +4 -3
  29. maleo_foundation/managers/cache.py +2 -1
  30. maleo_foundation/managers/client/base.py +25 -12
  31. maleo_foundation/managers/client/google/base.py +11 -4
  32. maleo_foundation/managers/client/google/parameter.py +9 -11
  33. maleo_foundation/managers/client/google/secret.py +53 -35
  34. maleo_foundation/managers/client/google/storage.py +52 -22
  35. maleo_foundation/managers/client/google/subscription.py +37 -39
  36. maleo_foundation/managers/client/maleo.py +18 -23
  37. maleo_foundation/managers/configuration.py +5 -9
  38. maleo_foundation/managers/credential.py +14 -17
  39. maleo_foundation/managers/db.py +51 -40
  40. maleo_foundation/managers/middleware.py +9 -9
  41. maleo_foundation/managers/service.py +47 -54
  42. maleo_foundation/middlewares/authentication.py +29 -54
  43. maleo_foundation/middlewares/base.py +83 -72
  44. maleo_foundation/middlewares/cors.py +8 -7
  45. maleo_foundation/models/__init__.py +2 -1
  46. maleo_foundation/models/responses.py +57 -29
  47. maleo_foundation/models/schemas/__init__.py +2 -1
  48. maleo_foundation/models/schemas/encryption.py +5 -2
  49. maleo_foundation/models/schemas/general.py +38 -18
  50. maleo_foundation/models/schemas/hash.py +2 -1
  51. maleo_foundation/models/schemas/key.py +5 -2
  52. maleo_foundation/models/schemas/parameter.py +45 -15
  53. maleo_foundation/models/schemas/result.py +35 -20
  54. maleo_foundation/models/schemas/signature.py +5 -2
  55. maleo_foundation/models/schemas/token.py +5 -2
  56. maleo_foundation/models/table.py +33 -27
  57. maleo_foundation/models/transfers/__init__.py +2 -1
  58. maleo_foundation/models/transfers/general/__init__.py +2 -1
  59. maleo_foundation/models/transfers/general/configurations/__init__.py +10 -4
  60. maleo_foundation/models/transfers/general/configurations/cache/__init__.py +3 -2
  61. maleo_foundation/models/transfers/general/configurations/cache/redis.py +13 -5
  62. maleo_foundation/models/transfers/general/configurations/client/__init__.py +5 -1
  63. maleo_foundation/models/transfers/general/configurations/client/maleo.py +38 -12
  64. maleo_foundation/models/transfers/general/configurations/database.py +5 -2
  65. maleo_foundation/models/transfers/general/configurations/middleware.py +22 -15
  66. maleo_foundation/models/transfers/general/configurations/service.py +2 -1
  67. maleo_foundation/models/transfers/general/credentials.py +2 -1
  68. maleo_foundation/models/transfers/general/database.py +11 -4
  69. maleo_foundation/models/transfers/general/key.py +13 -4
  70. maleo_foundation/models/transfers/general/request.py +28 -9
  71. maleo_foundation/models/transfers/general/settings.py +12 -22
  72. maleo_foundation/models/transfers/general/signature.py +4 -2
  73. maleo_foundation/models/transfers/general/token.py +34 -27
  74. maleo_foundation/models/transfers/parameters/__init__.py +2 -1
  75. maleo_foundation/models/transfers/parameters/client.py +15 -19
  76. maleo_foundation/models/transfers/parameters/encryption/__init__.py +2 -1
  77. maleo_foundation/models/transfers/parameters/encryption/aes.py +7 -5
  78. maleo_foundation/models/transfers/parameters/encryption/rsa.py +7 -5
  79. maleo_foundation/models/transfers/parameters/general.py +15 -13
  80. maleo_foundation/models/transfers/parameters/hash/__init__.py +2 -1
  81. maleo_foundation/models/transfers/parameters/hash/bcrypt.py +5 -5
  82. maleo_foundation/models/transfers/parameters/hash/hmac.py +6 -6
  83. maleo_foundation/models/transfers/parameters/hash/sha256.py +5 -5
  84. maleo_foundation/models/transfers/parameters/key.py +9 -8
  85. maleo_foundation/models/transfers/parameters/service.py +42 -48
  86. maleo_foundation/models/transfers/parameters/signature.py +7 -4
  87. maleo_foundation/models/transfers/parameters/token.py +10 -10
  88. maleo_foundation/models/transfers/results/__init__.py +2 -1
  89. maleo_foundation/models/transfers/results/client/__init__.py +2 -1
  90. maleo_foundation/models/transfers/results/client/controllers/__init__.py +2 -1
  91. maleo_foundation/models/transfers/results/client/controllers/http.py +10 -7
  92. maleo_foundation/models/transfers/results/client/service.py +12 -6
  93. maleo_foundation/models/transfers/results/encryption/__init__.py +2 -1
  94. maleo_foundation/models/transfers/results/encryption/aes.py +13 -5
  95. maleo_foundation/models/transfers/results/encryption/rsa.py +12 -4
  96. maleo_foundation/models/transfers/results/hash.py +7 -3
  97. maleo_foundation/models/transfers/results/key.py +18 -6
  98. maleo_foundation/models/transfers/results/service/__init__.py +2 -3
  99. maleo_foundation/models/transfers/results/service/controllers/__init__.py +2 -1
  100. maleo_foundation/models/transfers/results/service/controllers/rest.py +14 -11
  101. maleo_foundation/models/transfers/results/service/general.py +16 -10
  102. maleo_foundation/models/transfers/results/signature.py +12 -4
  103. maleo_foundation/models/transfers/results/token.py +10 -4
  104. maleo_foundation/rest_controller_result.py +23 -21
  105. maleo_foundation/types.py +15 -14
  106. maleo_foundation/utils/__init__.py +2 -1
  107. maleo_foundation/utils/cache.py +10 -13
  108. maleo_foundation/utils/client.py +25 -12
  109. maleo_foundation/utils/controller.py +59 -37
  110. maleo_foundation/utils/dependencies/__init__.py +2 -1
  111. maleo_foundation/utils/dependencies/auth.py +5 -12
  112. maleo_foundation/utils/dependencies/context.py +3 -4
  113. maleo_foundation/utils/exceptions.py +50 -28
  114. maleo_foundation/utils/extractor.py +18 -6
  115. maleo_foundation/utils/formatter/__init__.py +2 -1
  116. maleo_foundation/utils/formatter/case.py +5 -4
  117. maleo_foundation/utils/loaders/__init__.py +2 -1
  118. maleo_foundation/utils/loaders/credential/__init__.py +2 -1
  119. maleo_foundation/utils/loaders/credential/google.py +29 -15
  120. maleo_foundation/utils/loaders/json.py +3 -2
  121. maleo_foundation/utils/loaders/key/__init__.py +2 -1
  122. maleo_foundation/utils/loaders/key/rsa.py +26 -13
  123. maleo_foundation/utils/loaders/yaml.py +2 -1
  124. maleo_foundation/utils/logging.py +70 -46
  125. maleo_foundation/utils/merger.py +7 -9
  126. maleo_foundation/utils/query.py +41 -34
  127. maleo_foundation/utils/repository.py +29 -16
  128. maleo_foundation/utils/searcher.py +4 -6
  129. {maleo_foundation-0.3.46.dist-info → maleo_foundation-0.3.48.dist-info}/METADATA +14 -1
  130. maleo_foundation-0.3.48.dist-info/RECORD +137 -0
  131. maleo_foundation/expanded_types/repository.py +0 -68
  132. maleo_foundation/models/transfers/results/service/repository.py +0 -39
  133. maleo_foundation-0.3.46.dist-info/RECORD +0 -139
  134. {maleo_foundation-0.3.46.dist-info → maleo_foundation-0.3.48.dist-info}/WHEEL +0 -0
  135. {maleo_foundation-0.3.46.dist-info → maleo_foundation-0.3.48.dist-info}/top_level.txt +0 -0
@@ -6,6 +6,7 @@ from maleo_foundation.managers.client.base import ClientManager
6
6
  from maleo_foundation.utils.loaders.credential.google import GoogleCredentialsLoader
7
7
  from maleo_foundation.utils.logging import SimpleConfig
8
8
 
9
+
9
10
  class GoogleClientManager(ClientManager):
10
11
  def __init__(
11
12
  self,
@@ -14,11 +15,15 @@ class GoogleClientManager(ClientManager):
14
15
  log_config: SimpleConfig,
15
16
  service_key: BaseTypes.OptionalString = None,
16
17
  credentials: Optional[Credentials] = None,
17
- credentials_path: Optional[Union[Path, str]] = None
18
+ credentials_path: Optional[Union[Path, str]] = None,
18
19
  ) -> None:
19
20
  super().__init__(key, name, log_config, service_key)
20
- if (credentials is not None and credentials_path is not None) or (credentials is None and credentials_path is None):
21
- raise ValueError("Only either 'credentials' or 'credentials_path' can be passed as parameter")
21
+ if (credentials is not None and credentials_path is not None) or (
22
+ credentials is None and credentials_path is None
23
+ ):
24
+ raise ValueError(
25
+ "Only either 'credentials' or 'credentials_path' can be passed as parameter"
26
+ )
22
27
 
23
28
  if credentials is not None:
24
29
  self._credentials = credentials
@@ -33,4 +38,6 @@ class GoogleClientManager(ClientManager):
33
38
 
34
39
  @property
35
40
  def project_id(self) -> str:
36
- return self._project_id
41
+ if self._project_id is None:
42
+ raise ValueError("Project ID has not been initialized.")
43
+ return self._project_id
@@ -1,5 +1,3 @@
1
- from google.api_core import retry
2
- from google.api_core.exceptions import NotFound
3
1
  from google.cloud import parametermanager
4
2
  from google.oauth2.service_account import Credentials
5
3
  from pathlib import Path
@@ -8,33 +6,33 @@ from maleo_foundation.types import BaseTypes
8
6
  from maleo_foundation.utils.logging import SimpleConfig
9
7
  from .base import GoogleClientManager
10
8
 
9
+
11
10
  class GoogleParameterManager(GoogleClientManager):
12
11
  def __init__(
13
12
  self,
14
13
  log_config: SimpleConfig,
15
14
  service_key: BaseTypes.OptionalString = None,
16
15
  credentials: Optional[Credentials] = None,
17
- credentials_path: Optional[Union[Path, str]] = None
16
+ credentials_path: Optional[Union[Path, str]] = None,
18
17
  ) -> None:
19
18
  key = "google-parameter-manager"
20
19
  name = "GoogleParameterManager"
21
20
  super().__init__(
22
- key,
23
- name,
24
- log_config,
25
- service_key,
26
- credentials,
27
- credentials_path
21
+ key, name, log_config, service_key, credentials, credentials_path
22
+ )
23
+ self._client = parametermanager.ParameterManagerClient(
24
+ credentials=self._credentials
28
25
  )
29
- self._client = parametermanager.ParameterManagerClient(credentials=self._credentials)
30
26
  self._logger.info("Client manager initialized successfully")
31
27
 
32
28
  @property
33
29
  def client(self) -> parametermanager.ParameterManagerClient:
30
+ if self._client is None:
31
+ raise ValueError("Client has not been initialized.")
34
32
  return self._client
35
33
 
36
34
  def dispose(self) -> None:
37
35
  if self._client is not None:
38
36
  self._logger.info("Disposing client manager")
39
37
  self._client = None
40
- self._logger.info("Client manager disposed successfully")
38
+ self._logger.info("Client manager disposed successfully")
@@ -8,44 +8,39 @@ from maleo_foundation.types import BaseTypes
8
8
  from maleo_foundation.utils.logging import SimpleConfig
9
9
  from .base import GoogleClientManager
10
10
 
11
+
11
12
  class GoogleSecretManager(GoogleClientManager):
12
13
  def __init__(
13
14
  self,
14
15
  log_config: SimpleConfig,
15
16
  service_key: BaseTypes.OptionalString = None,
16
17
  credentials: Optional[Credentials] = None,
17
- credentials_path: Optional[Union[Path, str]] = None
18
+ credentials_path: Optional[Union[Path, str]] = None,
18
19
  ) -> None:
19
20
  key = "google-secret-manager"
20
21
  name = "GoogleSecretManager"
21
22
  super().__init__(
22
- key,
23
- name,
24
- log_config,
25
- service_key,
26
- credentials,
27
- credentials_path
23
+ key, name, log_config, service_key, credentials, credentials_path
24
+ )
25
+ self._client = secretmanager.SecretManagerServiceClient(
26
+ credentials=self._credentials
28
27
  )
29
- self._client = secretmanager.SecretManagerServiceClient(credentials=self._credentials)
30
28
  self._logger.info("Client manager initialized successfully")
31
29
 
32
30
  @property
33
31
  def client(self) -> secretmanager.SecretManagerServiceClient:
32
+ if self._client is None:
33
+ raise ValueError("Client has not been initialized.")
34
34
  return self._client
35
35
 
36
36
  def dispose(self) -> None:
37
37
  if self._client is not None:
38
38
  self._logger.info("Disposing client manager")
39
- self._client = None
40
39
  self._logger.info("Client manager disposed successfully")
41
40
 
42
41
  @retry.Retry(predicate=retry.if_exception_type(Exception), timeout=5)
43
- def get(
44
- self,
45
- name: str,
46
- version: str = "latest"
47
- ) -> str:
48
- #* Check if secret exists
42
+ def get(self, name: str, version: str = "latest") -> str:
43
+ # * Check if secret exists
49
44
  secret_name = f"projects/{self._project_id}/secrets/{name}"
50
45
  try:
51
46
  request = secretmanager.GetSecretRequest(name=secret_name)
@@ -54,59 +49,82 @@ class GoogleSecretManager(GoogleClientManager):
54
49
  self._logger.error("Secret '%s' did not exist", name, exc_info=True)
55
50
  raise
56
51
  except Exception:
57
- self._logger.error("Exception raised while checking secret '%s' existence", name, exc_info=True)
52
+ self._logger.error(
53
+ "Exception raised while checking secret '%s' existence",
54
+ name,
55
+ exc_info=True,
56
+ )
58
57
  raise
59
58
 
60
- #* Check if secret's version exists
59
+ # * Check if secret's version exists
61
60
  secret_version_name = f"{secret_name}/versions/{version}"
62
61
  try:
63
62
  request = secretmanager.GetSecretVersionRequest(name=secret_version_name)
64
63
  self._client.get_secret_version(request=request)
65
64
  except NotFound:
66
- self._logger.error("Secret '%s' with version '%s' did not exist", name, version, exc_info=True)
65
+ self._logger.error(
66
+ "Secret '%s' with version '%s' did not exist",
67
+ name,
68
+ version,
69
+ exc_info=True,
70
+ )
67
71
  raise
68
72
  except Exception:
69
- self._logger.error("Exception raised while checking secret '%s' with version '%s' existence", name, version, exc_info=True)
73
+ self._logger.error(
74
+ "Exception raised while checking secret '%s' with version '%s' existence",
75
+ name,
76
+ version,
77
+ exc_info=True,
78
+ )
70
79
  raise
71
80
 
72
- #* Access secret's version
81
+ # * Access secret's version
73
82
  try:
74
83
  request = secretmanager.AccessSecretVersionRequest(name=secret_version_name)
75
84
  response = self._client.access_secret_version(request=request)
76
- self._logger.info("Successfully retrieved secret '%s' with version '%s'", name, version)
85
+ self._logger.info(
86
+ "Successfully retrieved secret '%s' with version '%s'", name, version
87
+ )
77
88
  return response.payload.data.decode()
78
89
  except Exception:
79
- self._logger.error("Exception occured while retrieving secret '%s' with version '%s'", name, version, exc_info=True)
90
+ self._logger.error(
91
+ "Exception occured while retrieving secret '%s' with version '%s'",
92
+ name,
93
+ version,
94
+ exc_info=True,
95
+ )
80
96
  raise
81
97
 
82
98
  @retry.Retry(predicate=retry.if_exception_type(Exception), timeout=5)
83
- def create(
84
- self,
85
- name: str,
86
- data: str
87
- ) -> str:
99
+ def create(self, name: str, data: str) -> str:
88
100
  parent = f"projects/{self._project_id}"
89
101
  secret_path = f"{parent}/secrets/{name}"
90
102
  try:
91
- #* Check if the secret already exists
103
+ # * Check if the secret already exists
92
104
  request = secretmanager.GetSecretRequest(name=secret_path)
93
105
  self._client.get_secret(request=request)
94
106
  except NotFound:
95
- #* Secret does not exist, create it first
107
+ # * Secret does not exist, create it first
96
108
  try:
97
109
  secret = secretmanager.Secret(name=name, replication={"automatic": {}})
98
- request = secretmanager.CreateSecretRequest(parent=parent, secret_id=name, secret=secret)
110
+ request = secretmanager.CreateSecretRequest(
111
+ parent=parent, secret_id=name, secret=secret
112
+ )
99
113
  self._client.create_secret(request=request)
100
114
  except Exception:
101
115
  self._logger.error("Exception occured while creating secret '%s'", name)
102
116
  raise
103
117
 
104
- #* Add a new secret version
118
+ # * Add a new secret version
105
119
  try:
106
120
  payload = secretmanager.SecretPayload(data=data.encode())
107
- request = secretmanager.AddSecretVersionRequest(parent=secret_path, payload=payload)
108
- response = self._client.add_secret_version(request=request)
121
+ request = secretmanager.AddSecretVersionRequest(
122
+ parent=secret_path, payload=payload
123
+ )
124
+ self._client.add_secret_version(request=request)
109
125
  return data
110
126
  except Exception:
111
- self._logger.error("Exception occured while adding secret '%s' version", name)
112
- raise
127
+ self._logger.error(
128
+ "Exception occured while adding secret '%s' version", name
129
+ )
130
+ raise
@@ -10,6 +10,7 @@ from maleo_foundation.types import BaseTypes
10
10
  from maleo_foundation.utils.logging import SimpleConfig
11
11
  from .base import GoogleClientManager
12
12
 
13
+
13
14
  class GoogleCloudStorage(GoogleClientManager):
14
15
  def __init__(
15
16
  self,
@@ -18,16 +19,20 @@ class GoogleCloudStorage(GoogleClientManager):
18
19
  credentials: Optional[Credentials] = None,
19
20
  credentials_path: Optional[Union[Path, str]] = None,
20
21
  bucket_name: BaseTypes.OptionalString = None,
21
- redis: Optional[Redis] = None
22
+ redis: Optional[Redis] = None,
22
23
  ) -> None:
23
24
  key = "google-cloud-storage"
24
25
  name = "GoogleCloudStorage"
25
- super().__init__(key, name, log_config, service_key, credentials, credentials_path)
26
+ super().__init__(
27
+ key, name, log_config, service_key, credentials, credentials_path
28
+ )
26
29
  self._client = Client(credentials=self._credentials)
27
30
  self._bucket_name = bucket_name or os.getenv("GCS_BUCKET_NAME")
28
31
  if self._bucket_name is None:
29
32
  self._client.close()
30
- raise ValueError("GCS_BUCKET_NAME environment variable must be set if 'bucket_name' is set to None")
33
+ raise ValueError(
34
+ "GCS_BUCKET_NAME environment variable must be set if 'bucket_name' is set to None"
35
+ )
31
36
  self._bucket = self._client.lookup_bucket(bucket_name=self._bucket_name)
32
37
  if self._bucket is None:
33
38
  self._client.close()
@@ -38,10 +43,14 @@ class GoogleCloudStorage(GoogleClientManager):
38
43
 
39
44
  @property
40
45
  def bucket_name(self) -> str:
46
+ if self._bucket_name is None:
47
+ raise ValueError("Bucket name has not been initialized.")
41
48
  return self._bucket_name
42
49
 
43
50
  @property
44
51
  def bucket(self) -> Bucket:
52
+ if self._bucket is None:
53
+ raise ValueError("Bucket has not been initialized.")
45
54
  return self._bucket
46
55
 
47
56
  def dispose(self) -> None:
@@ -55,11 +64,11 @@ class GoogleCloudStorage(GoogleClientManager):
55
64
  self,
56
65
  content: bytes,
57
66
  location: str,
58
- content_type: Optional[str]=None,
59
- make_public: bool=False,
60
- expiration: BaseEnums=BaseEnums.Expiration.EXP_15MN,
61
- root_location_override: BaseTypes.OptionalString=None,
62
- set_in_redis: bool=True
67
+ content_type: Optional[str] = None,
68
+ make_public: bool = False,
69
+ expiration: BaseEnums.Expiration = BaseEnums.Expiration.EXP_15MN,
70
+ root_location_override: BaseTypes.OptionalString = None,
71
+ set_in_redis: bool = True,
63
72
  ) -> str:
64
73
  """
65
74
  Upload a file to Google Cloud Storage.
@@ -73,13 +82,20 @@ class GoogleCloudStorage(GoogleClientManager):
73
82
  Returns:
74
83
  str: The public URL or blob path depending on `make_public`.
75
84
  """
76
- if root_location_override is None or (isinstance(root_location_override, str) and len(root_location_override) <= 0):
85
+ if root_location_override is None or (
86
+ isinstance(root_location_override, str) and len(root_location_override) <= 0
87
+ ):
77
88
  blob_name = f"{self._root_location}/{location}"
78
89
  else:
79
90
  blob_name = f"{root_location_override}/{location}"
80
91
 
92
+ if self._bucket is None:
93
+ raise ValueError(
94
+ "Bucket is not initialized. Please check the bucket name and credentials."
95
+ )
96
+
81
97
  blob = self._bucket.blob(blob_name=blob_name)
82
- blob.upload_from_string(content, content_type=content_type)
98
+ blob.upload_from_string(content, content_type=content_type or "text/plain")
83
99
 
84
100
  if make_public:
85
101
  blob.make_public()
@@ -88,23 +104,27 @@ class GoogleCloudStorage(GoogleClientManager):
88
104
  url = blob.generate_signed_url(
89
105
  version="v4",
90
106
  expiration=timedelta(seconds=int(expiration)),
91
- method="GET"
107
+ method="GET",
92
108
  )
93
109
 
94
110
  if set_in_redis:
111
+ if self._redis is None:
112
+ raise ValueError("Can not use redis. Redis is not initialized")
95
113
  if make_public:
96
114
  await self._redis.set(f"{self.key}:{blob_name}", url)
97
115
  else:
98
- await self._redis.set(f"{self.key}:{blob_name}", url, ex=int(expiration))
116
+ await self._redis.set(
117
+ f"{self.key}:{blob_name}", url, ex=int(expiration)
118
+ )
99
119
 
100
120
  return url
101
121
 
102
122
  async def generate_signed_url(
103
123
  self,
104
124
  location: str,
105
- expiration: BaseEnums=BaseEnums.Expiration.EXP_15MN,
125
+ expiration: BaseEnums.Expiration = BaseEnums.Expiration.EXP_15MN,
106
126
  root_location_override: BaseTypes.OptionalString = None,
107
- use_redis: bool = True
127
+ use_redis: bool = True,
108
128
  ) -> str:
109
129
  """
110
130
  generate signed URL of a file in the bucket based on its location.
@@ -122,10 +142,17 @@ class GoogleCloudStorage(GoogleClientManager):
122
142
  if use_redis and self._redis is None:
123
143
  raise ValueError("Can not use redis. Redis is not initialized")
124
144
 
125
- if root_location_override is None or (isinstance(root_location_override, str) and len(root_location_override) <= 0):
126
- blob_name=f"{self._root_location}/{location}"
145
+ if root_location_override is None or (
146
+ isinstance(root_location_override, str) and len(root_location_override) <= 0
147
+ ):
148
+ blob_name = f"{self._root_location}/{location}"
127
149
  else:
128
- blob_name=f"{root_location_override}/{location}"
150
+ blob_name = f"{root_location_override}/{location}"
151
+
152
+ if self._bucket is None:
153
+ raise ValueError(
154
+ "Bucket is not initialized. Please check the bucket name and credentials."
155
+ )
129
156
 
130
157
  blob = self._bucket.blob(blob_name=blob_name)
131
158
  if not blob.exists():
@@ -139,9 +166,12 @@ class GoogleCloudStorage(GoogleClientManager):
139
166
  return url
140
167
 
141
168
  url = blob.generate_signed_url(
142
- version="v4",
143
- expiration=timedelta(seconds=int(expiration)),
144
- method="GET"
169
+ version="v4", expiration=timedelta(seconds=int(expiration)), method="GET"
145
170
  )
146
- await self._redis.set(f"{self.key}:{blob_name}", url, ex=int(expiration))
147
- return url
171
+
172
+ if use_redis:
173
+ if self._redis is None:
174
+ raise ValueError("Can not use redis. Redis is not initialized")
175
+ await self._redis.set(f"{self.key}:{blob_name}", url, ex=int(expiration))
176
+
177
+ return url
@@ -6,7 +6,7 @@ from google.cloud.pubsub_v1.subscriber.message import Message
6
6
  from google.oauth2.service_account import Credentials
7
7
  from pathlib import Path
8
8
  from pydantic import BaseModel
9
- from typing import Awaitable, Callable, Dict, List, Optional, Union
9
+ from typing import Awaitable, Callable, Dict, List, Optional, Union, cast
10
10
  from maleo_foundation.managers.client.google.base import GoogleClientManager
11
11
 
12
12
  SyncController = Callable[[str, Message], bool]
@@ -14,12 +14,14 @@ AsyncController = Callable[[str, Message], Awaitable[bool]]
14
14
  Controller = Union[SyncController, AsyncController]
15
15
  OptionalController = Optional[Controller]
16
16
 
17
+
17
18
  class SubscriptionConfigurations(BaseModel):
18
19
  subscription_name: str
19
20
  max_messages: int = 10
20
21
  ack_deadline: int = 10
21
22
  controller: OptionalController = None
22
23
 
24
+
23
25
  class SubscriptionManager(GoogleClientManager):
24
26
  def __init__(
25
27
  self,
@@ -31,35 +33,28 @@ class SubscriptionManager(GoogleClientManager):
31
33
  ):
32
34
  key = "google-subscription-manager"
33
35
  name = "GoogleSubscriptionManager"
34
- super().__init__(key, name, log_config, service_key, credentials, credentials_path)
36
+ super().__init__(
37
+ key, name, log_config, service_key, credentials, credentials_path
38
+ )
35
39
  self.subscriber = pubsub_v1.SubscriberClient(credentials=self._credentials)
36
40
  self.subscriptions = subscriptions
37
41
  self.active_listeners: Dict[str, StreamingPullFuture] = {}
38
42
  self.loop: Optional[asyncio.AbstractEventLoop] = None
39
43
 
40
44
  async def _handle_async_controller(
41
- self,
42
- controller: AsyncController,
43
- subscription_name: str,
44
- message: Message
45
+ self, controller: AsyncController, subscription_name: str, message: Message
45
46
  ) -> None:
46
47
  success = await controller(subscription_name, message)
47
48
  message.ack() if success else message.nack()
48
49
 
49
50
  def _handle_sync_controller(
50
- self,
51
- controller: SyncController,
52
- subscription_name: str,
53
- message: Message
51
+ self, controller: SyncController, subscription_name: str, message: Message
54
52
  ) -> None:
55
53
  success = controller(subscription_name, message)
56
54
  message.ack() if success else message.nack()
57
55
 
58
56
  def _message_callback(
59
- self,
60
- controller: OptionalController,
61
- subscription_name: str,
62
- message: Message
57
+ self, controller: OptionalController, subscription_name: str, message: Message
63
58
  ):
64
59
  # If controller is not given, conduct default message processing
65
60
  if controller is None:
@@ -73,58 +68,61 @@ class SubscriptionManager(GoogleClientManager):
73
68
  raise RuntimeError("Event loop not set in SubscriptionManager")
74
69
  asyncio.run_coroutine_threadsafe(
75
70
  self._handle_async_controller(
76
- controller,
77
- subscription_name,
78
- message
71
+ cast(AsyncController, controller), subscription_name, message
79
72
  ),
80
- self.loop
73
+ self.loop,
81
74
  )
82
75
  else:
83
76
  self._handle_sync_controller(
84
- controller,
85
- subscription_name,
86
- message
77
+ cast(SyncController, controller), subscription_name, message
87
78
  )
88
79
 
89
80
  def _default_message_processing(
90
- self,
91
- subscription_name: str,
92
- message: Message
81
+ self, subscription_name: str, message: Message
93
82
  ) -> None:
94
83
  try:
95
- self._logger.info("Default message processing for subscription '%s': %s", subscription_name, message.data.decode("utf-8"))
84
+ self._logger.info(
85
+ "Default message processing for subscription '%s': %s",
86
+ subscription_name,
87
+ message.data.decode("utf-8"),
88
+ )
96
89
  message.ack()
97
90
  except Exception as e:
98
- self._logger.error("Error handling message through default processor: %s", e, exc_info=True)
91
+ self._logger.error(
92
+ "Error handling message through default processor: %s", e, exc_info=True
93
+ )
99
94
  message.nack()
100
95
 
101
96
  async def _start_subscription_listener(
102
- self,
103
- config: SubscriptionConfigurations
97
+ self, config: SubscriptionConfigurations
104
98
  ) -> None:
105
- subscription_path = self.subscriber.subscription_path(self.credentials.project_id, config.subscription_name)
99
+ if self.credentials.project_id is None:
100
+ raise ValueError("Project ID must be set in credentials")
101
+ subscription_path = self.subscriber.subscription_path(
102
+ self.credentials.project_id, config.subscription_name
103
+ )
106
104
  flow_control = pubsub_v1.types.FlowControl(max_messages=config.max_messages)
107
105
  future = self.subscriber.subscribe(
108
106
  subscription_path,
109
107
  callback=lambda message: self._message_callback(
110
- config.controller,
111
- config.subscription_name,
112
- message
108
+ config.controller, config.subscription_name, message
113
109
  ),
114
110
  flow_control=flow_control,
115
- await_callbacks_on_shutdown=True
111
+ await_callbacks_on_shutdown=True,
116
112
  )
117
113
  self.active_listeners[subscription_path] = future
118
114
  try:
119
115
  await asyncio.get_event_loop().run_in_executor(None, future.result)
120
116
  except Exception as e:
121
117
  if not isinstance(e, asyncio.CancelledError):
122
- self._logger.error("Listener error for subscription '%s': %s", config.subscription_name, e, exc_info=True)
118
+ self._logger.error(
119
+ "Listener error for subscription '%s': %s",
120
+ config.subscription_name,
121
+ e,
122
+ exc_info=True,
123
+ )
123
124
 
124
- async def start_listeners(
125
- self,
126
- loop: asyncio.AbstractEventLoop
127
- ) -> None:
125
+ async def start_listeners(self, loop: asyncio.AbstractEventLoop) -> None:
128
126
  self.loop = loop
129
127
  for config in self.subscriptions:
130
128
  asyncio.create_task(self._start_subscription_listener(config))
@@ -137,4 +135,4 @@ class SubscriptionManager(GoogleClientManager):
137
135
  future.result()
138
136
  except Exception:
139
137
  pass
140
- self.active_listeners.clear()
138
+ self.active_listeners.clear()
@@ -6,16 +6,15 @@ from maleo_foundation.managers.client.base import (
6
6
  ClientHTTPController,
7
7
  ClientServiceControllers,
8
8
  ClientService,
9
- ClientControllers
9
+ ClientControllers,
10
10
  )
11
11
  from maleo_foundation.managers.service import ServiceManager
12
12
  from maleo_foundation.utils.logging import ClientLogger
13
13
 
14
+
14
15
  class MaleoClientHTTPController(ClientHTTPController):
15
16
  def __init__(
16
- self,
17
- service_manager: ServiceManager,
18
- manager: ClientHTTPControllerManager
17
+ self, service_manager: ServiceManager, manager: ClientHTTPControllerManager
19
18
  ):
20
19
  super().__init__(manager)
21
20
  self._service_manager = service_manager
@@ -24,18 +23,17 @@ class MaleoClientHTTPController(ClientHTTPController):
24
23
  def service_manager(self) -> ServiceManager:
25
24
  return self._service_manager
26
25
 
26
+
27
27
  class MaleoClientServiceControllers(ClientServiceControllers):
28
28
  model_config = ConfigDict(arbitrary_types_allowed=True)
29
29
 
30
- http:MaleoClientHTTPController = Field(..., description="Maleo's HTTP Client Controller")
30
+ http: MaleoClientHTTPController = Field( # type: ignore
31
+ ..., description="Maleo's HTTP Client Controller"
32
+ )
33
+
31
34
 
32
35
  class MaleoClientService(ClientService):
33
- def __init__(
34
- self,
35
- key: str,
36
- logger: ClientLogger,
37
- service_manager: ServiceManager
38
- ):
36
+ def __init__(self, key: str, logger: ClientLogger, service_manager: ServiceManager):
39
37
  super().__init__(logger)
40
38
  self._key = key
41
39
  self._service_manager = service_manager
@@ -48,21 +46,16 @@ class MaleoClientService(ClientService):
48
46
  def service_manager(self) -> ServiceManager:
49
47
  return self._service_manager
50
48
 
49
+
51
50
  class MaleoClientManager(ClientManager):
52
- def __init__(
53
- self,
54
- key: str,
55
- name: str,
56
- url: str,
57
- service_manager: ServiceManager
58
- ):
51
+ def __init__(self, key: str, name: str, url: str, service_manager: ServiceManager):
59
52
  self._url = url
60
53
  self._service_manager = service_manager
61
54
  super().__init__(
62
55
  key,
63
56
  name,
64
57
  service_manager.log_config,
65
- service_manager.configurations.service.key
58
+ service_manager.configurations.service.key,
66
59
  )
67
60
 
68
61
  @property
@@ -70,10 +63,12 @@ class MaleoClientManager(ClientManager):
70
63
  return self._service_manager
71
64
 
72
65
  def _initialize_controllers(self) -> None:
73
- #* Initialize managers
66
+ # * Initialize managers
74
67
  http_controller_manager = ClientHTTPControllerManager(url=self._url)
75
- self._controller_managers = ClientControllerManagers(http=http_controller_manager)
76
- #* Initialize controllers
68
+ self._controller_managers = ClientControllerManagers(
69
+ http=http_controller_manager
70
+ )
71
+ # * Initialize controllers
77
72
  #! This initialied an empty controllers. Extend this function in the actual class to initialize all controllers.
78
73
  self._controllers = ClientControllers()
79
74
 
@@ -84,4 +79,4 @@ class MaleoClientManager(ClientManager):
84
79
  async def dispose(self) -> None:
85
80
  self._logger.info("Disposing client manager")
86
81
  await self._controller_managers.http.dispose()
87
- self._logger.info("Client manager disposed successfully")
82
+ self._logger.info("Client manager disposed successfully")