Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
feat: add test find_all is called with generated dates
  • Loading branch information
Angeluz-07 committed Apr 30, 2020
commit f877e0ac1dc8149d622e934db2cb5a75b861e419
56 changes: 43 additions & 13 deletions commons/data_access_layer/cosmos_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import logging
import uuid
from datetime import datetime
from typing import Callable, List
from typing import Callable, List, Tuple

import azure.cosmos.cosmos_client as cosmos_client
import azure.cosmos.exceptions as exceptions
Expand Down Expand Up @@ -117,18 +117,15 @@ def create_sql_where_conditions(conditions: dict, container_name='c') -> str:
return ""

@staticmethod
def create_sql_custom_conditions(custom_conditions: List[str]) -> str:
if len(custom_conditions) > 0:
return """
AND {custom_conditions_clause}
""".format(
custom_conditions_clause=" AND ".join(custom_conditions)
)
def create_custom_sql_conditions(custom_sql_conditions: List[str]) -> str:
if len(custom_sql_conditions) > 0:
return "AND {custom_sql_conditions_clause}".format(
custom_sql_conditions_clause=" AND ".join(custom_sql_conditions))
else:
return ''

@staticmethod
def generate_condition_values(conditions: dict) -> dict:
def generate_params(conditions: dict) -> dict:
result = []
for k, v in conditions.items():
result.append({
Expand Down Expand Up @@ -165,7 +162,7 @@ def find(self, id: str, partition_key_value, peeker: 'function' = None,
function_mapper = self.get_mapper_or_dict(mapper)
return function_mapper(self.check_visibility(found_item, visible_only))

def find_all(self, partition_key_value: str, conditions: dict = {}, custom_conditions: List[str] = [],
def find_all(self, partition_key_value: str, conditions: dict = {}, custom_sql_conditions: List[str] = [],
custom_params: dict = {}, max_count=None, offset=0, visible_only=True, mapper: Callable = None):
# TODO Use the tenant_id param and change container alias
max_count = self.get_page_size_or(max_count)
Expand All @@ -174,16 +171,16 @@ def find_all(self, partition_key_value: str, conditions: dict = {}, custom_condi
{"name": "@offset", "value": offset},
{"name": "@max_count", "value": max_count},
]
params.extend(self.generate_condition_values(conditions))
params.extend(self.generate_params(conditions))
params.extend(custom_params)
result = self.container.query_items(query="""
SELECT * FROM c WHERE c.{partition_key_attribute}=@partition_key_value
{conditions_clause} {visibility_condition} {custom_conditions_clause} {order_clause}
{conditions_clause} {visibility_condition} {custom_sql_conditions_clause} {order_clause}
OFFSET @offset LIMIT @max_count
""".format(partition_key_attribute=self.partition_key_attribute,
visibility_condition=self.create_sql_condition_for_visibility(visible_only),
conditions_clause=self.create_sql_where_conditions(conditions),
custom_conditions_clause=self.create_sql_custom_conditions(custom_conditions),
custom_sql_conditions_clause=self.create_custom_sql_conditions(custom_sql_conditions),
order_clause=self.create_sql_order_clause()),
parameters=params,
partition_key=partition_key_value,
Expand Down Expand Up @@ -292,3 +289,36 @@ def generate_uuid4() -> str:
def init_app(app: Flask) -> None:
global cosmos_helper
cosmos_helper = CosmosDBFacade.from_flask_config(app)


def get_last_day_of_month(year: int, month: int) -> int:
from calendar import monthrange
return monthrange(year=year, month=month)[1]


def get_current_year() -> int:
return datetime.now().year


def get_current_month() -> int:
return datetime.now().month


def get_date_range_of_month(
year: int,
month: int
) -> Tuple[datetime, datetime]:
first_day_of_month = 1
start_date = datetime(year=year, month=month, day=first_day_of_month)

last_day_of_month = get_last_day_of_month(year=year, month=month)
end_date = datetime(
year=year,
month=month,
day=last_day_of_month,
hour=23,
minute=59,
second=59,
microsecond=999999
)
return start_date, end_date
2 changes: 1 addition & 1 deletion tests/commons/data_access_layer/cosmos_db_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -559,7 +559,7 @@ def test_repository_create_sql_where_conditions_with_no_values(cosmos_db_reposit


def test_repository_append_conditions_values(cosmos_db_repository: CosmosDBRepository):
result = cosmos_db_repository.generate_condition_values({'owner_id': 'mark', 'customer_id': 'ioet'})
result = cosmos_db_repository.generate_params({'owner_id': 'mark', 'customer_id': 'ioet'})

assert result is not None
assert result == [{'name': '@owner_id', 'value': 'mark'},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
from flask_restplus._http import HTTPStatus
from pytest_mock import MockFixture, pytest

from commons.data_access_layer.cosmos_db import current_datetime, current_datetime_str
from commons.data_access_layer.cosmos_db import current_datetime, \
current_datetime_str, get_date_range_of_month, get_current_month, \
get_current_year, datetime_str

fake = Faker()

Expand Down Expand Up @@ -470,3 +472,44 @@ def test_create_with_valid_uuid_format_should_return_created(client: FlaskClient
assert HTTPStatus.CREATED == response.status_code
repository_container_create_item_mock.assert_called()


@pytest.mark.parametrize(
'url,month,year',
[
('/time-entries?month=4&year=2020', 4, 2020),
('/time-entries?month=4', 4, get_current_year()),
('/time-entries', get_current_month(), get_current_year())
]
)
def test_find_all_is_called_with_generated_dates(client: FlaskClient,
mocker: MockFixture,
valid_header: dict,
tenant_id: str,
owner_id: str,
url: str,
month: int,
year: int):
from time_tracker_api.time_entries.time_entries_namespace import time_entries_dao
repository_find_all_mock = mocker.patch.object(time_entries_dao.repository,
'find_all',
return_value=fake_time_entry)

response = client.get(url,
headers=valid_header,
follow_redirects=True)

start_date, end_date = get_date_range_of_month(year, month)
custom_args = {
'start_date': datetime_str(start_date),
'end_date': datetime_str(end_date)
}

conditions = {
'owner_id': owner_id
}

assert HTTPStatus.OK == response.status_code
assert json.loads(response.data) is not None
repository_find_all_mock.assert_called_once_with(partition_key_value=tenant_id,
conditions=conditions,
custom_args=custom_args)
59 changes: 15 additions & 44 deletions time_tracker_api/time_entries/time_entries_model.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import abc
from dataclasses import dataclass, field
from typing import List, Callable, Tuple
from datetime import datetime
from typing import List, Callable

from azure.cosmos import PartitionKey
from flask_restplus._http import HTTPStatus

from commons.data_access_layer.cosmos_db import CosmosDBDao, CosmosDBRepository, CustomError, current_datetime_str, \
CosmosDBModel
CosmosDBModel, get_date_range_of_month, get_current_year, get_current_month
from commons.data_access_layer.database import CRUDDao
from time_tracker_api.security import current_user_id

Expand Down Expand Up @@ -76,31 +75,28 @@ def create_sql_ignore_id_condition(id: str):
return "AND c.id!=@ignore_id"

@staticmethod
def create_sql_date_range_filter(custom_args: dict) -> str:
if 'start_date' and 'end_date' in custom_args:
def create_sql_date_range_filter(date_range: dict) -> str:
if 'start_date' and 'end_date' in date_range:
return """
((c.start_date BETWEEN @start_date AND @end_date) OR
(c.end_date BETWEEN @start_date AND @end_date))
"""
else:
return ''

def find_all(self, partition_key_value: str, conditions: dict, custom_args: dict):
custom_conditions = []
custom_conditions.append(
self.create_sql_date_range_filter(custom_args)
def find_all(self, partition_key_value: str, conditions: dict, date_range: dict):
custom_sql_conditions = []
custom_sql_conditions.append(
self.create_sql_date_range_filter(date_range)
)

custom_params = [
{"name": "@start_date", "value": custom_args.get('start_date')},
{"name": "@end_date", "value": custom_args.get('end_date')},
]
custom_params = self.generate_params(date_range)

return CosmosDBRepository.find_all(
self,
partition_key_value=partition_key_value,
conditions=conditions,
custom_conditions=custom_conditions,
custom_sql_conditions=custom_sql_conditions,
custom_params=custom_params
)

Expand Down Expand Up @@ -128,7 +124,7 @@ def find_interception_with_date_range(self, start_date, end_date, owner_id, part
{"name": "@end_date", "value": end_date or current_datetime_str()},
{"name": "@ignore_id", "value": ignore_id},
]
params.extend(self.generate_condition_values(conditions))
params.extend(self.generate_params(conditions))
result = self.container.query_items(
query="""
SELECT * FROM c WHERE ((c.start_date BETWEEN @start_date AND @end_date)
Expand Down Expand Up @@ -159,7 +155,7 @@ def find_running(self, partition_key_value: str, owner_id: str, mapper: Callable
visibility_condition=self.create_sql_condition_for_visibility(True),
conditions_clause=self.create_sql_where_conditions(conditions),
),
parameters=self.generate_condition_values(conditions),
parameters=self.generate_params(conditions),
partition_key=partition_key_value,
max_item_count=1)

Expand Down Expand Up @@ -187,31 +183,6 @@ def validate_data(self, data):
description="There is another time entry in that date range")


def get_last_day_of_month(year: int, month: int) -> int:
from calendar import monthrange
return monthrange(year=year, month=month)[1]


def get_current_year() -> int:
return datetime.now().year


def get_current_month() -> int:
return datetime.now().month


def get_date_range_of_month(
year: int,
month: int
) -> Tuple[datetime, datetime]:
first_day_of_month = 1
start_date = datetime(year=year, month=month, day=first_day_of_month)

# TODO : fix bound as this would exclude the last day
last_day_of_month = get_last_day_of_month(year=year, month=month)
end_date = datetime(year=year, month=month, day=last_day_of_month)
return start_date, end_date


class TimeEntriesCosmosDBDao(TimeEntriesDao, CosmosDBDao):
def __init__(self, repository):
Expand Down Expand Up @@ -242,13 +213,13 @@ def get_all(self, conditions: dict = {}) -> list:

start_date, end_date = get_date_range_of_month(year, month)

custom_args = {
date_range = {
'start_date': start_date.isoformat(),
'end_date': end_date.isoformat()
'end_date': end_date.isoformat(),
}
return self.repository.find_all(partition_key_value=self.partition_key_value,
conditions=conditions,
custom_args=custom_args)
date_range=date_range)

def get(self, id):
return self.repository.find(id,
Expand Down
4 changes: 2 additions & 2 deletions time_tracker_api/time_entries/time_entries_namespace.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,11 +111,11 @@
# custom attributes filter
attributes_filter.add_argument('month', required=False,
store_missing=False,
help="(Filter) month to filter",
help="(Filter) Month to filter by",
location='args')
attributes_filter.add_argument('year', required=False,
store_missing=False,
help="(Filter) year to filter",
help="(Filter) Year to filter by",
location='args')

@ns.route('')
Expand Down