Skip to content

Commit 5e4bf5a

Browse files
authored
Release/0.0.6 (#10)
* Refactor the Plan printing functionality (#8) * Internal improvements (#9) * Check all Group attributes are mapped in a Flow * Use the `.get` function in `to_core` functions * Rename "Mutation" to "Migration" * Remove assertions from the `plan` function * Up package version to 0.0.6
1 parent 8289bae commit 5e4bf5a

20 files changed

+656
-282
lines changed

.github/workflows/tests.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ jobs:
3737
cache: "poetry"
3838
- run: poetry install
3939

40-
- run: poetry run pytest --cov=hnhm --cov-report xml --cov-report term tests/
40+
- run: poetry run pytest -v --cov=hnhm --cov-report xml --cov-report term tests/
4141
env:
4242
PG_DB: ${{ env.PG_DB }}
4343
PG_USER: ${{ env.PG_USER }}

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ Plan:
8686
|attribute 'first_name'
8787
|attribute 'last_name'
8888

89-
Apply mutations? [y/N]: y
89+
Apply migrations? [y/N]: y
9090
Applied!
9191
```
9292

hnhm/cli.py

Lines changed: 3 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,9 @@
33

44
import click
55

6-
from .hnhm import Plan, PlanType
6+
from .core import HnhmError
7+
from .plan_printer import print_plan
78
from .hnhm_registry import HnhmRegistry
8-
from .core import (
9-
HnhmError,
10-
CreateLink,
11-
LayoutType,
12-
RemoveLink,
13-
CreateGroup,
14-
RemoveGroup,
15-
CreateEntity,
16-
RemoveEntity,
17-
CreateAttribute,
18-
RemoveAttribute,
19-
AddGroupAttribute,
20-
RemoveGroupAttribute,
21-
)
229

2310

2411
def import_registry(module: str) -> HnhmRegistry:
@@ -33,91 +20,6 @@ def import_registry(module: str) -> HnhmRegistry:
3320
return registry
3421

3522

36-
def print_plan(plan: Plan):
37-
if plan.is_empty():
38-
click.secho("Your DWH is up to date.", fg="green")
39-
return
40-
41-
entities_mutations = sorted(plan.entities_mutations.items(), key=lambda kv: kv[0])
42-
links_mutations = sorted(plan.links_mutations.items(), key=lambda kv: kv[0])
43-
44-
click.secho("Plan:")
45-
for entity_name, plan_collection in entities_mutations:
46-
if plan_collection.type == PlanType.CREATE:
47-
symbol, color = "+", "green"
48-
elif plan_collection.type == PlanType.REMOVE:
49-
symbol, color = "-", "red"
50-
elif plan_collection.type == PlanType.UPDATE:
51-
symbol, color = "[u]", "yellow"
52-
else:
53-
raise HnhmError()
54-
55-
click.secho(f"\n{symbol} entity '{entity_name}'", fg=color)
56-
for entity_mutation in plan_collection.mutations:
57-
match entity_mutation:
58-
case CreateEntity(entity=entity):
59-
if entity.layout.type == LayoutType.HNHM:
60-
click.secho(f" + hub '{entity.name}'", fg="green")
61-
else:
62-
click.secho(f" + stage '{entity.name}'", fg="green")
63-
for attribute in entity.attributes.values():
64-
click.secho(f" |attribute '{attribute.name}'", fg="green")
65-
66-
case CreateAttribute(entity=_, attribute=attribute):
67-
click.secho(f" + attribute '{attribute.name}'", fg="green")
68-
69-
case CreateGroup(entity=_, group=group):
70-
click.secho(f" + group '{group.name}'", fg="green")
71-
for attribute in group.attributes.values():
72-
click.secho(f" |attribute '{attribute.name}'", fg="green")
73-
74-
case AddGroupAttribute(entity=_, group=group, attribute=attribute):
75-
click.secho(f" [u] group '{group.name}'", fg="yellow")
76-
click.secho(f" +attribute '{attribute.name}'", fg="green")
77-
78-
case RemoveEntity(entity=entity):
79-
if entity.layout.type == LayoutType.HNHM:
80-
click.secho(f" - hub '{entity.name}'", fg="red")
81-
else:
82-
click.secho(f" - stage '{entity.name}'", fg="red")
83-
for attribute in entity.attributes.values():
84-
click.secho(f" |attribute '{attribute.name}'", fg="red")
85-
86-
case RemoveAttribute(entity=_, attribute=attribute):
87-
click.secho(f" - attribute '{attribute.name}'", fg="red")
88-
89-
case RemoveGroup(entity=_, group=group):
90-
click.secho(f" - group '{group.name}'", fg="red")
91-
for attribute in group.attributes.values():
92-
click.secho(f" | attribute '{attribute.name}'", fg="red")
93-
94-
case RemoveGroupAttribute(entity=_, group=group, attribute=attribute):
95-
click.secho(f" [u] group '{group.name}'", fg="yellow")
96-
click.secho(f" -attribute '{attribute.name}'", fg="red")
97-
98-
for link_name, plan_collection in links_mutations:
99-
if plan_collection.type == PlanType.CREATE:
100-
symbol, color = "+", "green"
101-
elif plan_collection.type == PlanType.REMOVE:
102-
symbol, color = "-", "red"
103-
elif plan_collection.type == PlanType.UPDATE:
104-
symbol, color = "[u]", "yellow"
105-
else:
106-
raise HnhmError()
107-
108-
click.secho(f"\n{symbol} link '{link_name}'", fg=color)
109-
for link_mutation in plan_collection.mutations:
110-
match link_mutation:
111-
case RemoveLink(link=link):
112-
for element in link.elements:
113-
click.secho(f" |element '{element.entity.name}'", fg=color)
114-
case CreateLink(link=link):
115-
for element in link.elements:
116-
click.secho(f" |element '{element.entity.name}'", fg=color)
117-
118-
click.secho()
119-
120-
12123
@click.group()
12224
def cli():
12325
pass
@@ -153,7 +55,7 @@ def apply(dwh_module: str, yes: bool):
15355
if plan.is_empty():
15456
return
15557

156-
if yes or click.confirm("Apply mutations?"):
58+
if yes or click.confirm("Apply migrations?"):
15759
with registry.hnhm as hnhm:
15860
hnhm.apply(plan)
15961

hnhm/core/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
from .attribute import Type, Attribute, ChangeType
88
from .storage import Storage, InMemStorage, HnhmStorageData
99
from .tasks import Task, LoadHub, LoadLink, LoadGroup, LoadAttribute
10-
from .mutations import (
11-
Mutation,
10+
from .migrations import (
11+
Migration,
1212
CreateLink,
1313
RemoveLink,
1414
CreateGroup,
Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,29 +7,27 @@
77
from .attribute import Attribute
88

99

10-
class Mutation(pydantic.BaseModel):
11-
"""Mutation represents DWH change."""
12-
10+
class Migration(pydantic.BaseModel):
1311
priority: Priority
1412

1513

16-
class CreateEntity(Mutation):
14+
class CreateEntity(Migration):
1715
priority = Priority.FIRST
1816
entity: Entity
1917

2018
def __str__(self):
2119
return f"<CreateEntity '{self.entity.name}'>"
2220

2321

24-
class RemoveEntity(Mutation):
22+
class RemoveEntity(Migration):
2523
priority = Priority.SECOND
2624
entity: Entity
2725

2826
def __str__(self):
2927
return f"<RemoveEntity '{self.entity.name}'>"
3028

3129

32-
class CreateAttribute(Mutation):
30+
class CreateAttribute(Migration):
3331
priority = Priority.SECOND
3432
entity: Entity
3533
attribute: Attribute
@@ -38,7 +36,7 @@ def __str__(self):
3836
return f"<CreateAttribute '{self.attribute.name}' entity='{self.entity.name}'>"
3937

4038

41-
class RemoveAttribute(Mutation):
39+
class RemoveAttribute(Migration):
4240
priority = Priority.FIRST
4341
entity: Entity
4442
attribute: Attribute
@@ -47,7 +45,7 @@ def __str__(self):
4745
return f"<RemoveAttribute '{self.attribute.name}' entity='{self.entity.name}'>"
4846

4947

50-
class CreateGroup(Mutation):
48+
class CreateGroup(Migration):
5149
priority = Priority.SECOND
5250
entity: Entity
5351
group: Group
@@ -56,7 +54,7 @@ def __str__(self):
5654
return f"<CreateGroup '{self.group.name}' entity='{self.entity.name}'>"
5755

5856

59-
class RemoveGroup(Mutation):
57+
class RemoveGroup(Migration):
6058
priority = Priority.FIRST
6159
entity: Entity
6260
group: Group
@@ -65,7 +63,7 @@ def __str__(self):
6563
return f"<RemoveGroup '{self.group.name}' entity='{self.entity.name}'>"
6664

6765

68-
class AddGroupAttribute(Mutation):
66+
class AddGroupAttribute(Migration):
6967
"""Add an Attribute to an existing Group."""
7068

7169
priority = Priority.SECOND
@@ -81,7 +79,7 @@ def __str__(self):
8179
)
8280

8381

84-
class RemoveGroupAttribute(Mutation):
82+
class RemoveGroupAttribute(Migration):
8583
"""Remove an Attribute from an existing Group."""
8684

8785
priority = Priority.SECOND
@@ -97,15 +95,15 @@ def __str__(self):
9795
)
9896

9997

100-
class CreateLink(Mutation):
98+
class CreateLink(Migration):
10199
priority = Priority.SECOND
102100
link: Link
103101

104102
def __str__(self):
105103
return f"<CreateLink '{self.link.name}'>"
106104

107105

108-
class RemoveLink(Mutation):
106+
class RemoveLink(Migration):
109107
priority = Priority.FIRST
110108
link: Link
111109

hnhm/core/sql.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import abc
22

33
from .tasks import Task
4-
from .mutations import Mutation
4+
from .migrations import Migration
55

66

77
class Sql(abc.ABC):
88
"""Generates and executes sql code."""
99

1010
@abc.abstractmethod
11-
def generate_sql(self, mutation_or_task: Mutation | Task) -> str:
11+
def generate_sql(self, migration_or_task: Migration | Task) -> str:
1212
raise NotImplementedError
1313

1414
@abc.abstractmethod
@@ -17,7 +17,7 @@ def execute(self, sql: str):
1717

1818

1919
class FakeSql(Sql):
20-
def generate_sql(self, mutation_or_task: Mutation | Task) -> str:
20+
def generate_sql(self, migration_or_task: Migration | Task) -> str:
2121
return ""
2222

2323
def execute(self, sql: str):

hnhm/core/storage.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
from .link import Link
66
from .entity import Entity
7+
from .error import HnhmError
78

89

910
class HnhmStorageData(pydantic.BaseModel):
@@ -12,6 +13,52 @@ class HnhmStorageData(pydantic.BaseModel):
1213
entities: dict[str, Entity]
1314
links: dict[str, Link]
1415

16+
def check_entity_exists(self, entity: str):
17+
if entity not in self.entities:
18+
raise HnhmError(f"Entity '{entity}' doesn't exist.")
19+
20+
def check_entity_not_exists(self, entity: str):
21+
if entity in self.entities:
22+
raise HnhmError(f"Entity '{entity}' already exists.")
23+
24+
def check_link_exists(self, link: str):
25+
if link not in self.links:
26+
raise HnhmError(f"Link '{link}' doesn't exist.")
27+
28+
def check_link_not_exists(self, link: str):
29+
if link in self.links:
30+
raise HnhmError(f"Link '{link}' already exists.")
31+
32+
def check_attribute_exists(self, entity: str, attribute: str):
33+
self.check_entity_exists(entity)
34+
if attribute not in self.entities[entity].attributes:
35+
raise HnhmError(f"Attribute '{attribute}' doesn't exist.")
36+
37+
def check_attribute_not_exists(self, entity: str, attribute: str):
38+
self.check_entity_exists(entity)
39+
if attribute in self.entities[entity].attributes:
40+
raise HnhmError(f"Attribute '{attribute}' already exists.")
41+
42+
def check_group_exists(self, entity: str, group: str):
43+
self.check_entity_exists(entity)
44+
if group not in self.entities[entity].groups:
45+
raise HnhmError(f"Group '{group}' doesn't exist.")
46+
47+
def check_group_not_exists(self, entity: str, group: str):
48+
self.check_entity_exists(entity)
49+
if group in self.entities[entity].groups:
50+
raise HnhmError(f"Group '{group}' already exists.")
51+
52+
def check_group_attribute_exists(self, entity: str, group: str, attribute: str):
53+
self.check_group_exists(entity, group)
54+
if attribute not in self.entities[entity].groups[group].attributes:
55+
raise HnhmError(f"Attribute '{attribute}' doesn't exist.")
56+
57+
def check_group_attribute_not_exists(self, entity: str, group: str, attribute: str):
58+
self.check_group_exists(entity, group)
59+
if attribute in self.entities[entity].groups[group].attributes:
60+
raise HnhmError(f"Attribute '{attribute}' already exists.")
61+
1562

1663
class Storage(abc.ABC):
1764
@abc.abstractmethod

hnhm/flow.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,10 +138,25 @@ def load(
138138
if group_name not in groups_mapping:
139139
groups_mapping[group_name] = {}
140140
groups_mapping[group_name][attribute_target] = attribute_source
141-
# todo: check all attributes for group were mapped
142141
else:
143142
attributes_mapping[attribute_target] = attribute_source
144143

144+
# Check all attributes for a Group were mapped
145+
for group_name, group in target_entity.groups.items():
146+
group_mapping = groups_mapping.get(group_name)
147+
if not group_mapping:
148+
continue
149+
150+
for attribute in group.attributes.values():
151+
if attribute not in group_mapping:
152+
attribute_full_name = (
153+
f"{target_entity.name}.{group_name}.{attribute.name}"
154+
)
155+
raise HnhmError(
156+
f"Mapping not found for the attribute '{attribute_full_name}'."
157+
f" Please, provide all mappings for the group: '{target_entity.name}.{group_name}'."
158+
)
159+
145160
missing_keys = set(target_entity.keys) - set(keys_mapping.keys())
146161
if missing_keys:
147162
missing_keys_names = [key.name for key in missing_keys]

0 commit comments

Comments
 (0)