data-sourcerer 0.7.2__tar.gz → 0.7.3__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.
Files changed (136) hide show
  1. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/PKG-INFO +1 -1
  2. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/__init__.py +1 -1
  3. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/domain/storage_provider/entities.py +3 -1
  4. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/domain/storage_provider/services.py +12 -0
  5. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/infrastructure/storage_provider/services/azure.py +28 -8
  6. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/infrastructure/storage_provider/services/gcp.py +21 -17
  7. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/infrastructure/storage_provider/services/s3.py +18 -12
  8. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/infrastructure/utils.py +10 -0
  9. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/main/widgets/storage_content.py +4 -5
  10. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/.gitignore +0 -0
  11. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/LICENSE +0 -0
  12. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/README.md +0 -0
  13. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/pyproject.toml +0 -0
  14. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/domain/__init__.py +0 -0
  15. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/domain/access_credentials/__init__.py +0 -0
  16. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/domain/access_credentials/entities.py +0 -0
  17. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/domain/access_credentials/exceptions.py +0 -0
  18. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/domain/access_credentials/repositories.py +0 -0
  19. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/domain/access_credentials/services.py +0 -0
  20. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/domain/file_system/__init__.py +0 -0
  21. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/domain/file_system/entities.py +0 -0
  22. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/domain/file_system/exceptions.py +0 -0
  23. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/domain/file_system/services.py +0 -0
  24. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/domain/package_meta/__init__.py +0 -0
  25. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/domain/package_meta/entities.py +0 -0
  26. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/domain/package_meta/services.py +0 -0
  27. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/domain/settings/__init__.py +0 -0
  28. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/domain/settings/entities.py +0 -0
  29. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/domain/settings/repositories.py +0 -0
  30. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/domain/settings/services.py +0 -0
  31. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/domain/shared/__init__.py +0 -0
  32. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/domain/shared/entities.py +0 -0
  33. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/domain/storage/__init__.py +0 -0
  34. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/domain/storage/entities.py +0 -0
  35. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/domain/storage/repositories.py +0 -0
  36. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/domain/storage_provider/__init__.py +0 -0
  37. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/domain/storage_provider/exceptions.py +0 -0
  38. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/infrastructure/__init__.py +0 -0
  39. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/infrastructure/access_credentials/__init__.py +0 -0
  40. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/infrastructure/access_credentials/exceptions.py +0 -0
  41. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/infrastructure/access_credentials/registry.py +0 -0
  42. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/infrastructure/access_credentials/repositories.py +0 -0
  43. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/infrastructure/access_credentials/services.py +0 -0
  44. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/infrastructure/db/__init__.py +0 -0
  45. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/infrastructure/db/config.py +0 -0
  46. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/infrastructure/db/models.py +0 -0
  47. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/infrastructure/file_system/__init__.py +0 -0
  48. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/infrastructure/file_system/exceptions.py +0 -0
  49. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/infrastructure/file_system/services.py +0 -0
  50. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/infrastructure/package_meta/__init__.py +0 -0
  51. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/infrastructure/package_meta/services.py +0 -0
  52. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/infrastructure/settings/__init__.py +0 -0
  53. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/infrastructure/settings/repositories.py +0 -0
  54. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/infrastructure/settings/services.py +0 -0
  55. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/infrastructure/storage/__init__.py +0 -0
  56. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/infrastructure/storage/repositories.py +0 -0
  57. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/infrastructure/storage/services.py +0 -0
  58. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/infrastructure/storage_provider/__init__.py +0 -0
  59. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/infrastructure/storage_provider/exceptions.py +0 -0
  60. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/infrastructure/storage_provider/registry.py +0 -0
  61. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/infrastructure/storage_provider/services/__init__.py +0 -0
  62. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/__init__.py +0 -0
  63. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/app.py +0 -0
  64. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/di_container.py +0 -0
  65. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/__init__.py +0 -0
  66. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/about/__init__.py +0 -0
  67. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/about/main.py +0 -0
  68. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/about/styles.tcss +0 -0
  69. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/critical_error/__init__.py +0 -0
  70. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/critical_error/main.py +0 -0
  71. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/critical_error/styles.tcss +0 -0
  72. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/file_system_finder/__init__.py +0 -0
  73. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/file_system_finder/main.py +0 -0
  74. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/file_system_finder/styles.tcss +0 -0
  75. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/file_system_finder/widgets/__init__.py +0 -0
  76. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/file_system_finder/widgets/file_system_navigator.py +0 -0
  77. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/main/__init__.py +0 -0
  78. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/main/main.py +0 -0
  79. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/main/messages/__init__.py +0 -0
  80. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/main/messages/delete_request.py +0 -0
  81. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/main/messages/download_request.py +0 -0
  82. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/main/messages/preview_request.py +0 -0
  83. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/main/messages/refresh_storages_list_request.py +0 -0
  84. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/main/messages/resizing_rule.py +0 -0
  85. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/main/messages/select_storage_item.py +0 -0
  86. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/main/messages/uncheck_files_request.py +0 -0
  87. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/main/messages/upload_request.py +0 -0
  88. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/main/mixins/__init__.py +0 -0
  89. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/main/mixins/resize_containers_watcher_mixin.py +0 -0
  90. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/main/styles.tcss +0 -0
  91. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/main/widgets/__init__.py +0 -0
  92. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/main/widgets/gradient.py +0 -0
  93. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/main/widgets/resizing_rule.py +0 -0
  94. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/main/widgets/storage_list_sidebar.py +0 -0
  95. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/preview_content/__init__.py +0 -0
  96. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/preview_content/main.py +0 -0
  97. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/preview_content/styles.tcss +0 -0
  98. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/preview_content/text_area_style.py +0 -0
  99. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/provider_creds_list/__init__.py +0 -0
  100. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/provider_creds_list/main.py +0 -0
  101. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/provider_creds_list/messages/__init__.py +0 -0
  102. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/provider_creds_list/messages/reload_credentials_request.py +0 -0
  103. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/provider_creds_list/styles.tcss +0 -0
  104. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/provider_creds_registration/__init__.py +0 -0
  105. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/provider_creds_registration/main.py +0 -0
  106. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/provider_creds_registration/styles.tcss +0 -0
  107. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/question/__init__.py +0 -0
  108. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/question/main.py +0 -0
  109. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/question/styles.tcss +0 -0
  110. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/settings/__init__.py +0 -0
  111. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/settings/main.py +0 -0
  112. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/settings/styles.tcss +0 -0
  113. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/shared/__init__.py +0 -0
  114. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/shared/containers.py +0 -0
  115. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/shared/modal_screens.py +0 -0
  116. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/shared/widgets/__init__.py +0 -0
  117. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/shared/widgets/button.py +0 -0
  118. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/shared/widgets/labeled_input.py +0 -0
  119. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/shared/widgets/spinner.py +0 -0
  120. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/storage_action_progress/__init__.py +0 -0
  121. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/storage_action_progress/main.py +0 -0
  122. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/storage_action_progress/styles.tcss +0 -0
  123. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/storages_list/__init__.py +0 -0
  124. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/storages_list/main.py +0 -0
  125. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/storages_list/messages/__init__.py +0 -0
  126. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/storages_list/messages/reload_storages_request.py +0 -0
  127. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/storages_list/styles.tcss +0 -0
  128. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/storages_registration/__init__.py +0 -0
  129. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/storages_registration/main.py +0 -0
  130. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/screens/storages_registration/styles.tcss +0 -0
  131. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/settings.py +0 -0
  132. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/themes/__init__.py +0 -0
  133. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/themes/github_dark.py +0 -0
  134. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/presentation/utils.py +0 -0
  135. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/settings.py +0 -0
  136. {data_sourcerer-0.7.2 → data_sourcerer-0.7.3}/sourcerer/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: data-sourcerer
