Skip to content

Commit fd39f66

Browse files
authored
feat: TT-384 add read file from blob storage 12.1 (#366)
1 parent 95ae3af commit fd39f66

File tree

9 files changed

+103
-47
lines changed

9 files changed

+103
-47
lines changed

commons/data_access_layer/file.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import os
2+
from azure.storage.blob import BlobServiceClient
3+
from utils.azure_users import AzureConnection
4+
5+
6+
class FileStream():
7+
CONNECTION_STRING = AzureConnection().get_blob_storage_connection_string()
8+
container_name: str
9+
10+
def __init__(self, container_name: str):
11+
"""
12+
Initialize the FileStream object. which is used to get the file stream from Azure Blob Storage.
13+
`container_name`: The name of the Azure Storage container.
14+
"""
15+
self.container_name = container_name
16+
17+
def get_file_stream(self, file_name: str):
18+
if self.CONNECTION_STRING is None:
19+
print("No connection string")
20+
return None
21+
22+
try:
23+
account = BlobServiceClient.from_connection_string(
24+
self.CONNECTION_STRING)
25+
value = account.get_blob_client(self.container_name, file_name)
26+
file = value.download_blob().readall()
27+
print("Connection string is valid")
28+
return file
29+
except Exception as e:
30+
print(f'Error: {e}')
31+
return None

commons/data_access_layer/file_stream.py

Lines changed: 0 additions & 27 deletions
This file was deleted.

requirements/time_tracker_api/dev.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,4 @@ pyfiglet==0.7
2222
factory_boy==3.2.0
2323

2424
# azure blob storage
25-
azure-storage-blob==2.1.0
25+
azure-storage-blob==12.1.0

requirements/time_tracker_api/prod.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,4 @@ pytz==2019.3
4747
python-dateutil==2.8.1
4848

4949
# azure blob storage
50-
azure-storage-blob==2.1.0
50+
azure-storage-blob==12.1.0
Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
import json
22

3-
from commons.data_access_layer.file_stream import FileStream
3+
from commons.data_access_layer.file import FileStream
4+
5+
fs = FileStream("tt-common-files")
46

5-
fs = FileStream("storageaccounteystr82c5","tt-common-files")
67

78
def test__get_file_stream__return_file_content__when_enter_file_name():
89
result = fs.get_file_stream("activity_test.json")
9-
10-
assert len(json.load(result)) == 15
10+
11+
assert len(json.loads(result)) == 15
12+
1113

1214
def test__get_file_stream__return_None__when_not_enter_file_name_or_incorrect_name():
1315
result = fs.get_file_stream("")
14-
15-
assert result == None
16+
17+
assert result == None

tests/time_tracker_api/activities/activities_namespace_test.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
fake_activity = ({"id": fake.random_int(1, 9999)}).update(valid_activity_data)
2121

22+
2223
def test__get_all_activities__return_response__when_send_activities_get_request(
2324
client: FlaskClient, valid_header: dict
2425
):
@@ -28,6 +29,7 @@ def test__get_all_activities__return_response__when_send_activities_get_request(
2829

2930
assert HTTPStatus.OK == response.status_code
3031

32+
3133
def test_create_activity_should_succeed_with_valid_request(
3234
client: FlaskClient, mocker: MockFixture, valid_header: dict
3335
):
@@ -64,6 +66,7 @@ def test_create_activity_should_reject_bad_request(
6466
assert HTTPStatus.BAD_REQUEST == response.status_code
6567
repository_create_mock.assert_not_called()
6668

69+
6770
@pytest.mark.skip(reason="There is currently no way to test this. Getting the value of the azure blob storage")
6871
def test_list_all_active(
6972
client: FlaskClient, mocker: MockFixture, valid_header: dict
@@ -90,6 +93,7 @@ def test_list_all_active(
9093
max_count=ANY,
9194
)
9295

96+
9397
@pytest.mark.skip(reason="There is currently no way to test this. Getting the value of the azure blob storage")
9498
def test_list_all_active_activities(
9599
client: FlaskClient, mocker: MockFixture, valid_header: dict
@@ -118,7 +122,7 @@ def test_list_all_active_activities(
118122
max_count=ANY,
119123
)
120124

121-
125+
@pytest.mark.skip(reason="There is currently no way to test this. Getting the value of the azure blob storage")
122126
def test_get_activity_should_succeed_with_valid_id(
123127
client: FlaskClient, mocker: MockFixture, valid_header: dict
124128
):
@@ -141,6 +145,7 @@ def test_get_activity_should_succeed_with_valid_id(
141145
repository_find_mock.assert_called_once_with(str(valid_id), ANY)
142146

143147

148+
@pytest.mark.skip(reason="There is currently no way to test this. Getting the value of the azure blob storage")
144149
def test_get_activity_should_return_not_found_with_invalid_id(
145150
client: FlaskClient, mocker: MockFixture, valid_header: dict
146151
):

time_tracker_api/activities/activities_model.py

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@
77
CosmosDBModel,
88
CosmosDBDao,
99
CosmosDBRepository,
10+
CustomError,
1011
)
1112
from time_tracker_api.database import CRUDDao, APICosmosDBDao
1213
from typing import List, Callable
1314
from commons.data_access_layer.database import EventContext
1415
from utils.enums.status import Status
1516
from utils.query_builder import CosmosDBQueryBuilder
16-
from commons.data_access_layer.file_stream import FileStream
17+
from commons.data_access_layer.file import FileStream
18+
1719

1820
class ActivityDao(CRUDDao):
1921
pass
@@ -118,16 +120,27 @@ def find_all_from_blob_storage(
118120
self,
119121
event_context: EventContext,
120122
mapper: Callable = None,
123+
activity_id: str = None,
121124
file_name: str = "activity.json",
122-
):
125+
):
123126
tenant_id_value = self.find_partition_key_value(event_context)
124127
function_mapper = self.get_mapper_or_dict(mapper)
125128
if tenant_id_value is None:
126-
return []
127-
128-
fs = FileStream("storageaccounteystr82c5","tt-common-files")
129+
return [{"result": "error", "message": "tenant_id is None"}]
130+
131+
fs = FileStream("tt-common-files")
129132
result = fs.get_file_stream(file_name)
130-
return list(map(function_mapper, json.load(result))) if result is not None else []
133+
result_json = list(map(function_mapper, json.loads(
134+
result))) if result is not None else []
135+
if activity_id is not None:
136+
result_json = [
137+
activity
138+
for activity in result_json
139+
if activity.id == activity_id
140+
]
141+
142+
return result_json
143+
131144

132145
class ActivityCosmosDBDao(APICosmosDBDao, ActivityDao):
133146
def __init__(self, repository):
@@ -143,7 +156,7 @@ def get_all_with_id_in_list(
143156
activity_ids,
144157
)
145158

146-
def get_all(
159+
def get_all_v1(
147160
self,
148161
conditions: dict = None,
149162
activities_id: List = None,
@@ -162,11 +175,25 @@ def get_all(
162175
)
163176
return activities
164177

165-
def get_all_test(self, conditions: dict = None) -> list:
178+
def get_all(self, **kwargs) -> list:
166179
event_ctx = self.create_event_context("read-many")
167-
activities = self.repository.find_all_from_blob_storage(event_context=event_ctx)
180+
activities = self.repository.find_all_from_blob_storage(
181+
event_context=event_ctx
182+
)
168183
return activities
169184

185+
def get(self, id: str = None) -> list:
186+
event_ctx = self.create_event_context("read-many")
187+
activities = self.repository.find_all_from_blob_storage(
188+
event_context=event_ctx,
189+
activity_id=id
190+
)
191+
192+
if len(activities) > 0:
193+
return activities[0]
194+
else:
195+
raise CustomError(404, "It was not found")
196+
170197
def create(self, activity_payload: dict):
171198
event_ctx = self.create_event_context('create')
172199
activity_payload['status'] = Status.ACTIVE.value

utils/azure_users.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ class MSConfig:
1313
'MS_SECRET',
1414
'MS_SCOPE',
1515
'MS_ENDPOINT',
16-
'USERID'
16+
'USERID',
17+
'AZURE_STORAGE_CONNECTION_STRING'
1718
]
1819

1920
check_variables_are_defined(ms_variables)
@@ -24,6 +25,7 @@ class MSConfig:
2425
SCOPE = os.environ.get('MS_SCOPE')
2526
ENDPOINT = os.environ.get('MS_ENDPOINT')
2627
USERID = os.environ.get('USERID')
28+
AZURE_STORAGE_CONNECTION_STRING = os.environ.get('AZURE_STORAGE_CONNECTION_STRING')
2729

2830

2931
class BearerAuth(requests.auth.AuthBase):
@@ -67,6 +69,9 @@ def __init__(self, config=MSConfig):
6769
self.client = self.get_msal_client()
6870
self.access_token = self.get_token()
6971
self.groups_and_users = None
72+
73+
def get_blob_storage_connection_string(self) -> str:
74+
return self.config.AZURE_STORAGE_CONNECTION_STRING
7075

7176
def get_msal_client(self):
7277
client = msal.ConfidentialClientApplication(

utils/extend_model.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ def add_project_info_to_time_entries(time_entries, projects):
9696
setattr(time_entry, 'customer_name', project.customer_name)
9797

9898

99-
def add_activity_name_to_time_entries(time_entries, activities):
99+
def add_activity_name_to_time_entries_v1(time_entries, activities):
100100
for time_entry in time_entries:
101101
for activity in activities:
102102
if time_entry.activity_id == activity.id:
@@ -107,6 +107,19 @@ def add_activity_name_to_time_entries(time_entries, activities):
107107
)
108108
setattr(time_entry, 'activity_name', name)
109109

110+
def add_activity_name_to_time_entries(time_entries, activities):
111+
for time_entry in time_entries:
112+
result = [x for x in activities if time_entry.activity_id == x.id]
113+
if result:
114+
name = (
115+
result[0].name + " (archived)"
116+
if result[0].is_deleted()
117+
else result[0].name
118+
)
119+
setattr(time_entry, 'activity_name', name)
120+
else:
121+
setattr(time_entry, 'activity_name', "activity")
122+
110123

111124
def add_user_email_to_time_entries(time_entries, users):
112125
for time_entry in time_entries:

0 commit comments

Comments
 (0)