Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
eb62162
refactor: TT-185 create SQLBuilder, TimeEntryQueryBuilder and find_al…
kellycastrof Mar 24, 2021
031b883
test: TT-185 query_builder tests
kellycastrof Mar 25, 2021
262ace0
test: TT-185 add test methods for TimeEntryQueryBuilder and new funct…
kellycastrof Mar 26, 2021
7914fdf
build: TT-199 build(deps): bump jinja2 in /requirements/time_tracker…
dependabot[bot] Mar 26, 2021
2b57549
refactor: TT-185 create SQLBuilder, TimeEntryQueryBuilder and find_al…
kellycastrof Mar 24, 2021
7606663
test: TT-185 query_builder tests
kellycastrof Mar 25, 2021
f1fc8a6
test: TT-185 add test methods for TimeEntryQueryBuilder and new funct…
kellycastrof Mar 26, 2021
be72ff8
refactor: TT-185 rename get_string_without_empty_spaces to remove_whi…
kellycastrof Mar 26, 2021
3401b39
refactor: TT-185 add time_entries_id in condition
kellycastrof Mar 26, 2021
2342ce9
refactor: TT-185 delete empty lines
kellycastrof Mar 26, 2021
5358c40
refactor: TT-185 delete isintance validation
kellycastrof Mar 26, 2021
a403edb
refactor: TT-185 improve function remove_white_spaces
Angeluz-07 Mar 26, 2021
d538899
refactor: TT-185 change column to columns
Angeluz-07 Mar 26, 2021
06cbca7
refactor: TT-185 add more scenarios to test_add_sql_in_condition_shou…
Angeluz-07 Mar 26, 2021
a1331fc
refactor: TT-185 add more scenarios to test__build_where_should_retur…
Angeluz-07 Mar 26, 2021
c44f20e
refactor: TT-185 improve test_TimeEntryQueryBuilder_is_subclass_Cosmo…
Angeluz-07 Mar 26, 2021
f17d841
refactor: TT-185 rename args in TimeEntriesRepository
Angeluz-07 Mar 26, 2021
be6e9b1
refactor: TT-185 change the scenarios in test_add_sql_date_range_cond…
kellycastrof Mar 26, 2021
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
refactor: TT-185 create SQLBuilder, TimeEntryQueryBuilder and find_al…
…l_v2 method in time entries repository
  • Loading branch information
kellycastrof committed Mar 26, 2021
commit 2b57549eaac65c98de83f51ba9915524fc9e87fa
55 changes: 55 additions & 0 deletions tests/time_tracker_api/time_entries/time_entries_model_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
)
from time_tracker_api.time_entries.time_entries_repository import (
TimeEntryCosmosDBRepository,
TimeEntryCosmosDBModel,
)