3
- Version: 0.7.2
3
+ Version: 0.7.3
4
4
  Summary: Sourcerer is a terminal cloud storage navigator.
5
5
  Author-email: Bohdana Kuzmenko <bohdana.kuzmenko.dev@gmail.com>
6
6
  License: MIT
@@ -14,4 +14,4 @@ The application is structured using a clean architecture approach with:
14
14
 
15
15
  name = "sourcerer"
16
16
  package_name = "data_sourcerer"
17
- __version__ = "0.7.2"
17
+ __version__ = "0.7.3"
@@ -47,6 +47,7 @@ class Folder(Struct):
47
47
  """
48
48
 
49
49
  key: str
50
+ parent_path: str
50
51
 
51
52
 
52
53
  class File(Struct):
@@ -65,7 +66,8 @@ class File(Struct):
65
66
  key: str
66
67
  size: int
67
68
  is_text: bool
68
- date_modified: datetime | None = None
69
+ date_modified: datetime
70
+ parent_path: str
69
71
 
70
72
 
71
73
  class StorageContent(Struct):
@@ -140,3 +140,15 @@ class BaseStorageProviderService(ABC):
140
140
  Returns:
141
141
  int: Size of the storage item in bytes
142
142
  """
143
+
144
+ def _normalize_path(self, path: str | None) -> str:
145
+ if path:
146
+ return path.rstrip("/") + "/"
147
+ return ""
148
+
149
+ def _get_parent_path(self, path: str, prefix: str) -> tuple[str, int]:
150
+ prefix = prefix.strip("/")
151
+ prefix_dirs = prefix.rsplit("/", 1)[0] if "/" in prefix else ""
152
+ parent_path = (path + prefix_dirs).rstrip("/") + "/"
153
+ prefix_folders_len = len(parent_path.lstrip("/"))
154
+ return parent_path, prefix_folders_len
@@ -38,7 +38,7 @@ from sourcerer.infrastructure.storage_provider.exceptions import (
38
38
  UploadStorageItemsError,
39
39
  )
