Skip to content

Commit dc61f7f

Browse files
authored
[BUGFIX] fix vars for role names (#177)
* fix vars for role names --------- Co-authored-by: TJ Murphy <1796+teej@users.noreply.github.com>
1 parent cf5a6c3 commit dc61f7f

File tree

6 files changed

+63
-21
lines changed

6 files changed

+63
-21
lines changed

tests/test_blueprint.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -638,6 +638,14 @@ def test_blueprint_vars_spec(session_ctx):
638638
blueprint.generate_manifest(session_ctx)
639639

640640

641+
def test_blueprint_vars_in_owner(session_ctx):
642+
blueprint = Blueprint(
643+
resources=[res.Schema(name="schema", owner="role_{{ var.role_name }}", database="STATIC_DATABASE")],
644+
vars={"role_name": "role123"},
645+
)
646+
assert blueprint.generate_manifest(session_ctx)
647+
648+
641649
def test_blueprint_allowlist(session_ctx, remote_state):
642650
blueprint = Blueprint(
643651
resources=[res.Role(name="role1")],

tests/test_vars.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,14 @@ def test_blueprint_vars_comparison_with_system_names():
99

1010
schema = res.Schema(name=var.schema_name)
1111
assert isinstance(schema.name, VarString)
12+
13+
14+
def test_vars_in_owner():
15+
schema = res.Schema(name="schema", owner="role_{{ var.role_name }}")
16+
assert isinstance(schema._data.owner, VarString)
17+
18+
19+
def test_vars_database_role():
20+
role = res.DatabaseRole(name="role_{{ var.role_name }}", database="db_{{ var.db_name }}")
21+
assert isinstance(role._data.name, VarString)
22+
assert isinstance(role._data.database, VarString)

titan/blueprint.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -675,6 +675,12 @@ def _resolve_vars(self):
675675
for resource in self._staged:
676676
resource._resolve_vars(self._config.vars)
677677

678+
def _resolve_role_refs(self):
679+
for resource in _walk(self._root):
680+
if isinstance(resource, ResourcePointer):
681+
continue
682+
resource._resolve_role_refs()
683+
678684
def _build_resource_graph(self, session_ctx: SessionContext) -> None:
679685
"""
680686
Convert the staged resources into a directed graph of resources
@@ -921,6 +927,7 @@ def _finalize(self, session_ctx: SessionContext) -> None:
921927
self._finalized = True
922928
self._resolve_vars()
923929
self._build_resource_graph(session_ctx)
930+
self._resolve_role_refs()
924931
self._create_tag_references()
925932
self._create_ownership_refs(session_ctx)
926933
self._create_grandparent_refs()

titan/resources/grant.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,6 @@ def __init__(
185185
owner=owner,
186186
)
187187

188-
self.requires(self._data.to)
189188
granted_on = None
190189
if on_type:
191190
granted_on = ResourcePointer(name=on, resource_type=on_type)
@@ -336,7 +335,7 @@ class FutureGrant(Resource):
336335
def __init__(
337336
self,
338337
priv: str,
339-
to: Role,
338+
to: Union[Role, DatabaseRole],
340339
grant_option: bool = False,
341340
**kwargs,
342341
):
@@ -391,7 +390,6 @@ def __init__(
391390
to=to,
392391
grant_option=grant_option,
393392
)
394-
self.requires(self._data.to)
395393
if granted_in_ref:
396394
self.requires(granted_in_ref)
397395

@@ -592,7 +590,6 @@ def __init__(
592590
to=to,
593591
grant_option=grant_option,
594592
)
595-
self.requires(self._data.to)
596593

597594
@classmethod
598595
def from_sql(cls, sql):

titan/resources/resource.py

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,12 @@ def _coerce_resource_field(field_value, field_type):
8989
return {k: _coerce_resource_field(v, field_type=dict_types[1]) for k, v in field_value.items()}
9090

9191
elif field_type is RoleRef:
92-
return convert_role_ref(field_value)
92+
if isinstance(field_value, str) and string_contains_var(field_value):
93+
return VarString(field_value)
94+
elif isinstance(field_value, (Resource, VarString, str)):
95+
return convert_role_ref(field_value)
96+
else:
97+
raise TypeError
9398

9499
# Check for field_value's type in a Union
95100
elif get_origin(field_type) == Union:
@@ -139,14 +144,7 @@ def _coerce_resource_field(field_value, field_type):
139144
elif field_type is ResourceTags:
140145
return ResourceTags(field_value)
141146
elif field_type is str:
142-
if isinstance(field_value, str) and string_contains_var(field_value):
143-
return VarString(field_value)
144-
elif isinstance(field_value, VarString):
145-
return field_value
146-
elif not isinstance(field_value, str):
147-
raise TypeError
148-
else:
149-
return field_value
147+
return convert_to_varstring(field_value)
150148
elif field_type is float:
151149
if isinstance(field_value, float):
152150
return field_value
@@ -477,6 +475,15 @@ def _render_vars(field_value):
477475
if isinstance(self, NamedResource) and isinstance(self._name, VarString):
478476
self._name = ResourceName(self._name.to_string(vars))
479477

478+
def _resolve_role_refs(self):
479+
for f in fields(self._data):
480+
if f.type == RoleRef:
481+
field_value = getattr(self._data, f.name)
482+
new_value = convert_role_ref(field_value)
483+
setattr(self._data, f.name, new_value)
484+
if new_value.name != "":
485+
self.requires(new_value)
486+
480487
def to_pointer(self):
481488
return ResourcePointer(
482489
name=str(self.fqn),
@@ -639,7 +646,7 @@ def container(self):
639646
@property
640647
def database(self):
641648
if isinstance(self.scope, DatabaseScope):
642-
return self.container.name
649+
return self.container.name # type: ignore
643650
else:
644651
raise ValueError("ResourcePointer does not have a database")
645652

@@ -703,16 +710,15 @@ def convert_to_resource(
703710

704711
def convert_role_ref(role_ref: RoleRef) -> Resource:
705712
if role_ref.__class__.__name__ == "Role":
706-
return role_ref
713+
return role_ref # type: ignore
707714
elif role_ref.__class__.__name__ == "DatabaseRole":
708-
return role_ref
715+
return role_ref # type: ignore
709716
elif isinstance(role_ref, ResourcePointer) and role_ref.resource_type in (
710717
ResourceType.DATABASE_ROLE,
711718
ResourceType.ROLE,
712719
):
713720
return role_ref
714-
715-
elif isinstance(role_ref, str) or isinstance(role_ref, ResourceName):
721+
elif isinstance(role_ref, (str, ResourceName)):
716722
return ResourcePointer(name=role_ref, resource_type=infer_role_type_from_name(role_ref))
717723
else:
718724
raise TypeError
@@ -728,3 +734,14 @@ def infer_role_type_from_name(name: Union[str, ResourceName]) -> ResourceType:
728734
return ResourceType.DATABASE_ROLE
729735
else:
730736
return ResourceType.ROLE
737+
738+
739+
def convert_to_varstring(value: Union[str, ResourceName]) -> Union[VarString, str]:
740+
if isinstance(value, str) and string_contains_var(value):
741+
return VarString(value)
742+
elif isinstance(value, VarString):
743+
return value
744+
elif not isinstance(value, str):
745+
raise TypeError
746+
else:
747+
return value

titan/role_ref.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
from typing import Union, TYPE_CHECKING
1+
from typing import TYPE_CHECKING, Union
2+
3+
from .var import VarString
24

35
if TYPE_CHECKING:
4-
from titan.resources.role import Role, DatabaseRole
6+
from titan.resources.role import DatabaseRole, Role
57

6-
RoleRef = Union["Role", "DatabaseRole", str]
8+
RoleRef = Union["Role", "DatabaseRole", VarString, str]

0 commit comments

Comments
 (0)