diff --git a/tests/test_resource_containers.py b/tests/test_resource_containers.py index 99718ff..3a04869 100644 --- a/tests/test_resource_containers.py +++ b/tests/test_resource_containers.py @@ -73,3 +73,62 @@ def test_resource_already_belongs_to_container(): with pytest.raises(ResourceHasContainerException): schema2.add(view) + + +def test_resource_container_init(): + # Explicitly set container chain - 1st degree links + db = res.Database(name="DB") + schema = res.Schema(name="SCH", database=db) + assert schema.container == db + assert str(schema.fqn) == "DB.SCH" + task = res.Task(name="TASK", schema=schema) + assert task.container == schema + assert task.container.container == db + assert str(task.fqn) == "DB.SCH.TASK" + + # Explicitly set container chain - 2nd degree link + db = res.Database(name="DB") + schema = res.Schema(name="SCH", database=db) + assert schema.container == db + assert str(schema.fqn) == "DB.SCH" + task = res.Task(name="TASK", database=db, schema=schema) + assert task.container == schema + assert task.container.container == db + assert str(task.fqn) == "DB.SCH.TASK" + + # Build container chain bottoms-up + db = res.Database(name="DB") + schema = res.Schema(name="SCH") + task = res.Task(name="TASK", database=db, schema=schema) + assert task.container == schema + assert task.container.container == db + assert str(task.fqn) == "DB.SCH.TASK" + + # Init with fully qualified name + task = res.Task(name="DB.SCH.TASK") + assert task.container.name == "SCH" + assert task.container.container.name == "DB" + assert str(task.fqn) == "DB.SCH.TASK" + + # Partially qualified name + task = res.Task(name="SCH.TASK") + assert task.container.name == "SCH" + assert str(task.fqn) == "SCH.TASK" + + # Mix string-specified and object-specified container + db = "DB" + schema = res.Schema(name="SCH", database=db) + assert schema.container.name == db + assert str(schema.fqn) == "DB.SCH" + task = res.Task(name="TASK", database=db, schema=schema) + assert task.container == schema + assert task.container.container.name == db + assert str(task.fqn) == "DB.SCH.TASK" + + +def test_prevent_container_chaining_if_already_set(): + db1 = res.Database(name="DB1") + db2 = res.Database(name="DB2") + schema = res.Schema(name="SCH", database=db1) + with pytest.raises(ResourceHasContainerException): + res.Task(name="TASK", database=db2, schema=schema) diff --git a/tests/test_resource_rendering.py b/tests/test_resource_rendering.py new file mode 100644 index 0000000..fdd366a --- /dev/null +++ b/tests/test_resource_rendering.py @@ -0,0 +1,29 @@ +import titan.resources as res + + +def test_resource_pointer_rendering(): + db = res.Database(name="DB") + schema = res.Schema(name="SCH") + network_rule = res.NetworkRule( + name="TITAN_TEST_NETWORK_RULE", + type="IPV4", + value_list=["85.83.225.229"], + mode="INGRESS", + database=db, + schema=schema, + ) + + network_policy = res.NetworkPolicy( + name="TITAN_TEST_NETWORK_POLICY", + allowed_network_rule_list=[network_rule], + blocked_network_rule_list=None, + allowed_ip_list=None, + blocked_ip_list=None, + database=db, + schema=schema, + ) + rendered = network_policy.create_sql() + assert ( + rendered + == "CREATE NETWORK POLICY TITAN_TEST_NETWORK_POLICY ALLOWED_NETWORK_RULE_LIST = (DB.SCH.TITAN_TEST_NETWORK_RULE)" + ) diff --git a/titan/resources/resource.py b/titan/resources/resource.py index dac41fa..da67a48 100644 --- a/titan/resources/resource.py +++ b/titan/resources/resource.py @@ -328,8 +328,8 @@ def _serialize(field, value): elif isinstance(value, Resource): if getattr(value, "serialize_inline", False): return value.to_dict() - if hasattr(value._data, "name"): - return getattr(value._data, "name") + elif isinstance(value, NamedResource): + return str(value.fqn) else: raise Exception(f"Cannot serialize {value}") elif isinstance(value, ParseableEnum): @@ -376,10 +376,10 @@ def requires(self, *resources: "Resource"): def _register_scope(self, database=None, schema=None): if isinstance(database, str): - database: ResourceContainer = ResourcePointer(name=database, resource_type=ResourceType.DATABASE) + database = ResourcePointer(name=database, resource_type=ResourceType.DATABASE) if isinstance(schema, str): - schema: ResourceContainer = ResourcePointer(name=schema, resource_type=ResourceType.SCHEMA) + schema = ResourcePointer(name=schema, resource_type=ResourceType.SCHEMA) if database is not None: database.add(schema) @@ -389,9 +389,15 @@ def _register_scope(self, database=None, schema=None): if database is not None: database.add(self) - if isinstance(self.scope, SchemaScope): + elif isinstance(self.scope, SchemaScope): if schema is not None: schema.add(self) + if database is not None: + if schema.container is None: + database.add(schema) + elif schema.container != database: + raise ResourceHasContainerException(f"Schema {schema} does not belong to database {database}") + elif database is not None: database.find(name="PUBLIC", resource_type=ResourceType.SCHEMA).add(self)