40
40
  from sourcerer.infrastructure.storage_provider.registry import storage_provider
41
- from sourcerer.infrastructure.utils import generate_uuid, is_text_file
41
+ from sourcerer.infrastructure.utils import generate_uuid, is_text_file, join_non_empty
42
42
  from sourcerer.settings import DOWNLOAD_BLOCK_SIZE, MULTIPART_UPLOAD_BLOCK_SIZE
43
43
 
44
44
 
@@ -140,26 +140,42 @@ class AzureStorageProviderService(BaseStorageProviderService):
140
140
  try:
141
141
  containers_client = self.get_containers_client(storage)
142
142
  files = []
143
-
144
143
  folders = set()
144
+ parent_path = ""
145
+ prefix = prefix.strip("/")
146
+
145
147
  if not path:
146
148
  folders.update([i.name for i in containers_client.list_containers()])
149
+
147
150
  else:
148
151
  path_parts = path.split("/", 1)
149
-
150
152
  container = path_parts[0]
151
- base_path = "" if len(path_parts) == 1 else path_parts[1] + "/"
152
153
 
153
154
  blobs_client = containers_client.get_container_client(container)
154
155
 
156
+ base_path = "" if len(path_parts) == 1 else path_parts[1] + "/"
157
+
158
+ prefix_dirs = prefix.rsplit("/", 1)[0] if "/" in prefix else ""
159
+ parent_path = join_non_empty(
160
+ [
161
+ container.strip("/"),
162
+ base_path.strip("/"),
163
+ prefix_dirs.strip("/"),
164
+ ],
165
+ "/",
166
+ )
167
+ parent_path = parent_path.rstrip("/") + "/"
168
+
155
169
  for blob in blobs_client.walk_blobs(
156
170
  name_starts_with=base_path + prefix, delimiter="/"
157
171
  ):
158
- remaining_path = blob.name[len(base_path) :]
172
+ remaining_path = blob.name[
173
+ len(base_path) + len(prefix_dirs) :
174
+ ].lstrip("/")
175
+
159
176
  if "/" in remaining_path:
160
177
  folder_name = remaining_path.split("/")[0]
161
- if folder_name not in folders:
162
- folders.add(folder_name)
178
+ folders.add(folder_name)
163
179
  continue # skip subfolders
164
180
 
165
181
  files.append(
@@ -169,9 +185,13 @@ class AzureStorageProviderService(BaseStorageProviderService):
169
185
  size=blob.size, # type: ignore
170
186
  date_modified=blob.last_modified, # type: ignore
171
187
  is_text=is_text_file(blob.name),
188
+ parent_path=parent_path,
172
189
  )
173
190
  )
174
- return StorageContent(files=files, folders=[Folder(key) for key in folders])
191
+ return StorageContent(
192
+ files=files,
193
+ folders=[Folder(key.strip("/"), parent_path) for key in folders],
194
+ )
175
195
  except Exception as ex:
176
196
  raise ListStorageItemsError(str(ex)) from ex
177
197
 
@@ -125,31 +125,35 @@ class GCPStorageProviderService(BaseStorageProviderService):
125
125
  ListStorageItemsError: If an error occurs while listing items
