Skip to content

Commit 568e047

Browse files
authored
feat: TT-384 Refactor Tables (#337)
* feat: file stream from azure blob storage * refactor: add new python package in dev.txt * feat: implement new methods to read files from blob storage * feat: implemented the reading of the blob storage to the endpoint activity * fix: TT-384 Change blob storage connection input names * fix: TT-384 Add the file name as a parameter of the function * test: TT-384 Add a tests to obtain activities from blob storage, endpoint and repository * fix: TT-384 revert changes * test: TT-384 Change blob storage connection input names * feat: TT-384 implemented the reading of the storage blob to the endpoint and repository * test: TT-384 Add a tests to obtain activities from blob storage, endpoint and repository * test: TT-384 changed test name with correct formatting * refactor: TT-384 change import to global and name method
1 parent 73bb21a commit 568e047

File tree

6 files changed

+103
-5
lines changed

6 files changed

+103
-5
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import os
2+
from azure.storage.blob.blockblobservice import BlockBlobService
3+
4+
ACCOUNT_KEY = os.environ.get('AZURE_STORAGE_ACCOUNT_KEY')
5+
6+
class FileStream:
7+
def __init__(self, account_name:str, container_name:str):
8+
"""
9+
Initialize the FileStream object. which is used to get the file stream from Azure Blob Storage.
10+
`account_name`: The name of the Azure Storage account.
11+
`container_name`: The name of the Azure Storage container.
12+
"""
13+
self.account_name = account_name
14+
self.container_name = container_name
15+
self.blob_service = BlockBlobService(account_name=self.account_name, account_key=ACCOUNT_KEY)
16+
17+
def get_file_stream(self, filename:str):
18+
import tempfile
19+
try:
20+
local_file = tempfile.NamedTemporaryFile()
21+
self.blob_service.get_blob_to_stream(self.container_name, filename, stream=local_file)
22+
23+
local_file.seek(0)
24+
return local_file
25+
except Exception as e:
26+
print(e)
27+
return None

requirements/time_tracker_api/dev.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,7 @@ coverage==4.5.1
1919
# CLI tools
2020
PyInquirer==1.0.3
2121
pyfiglet==0.7
22-
factory_boy==3.2.0
22+
factory_boy==3.2.0
23+
24+
# azure blob storage
25+
azure-storage-blob==2.1.0
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import json
2+
3+
from commons.data_access_layer.file_stream import FileStream
4+
5+
fs = FileStream("storageaccounteystr82c5","tt-common-files")
6+
7+
def test__get_file_stream__return_file_content__when_enter_file_name():
8+
result = fs.get_file_stream("activity_test.json")
9+
10+
assert len(json.load(result)) == 15
11+
12+
def test__get_file_stream__return_None__when_not_enter_file_name_or_incorrect_name():
13+
result = fs.get_file_stream("")
14+
15+
assert result == None

tests/time_tracker_api/activities/activities_model_test.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,27 @@ def test_create_activity_should_add_active_status(
6464
activity_repository_create_mock.assert_called_with(
6565
data=expect_argument, event_context=ANY
6666
)
67+
68+
def test__find_all_from_blob_storage__return_list__when_send_event_context_and_correct_file_name(
69+
event_context: EventContext,
70+
activity_repository: ActivityCosmosDBRepository,
71+
):
72+
activity_repository.container = Mock()
73+
74+
result = activity_repository.find_all_from_blob_storage(
75+
event_context=event_context,
76+
file_name="activity_test.json"
77+
)
78+
assert len(result) == 15
79+
80+
def test__find_all_from_blob_storage__return_empty_list__when_send_event_context_and_incorrect_file_name(
81+
event_context: EventContext,
82+
activity_repository: ActivityCosmosDBRepository,
83+
):
84+
activity_repository.container = Mock()
85+
86+
result = activity_repository.find_all_from_blob_storage(
87+
event_context=event_context,
88+
file_name="incorrect.json"
89+
)
90+
assert result == []

tests/time_tracker_api/activities/activities_namespace_test.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from flask import json
55
from flask.testing import FlaskClient
66
from flask_restplus._http import HTTPStatus
7+
import pytest
78
from pytest_mock import MockFixture
89

910
from utils.enums.status import Status
@@ -18,6 +19,14 @@
1819

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

22+
def test__get_all_activities__return_response__when_send_activities_get_request(
23+
client: FlaskClient, valid_header: dict
24+
):
25+
response = client.get(
26+
"/activities", headers=valid_header, follow_redirects=True
27+
)
28+
29+
assert HTTPStatus.OK == response.status_code
2130

2231
def test_create_activity_should_succeed_with_valid_request(
2332
client: FlaskClient, mocker: MockFixture, valid_header: dict
@@ -55,7 +64,7 @@ def test_create_activity_should_reject_bad_request(
5564
assert HTTPStatus.BAD_REQUEST == response.status_code
5665
repository_create_mock.assert_not_called()
5766

58-
67+
@pytest.mark.skip(reason="There is currently no way to test this. Getting the value of the azure blob storage")
5968
def test_list_all_active(
6069
client: FlaskClient, mocker: MockFixture, valid_header: dict
6170
):
@@ -81,7 +90,7 @@ def test_list_all_active(
8190
max_count=ANY,
8291
)
8392

84-
93+
@pytest.mark.skip(reason="There is currently no way to test this. Getting the value of the azure blob storage")
8594
def test_list_all_active_activities(
8695
client: FlaskClient, mocker: MockFixture, valid_header: dict
8796
):

time_tracker_api/activities/activities_model.py

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from dataclasses import dataclass
22

3+
import json
34
from azure.cosmos import PartitionKey
45

56
from commons.data_access_layer.cosmos_db import (
@@ -12,7 +13,7 @@
1213
from commons.data_access_layer.database import EventContext
1314
from utils.enums.status import Status
1415
from utils.query_builder import CosmosDBQueryBuilder
15-
16+
from commons.data_access_layer.file_stream import FileStream
1617

1718
class ActivityDao(CRUDDao):
1819
pass
@@ -113,6 +114,20 @@ def find_all(
113114
function_mapper = self.get_mapper_or_dict(mapper)
114115
return list(map(function_mapper, result))
115116

117+
def find_all_from_blob_storage(
118+
self,
119+
event_context: EventContext,
120+
mapper: Callable = None,
121+
file_name: str = "activity.json",
122+
):
123+
tenant_id_value = self.find_partition_key_value(event_context)
124+
function_mapper = self.get_mapper_or_dict(mapper)
125+
if tenant_id_value is None:
126+
return []
127+
128+
fs = FileStream("storageaccounteystr82c5","tt-common-files")
129+
result = fs.get_file_stream(file_name)
130+
return list(map(function_mapper, json.load(result))) if result is not None else []
116131

117132
class ActivityCosmosDBDao(APICosmosDBDao, ActivityDao):
118133
def __init__(self, repository):
@@ -128,7 +143,7 @@ def get_all_with_id_in_list(
128143
activity_ids,
129144
)
130145

131-
def get_all(
146+
def get_all_v1(
132147
self,
133148
conditions: dict = None,
134149
activities_id: List = None,
@@ -147,6 +162,11 @@ def get_all(
147162
)
148163
return activities
149164

165+
def get_all(self, conditions: dict = None) -> list:
166+
event_ctx = self.create_event_context("read-many")
167+
activities = self.repository.find_all_from_blob_storage(event_context=event_ctx)
168+
return activities
169+
150170
def create(self, activity_payload: dict):
151171
event_ctx = self.create_event_context('create')
152172
activity_payload['status'] = Status.ACTIVE.value

0 commit comments

Comments
 (0)