diff --git a/metrics_layer/core/model/field.py b/metrics_layer/core/model/field.py index a09fc900..b92c1de9 100644 --- a/metrics_layer/core/model/field.py +++ b/metrics_layer/core/model/field.py @@ -18,6 +18,9 @@ from .set import Set SQL_KEYWORDS = {"order", "group", "by", "as", "from", "select", "on", "with", "drop", "table"} +VALID_NUMERIC_GEOGRAPHIC_ROLES = ["latitude", "longitude"] +VALID_STRING_GEOGRAPHIC_ROLES = ["city", "county", "country", "post_code", "state"] +VALID_GEOGRAPHIC_ROLES = VALID_STRING_GEOGRAPHIC_ROLES + VALID_NUMERIC_GEOGRAPHIC_ROLES VALID_TIMEFRAMES = [ "raw", "time", @@ -207,6 +210,7 @@ def valid_properties(self): "link", "canon_date", "case", + "geographic_role", ] return shared_properties + dimension_only elif self.field_type == ZenlyticFieldType.dimension_group: @@ -1767,6 +1771,57 @@ def collect_errors(self): ), ) ) + if "geographic_role" in self._definition and not isinstance(self.geographic_role, str): + errors.append( + self._error( + self._definition.get("geographic_role"), + ( + f"Field {self.name} in view {self.view.name} has an invalid geographic_role" + f" {self.geographic_role}. geographic_role must be a string." + ), + ) + ) + elif "geographic_role" in self._definition: + if self.geographic_role not in VALID_GEOGRAPHIC_ROLES: + errors.append( + self._error( + self._definition.get("geographic_role"), + ( + f"Field {self.name} in view {self.view.name} has an invalid geographic_role" + f" {self.geographic_role}. Valid geographic_roles are:" + f" {VALID_GEOGRAPHIC_ROLES}" + ), + ) + ) + elif ( + str(self.type) == ZenlyticType.string + and self.geographic_role not in VALID_STRING_GEOGRAPHIC_ROLES + ): + errors.append( + self._error( + self._definition.get("geographic_role"), + ( + f"Field {self.name} in view {self.view.name} has an invalid geographic_role" + f" {self.geographic_role}. Valid geographic_roles for dimensions of type" + f" string are: {VALID_STRING_GEOGRAPHIC_ROLES}" + ), + ) + ) + elif ( + str(self.type) == ZenlyticType.number + and self.geographic_role not in VALID_NUMERIC_GEOGRAPHIC_ROLES + ): + errors.append( + self._error( + self._definition.get("geographic_role"), + ( + f"Field {self.name} in view {self.view.name} has an invalid geographic_role" + f" {self.geographic_role}. Valid geographic_roles for dimensions of type" + f" number are: {VALID_NUMERIC_GEOGRAPHIC_ROLES}" + ), + ) + ) + if ( str(self.field_type) == ZenlyticFieldType.dimension and str(self.type) == ZenlyticType.string diff --git a/tests/config/metrics_layer_config/views/test_customers.yml b/tests/config/metrics_layer_config/views/test_customers.yml index 50e11401..f041133c 100644 --- a/tests/config/metrics_layer_config/views/test_customers.yml +++ b/tests/config/metrics_layer_config/views/test_customers.yml @@ -45,6 +45,7 @@ fields: type: string sql: '${TABLE}.region' searchable: true + geographic_role: country - name: gender field_type: 'dimension' diff --git a/tests/test_project_validation.py b/tests/test_project_validation.py index 131fa102..4dd18081 100644 --- a/tests/test_project_validation.py +++ b/tests/test_project_validation.py @@ -2179,6 +2179,66 @@ def test_validation_with_replaced_view_properties(connection, name, value, error " 512 characters." ], ), + ( + "revenue_per_session", + "geographic_role", + "latitude", + [ + "Property geographic_role is present on Field revenue_per_session in view " + "order_lines, but it is not a valid property." + ], + ), + ( + "parent_channel", + "geographic_role", + -1, + [ + "Field parent_channel in view order_lines has an invalid geographic_role -1. " + "geographic_role must be a string." + ], + ), + ( + "parent_channel", + "geographic_role", + "latitude", + [ + "Field parent_channel in view order_lines has an invalid geographic_role " + "latitude. Valid geographic_roles for dimensions of type string are: ['city', " + "'county', 'country', 'post_code', 'state']" + ], + ), + ( + "inventory_qty", + "geographic_role", + "country", + [ + "Field inventory_qty in view order_lines has an invalid geographic_role " + "country. Valid geographic_roles for dimensions of type number are: " + "['latitude', 'longitude']" + ], + ), + ( + "parent_channel", + "geographic_role", + "zip_code", + [ + "Field parent_channel in view order_lines has an invalid geographic_role " + "zip_code. Valid geographic_roles are: ['city', 'county', 'country', " + "'post_code', 'state', 'latitude', 'longitude']" + ], + ), + ( + "inventory_qty", + "geographic_role", + "longitude", + [], + ), + ( + "parent_channel", + "geographic_role", + "post_code", + [], + ), ], ) def test_validation_with_replaced_field_properties(connection, field_name, property_name, value, errors):