126
126
  """
127
127
  try:
128
- files = []
129
- folders = []
130
- if path and not path.endswith("/"):
131
- path += "/"
128
+ path = self._normalize_path(path)
129
+ parent_path, prefix_folders_len = self._get_parent_path(path, prefix)
132
130
 
133
131
  bucket = self.client.bucket(storage)
134
132
 
135
133
  blobs = bucket.list_blobs(
136
- prefix=path + prefix, delimiter=PATH_DELIMITER, max_results=PAGE_SIZE
134
+ prefix=(path + prefix).lstrip("/"),
135
+ delimiter=PATH_DELIMITER,
136
+ max_results=PAGE_SIZE,
137
137
  )
138
138
 
139
- for blob in blobs:
140
- files.append(
141
- File(
142
- generate_uuid(),
143
- blob.name[len(path) :],
144
- size=blob.size,
145
- date_modified=blob.updated.date(),
146
- is_text=is_text_file(blob.name),
147
- )
139
+ files = [
140
+ File(
141
+ generate_uuid(),
142
+ blob.name[prefix_folders_len:],
143
+ size=blob.size,
144
+ date_modified=blob.updated.date(),
145
+ is_text=is_text_file(blob.name),
146
+ parent_path=parent_path,
148
147
  )
148
+ for blob in blobs
149
+ ]
149
150
 
150
- for folder in blobs.prefixes:
151
- relative_path = folder[len(path) :]
152
- folders.append(Folder(relative_path))
151
+ folders = [
152
+ Folder(
153
+ key=folder[prefix_folders_len:].strip("/"), parent_path=parent_path
154
+ )
155
+ for folder in blobs.prefixes
156
+ ]
153
157
 
154
158
  return StorageContent(files=files, folders=folders)
155
159
 
@@ -153,12 +153,13 @@ class S3ProviderService(BaseStorageProviderService):
153
153
  Raises:
154
154
  ListStorageItemsError: If an error occurs while listing items
155
155
  """
156
- if path and not path.endswith("/"):
157
- path += "/"
156
+ path = self._normalize_path(path)
157
+ parent_path, prefix_folders_len = self._get_parent_path(path, prefix)
158
+
158
159
  try:
159
160
  result = self.client.list_objects_v2(
160
161
  Bucket=storage,
161
- Prefix=path + prefix,
162
+ Prefix=(path + prefix).lstrip("/"),
162
163
  Delimiter=PATH_DELIMITER,
163
164
  MaxKeys=PAGE_SIZE,
164
165
  )
@@ -166,20 +167,25 @@ class S3ProviderService(BaseStorageProviderService):
166
167
  raise ListStorageItemsError(str(ex)) from ex
167
168
 
168
169
  folders = [
169
- Folder(i.get("Prefix").replace(path, ""))
170
- for i in result.get("CommonPrefixes", [])
171
- if i.get("Prefix")
170
+ Folder(
171
+ key=path.get("Prefix")[prefix_folders_len:].strip("/"),
172
+ parent_path=parent_path,
173
+ )
174
+ for path in result.get("CommonPrefixes", [])
175
+ if path.get("Prefix")
172
176
  ]
177
+
173
178
  files = [
174
179
  File(
175
- generate_uuid(),
176
- i.get("Key").replace(path, ""),
177
- i.get("Size"),
178
- is_text_file(i.get("Key")),
179
- i.get("LastModified"),
180
+ uuid=generate_uuid(),
181
+ key=i.get("Key")[prefix_folders_len:],
182
+ size=i.get("Size"),
183
+ is_text=is_text_file(i.get("Key")),
184
+ date_modified=i.get("LastModified"),
185
+ parent_path=parent_path,
180
186
  )
181
187
  for i in result.get("Contents", [])
182
- if i.get("Key").replace(path, "")
188
+ if i.get("Key")[prefix_folders_len:]
183
189
  ]
184
190
  return StorageContent(files=files, folders=folders)
185
191
 
@@ -218,3 +218,13 @@ def generate_unique_name() -> str:
218
218
  adjective = secrets.choice(adjectives)
219
219
  name = secrets.choice(names)
220
220
  return f"{adjective}_{name}"
221
+
222
+
223
+ def join_non_empty(items: list, separator: str):
224
+ """join strings with separator skipping empty values
225
+
226
+ Args:
227
+ items (list): A list of strings.
228
+ separator(str): The separator to join strings with.
229
+ """
230
+ return separator.join([str(i) for i in items if i])
@@ -256,13 +256,12 @@ class FolderItem(StorageContentItem):
256
256
 
257
257
  def _select(self, widget=None):
258
258
  """Select the folder."""
259
- path = self.folder.key
260
- if self.parent_path:
261
- path = self.parent_path.strip("/") + "/" + path
262
-
263
259
  self.post_message(
264
260
  SelectStorageItem(
265
- self.storage, path, self.access_credentials_uuid, focus_content=True
261
+ self.storage,
262
+ self.folder.parent_path + self.folder.key,
263
+ self.access_credentials_uuid,
264
+ focus_content=True,
266
265
  )
267
266
  )
268
267
 
File without changes
File without changes