Skip to content

Commit 819ca72

Browse files
author
EliuX
committed
Close #55 Create activity model for Cosmos DB
1 parent 8e287d8 commit 819ca72

File tree

6 files changed

+76
-64
lines changed

6 files changed

+76
-64
lines changed

commons/data_access_layer/cosmos_db.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -168,28 +168,31 @@ def __init__(self, repository: CosmosDBRepository):
168168
self.repository = repository
169169

170170
def get_all(self) -> list:
171-
tenant_id: str = current_user_tenant_id()
171+
tenant_id: str = self.partition_key_value
172172
return self.repository.find_all(partition_key_value=tenant_id)
173173

174174
def get(self, id):
175-
tenant_id: str = current_user_tenant_id()
175+
tenant_id: str = self.partition_key_value
176176
return self.repository.find(id, partition_key_value=tenant_id)
177177

178178
def create(self, data: dict):
179179
data['id'] = str(uuid.uuid4())
180-
data['tenant_id'] = current_user_tenant_id()
180+
data['tenant_id'] = self.partition_key_value
181181
return self.repository.create(data)
182182

183183
def update(self, id, data: dict):
184-
tenant_id: str = current_user_tenant_id()
185184
return self.repository.partial_update(id,
186185
changes=data,
187-
partition_key_value=tenant_id)
186+
partition_key_value=self.partition_key_value)
188187

189188
def delete(self, id):
190189
tenant_id: str = current_user_tenant_id()
191190
self.repository.delete(id, partition_key_value=tenant_id)
192191

192+
@property
193+
def partition_key_value(self):
194+
return current_user_tenant_id()
195+
193196

194197
def init_app(app: Flask) -> None:
195198
global cosmos_helper

tests/time_tracker_api/activities/activities_namespace_test.py

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
from faker import Faker
22
from flask import json
33
from flask.testing import FlaskClient
4-
from pytest_mock import MockFixture
54
from flask_restplus._http import HTTPStatus
5+
from pytest_mock import MockFixture
6+
7+
from time_tracker_api.security import current_user_tenant_id
68

79
fake = Faker()
810

