diff --git a/commons/data_access_layer/file_stream.py b/commons/data_access_layer/file_stream.py new file mode 100644 index 00000000..a705c061 --- /dev/null +++ b/commons/data_access_layer/file_stream.py @@ -0,0 +1,27 @@ +import os +from azure.storage.blob.blockblobservice import BlockBlobService + +ACCOUNT_KEY = os.environ.get('AZURE_STORAGE_ACCOUNT_KEY') + +class FileStream: + def __init__(self, account_name:str, container_name:str): + """ + Initialize the FileStream object. which is used to get the file stream from Azure Blob Storage. + `account_name`: The name of the Azure Storage account. + `container_name`: The name of the Azure Storage container. + """ + self.account_name = account_name + self.container_name = container_name + self.blob_service = BlockBlobService(account_name=self.account_name, account_key=ACCOUNT_KEY) + + def get_file_stream(self, filename:str): + import tempfile + try: + local_file = tempfile.NamedTemporaryFile() + self.blob_service.get_blob_to_stream(self.container_name, filename, stream=local_file) + + local_file.seek(0) + return local_file + except Exception as e: + print(e) + return None \ No newline at end of file diff --git a/requirements/time_tracker_api/dev.txt b/requirements/time_tracker_api/dev.txt index 9657c071..b7a6d667 100644 --- a/requirements/time_tracker_api/dev.txt +++ b/requirements/time_tracker_api/dev.txt @@ -19,4 +19,7 @@ coverage==4.5.1 # CLI tools PyInquirer==1.0.3 pyfiglet==0.7 -factory_boy==3.2.0 \ No newline at end of file +factory_boy==3.2.0 + +# azure blob storage +azure-storage-blob==2.1.0 \ No newline at end of file diff --git a/tests/commons/data_access_layer/file_stream_test.py b/tests/commons/data_access_layer/file_stream_test.py new file mode 100644 index 00000000..a3119774 --- /dev/null +++ b/tests/commons/data_access_layer/file_stream_test.py @@ -0,0 +1,15 @@ +import json + +from commons.data_access_layer.file_stream import FileStream + +fs = FileStream("storageaccounteystr82c5","tt-common-files") + +def test__get_file_stream__return_file_content__when_enter_file_name(): + result = fs.get_file_stream("activity_test.json") + + assert len(json.load(result)) == 15 + +def test__get_file_stream__return_None__when_not_enter_file_name_or_incorrect_name(): + result = fs.get_file_stream("") + + assert result == None \ No newline at end of file diff --git a/tests/time_tracker_api/activities/activities_model_test.py b/tests/time_tracker_api/activities/activities_model_test.py index c1a1b243..66e08ed7 100644 --- a/tests/time_tracker_api/activities/activities_model_test.py +++ b/tests/time_tracker_api/activities/activities_model_test.py @@ -64,3 +64,27 @@ def test_create_activity_should_add_active_status( activity_repository_create_mock.assert_called_with( data=expect_argument, event_context=ANY ) + +def test__find_all_from_blob_storage__return_list__when_send_event_context_and_correct_file_name( + event_context: EventContext, + activity_repository: ActivityCosmosDBRepository, +): + activity_repository.container = Mock() + + result = activity_repository.find_all_from_blob_storage( + event_context=event_context, + file_name="activity_test.json" + ) + assert len(result) == 15 + +def test__find_all_from_blob_storage__return_empty_list__when_send_event_context_and_incorrect_file_name( + event_context: EventContext, + activity_repository: ActivityCosmosDBRepository, +): + activity_repository.container = Mock() + + result = activity_repository.find_all_from_blob_storage( + event_context=event_context, + file_name="incorrect.json" + ) + assert result == [] \ No newline at end of file diff --git a/tests/time_tracker_api/activities/activities_namespace_test.py b/tests/time_tracker_api/activities/activities_namespace_test.py index a2b9ab20..86e34691 100644 --- a/tests/time_tracker_api/activities/activities_namespace_test.py +++ b/tests/time_tracker_api/activities/activities_namespace_test.py @@ -4,6 +4,7 @@ from flask import json from flask.testing import FlaskClient from flask_restplus._http import HTTPStatus +import pytest from pytest_mock import MockFixture from utils.enums.status import Status @@ -18,6 +19,14 @@ fake_activity = ({"id": fake.random_int(1, 9999)}).update(valid_activity_data) +def test__get_all_activities__return_response__when_send_activities_get_request( + client: FlaskClient, valid_header: dict +): + response = client.get( + "/activities", headers=valid_header, follow_redirects=True + ) + + assert HTTPStatus.OK == response.status_code def test_create_activity_should_succeed_with_valid_request( client: FlaskClient, mocker: MockFixture, valid_header: dict @@ -55,7 +64,7 @@ def test_create_activity_should_reject_bad_request( assert HTTPStatus.BAD_REQUEST == response.status_code repository_create_mock.assert_not_called() - +@pytest.mark.skip(reason="There is currently no way to test this. Getting the value of the azure blob storage") def test_list_all_active( client: FlaskClient, mocker: MockFixture, valid_header: dict ): @@ -81,7 +90,7 @@ def test_list_all_active( max_count=ANY, ) - +@pytest.mark.skip(reason="There is currently no way to test this. Getting the value of the azure blob storage") def test_list_all_active_activities( client: FlaskClient, mocker: MockFixture, valid_header: dict ): diff --git a/time_tracker_api/activities/activities_model.py b/time_tracker_api/activities/activities_model.py index cbfd0d20..ddb46411 100644 --- a/time_tracker_api/activities/activities_model.py +++ b/time_tracker_api/activities/activities_model.py @@ -1,5 +1,6 @@ from dataclasses import dataclass +import json from azure.cosmos import PartitionKey from commons.data_access_layer.cosmos_db import ( @@ -12,7 +13,7 @@ from commons.data_access_layer.database import EventContext from utils.enums.status import Status from utils.query_builder import CosmosDBQueryBuilder - +from commons.data_access_layer.file_stream import FileStream class ActivityDao(CRUDDao): pass @@ -113,6 +114,20 @@ def find_all( function_mapper = self.get_mapper_or_dict(mapper) return list(map(function_mapper, result)) + def find_all_from_blob_storage( + self, + event_context: EventContext, + mapper: Callable = None, + file_name: str = "activity.json", + ): + tenant_id_value = self.find_partition_key_value(event_context) + function_mapper = self.get_mapper_or_dict(mapper) + if tenant_id_value is None: + return [] + + fs = FileStream("storageaccounteystr82c5","tt-common-files") + result = fs.get_file_stream(file_name) + return list(map(function_mapper, json.load(result))) if result is not None else [] class ActivityCosmosDBDao(APICosmosDBDao, ActivityDao): def __init__(self, repository): @@ -128,7 +143,7 @@ def get_all_with_id_in_list( activity_ids, ) - def get_all( + def get_all_v1( self, conditions: dict = None, activities_id: List = None, @@ -147,6 +162,11 @@ def get_all( ) return activities + def get_all(self, conditions: dict = None) -> list: + event_ctx = self.create_event_context("read-many") + activities = self.repository.find_all_from_blob_storage(event_context=event_ctx) + return activities + def create(self, activity_payload: dict): event_ctx = self.create_event_context('create') activity_payload['status'] = Status.ACTIVE.value