Skip to content

Commit 50f8d46

Browse files
authored
feat: TT-293 Create Script to generate data in the Database (#310)
* refactor: TT-293 refactor docker-compose to execute a valid entrypoint.sh * feat: TT-293 add CLI functionality in order to manage entities data in the database * feat: TT-293 add extra security layer to avoid use cli in a non-development environment
1 parent c8c22d3 commit 50f8d46

36 files changed

+1362
-129
lines changed

cosmosdb-emulator/init_emulator.sh

Lines changed: 0 additions & 36 deletions
This file was deleted.

cosmosdb-emulator/init_emulator_db.py

Lines changed: 0 additions & 64 deletions
This file was deleted.

cosmosdb_emulator/cli.sh

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#!/bin/sh
2+
COMMAND=$@
3+
API_CONTAINER_NAME="time-tracker-backend_api"
4+
TIME_TRACKER_CLI_URL="cosmosdb_emulator/time_tracker_cli"
5+
DEFAULT_SCRIPT_NAME="main.py"
6+
FIRST_ARG=$1
7+
8+
execute(){
9+
docker exec -it $API_CONTAINER_NAME sh "cosmosdb_emulator/verify_environment.sh"
10+
11+
if [ "$FIRST_ARG" != "$DEFAULT_SCRIPT_NAME" ]; then
12+
echo "Do not forget that the file name is $DEFAULT_SCRIPT_NAME and needs to be sent as first parameter"
13+
echo "For example: ./cli.sh main.py"
14+
exit 0
15+
fi
16+
17+
TIME_TRACKER_CLI="python3 $COMMAND"
18+
19+
docker exec -it $API_CONTAINER_NAME sh -c "cd $TIME_TRACKER_CLI_URL && $TIME_TRACKER_CLI"
20+
}
21+
22+
execute

cosmosdb-emulator/entrypoint.sh renamed to cosmosdb_emulator/entrypoint.sh

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
11
#!/bin/sh
22

3-
until curl -ksf "${DATABASE_ACCOUNT_URI}/_explorer/emulator.pem" -o 'cosmosdb-emulator/emulatorcert.crt'; do
3+
until curl -ksf "${DATABASE_ACCOUNT_URI}/_explorer/emulator.pem" -o 'cosmosdb_emulator/emulatorcert.crt'; do
44
echo "Waiting for Cosmosdb to start..."
55
sleep 10
66
done
77

8+
source cosmosdb_emulator/verify_environment.sh
9+
810
echo "Container cosmosemulator started."
911

1012
echo "Importing SSL..."
11-
cp cosmosdb-emulator/emulatorcert.crt /usr/local/share/ca-certificates/
12-
cp cosmosdb-emulator/emulatorcert.crt /usr/share/ca-certificates/
13+
cp cosmosdb_emulator/emulatorcert.crt /usr/local/share/ca-certificates/
14+
cp cosmosdb_emulator/emulatorcert.crt /usr/share/ca-certificates/
1315
update-ca-certificates --fresh
1416
echo "Importing Containers..."
1517
export REQUESTS_CA_BUNDLE=/etc/ssl/certs/
16-
python3 ./cosmosdb-emulator/init_emulator_db.py
18+
python3 ./cosmosdb_emulator/init_emulator_db.py
1719
echo "Installation succeed!!"
1820

1921
echo "Starting Flask!!"
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
from azure.cosmos import exceptions, CosmosClient, PartitionKey
2+
import os, sys
3+
4+
sys.path.append("/usr/src/app")
5+
6+
DATABASE_ACCOUNT_URI = os.environ.get('DATABASE_ACCOUNT_URI')
7+
DATABASE_MASTER_KEY = os.environ.get('DATABASE_MASTER_KEY')
8+
DATABASE_NAME = os.environ.get('DATABASE_NAME')
9+
10+
client = CosmosClient(DATABASE_ACCOUNT_URI, DATABASE_MASTER_KEY)
11+
database = client.create_database_if_not_exists(id=DATABASE_NAME)
12+
13+
print("Creating TimeTracker initial initial database schema...")
14+
15+
try:
16+
print('- Project')
17+
from time_tracker_api.projects.projects_model import (
18+
container_definition as project_definition,
19+
)
20+
21+
database.create_container_if_not_exists(**project_definition)
22+
23+
print('- Project type')
24+
from time_tracker_api.project_types.project_types_model import (
25+
container_definition as project_type_definition,
26+
)
27+
28+
database.create_container_if_not_exists(**project_type_definition)
29+
30+
print('- Activity')
31+
from time_tracker_api.activities.activities_model import (
32+
container_definition as activity_definition,
33+
)
34+
35+
database.create_container_if_not_exists(**activity_definition)
36+
37+
print('- Customer')
38+
from time_tracker_api.customers.customers_model import (
39+
container_definition as customer_definition,
40+
)
41+
42+
database.create_container_if_not_exists(**customer_definition)
43+
44+
print('- Time entry')
45+
from time_tracker_api.time_entries.time_entries_model import (
46+
container_definition as time_entry_definition,
47+
)
48+
49+
database.create_container_if_not_exists(**time_entry_definition)
50+
51+
print('- Technology')
52+
from time_tracker_api.technologies.technologies_model import (
53+
container_definition as technologies_definition,
54+
)
55+
56+
database.create_container_if_not_exists(**technologies_definition)
57+
except exceptions.CosmosResourceExistsError as e:
58+
print(
59+
"Unexpected error while creating initial database schema: %s"
60+
% e.message
61+
)
62+
63+
print("Done!")
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import os
2+
import sys
3+
4+
from azure.cosmos import CosmosClient
5+
from azure.cosmos.exceptions import (
6+
CosmosResourceExistsError,
7+
CosmosResourceNotFoundError,
8+
)
9+
10+
from cosmosdb_emulator.time_tracker_cli.data_target.data_target import (
11+
DataTarget,
12+
)
13+
from cosmosdb_emulator.time_tracker_cli.enums.entites import (
14+
TimeTrackerEntities,
15+
)
16+
from cosmosdb_emulator.time_tracker_cli.utils.activity import get_activity_json
17+
from cosmosdb_emulator.time_tracker_cli.utils.customer import get_customer_json
18+
from cosmosdb_emulator.time_tracker_cli.utils.project import get_project_json
19+
from cosmosdb_emulator.time_tracker_cli.utils.project_type import (
20+
get_project_type_json,
21+
)
22+
from cosmosdb_emulator.time_tracker_cli.utils.time_entry import get_entry_json
23+
24+
from time_tracker_api.customers.customers_model import (
25+
container_definition as customer_definition,
26+
)
27+
from time_tracker_api.project_types.project_types_model import (
28+
container_definition as project_type_definition,
29+
)
30+
from time_tracker_api.projects.projects_model import (
31+
container_definition as project_definition,
32+
)
33+
from time_tracker_api.activities.activities_model import (
34+
container_definition as activity_definition,
35+
)
36+
from time_tracker_api.time_entries.time_entries_model import (
37+
container_definition as time_entry_definition,
38+
)
39+
40+
DATABASE_ACCOUNT_URI = os.environ.get('DATABASE_ACCOUNT_URI')
41+
DATABASE_MASTER_KEY = os.environ.get('DATABASE_MASTER_KEY')
42+
DATABASE_NAME = os.environ.get('DATABASE_NAME')
43+
44+
45+
class CosmosDataTarget(DataTarget):
46+
def __init__(self):
47+
self.cosmos_client = CosmosClient(
48+
DATABASE_ACCOUNT_URI, DATABASE_MASTER_KEY
49+
)
50+
self.database = self.cosmos_client.create_database_if_not_exists(
51+
DATABASE_NAME
52+
)
53+
54+
@staticmethod
55+
def get_container_definition_by_entity_name(container_name: str) -> dict:
56+
containers_definition = {
57+
TimeTrackerEntities.CUSTOMER.value: customer_definition,
58+
TimeTrackerEntities.PROJECT_TYPE.value: project_type_definition,
59+
TimeTrackerEntities.PROJECT.value: project_definition,
60+
TimeTrackerEntities.ACTIVITY.value: activity_definition,
61+
TimeTrackerEntities.TIME_ENTRY.value: time_entry_definition,
62+
}
63+
64+
return containers_definition.get(container_name)
65+
66+
@staticmethod
67+
def get_json_method_entity_name(entity_name):
68+
available_json = {
69+
TimeTrackerEntities.CUSTOMER.value: get_customer_json,
70+
TimeTrackerEntities.PROJECT_TYPE.value: get_project_type_json,
71+
TimeTrackerEntities.PROJECT.value: get_project_json,
72+
TimeTrackerEntities.ACTIVITY.value: get_activity_json,
73+
TimeTrackerEntities.TIME_ENTRY.value: get_entry_json,
74+
}
75+
76+
return available_json.get(entity_name)
77+
78+
def delete(self, entities: dict):
79+
for entity in entities:
80+
entity_container_definition = (
81+
CosmosDataTarget.get_container_definition_by_entity_name(
82+
entity
83+
)
84+
)
85+
entity_container_id = entity_container_definition.get('id')
86+
try:
87+
self.database.delete_container(entity_container_id)
88+
self.database.create_container_if_not_exists(
89+
**entity_container_definition
90+
)
91+
except CosmosResourceNotFoundError:
92+
pass
93+
94+
def save(self, entities: dict):
95+
for entity in entities:
96+
entity_container_definition = (
97+
CosmosDataTarget.get_container_definition_by_entity_name(
98+
entity
99+
)
100+
)
101+
entities_list = entities.get(entity)
102+
entity_container = self.database.create_container_if_not_exists(
103+
**entity_container_definition
104+
)
105+
106+
for element in entities_list:
107+
get_json_entity = CosmosDataTarget.get_json_method_entity_name(
108+
entity
109+
)
110+
json_entity = get_json_entity(element)
111+
try:
112+
entity_container.create_item(body=json_entity)
113+
except CosmosResourceExistsError:
114+
print(
115+
f'The {entity} entity with the ID ({element.id}) already exists, so it has not been created.'
116+
)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
from abc import ABC, abstractmethod
2+
3+
4+
class DataTarget(ABC):
5+
@abstractmethod
6+
def save(self, entities: dict):
7+
pass
8+
9+
@abstractmethod
10+
def delete(self, entities: set):
11+
pass
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from enum import Enum
2+
3+
4+
class TimeTrackerEntities(Enum):
5+
def __str__(self):
6+
return str(self.value)
7+
8+
CUSTOMER = 'Customers'
9+
PROJECT = 'Projects'
10+
PROJECT_TYPE = 'Project-Types'
11+
ACTIVITY = 'Activities'
12+
TIME_ENTRY = 'Time-entries'
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
from typing import NamedTuple
2+
3+
from factory import Factory, Faker
4+
5+
from cosmosdb_emulator.time_tracker_cli.providers.common import CommonProvider
6+
from cosmosdb_emulator.time_tracker_cli.utils.common import (
7+
get_time_tracker_tenant_id,
8+
)
9+
10+
Faker.add_provider(CommonProvider)
11+
12+
13+
class Activity(NamedTuple):
14+
id: str
15+
name: str
16+
description: str
17+
status: str
18+
tenant_id: str
19+
20+
21+
class ActivityFactory(Factory):
22+
class Meta:
23+
model = Activity
24+
25+
id = Faker('uuid4')
26+
name = Faker('job')
27+
description = Faker('sentence', nb_words=6)
28+
status = Faker('status')
29+
tenant_id = get_time_tracker_tenant_id()

0 commit comments

Comments
 (0)