@@ -26,7 +28,7 @@ def test_create_activity_should_succeed_with_valid_request(client: FlaskClient,
2628
response = client.post("/activities", json=valid_activity_data, follow_redirects=True)
2729

2830
assert HTTPStatus.CREATED == response.status_code
29-
repository_create_mock.assert_called_once_with(valid_activity_data)
31+
repository_create_mock.assert_called_once()
3032

3133

3234
def test_create_activity_should_reject_bad_request(client: FlaskClient, mocker: MockFixture):
@@ -54,7 +56,7 @@ def test_list_all_activities(client: FlaskClient, mocker: MockFixture):
5456
assert [] == json_data
5557
repository_find_all_mock.assert_called_once()
5658

57-
#HEY
59+
5860
def test_get_activity_should_succeed_with_valid_id(client: FlaskClient, mocker: MockFixture):
5961
from time_tracker_api.activities.activities_namespace import activity_dao
6062

@@ -68,7 +70,8 @@ def test_get_activity_should_succeed_with_valid_id(client: FlaskClient, mocker:
6870

6971
assert HTTPStatus.OK == response.status_code
7072
fake_activity == json.loads(response.data)
71-
repository_find_mock.assert_called_once_with(str(valid_id))
73+
repository_find_mock.assert_called_once_with(str(valid_id),
74+
partition_key_value=current_user_tenant_id())
7275

7376

7477
def test_get_activity_should_return_not_found_with_invalid_id(client: FlaskClient, mocker: MockFixture):
@@ -84,7 +87,8 @@ def test_get_activity_should_return_not_found_with_invalid_id(client: FlaskClien
8487
response = client.get("/activities/%s" % invalid_id, follow_redirects=True)
8588

8689
assert HTTPStatus.NOT_FOUND == response.status_code
87-
repository_find_mock.assert_called_once_with(str(invalid_id))
90+
repository_find_mock.assert_called_once_with(str(invalid_id),
91+
partition_key_value=current_user_tenant_id())
8892

8993

9094
def test_get_activity_should_return_422_for_invalid_id_format(client: FlaskClient, mocker: MockFixture):
@@ -100,28 +104,31 @@ def test_get_activity_should_return_422_for_invalid_id_format(client: FlaskClien
100104
response = client.get("/activities/%s" % invalid_id, follow_redirects=True)
101105

102106
assert HTTPStatus.UNPROCESSABLE_ENTITY == response.status_code
103-
repository_find_mock.assert_called_once_with(str(invalid_id))
107+
repository_find_mock.assert_called_once_with(str(invalid_id),
108+
partition_key_value=current_user_tenant_id())
104109

105110

106111
def test_update_activity_should_succeed_with_valid_data(client: FlaskClient, mocker: MockFixture):
107112
from time_tracker_api.activities.activities_namespace import activity_dao
108113

109114
repository_update_mock = mocker.patch.object(activity_dao.repository,
110-
'update',
115+
'partial_update',
111116
return_value=fake_activity)
112117

113118
valid_id = fake.random_int(1, 9999)
114119
response = client.put("/activities/%s" % valid_id, json=valid_activity_data, follow_redirects=True)
115120

116121
assert HTTPStatus.OK == response.status_code
117122
fake_activity == json.loads(response.data)
118-
repository_update_mock.assert_called_once_with(str(valid_id), valid_activity_data)
123+
repository_update_mock.assert_called_once_with(str(valid_id),
124+
changes=valid_activity_data,
125+
partition_key_value=current_user_tenant_id())
119126

120127

121128
def test_update_activity_should_reject_bad_request(client: FlaskClient, mocker: MockFixture):
122129
from time_tracker_api.activities.activities_namespace import activity_dao
123130
repository_update_mock = mocker.patch.object(activity_dao.repository,
124-
'update',
131+
'partial_update',
125132
return_value=fake_activity)
126133

127134
valid_id = fake.random_int(1, 9999)
@@ -138,15 +145,17 @@ def test_update_activity_should_return_not_found_with_invalid_id(client: FlaskCl
138145
invalid_id = fake.random_int(1, 9999)
139146

140147
repository_update_mock = mocker.patch.object(activity_dao.repository,
141-
'update',
148+
'partial_update',
142149
side_effect=NotFound)
143150

144151
response = client.put("/activities/%s" % invalid_id,
145152
json=valid_activity_data,
146153
follow_redirects=True)
147154

148155
assert HTTPStatus.NOT_FOUND == response.status_code
149-
repository_update_mock.assert_called_once_with(str(invalid_id), valid_activity_data)
156+
repository_update_mock.assert_called_once_with(str(invalid_id),
157+
changes=valid_activity_data,
158+
partition_key_value=current_user_tenant_id())
150159

151160

152161
def test_delete_activity_should_succeed_with_valid_id(client: FlaskClient, mocker: MockFixture):
@@ -155,14 +164,15 @@ def test_delete_activity_should_succeed_with_valid_id(client: FlaskClient, mocke
155164
valid_id = fake.random_int(1, 9999)
156165

157166
repository_remove_mock = mocker.patch.object(activity_dao.repository,
158-
'remove',
167+
'delete',
159168
return_value=None)
160169

161170
response = client.delete("/activities/%s" % valid_id, follow_redirects=True)
162171

163172
assert HTTPStatus.NO_CONTENT == response.status_code
164173
assert b'' == response.data
165-
repository_remove_mock.assert_called_once_with(str(valid_id))
174+
repository_remove_mock.assert_called_once_with(str(valid_id),
175+
partition_key_value=current_user_tenant_id())
166176

167177

168178
def test_delete_activity_should_return_not_found_with_invalid_id(client: FlaskClient, mocker: MockFixture):
@@ -172,13 +182,14 @@ def test_delete_activity_should_return_not_found_with_invalid_id(client: FlaskCl
172182
invalid_id = fake.random_int(1, 9999)
173183

174184
repository_remove_mock = mocker.patch.object(activity_dao.repository,
175-
'remove',
185+
'delete',
176186
side_effect=NotFound)
177187

178188
response = client.delete("/activities/%s" % invalid_id, follow_redirects=True)
179189

180190
assert HTTPStatus.NOT_FOUND == response.status_code
181-
repository_remove_mock.assert_called_once_with(str(invalid_id))
191+
repository_remove_mock.assert_called_once_with(str(invalid_id),
192+
partition_key_value=current_user_tenant_id())
182193

183194

184195
def test_delete_activity_should_return_422_for_invalid_id_format(client: FlaskClient, mocker: MockFixture):
@@ -188,10 +199,11 @@ def test_delete_activity_should_return_422_for_invalid_id_format(client: FlaskCl
188199
invalid_id = fake.company()
189200

190201
repository_remove_mock = mocker.patch.object(activity_dao.repository,
191-
'remove',
202+
'delete',
192203
side_effect=UnprocessableEntity)
193204

194205
response = client.delete("/activities/%s" % invalid_id, follow_redirects=True)
195206

196207
assert HTTPStatus.UNPROCESSABLE_ENTITY == response.status_code
197-
repository_remove_mock.assert_called_once_with(str(invalid_id))
208+
repository_remove_mock.assert_called_once_with(str(invalid_id),
209+
partition_key_value=current_user_tenant_id())

tests/time_tracker_api/projects/projects_namespace_test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ def test_update_project_should_reject_bad_request(client: FlaskClient, mocker: M
135135
"project_type_id": fake.pyint(min_value=1, max_value=100),
136136
})
137137
repository_update_mock = mocker.patch.object(project_dao.repository,
138-
'update',
138+
'partial_update',
139139
return_value=fake_project)
140140

141141
valid_id = fake.random_int(1, 9999)
Lines changed: 32 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,15 @@
1+
from dataclasses import dataclass
2+
13
from azure.cosmos import PartitionKey
24

5+
from commons.data_access_layer.cosmos_db import CosmosDBModel, CosmosDBDao, CosmosDBRepository
36
from commons.data_access_layer.database import CRUDDao
47

58

69
class ActivityDao(CRUDDao):
710
pass
811

912

10-
def create_dao() -> ActivityDao:
11-
from sqlalchemy_utils import UUIDType
12-
import uuid
13-
from commons.data_access_layer.sql import db
14-
from commons.data_access_layer.sql import SQLCRUDDao
15-
16-
class ActivitySQLModel(db.Model):
17-
__tablename__ = 'activity'
18-
id = db.Column(UUIDType(binary=False), primary_key=True, default=uuid.uuid4)
19-
name = db.Column(db.String(50), unique=True, nullable=False)
20-
description = db.Column(db.String(250), unique=False, nullable=False)
21-
deleted = db.Column(UUIDType(binary=False), default=uuid.uuid4)
22-
tenant_id = db.Column(UUIDType(binary=False), default=uuid.uuid4)
23-
24-
def __repr__(self):
25-
return '<Activity %r>' % self.name
26-
27-
def __str___(self):
28-
return "the activity \"%s\"" % self.name
29-
30-
class ActivitySQLDao(ActivityDao, SQLCRUDDao):
31-
def __init__(self):
32-
SQLCRUDDao.__init__(self, ActivitySQLModel)
33-
34-
return ActivitySQLDao()
35-
36-
3713
container_definition = {
3814
'id': 'activity',
3915
'partition_key': PartitionKey(path='/tenant_id'),
@@ -43,3 +19,32 @@ def __init__(self):
4319
]
4420
}
4521
}
22+
23+
24+
@dataclass()
25+
class ActivityCosmosDBModel(CosmosDBModel):
26+
id: str
27+
name: str
28+
description: str
29+
deleted: str
30+
tenant_id: str
31+
32+
def __init__(self, data):
33+
super(ActivityCosmosDBModel, self).__init__(data)
34+
35+
def __repr__(self):
36+
return '<Activity %r>' % self.name
37+
38+
def __str___(self):
39+
return "the activity \"%s\"" % self.name
40+
41+
42+
def create_dao() -> ActivityDao:
43+
repository = CosmosDBRepository.from_definition(container_definition,
44+
mapper=ActivityCosmosDBModel)
45+
46+
class ActivityCosmosDBDao(CosmosDBDao, ActivityDao):
47+
def __init__(self):
48+
CosmosDBDao.__init__(self, repository)
49+
50+
return ActivityCosmosDBDao()

time_tracker_api/activities/activities_namespace.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,6 @@
2222
title='Description',
2323
description='Comments about the activity',
2424
example=faker.paragraph(),
25-
),
26-
'tenant_id': fields.String(
27-
required=True,
28-
title='Identifier of Tenant',
29-
description='Tenant this activity belongs to',
30-
example=faker.uuid4(),
3125
)
3226
})
3327

@@ -39,6 +33,12 @@
3933
description='The unique identifier',
4034
example=faker.uuid4(),
4135
),
36+
'tenant_id': fields.String(
37+
required=True,
38+
title='Identifier of Tenant',
39+
description='Tenant this activity belongs to',
40+
example=faker.uuid4(),
41+
),
4242
}
4343
activity_response_fields.update(audit_fields)
4444

time_tracker_api/projects/projects_model.py

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,11 @@
55
from commons.data_access_layer.cosmos_db import CosmosDBModel, CosmosDBDao, CosmosDBRepository
66
from commons.data_access_layer.database import CRUDDao
77

8-
"""
9-
Protocols
10-
"""
11-
128

139
class ProjectDao(CRUDDao):
1410
pass
1511

1612

17-
"""
18-
Cosmos DB
19-
"""
20-
2113
container_definition = {
2214
'id': 'project',
2315
'partition_key': PartitionKey(path='/tenant_id'),

0 commit comments

Comments
 (0)