Expand Down Expand Up @@ -266,3 +267,57 @@ def test_updated_item_without_deleted_key_should_call_validate_data(
time_entry_repository.on_update({}, event_context)
on_update_mock.assert_called_once()
time_entry_repository.validate_data.assert_called_once()


@pytest.mark.parametrize(
'owner_list,expected_result',
[
([], ""),
(None, ""),
(["id"], "AND c.owner_id IN ('id')"),
(["id1", "id2"], "AND c.owner_id IN ('id1', 'id2')"),
],
)
def test_create_owner_condition(
owner_list,
expected_result,
event_context: EventContext,
time_entry_repository: TimeEntryCosmosDBRepository,
):
result = time_entry_repository.create_owner_condition(owner_list)
assert result == expected_result


def test_find_all_v2(
event_context: EventContext,
time_entry_repository: TimeEntryCosmosDBRepository,
):
expected_item = {
'id': 'id',
'start_date': '2021-03-22T10:00:00.000Z',
'end_date': "2021-03-22T11:00:00.000Z",
'description': 'do some testing',
'tenant_id': 'tenant_id',
'project_id': 'project_id',
'activity_id': 'activity_id',
'technologies': ['python'],
}
query_items_mock = Mock(return_value=[expected_item])
time_entry_repository.container = Mock()
time_entry_repository.container.query_items = query_items_mock

result = time_entry_repository.find_all_v2(
event_context,
['owner_id'],
{
'start_date': "2021-03-22T10:00:00.000Z",
'end_date': "2021-03-22T11:00:00.000Z",
},
)

print(result)
assert len(result) == 1
time_entry = result[0]
print(time_entry.__dict__)
assert isinstance(time_entry, TimeEntryCosmosDBModel)
assert time_entry.__dict__ == expected_item
22 changes: 22 additions & 0 deletions time_tracker_api/time_entries/time_entries_query_builder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from utils.query_builder import CosmosDBQueryBuilder


class TimeEntryQueryBuilder(CosmosDBQueryBuilder):
def __init__(self):
super().__init__()

def add_sql_date_range_condition(self, dates: tuple = None):
if dates and len(dates) == 2:
start_date, end_date = dates
condition = """
((c.start_date BETWEEN @start_date AND @end_date) OR
(c.end_date BETWEEN @start_date AND @end_date))
"""
self.where_conditions.append(condition)
self.parameters.extend(
[
{'name': '@start_date', 'value': start_date},
{'name': '@end_date', 'value': end_date},
]
)
return self
37 changes: 37 additions & 0 deletions time_tracker_api/time_entries/time_entries_repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
from commons.data_access_layer.database import EventContext
from typing import List, Callable
from time_tracker_api.projects import projects_model
from time_tracker_api.time_entries.time_entries_query_builder import (
TimeEntryQueryBuilder,
)


class TimeEntryCosmosDBRepository(CosmosDBRepository):
Expand Down Expand Up @@ -196,6 +199,40 @@ def find_all(
abort(HTTPStatus.NOT_FOUND, "Time entry not found")
return time_entries

def find_all_v2(
self,
event_context: EventContext,
owner_id_list: List[str],
date_range: tuple = None,
fields: dict = None,
limit: int = None,
offset: int = 0,
visible_only=True,
mapper: Callable = None,
):
limit = self.get_page_size_or(limit)
partition_key_value = self.find_partition_key_value(event_context)
query_builder = (
TimeEntryQueryBuilder()
.add_sql_date_range_condition(date_range)
.add_sql_in_condition(owner_id_list)
.add_sql_where_equal_condition(fields)
.add_sql_limit_condition(limit)
.add_sql_offset_condition(offset)
.build()
)

query_str = query_builder.get_query()
params = query_builder.get_parameters()

result = self.container.query_items(
query=query_str,
parameters=params,
partition_key=partition_key_value,
)
function_mapper = self.get_mapper_or_dict(mapper)
return list(map(function_mapper, result))

def on_create(self, new_item_data: dict, event_context: EventContext):
CosmosDBRepository.on_create(self, new_item_data, event_context)

Expand Down
96 changes: 96 additions & 0 deletions utils/query_builder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
from typing import List
from utils.repository import convert_list_to_tuple_string


class CosmosDBQueryBuilder:
query: str

def __init__(self):
super().__init__()
self.query = ""
self.parameters = []
self.select_conditions = []
self.where_conditions = []
self.limit = None
self.offset = None

def add_select_conditions(self, column: List[str] = None):
column = column if column else ["*"]
self.select_conditions.extend(column)
return self

def add_sql_in_condition(
self, attribute: str = None, ids_list: List[str] = None
):
if ids_list and attribute and len(ids_list) > 0:
ids_values = convert_list_to_tuple_string(ids_list)
self.where_conditions.append(f"c.{attribute} IN {ids_values}")
return self

def add_sql_where_equal_condition(self, data: dict = None):
if data:
for k, v in data.items():
condition = f"c.{k} = @{k}"
self.where_conditions.append(condition)
self.parameters.append({'name': f'@{k}', 'value': v})
return self

def add_sql_visibility_condition(self, visible_only: bool):
if visible_only:
self.where_conditions.append('NOT IS_DEFINED(c.deleted)')
return self

def add_sql_limit_condition(self, limit):
if limit and isinstance(limit, int):
self.limit = limit
return self

def add_sql_offset_condition(self, offset):
if offset and isinstance(offset, int):
self.offset = offset
return self

def build_select(self):
if len(self.select_conditions) < 1:
self.select_conditions.append("*")
return ",".join(self.select_conditions)

def build_where(self):
if len(self.where_conditions) > 0:
return "WHERE " + " AND ".join(self.where_conditions)
else:
return ""

def build_offset(self):
if self.offset:
self.parameters.append({'name': '@offset', 'value': self.offset})
return "OFFSET @offset"
else:
return ""

def build_limit(self):
if self.limit:
self.parameters.append({'name': '@limit', 'value': self.limit})
return "LIMIT @limit"
else:
return ""

def build(self):
self.query = """
SELECT {select_conditions} FROM c
{where_conditions}
{offset_condition}
{limit_condition}
""".format(
select_conditions=self.build_select(),
where_conditions=self.build_where(),
offset_condition=self.build_offset(),
limit_condition=self.build_limit(),
)
return self

def get_query(self):
return self.query

def get_parameters(self):
return self.parameters