Skip to content

Commit ef95e92

Browse files
Stop allowing hyphens in metric names and add name validation tests
1 parent 11ada88 commit ef95e92

File tree

3 files changed

+43
-2
lines changed

3 files changed

+43
-2
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
kind: Fixes
2+
body: For metric names, fix bug allowing hyphens (not allowed in metricflow already), make validation throw ValidationErrors (not ParsingErrors), and add tests.
3+
time: 2025-11-07T16:05:15.946331-08:00
4+
custom:
5+
Author: theyostalservice
6+
Issue: n/a

core/dbt/contracts/graph/unparsed.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -638,11 +638,11 @@ def validate(cls, data):
638638
errors.append("cannot contain more than 250 characters")
639639
if not (re.match(r"^[A-Za-z]", data["name"])):
640640
errors.append("must begin with a letter")
641-
if not (re.match(r"[\w-]+$", data["name"])):
641+
if not (re.match(r"[\w]+$", data["name"])):
642642
errors.append("must contain only letters, numbers and underscores")
643643

644644
if errors:
645-
raise ParsingError(
645+
raise ValidationError(
646646
f"The metric name '{data['name']}' is invalid. It {', '.join(e for e in errors)}"
647647
)
648648

tests/unit/contracts/graph/test_unparsed.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -933,6 +933,41 @@ def test_bad_tags(self):
933933
tst["tags"] = [123]
934934
self.assert_fails_validation(tst)
935935

936+
def test_bad_metric_name_with_spaces(self):
937+
tst = self.get_ok_dict()
938+
tst["name"] = "metric name with spaces"
939+
self.assert_fails_validation(tst)
940+
941+
def test_bad_metric_name_too_long(self):
942+
tst = self.get_ok_dict()
943+
tst["name"] = "a" * 251
944+
self.assert_fails_validation(tst)
945+
946+
def test_bad_metric_name_does_not_start_with_letter(self):
947+
tst = self.get_ok_dict()
948+
tst["name"] = "123metric"
949+
self.assert_fails_validation(tst)
950+
951+
tst["name"] = "_metric"
952+
self.assert_fails_validation(tst)
953+
954+
def test_bad_metric_name_contains_special_characters(self):
955+
tst = self.get_ok_dict()
956+
tst["name"] = "metric!name"
957+
self.assert_fails_validation(tst)
958+
959+
tst["name"] = "metric@name"
960+
self.assert_fails_validation(tst)
961+
962+
tst["name"] = "metric#name"
963+
self.assert_fails_validation(tst)
964+
965+
tst["name"] = "metric$name"
966+
self.assert_fails_validation(tst)
967+
968+
tst["name"] = "metric-name"
969+
self.assert_fails_validation(tst)
970+
936971

937972
class TestUnparsedVersion(ContractTestCase):
938973
ContractType = UnparsedVersion

0 commit comments

Comments
 (0)