Reland "[Multi Event Codes] Copy event_codes -> metric_dimensions[0]"

This is a reland of 0e5cd5b505d23bde4a356f4eb7ef46206ff2abbb.

Change-Id: Ic8e8ae9cba0ea5862a177c38f4fa02f3bd228155
diff --git a/algorithms/rappor/rappor_config_helper.cc b/algorithms/rappor/rappor_config_helper.cc
index 596d5ac..b4b8d9b 100644
--- a/algorithms/rappor/rappor_config_helper.cc
+++ b/algorithms/rappor/rappor_config_helper.cc
@@ -124,9 +124,27 @@
   }
 }
 
+// Calculates the number of categories based on the metric_definition.
+//
+// - If there are no metric_dimensions, returns max_event_code() + 1 (this
+//   assumes an old registry)
+//
+// - If there is exactly 1 metric_dimensions, return
+//   metric_dimensions[0].max_event_code() + 1 (this is the new registry).
+//
+// - Otherwise, return 0 and report an error (this is not a supported registry).
 size_t RapporConfigHelper::BasicRapporNumCategories(
     const MetricDefinition& metric_definition) {
-  return metric_definition.max_event_code() + 1;
+  if (metric_definition.metric_dimensions_size() == 0) {
+    return metric_definition.max_event_code() + 1;
+  } else if (metric_definition.metric_dimensions_size() == 1) {
+    return metric_definition.metric_dimensions(0).max_event_code() + 1;
+  } else {
+    LOG(ERROR) << "Invalid Cobalt registry: Metric "
+               << metric_definition.metric_name()
+               << " has too many metric_dimensions.";
+    return 0;
+  }
 }
 
 size_t RapporConfigHelper::StringRapporNumCohorts(
diff --git a/algorithms/rappor/rappor_config_helper_test.cc b/algorithms/rappor/rappor_config_helper_test.cc
index 3c9489c..34096e2 100644
--- a/algorithms/rappor/rappor_config_helper_test.cc
+++ b/algorithms/rappor/rappor_config_helper_test.cc
@@ -40,11 +40,11 @@
   EXPECT_EQ(1u,
             RapporConfigHelper::BasicRapporNumCategories(metric_definition));
 
-  metric_definition.set_max_event_code(0);
+  metric_definition.add_metric_dimensions()->set_max_event_code(0);
   EXPECT_EQ(1u,
             RapporConfigHelper::BasicRapporNumCategories(metric_definition));
 
-  metric_definition.set_max_event_code(10);
+  metric_definition.mutable_metric_dimensions(0)->set_max_event_code(10);
   EXPECT_EQ(11u,
             RapporConfigHelper::BasicRapporNumCategories(metric_definition));
 }
diff --git a/config/config_change_validator/src/config_change_validator_main.go b/config/config_change_validator/src/config_change_validator_main.go
index 08cc807..a956e7b 100644
--- a/config/config_change_validator/src/config_change_validator_main.go
+++ b/config/config_change_validator/src/config_change_validator_main.go
@@ -133,8 +133,33 @@
 		return nil
 	}
 
-	for code, oldName := range oldMetric.EventCodes {
-		_, ok := newMetric.EventCodes[code]
+	oldDimensions := map[int]*config.MetricDefinition_MetricDimension{}
+	newDimensions := map[int]*config.MetricDefinition_MetricDimension{}
+
+	for ix, dim := range oldMetric.MetricDimensions {
+		oldDimensions[ix] = dim
+	}
+
+	for ix, dim := range newMetric.MetricDimensions {
+		newDimensions[ix] = dim
+	}
+
+	for ix, oldDimension := range oldDimensions {
+		newDimension, ok := newDimensions[ix]
+		if ok {
+			err := CompareDimensions(oldDimension, newDimension)
+			if err != nil {
+				return fmt.Errorf("for dimension index '%d': %v", ix, err)
+			}
+		}
+	}
+
+	return nil
+}
+
+func CompareDimensions(oldDimension, newDimension *config.MetricDefinition_MetricDimension) error {
+	for code, oldName := range oldDimension.EventCodes {
+		_, ok := newDimension.EventCodes[code]
 		if !ok {
 			return fmt.Errorf("removing an event code is not allowed: %d: %s", code, oldName)
 		}
diff --git a/config/config_parser/src/config_parser/filter_test.go b/config/config_parser/src/config_parser/filter_test.go
index e5ca4fc..1865234 100644
--- a/config/config_parser/src/config_parser/filter_test.go
+++ b/config/config_parser/src/config_parser/filter_test.go
@@ -27,18 +27,6 @@
 	},
 
 	{
-		before: &config.MetricDefinition{
-			MetricName: "A Metric!",
-			EventCodes: map[uint32]string{
-				0: "An event code",
-			},
-		},
-		after: &config.MetricDefinition{
-			MetricName: "A Metric!",
-		},
-	},
-
-	{
 		before: &config.ReportDefinition{
 			ReportName:    "A Report!",
 			CandidateList: []string{"Candidate1", "Candidate2", "Candidate3"},
@@ -47,42 +35,6 @@
 			ReportName: "A Report!",
 		},
 	},
-
-	{
-		before: &config.CobaltRegistry{
-			Customers: []*config.CustomerConfig{
-				&config.CustomerConfig{
-					Projects: []*config.ProjectConfig{
-						&config.ProjectConfig{
-							Metrics: []*config.MetricDefinition{
-								&config.MetricDefinition{
-									MetricName: "A Metric!",
-									EventCodes: map[uint32]string{
-										0: "An event code",
-									},
-								},
-							},
-						},
-					},
-				},
-			},
-		},
-		after: &config.CobaltRegistry{
-			Customers: []*config.CustomerConfig{
-				&config.CustomerConfig{
-					Projects: []*config.ProjectConfig{
-						&config.ProjectConfig{
-							Metrics: []*config.MetricDefinition{
-								&config.MetricDefinition{
-									MetricName: "A Metric!",
-								},
-							},
-						},
-					},
-				},
-			},
-		},
-	},
 }
 
 func TestFilter(t *testing.T) {
diff --git a/config/config_parser/src/config_parser/project_config.go b/config/config_parser/src/config_parser/project_config.go
index 305a6d5..5489ce9 100644
--- a/config/config_parser/src/config_parser/project_config.go
+++ b/config/config_parser/src/config_parser/project_config.go
@@ -85,6 +85,21 @@
 		for _, r := range e.Reports {
 			r.Id = IdFromName(r.ReportName)
 		}
+
+		// TODO(zmbush): Remove once EventCodes are fully deprecated
+		if len(e.EventCodes) > 0 {
+			e.MetricDimensions = []*config.MetricDefinition_MetricDimension{
+				&config.MetricDefinition_MetricDimension{
+					EventCodes:   e.EventCodes,
+					MaxEventCode: e.MaxEventCode,
+				},
+			}
+
+			// TODO(zmbush): To test this change:
+			// e.EventCodes = nil
+			// e.MaxEventCode = 0
+			// e.MetricDimensions[0].AlsoTreatAsLegacy: true,
+		}
 	}
 
 	return nil
diff --git a/config/config_parser/src/config_validator/metric_definitions.go b/config/config_parser/src/config_validator/metric_definitions.go
index 64a76b0..393d2fe 100644
--- a/config/config_parser/src/config_validator/metric_definitions.go
+++ b/config/config_parser/src/config_validator/metric_definitions.go
@@ -66,10 +66,6 @@
 		return fmt.Errorf("Error in meta_data: %v", err)
 	}
 
-	if m.MaxEventCode != 0 && m.MetricType != config.MetricDefinition_EVENT_OCCURRED {
-		return fmt.Errorf("Metric %s has max_event_code set. max_event_code can only be set for metrics for metric type EVENT_OCCURRED.", m.MetricName)
-	}
-
 	if m.IntBuckets != nil && m.MetricType != config.MetricDefinition_INT_HISTOGRAM {
 		return fmt.Errorf("Metric %s has int_buckets set. int_buckets can only be set for metrics for metric type INT_HISTOGRAM.", m.MetricName)
 	}
@@ -122,22 +118,8 @@
 }
 
 // Validate the event_codes and max_event_code fields.
-func validateEventCodes(m config.MetricDefinition) error {
-	if len(m.EventCodes) > 0 {
-		if m.MaxEventCode >= 1024 {
-			return fmt.Errorf("max_event_code must be less than 1024. (Configured: %v)", m.MaxEventCode)
-		}
-
-		if m.MaxEventCode == 0 {
-			return nil
-		}
-
-		for i, _ := range m.EventCodes {
-			if i > m.MaxEventCode {
-				return fmt.Errorf("Event code %v is greater than max_event_code %v.", i, m.MaxEventCode)
-			}
-		}
-	} else if len(m.MetricDimensions) > 0 {
+func validateMetricDimensions(m config.MetricDefinition) error {
+	if len(m.MetricDimensions) > 0 {
 		if len(m.MetricDimensions) > 5 {
 			return fmt.Errorf("there can be at most 5 dimensions in metric_dimensions")
 		}
@@ -178,7 +160,7 @@
 			return fmt.Errorf("metric_dimensions have too many possible representations: %v. May be no more than 1024", num_options)
 		}
 	} else {
-		return fmt.Errorf("no event_codes or metric_dimensions listed for metric of type %s.", m.MetricType)
+		return fmt.Errorf("no metric_dimensions listed for metric of type %v+.", m)
 	}
 
 	return nil
@@ -193,13 +175,13 @@
 	case config.MetricDefinition_EVENT_OCCURRED:
 		return validateEventOccurred(m)
 	case config.MetricDefinition_EVENT_COUNT:
-		return validateEventCodes(m)
+		return validateMetricDimensions(m)
 	case config.MetricDefinition_ELAPSED_TIME:
-		return validateEventCodes(m)
+		return validateMetricDimensions(m)
 	case config.MetricDefinition_FRAME_RATE:
-		return validateEventCodes(m)
+		return validateMetricDimensions(m)
 	case config.MetricDefinition_MEMORY_USAGE:
-		return validateEventCodes(m)
+		return validateMetricDimensions(m)
 	case config.MetricDefinition_INT_HISTOGRAM:
 		return validateIntHistogram(m)
 	case config.MetricDefinition_STRING_USED:
@@ -212,17 +194,21 @@
 }
 
 func validateEventOccurred(m config.MetricDefinition) error {
-	if m.MaxEventCode == 0 && len(m.MetricDimensions) == 0 {
+	if len(m.MetricDimensions) == 0 || m.MetricDimensions[0].MaxEventCode == 0 {
 		return fmt.Errorf("No max_event_code specified for metric of type EVENT_OCCURRED.")
 	}
 
+	if len(m.MetricDimensions) > 1 {
+		return fmt.Errorf("Too many metric dimensions specified for metric of type EVENT_OCCURRED.")
+	}
+
 	for i, md := range m.MetricDimensions {
 		if md.MaxEventCode == 0 {
 			return fmt.Errorf("No max_event_code specified for metric_dimension %v.", i)
 		}
 	}
 
-	return validateEventCodes(m)
+	return validateMetricDimensions(m)
 }
 func validateIntHistogram(m config.MetricDefinition) error {
 	if m.IntBuckets == nil {
@@ -231,20 +217,20 @@
 
 	// TODO(azani): Validate bucket definition.
 
-	return validateEventCodes(m)
+	return validateMetricDimensions(m)
 }
 
 func validateStringUsed(m config.MetricDefinition) error {
-	if len(m.EventCodes) > 0 {
-		return fmt.Errorf("event_codes must not be set for metrics of type STRING_USED")
+	if len(m.MetricDimensions) > 0 {
+		return fmt.Errorf("metric_dimensions must not be set for metrics of type STRING_USED")
 	}
 	return nil
 }
 
 // TODO(ninai): remove this function when Cobalt 1.0 is done.
 func validateCustomOld(m config.MetricDefinition) error {
-	if len(m.EventCodes) > 0 {
-		return fmt.Errorf("event_codes must not be set for metrics of type CUSTOM")
+	if len(m.MetricDimensions) > 0 {
+		return fmt.Errorf("metric_dimensions must not be set for metrics of type CUSTOM")
 	}
 	if len(m.Parts) == 0 {
 		return fmt.Errorf("No parts specified for metric of type CUSTOM.")
@@ -263,8 +249,8 @@
 		return nil
 	}
 
-	if len(m.EventCodes) > 0 {
-		return fmt.Errorf("event_types must not be set for metrics of type CUSTOM")
+	if len(m.MetricDimensions) > 0 {
+		return fmt.Errorf("metric_dimensions must not be set for metrics of type CUSTOM")
 	}
 
 	if m.ProtoName == "" {
diff --git a/config/config_parser/src/config_validator/metric_definitions_test.go b/config/config_parser/src/config_validator/metric_definitions_test.go
index 8b51e12..6151969 100644
--- a/config/config_parser/src/config_validator/metric_definitions_test.go
+++ b/config/config_parser/src/config_validator/metric_definitions_test.go
@@ -47,7 +47,11 @@
 		MetricName: name,
 		MetricType: config.MetricDefinition_EVENT_COUNT,
 		MetaData:   &metadata,
-		EventCodes: map[uint32]string{1: "hello_world"},
+		MetricDimensions: []*config.MetricDefinition_MetricDimension{
+			&config.MetricDefinition_MetricDimension{
+				EventCodes: map[uint32]string{1: "hello_world"},
+			},
+		},
 	}
 }
 
@@ -108,24 +112,6 @@
 	}
 }
 
-// Test that max_event_code can only be set if the metric type is EVENT_OCCURRED.
-func TestValidateMaxEventCodeOnlySetIfEventOccurred(t *testing.T) {
-	m := makeValidMetric()
-	m.MaxEventCode = 10
-	m.MetricType = config.MetricDefinition_EVENT_OCCURRED
-
-	if err := validateMetricDefinition(m); err != nil {
-		t.Errorf("Rejected valid metric definition with max_event_code set: %v", err)
-	}
-
-	for _, mt := range metricTypesExcept(config.MetricDefinition_EVENT_OCCURRED) {
-		m.MetricType = mt
-		if err := validateMetricDefinition(m); err == nil {
-			t.Errorf("Accepted metric definition with type %s with max_event_code set.", mt)
-		}
-	}
-}
-
 // Test that int_buckets can only be set if the metric type is INT_HISTOGRAM.
 func TestValidateIntBucketsSetOnlyForIntHistogram(t *testing.T) {
 	m := makeValidMetric()
@@ -149,7 +135,7 @@
 	m := makeValidMetric()
 	m.Parts = map[string]*config.MetricPart{"hello": nil}
 	m.MetricType = config.MetricDefinition_CUSTOM
-	m.EventCodes = map[uint32]string{}
+	m.MetricDimensions = nil
 	if err := validateMetricDefinition(m); err != nil {
 		t.Errorf("Rejected valid CUSTOM metric definition: %v", err)
 	}
@@ -166,7 +152,7 @@
 	m := makeValidMetric()
 	m.ProtoName = "$team_name.test.ProtoName"
 	m.MetricType = config.MetricDefinition_CUSTOM
-	m.EventCodes = map[uint32]string{}
+	m.MetricDimensions = nil
 
 	if err := validateMetricDefinition(m); err != nil {
 		t.Errorf("Rejected valid CUSTOM metric definition: %v", err)
@@ -246,41 +232,41 @@
 
 func TestValidateEventCodesMaxEventCodeTooBig(t *testing.T) {
 	m := makeValidMetric()
-	m.MaxEventCode = 1024
-	m.EventCodes = map[uint32]string{
+	m.MetricDimensions[0].MaxEventCode = 1024
+	m.MetricDimensions[0].EventCodes = map[uint32]string{
 		1: "hello_world",
 	}
 
-	if err := validateEventCodes(m); err == nil {
+	if err := validateMetricDimensions(m); err == nil {
 		t.Error("Accepted max_event_code with value no less than 1024.")
 	}
 }
 
 func TestValidateEventCodesIndexLargerThanMax(t *testing.T) {
 	m := makeValidMetric()
-	m.MaxEventCode = 100
-	m.EventCodes = map[uint32]string{
+	m.MetricDimensions[0].MaxEventCode = 100
+	m.MetricDimensions[0].EventCodes = map[uint32]string{
 		1:   "hello_world",
 		101: "blah",
 	}
 
-	if err := validateEventCodes(m); err == nil {
+	if err := validateMetricDimensions(m); err == nil {
 		t.Error("Accepted event type with index larger than max_event_code.")
 	}
 }
 
 func TestValidateEventCodesNoEventCodes(t *testing.T) {
 	m := makeValidMetric()
-	m.EventCodes = map[uint32]string{}
+	m.MetricDimensions[0].EventCodes = map[uint32]string{}
 
-	if err := validateEventCodes(m); err == nil {
+	if err := validateMetricDimensions(m); err == nil {
 		t.Error("Accepted metric with no event types.")
 	}
 }
 
 func TestValidateEventOccurredNoMax(t *testing.T) {
 	m := makeValidMetric()
-	m.MaxEventCode = 0
+	m.MetricDimensions[0].MaxEventCode = 0
 
 	if err := validateEventOccurred(m); err == nil {
 		t.Error("Accepted EVENT_OCCURRED metric with no max_event_code.")
@@ -298,7 +284,7 @@
 
 func TestValidateStringUsedEventCodesSet(t *testing.T) {
 	m := makeValidMetric()
-	m.EventCodes = map[uint32]string{1: "hello"}
+	m.MetricDimensions[0].EventCodes = map[uint32]string{1: "hello"}
 
 	if err := validateStringUsed(m); err == nil {
 		t.Error("Accepted STRING_USED metric with event_codes set.")
@@ -308,7 +294,7 @@
 func TestValidateCustomEventCodesSetOld(t *testing.T) {
 	m := makeValidMetric()
 	m.Parts = map[string]*config.MetricPart{"hello": nil}
-	m.EventCodes = map[uint32]string{1: "hello"}
+	m.MetricDimensions[0].EventCodes = map[uint32]string{1: "hello"}
 	if err := validateCustom(m); err == nil {
 		t.Error("Accepted CUSTOM metric with event_codes set.")
 	}
@@ -317,7 +303,7 @@
 func TestValidateCustomEventCodesSet(t *testing.T) {
 	m := makeValidMetric()
 	m.ProtoName = "test.ProtoName"
-	m.EventCodes = map[uint32]string{1: "hello"}
+	m.MetricDimensions[0].EventCodes = map[uint32]string{1: "hello"}
 
 	if err := validateCustom(m); err == nil {
 		t.Error("Accepted CUSTOM metric with event_codes set.")
@@ -326,7 +312,7 @@
 
 func TestValidateCustomNoParts(t *testing.T) {
 	m := makeValidMetric()
-	m.EventCodes = map[uint32]string{}
+	m.MetricDimensions[0].EventCodes = map[uint32]string{}
 	m.Parts = map[string]*config.MetricPart{}
 	if err := validateCustom(m); err == nil {
 		t.Error("Accepted CUSTOM metric with no parts.")
@@ -335,7 +321,7 @@
 
 func TestValidateCustomInvalidPartName(t *testing.T) {
 	m := makeValidMetric()
-	m.EventCodes = map[uint32]string{}
+	m.MetricDimensions[0].EventCodes = map[uint32]string{}
 	m.Parts = map[string]*config.MetricPart{"_invalid_name": nil}
 	if err := validateCustom(m); err == nil {
 		t.Error("Accepted CUSTOM metric with invalid part name.")
@@ -353,7 +339,7 @@
 
 func TestValidateCustomInvalidProtoName(t *testing.T) {
 	m := makeValidMetric()
-	m.EventCodes = map[uint32]string{}
+	m.MetricDimensions[0].EventCodes = map[uint32]string{}
 	m.ProtoName = "_invalid.ProtoName"
 
 	if err := validateCustom(m); err == nil {
diff --git a/config/config_parser/src/source_generator/source_generator_test_files/golden_v1.cb.dart b/config/config_parser/src/source_generator/source_generator_test_files/golden_v1.cb.dart
index 7990231..d80b69e 100644
--- a/config/config_parser/src/source_generator/source_generator_test_files/golden_v1.cb.dart
+++ b/config/config_parser/src/source_generator/source_generator_test_files/golden_v1.cb.dart
@@ -21,6 +21,16 @@
 const int TheOtherMetricNameEventCode_AnotherEvent = TheOtherMetricNameEventCode::AnotherEvent;
 const int TheOtherMetricNameEventCode_AThirdEvent = TheOtherMetricNameEventCode::AThirdEvent;
 
+// Enum for the_other_metric_name (Metric Dimension 0)
+class TheOtherMetricNameMetricDimension0 {
+  static const int AnEvent = 0;
+  static const int AnotherEvent = 1;
+  static const int AThirdEvent = 2;
+}
+const int TheOtherMetricNameMetricDimension0_AnEvent = TheOtherMetricNameMetricDimension0::AnEvent;
+const int TheOtherMetricNameMetricDimension0_AnotherEvent = TheOtherMetricNameMetricDimension0::AnotherEvent;
+const int TheOtherMetricNameMetricDimension0_AThirdEvent = TheOtherMetricNameMetricDimension0::AThirdEvent;
+
 // Enum for event groups (Metric Dimension The First Group)
 class EventGroupsMetricDimensionTheFirstGroup {
   static const int AnEvent = 0;
@@ -54,4 +64,4 @@
 const int EventGroupsMetricDimension2_Name = EventGroupsMetricDimension2::Name;
 
 // The base64 encoding of the bytes of a serialized CobaltRegistry proto message.
-const String config = 'Ku0DCghjdXN0b21lchAKGt4DCgdwcm9qZWN0EAUaXQoPdGhlX21ldHJpY19uYW1lEAoYBSBkYhUKCnRoZV9yZXBvcnQQu6WL8QgYj05iGgoQdGhlX290aGVyX3JlcG9ydBDK3M3qARgGcghjdXN0b21lcnoHcHJvamVjdBqAAQoVdGhlX290aGVyX21ldHJpY19uYW1lEAoYBSDIASgBMgsIABIHQW5FdmVudDIQCAESDEFub3RoZXJFdmVudDIRCAISDUEgdGhpcmQgZXZlbnQ4yAFQAWIUCgp0aGVfcmVwb3J0ELuli/EIGAdyCGN1c3RvbWVyegdwcm9qZWN0Gu4BCgxldmVudCBncm91cHMQChgFIKwCKAFQAWIUCgp0aGVfcmVwb3J0ELuli/EIGAdyCGN1c3RvbWVyegdwcm9qZWN0ggFFCg9UaGUgRmlyc3QgR3JvdXASCwgAEgdBbkV2ZW50EhAIARIMQW5vdGhlckV2ZW50EhEIAhINQSB0aGlyZCBldmVudBgCggE5Cg5BIHNlY29uZCBncm91cBIICAESBFRoaXMSBggCEgJJcxILCAMSB2Fub3RoZXISCAgEEgRUZXN0ggElEg4IABIKVGhpc01ldHJpYxIJCAISBUhhc05vEggIBBIETmFtZQ==';
+const String config = 'KqUECghjdXN0b21lchAKGpYECgdwcm9qZWN0EAUaXQoPdGhlX21ldHJpY19uYW1lEAoYBSBkYhUKCnRoZV9yZXBvcnQQu6WL8QgYj05iGgoQdGhlX290aGVyX3JlcG9ydBDK3M3qARgGcghjdXN0b21lcnoHcHJvamVjdBq4AQoVdGhlX290aGVyX21ldHJpY19uYW1lEAoYBSDIASgBMgsIABIHQW5FdmVudDIQCAESDEFub3RoZXJFdmVudDIRCAISDUEgdGhpcmQgZXZlbnQ4yAFQAWIUCgp0aGVfcmVwb3J0ELuli/EIGAdyCGN1c3RvbWVyegdwcm9qZWN0ggE1EgsIABIHQW5FdmVudBIQCAESDEFub3RoZXJFdmVudBIRCAISDUEgdGhpcmQgZXZlbnQYyAEa7gEKDGV2ZW50IGdyb3VwcxAKGAUgrAIoAVABYhQKCnRoZV9yZXBvcnQQu6WL8QgYB3IIY3VzdG9tZXJ6B3Byb2plY3SCAUUKD1RoZSBGaXJzdCBHcm91cBILCAASB0FuRXZlbnQSEAgBEgxBbm90aGVyRXZlbnQSEQgCEg1BIHRoaXJkIGV2ZW50GAKCATkKDkEgc2Vjb25kIGdyb3VwEggIARIEVGhpcxIGCAISAklzEgsIAxIHYW5vdGhlchIICAQSBFRlc3SCASUSDggAEgpUaGlzTWV0cmljEgkIAhIFSGFzTm8SCAgEEgROYW1l';
diff --git a/config/config_parser/src/source_generator/source_generator_test_files/golden_v1.cb.h b/config/config_parser/src/source_generator/source_generator_test_files/golden_v1.cb.h
index 38b96a0..b556a31 100644
--- a/config/config_parser/src/source_generator/source_generator_test_files/golden_v1.cb.h
+++ b/config/config_parser/src/source_generator/source_generator_test_files/golden_v1.cb.h
@@ -23,6 +23,19 @@
 const TheOtherMetricNameEventCode TheOtherMetricNameEventCode_AnotherEvent = TheOtherMetricNameEventCode::AnotherEvent;
 const TheOtherMetricNameEventCode TheOtherMetricNameEventCode_AThirdEvent = TheOtherMetricNameEventCode::AThirdEvent;
 
+// Enum for the_other_metric_name (Metric Dimension 0)
+namespace the_other_metric_name_metric_dimension_0_scope {
+enum Enum {
+  AnEvent = 0,
+  AnotherEvent = 1,
+  AThirdEvent = 2,
+};
+}  // the_other_metric_name_metric_dimension_0_scope
+typedef the_other_metric_name_metric_dimension_0_scope::Enum TheOtherMetricNameMetricDimension0;
+const TheOtherMetricNameMetricDimension0 TheOtherMetricNameMetricDimension0_AnEvent = TheOtherMetricNameMetricDimension0::AnEvent;
+const TheOtherMetricNameMetricDimension0 TheOtherMetricNameMetricDimension0_AnotherEvent = TheOtherMetricNameMetricDimension0::AnotherEvent;
+const TheOtherMetricNameMetricDimension0 TheOtherMetricNameMetricDimension0_AThirdEvent = TheOtherMetricNameMetricDimension0::AThirdEvent;
+
 // Enum for event groups (Metric Dimension The First Group)
 namespace event_groups_metric_dimension_the_first_group_scope {
 enum Enum {
@@ -65,4 +78,4 @@
 const EventGroupsMetricDimension2 EventGroupsMetricDimension2_Name = EventGroupsMetricDimension2::Name;
 
 // The base64 encoding of the bytes of a serialized CobaltRegistry proto message.
-const char kConfig[] = "Ku0DCghjdXN0b21lchAKGt4DCgdwcm9qZWN0EAUaXQoPdGhlX21ldHJpY19uYW1lEAoYBSBkYhUKCnRoZV9yZXBvcnQQu6WL8QgYj05iGgoQdGhlX290aGVyX3JlcG9ydBDK3M3qARgGcghjdXN0b21lcnoHcHJvamVjdBqAAQoVdGhlX290aGVyX21ldHJpY19uYW1lEAoYBSDIASgBMgsIABIHQW5FdmVudDIQCAESDEFub3RoZXJFdmVudDIRCAISDUEgdGhpcmQgZXZlbnQ4yAFQAWIUCgp0aGVfcmVwb3J0ELuli/EIGAdyCGN1c3RvbWVyegdwcm9qZWN0Gu4BCgxldmVudCBncm91cHMQChgFIKwCKAFQAWIUCgp0aGVfcmVwb3J0ELuli/EIGAdyCGN1c3RvbWVyegdwcm9qZWN0ggFFCg9UaGUgRmlyc3QgR3JvdXASCwgAEgdBbkV2ZW50EhAIARIMQW5vdGhlckV2ZW50EhEIAhINQSB0aGlyZCBldmVudBgCggE5Cg5BIHNlY29uZCBncm91cBIICAESBFRoaXMSBggCEgJJcxILCAMSB2Fub3RoZXISCAgEEgRUZXN0ggElEg4IABIKVGhpc01ldHJpYxIJCAISBUhhc05vEggIBBIETmFtZQ==";
+const char kConfig[] = "KqUECghjdXN0b21lchAKGpYECgdwcm9qZWN0EAUaXQoPdGhlX21ldHJpY19uYW1lEAoYBSBkYhUKCnRoZV9yZXBvcnQQu6WL8QgYj05iGgoQdGhlX290aGVyX3JlcG9ydBDK3M3qARgGcghjdXN0b21lcnoHcHJvamVjdBq4AQoVdGhlX290aGVyX21ldHJpY19uYW1lEAoYBSDIASgBMgsIABIHQW5FdmVudDIQCAESDEFub3RoZXJFdmVudDIRCAISDUEgdGhpcmQgZXZlbnQ4yAFQAWIUCgp0aGVfcmVwb3J0ELuli/EIGAdyCGN1c3RvbWVyegdwcm9qZWN0ggE1EgsIABIHQW5FdmVudBIQCAESDEFub3RoZXJFdmVudBIRCAISDUEgdGhpcmQgZXZlbnQYyAEa7gEKDGV2ZW50IGdyb3VwcxAKGAUgrAIoAVABYhQKCnRoZV9yZXBvcnQQu6WL8QgYB3IIY3VzdG9tZXJ6B3Byb2plY3SCAUUKD1RoZSBGaXJzdCBHcm91cBILCAASB0FuRXZlbnQSEAgBEgxBbm90aGVyRXZlbnQSEQgCEg1BIHRoaXJkIGV2ZW50GAKCATkKDkEgc2Vjb25kIGdyb3VwEggIARIEVGhpcxIGCAISAklzEgsIAxIHYW5vdGhlchIICAQSBFRlc3SCASUSDggAEgpUaGlzTWV0cmljEgkIAhIFSGFzTm8SCAgEEgROYW1l";
diff --git a/config/config_parser/src/source_generator/source_generator_test_files/golden_v1.cb.rs b/config/config_parser/src/source_generator/source_generator_test_files/golden_v1.cb.rs
index 1ef2de9..75bb9f6 100644
--- a/config/config_parser/src/source_generator/source_generator_test_files/golden_v1.cb.rs
+++ b/config/config_parser/src/source_generator/source_generator_test_files/golden_v1.cb.rs
@@ -15,6 +15,13 @@
   AThirdEvent = 2,
 }
 
+// Enum for the_other_metric_name (Metric Dimension 0)
+pub enum TheOtherMetricNameMetricDimension0 {
+  AnEvent = 0,
+  AnotherEvent = 1,
+  AThirdEvent = 2,
+}
+
 // Enum for event groups (Metric Dimension The First Group)
 pub enum EventGroupsMetricDimensionTheFirstGroup {
   AnEvent = 0,
@@ -38,4 +45,4 @@
 }
 
 // The base64 encoding of the bytes of a serialized CobaltRegistry proto message.
-pub const CONFIG: &str = "Ku0DCghjdXN0b21lchAKGt4DCgdwcm9qZWN0EAUaXQoPdGhlX21ldHJpY19uYW1lEAoYBSBkYhUKCnRoZV9yZXBvcnQQu6WL8QgYj05iGgoQdGhlX290aGVyX3JlcG9ydBDK3M3qARgGcghjdXN0b21lcnoHcHJvamVjdBqAAQoVdGhlX290aGVyX21ldHJpY19uYW1lEAoYBSDIASgBMgsIABIHQW5FdmVudDIQCAESDEFub3RoZXJFdmVudDIRCAISDUEgdGhpcmQgZXZlbnQ4yAFQAWIUCgp0aGVfcmVwb3J0ELuli/EIGAdyCGN1c3RvbWVyegdwcm9qZWN0Gu4BCgxldmVudCBncm91cHMQChgFIKwCKAFQAWIUCgp0aGVfcmVwb3J0ELuli/EIGAdyCGN1c3RvbWVyegdwcm9qZWN0ggFFCg9UaGUgRmlyc3QgR3JvdXASCwgAEgdBbkV2ZW50EhAIARIMQW5vdGhlckV2ZW50EhEIAhINQSB0aGlyZCBldmVudBgCggE5Cg5BIHNlY29uZCBncm91cBIICAESBFRoaXMSBggCEgJJcxILCAMSB2Fub3RoZXISCAgEEgRUZXN0ggElEg4IABIKVGhpc01ldHJpYxIJCAISBUhhc05vEggIBBIETmFtZQ==";
+pub const CONFIG: &str = "KqUECghjdXN0b21lchAKGpYECgdwcm9qZWN0EAUaXQoPdGhlX21ldHJpY19uYW1lEAoYBSBkYhUKCnRoZV9yZXBvcnQQu6WL8QgYj05iGgoQdGhlX290aGVyX3JlcG9ydBDK3M3qARgGcghjdXN0b21lcnoHcHJvamVjdBq4AQoVdGhlX290aGVyX21ldHJpY19uYW1lEAoYBSDIASgBMgsIABIHQW5FdmVudDIQCAESDEFub3RoZXJFdmVudDIRCAISDUEgdGhpcmQgZXZlbnQ4yAFQAWIUCgp0aGVfcmVwb3J0ELuli/EIGAdyCGN1c3RvbWVyegdwcm9qZWN0ggE1EgsIABIHQW5FdmVudBIQCAESDEFub3RoZXJFdmVudBIRCAISDUEgdGhpcmQgZXZlbnQYyAEa7gEKDGV2ZW50IGdyb3VwcxAKGAUgrAIoAVABYhQKCnRoZV9yZXBvcnQQu6WL8QgYB3IIY3VzdG9tZXJ6B3Byb2plY3SCAUUKD1RoZSBGaXJzdCBHcm91cBILCAASB0FuRXZlbnQSEAgBEgxBbm90aGVyRXZlbnQSEQgCEg1BIHRoaXJkIGV2ZW50GAKCATkKDkEgc2Vjb25kIGdyb3VwEggIARIEVGhpcxIGCAISAklzEgsIAxIHYW5vdGhlchIICAQSBFRlc3SCASUSDggAEgpUaGlzTWV0cmljEgkIAhIFSGFzTm8SCAgEEgROYW1l";
diff --git a/config/config_parser/src/source_generator/source_generator_test_files/golden_v1_filtered.cb.dart b/config/config_parser/src/source_generator/source_generator_test_files/golden_v1_filtered.cb.dart
index 385c3d5..57a1e9e 100644
--- a/config/config_parser/src/source_generator/source_generator_test_files/golden_v1_filtered.cb.dart
+++ b/config/config_parser/src/source_generator/source_generator_test_files/golden_v1_filtered.cb.dart
@@ -21,6 +21,16 @@
 const int TheOtherMetricNameEventCode_AnotherEvent = TheOtherMetricNameEventCode::AnotherEvent;
 const int TheOtherMetricNameEventCode_AThirdEvent = TheOtherMetricNameEventCode::AThirdEvent;
 
+// Enum for the_other_metric_name (Metric Dimension 0)
+class TheOtherMetricNameMetricDimension0 {
+  static const int AnEvent = 0;
+  static const int AnotherEvent = 1;
+  static const int AThirdEvent = 2;
+}
+const int TheOtherMetricNameMetricDimension0_AnEvent = TheOtherMetricNameMetricDimension0::AnEvent;
+const int TheOtherMetricNameMetricDimension0_AnotherEvent = TheOtherMetricNameMetricDimension0::AnotherEvent;
+const int TheOtherMetricNameMetricDimension0_AThirdEvent = TheOtherMetricNameMetricDimension0::AThirdEvent;
+
 // Enum for event groups (Metric Dimension The First Group)
 class EventGroupsMetricDimensionTheFirstGroup {
   static const int AnEvent = 0;
@@ -54,4 +64,4 @@
 const int EventGroupsMetricDimension2_Name = EventGroupsMetricDimension2::Name;
 
 // The base64 encoding of the bytes of a serialized CobaltRegistry proto message.
-const String config = 'KoACCghjdXN0b21lchAKGvEBCgdwcm9qZWN0EAUaSgoPdGhlX21ldHJpY19uYW1lEAoYBSBkYhUKCnRoZV9yZXBvcnQQu6WL8QgYj05iGgoQdGhlX290aGVyX3JlcG9ydBDK3M3qARgGGjsKFXRoZV9vdGhlcl9tZXRyaWNfbmFtZRAKGAUgyAEoATjIAVABYhQKCnRoZV9yZXBvcnQQu6WL8QgYBxpbCgxldmVudCBncm91cHMQChgFIKwCKAFQAWIUCgp0aGVfcmVwb3J0ELuli/EIGAeCARMKD1RoZSBGaXJzdCBHcm91cBgCggEQCg5BIHNlY29uZCBncm91cIIBAA==';
+const String config = 'KrkDCghjdXN0b21lchAKGqoDCgdwcm9qZWN0EAUaSgoPdGhlX21ldHJpY19uYW1lEAoYBSBkYhUKCnRoZV9yZXBvcnQQu6WL8QgYj05iGgoQdGhlX290aGVyX3JlcG9ydBDK3M3qARgGGnMKFXRoZV9vdGhlcl9tZXRyaWNfbmFtZRAKGAUgyAEoATjIAVABYhQKCnRoZV9yZXBvcnQQu6WL8QgYB4IBNRILCAASB0FuRXZlbnQSEAgBEgxBbm90aGVyRXZlbnQSEQgCEg1BIHRoaXJkIGV2ZW50GMgBGtsBCgxldmVudCBncm91cHMQChgFIKwCKAFQAWIUCgp0aGVfcmVwb3J0ELuli/EIGAeCAUUKD1RoZSBGaXJzdCBHcm91cBILCAASB0FuRXZlbnQSEAgBEgxBbm90aGVyRXZlbnQSEQgCEg1BIHRoaXJkIGV2ZW50GAKCATkKDkEgc2Vjb25kIGdyb3VwEggIARIEVGhpcxIGCAISAklzEgsIAxIHYW5vdGhlchIICAQSBFRlc3SCASUSDggAEgpUaGlzTWV0cmljEgkIAhIFSGFzTm8SCAgEEgROYW1l';
diff --git a/config/config_parser/src/source_generator/source_generator_test_files/golden_v1_filtered.cb.h b/config/config_parser/src/source_generator/source_generator_test_files/golden_v1_filtered.cb.h
index fd1f0c2..e5e4b65 100644
--- a/config/config_parser/src/source_generator/source_generator_test_files/golden_v1_filtered.cb.h
+++ b/config/config_parser/src/source_generator/source_generator_test_files/golden_v1_filtered.cb.h
@@ -23,6 +23,19 @@
 const TheOtherMetricNameEventCode TheOtherMetricNameEventCode_AnotherEvent = TheOtherMetricNameEventCode::AnotherEvent;
 const TheOtherMetricNameEventCode TheOtherMetricNameEventCode_AThirdEvent = TheOtherMetricNameEventCode::AThirdEvent;
 
+// Enum for the_other_metric_name (Metric Dimension 0)
+namespace the_other_metric_name_metric_dimension_0_scope {
+enum Enum {
+  AnEvent = 0,
+  AnotherEvent = 1,
+  AThirdEvent = 2,
+};
+}  // the_other_metric_name_metric_dimension_0_scope
+typedef the_other_metric_name_metric_dimension_0_scope::Enum TheOtherMetricNameMetricDimension0;
+const TheOtherMetricNameMetricDimension0 TheOtherMetricNameMetricDimension0_AnEvent = TheOtherMetricNameMetricDimension0::AnEvent;
+const TheOtherMetricNameMetricDimension0 TheOtherMetricNameMetricDimension0_AnotherEvent = TheOtherMetricNameMetricDimension0::AnotherEvent;
+const TheOtherMetricNameMetricDimension0 TheOtherMetricNameMetricDimension0_AThirdEvent = TheOtherMetricNameMetricDimension0::AThirdEvent;
+
 // Enum for event groups (Metric Dimension The First Group)
 namespace event_groups_metric_dimension_the_first_group_scope {
 enum Enum {
@@ -65,4 +78,4 @@
 const EventGroupsMetricDimension2 EventGroupsMetricDimension2_Name = EventGroupsMetricDimension2::Name;
 
 // The base64 encoding of the bytes of a serialized CobaltRegistry proto message.
-const char kConfig[] = "KoACCghjdXN0b21lchAKGvEBCgdwcm9qZWN0EAUaSgoPdGhlX21ldHJpY19uYW1lEAoYBSBkYhUKCnRoZV9yZXBvcnQQu6WL8QgYj05iGgoQdGhlX290aGVyX3JlcG9ydBDK3M3qARgGGjsKFXRoZV9vdGhlcl9tZXRyaWNfbmFtZRAKGAUgyAEoATjIAVABYhQKCnRoZV9yZXBvcnQQu6WL8QgYBxpbCgxldmVudCBncm91cHMQChgFIKwCKAFQAWIUCgp0aGVfcmVwb3J0ELuli/EIGAeCARMKD1RoZSBGaXJzdCBHcm91cBgCggEQCg5BIHNlY29uZCBncm91cIIBAA==";
+const char kConfig[] = "KrkDCghjdXN0b21lchAKGqoDCgdwcm9qZWN0EAUaSgoPdGhlX21ldHJpY19uYW1lEAoYBSBkYhUKCnRoZV9yZXBvcnQQu6WL8QgYj05iGgoQdGhlX290aGVyX3JlcG9ydBDK3M3qARgGGnMKFXRoZV9vdGhlcl9tZXRyaWNfbmFtZRAKGAUgyAEoATjIAVABYhQKCnRoZV9yZXBvcnQQu6WL8QgYB4IBNRILCAASB0FuRXZlbnQSEAgBEgxBbm90aGVyRXZlbnQSEQgCEg1BIHRoaXJkIGV2ZW50GMgBGtsBCgxldmVudCBncm91cHMQChgFIKwCKAFQAWIUCgp0aGVfcmVwb3J0ELuli/EIGAeCAUUKD1RoZSBGaXJzdCBHcm91cBILCAASB0FuRXZlbnQSEAgBEgxBbm90aGVyRXZlbnQSEQgCEg1BIHRoaXJkIGV2ZW50GAKCATkKDkEgc2Vjb25kIGdyb3VwEggIARIEVGhpcxIGCAISAklzEgsIAxIHYW5vdGhlchIICAQSBFRlc3SCASUSDggAEgpUaGlzTWV0cmljEgkIAhIFSGFzTm8SCAgEEgROYW1l";
diff --git a/config/config_parser/src/source_generator/source_generator_test_files/golden_v1_filtered.cb.rs b/config/config_parser/src/source_generator/source_generator_test_files/golden_v1_filtered.cb.rs
index e99c96d..93de0d3 100644
--- a/config/config_parser/src/source_generator/source_generator_test_files/golden_v1_filtered.cb.rs
+++ b/config/config_parser/src/source_generator/source_generator_test_files/golden_v1_filtered.cb.rs
@@ -15,6 +15,13 @@
   AThirdEvent = 2,
 }
 
+// Enum for the_other_metric_name (Metric Dimension 0)
+pub enum TheOtherMetricNameMetricDimension0 {
+  AnEvent = 0,
+  AnotherEvent = 1,
+  AThirdEvent = 2,
+}
+
 // Enum for event groups (Metric Dimension The First Group)
 pub enum EventGroupsMetricDimensionTheFirstGroup {
   AnEvent = 0,
@@ -38,4 +45,4 @@
 }
 
 // The base64 encoding of the bytes of a serialized CobaltRegistry proto message.
-pub const CONFIG: &str = "KoACCghjdXN0b21lchAKGvEBCgdwcm9qZWN0EAUaSgoPdGhlX21ldHJpY19uYW1lEAoYBSBkYhUKCnRoZV9yZXBvcnQQu6WL8QgYj05iGgoQdGhlX290aGVyX3JlcG9ydBDK3M3qARgGGjsKFXRoZV9vdGhlcl9tZXRyaWNfbmFtZRAKGAUgyAEoATjIAVABYhQKCnRoZV9yZXBvcnQQu6WL8QgYBxpbCgxldmVudCBncm91cHMQChgFIKwCKAFQAWIUCgp0aGVfcmVwb3J0ELuli/EIGAeCARMKD1RoZSBGaXJzdCBHcm91cBgCggEQCg5BIHNlY29uZCBncm91cIIBAA==";
+pub const CONFIG: &str = "KrkDCghjdXN0b21lchAKGqoDCgdwcm9qZWN0EAUaSgoPdGhlX21ldHJpY19uYW1lEAoYBSBkYhUKCnRoZV9yZXBvcnQQu6WL8QgYj05iGgoQdGhlX290aGVyX3JlcG9ydBDK3M3qARgGGnMKFXRoZV9vdGhlcl9tZXRyaWNfbmFtZRAKGAUgyAEoATjIAVABYhQKCnRoZV9yZXBvcnQQu6WL8QgYB4IBNRILCAASB0FuRXZlbnQSEAgBEgxBbm90aGVyRXZlbnQSEQgCEg1BIHRoaXJkIGV2ZW50GMgBGtsBCgxldmVudCBncm91cHMQChgFIKwCKAFQAWIUCgp0aGVfcmVwb3J0ELuli/EIGAeCAUUKD1RoZSBGaXJzdCBHcm91cBILCAASB0FuRXZlbnQSEAgBEgxBbm90aGVyRXZlbnQSEQgCEg1BIHRoaXJkIGV2ZW50GAKCATkKDkEgc2Vjb25kIGdyb3VwEggIARIEVGhpcxIGCAISAklzEgsIAxIHYW5vdGhlchIICAQSBFRlc3SCASUSDggAEgpUaGlzTWV0cmljEgkIAhIFSGFzTm8SCAgEEgROYW1l";
diff --git a/config/config_parser/src/source_generator/source_outputter.go b/config/config_parser/src/source_generator/source_outputter.go
index a1b6898..09d3b03 100644
--- a/config/config_parser/src/source_generator/source_outputter.go
+++ b/config/config_parser/src/source_generator/source_outputter.go
@@ -178,6 +178,7 @@
 
 	for _, metric := range c.Customers[0].Projects[0].Metrics {
 		events := make(map[uint32]string)
+		// TODO(zmbush): Remove once EventCodes deprecation is complete.
 		for value, name := range metric.EventCodes {
 			events[value] = name
 		}
@@ -194,6 +195,9 @@
 					varname = "Metric Dimension " + md.Dimension
 				}
 				so.writeEnum(metric.MetricName, varname, events)
+				if md.AlsoTreatAsLegacy {
+					so.writeEnum(metric.MetricName, "EventCode", events)
+				}
 			}
 		}
 	}
diff --git a/config/metric_definition.proto b/config/metric_definition.proto
index c918934..5109c70 100644
--- a/config/metric_definition.proto
+++ b/config/metric_definition.proto
@@ -241,8 +241,7 @@
     // human-readable labels for the codes. It is OK to add new elements to this
     // map or to change the spelling of labels after data collection has
     // started. It is not OK to change the meaning of any of the codes.
-    map<uint32, string> event_codes = 2
-        [(cobalt_options).hide_on_client = true];
+    map<uint32, string> event_codes = 2;
 
     // max_event_code is the maximal value for any event in this dimension.
     // Subject to the following rules:
@@ -259,6 +258,13 @@
     //    above rules are not violated.
     // 5. For metrics of the type EVENT_OCCURRED, there may be only 1 dimension.
     uint32 max_event_code = 3;
+
+    // Enables soft transitions in the source_generator. Will generate enums for
+    // this dimension, as well as enums as if this was a legacy event_codes map.
+    // This is only a temporary field, and will only work properly if there is
+    // only one dimension with this field set.  TODO(zmbush): Remove once
+    // metric_dimension migration is complete.
+    bool also_treat_as_legacy = 4;
   }
 
   // A list of MetricDimensions.
diff --git a/logger/encoder_test.cc b/logger/encoder_test.cc
index e9ecb27..2346c1c 100644
--- a/logger/encoder_test.cc
+++ b/logger/encoder_test.cc
@@ -43,7 +43,9 @@
   customer_id: 1
   project_id: 1
   id: 1
-  max_event_code: 100
+  metric_dimensions: {
+    max_event_code: 100
+  }
   reports: {
     report_name: "ErrorCountsByType"
     id: 123
@@ -131,7 +133,9 @@
   customer_id: 1
   project_id: 1
   id: 9
-  max_event_code: 1
+  metric_dimensions: {
+    max_event_code: 1
+  }
   reports: {
     report_name: "DeviceBoots_UniqueDevices"
     id: 91
diff --git a/logger/event_aggregator_test.cc b/logger/event_aggregator_test.cc
index f05aa14..0156eca 100644
--- a/logger/event_aggregator_test.cc
+++ b/logger/event_aggregator_test.cc
@@ -150,7 +150,9 @@
   customer_id: 1
   project_id: 1
   id: 10
-  max_event_code: 1
+  metric_dimensions: {
+    max_event_code: 1
+  }
   reports: {
     report_name: "DeviceBoots_UniqueDevices"
     id: 101
@@ -166,7 +168,9 @@
   customer_id: 1
   project_id: 1
   id: 20
-  max_event_code: 4
+  metric_dimensions: {
+    max_event_code: 4
+  }
   reports: {
     report_name: "FeaturesActive_UniqueDevices"
     id: 201
@@ -183,7 +187,9 @@
   customer_id: 1
   project_id: 1
   id: 30
-  max_event_code: 2
+  metric_dimensions: {
+    max_event_code: 2
+  }
   reports: {
     report_name: "ErrorsOccurred_SimpleCount"
     id: 301
@@ -236,7 +242,9 @@
   customer_id: 1
   project_id: 1
   id: 10
-  max_event_code: 1
+  metric_dimensions: {
+    max_event_code: 1
+  }
   reports: {
     report_name: "DeviceBoots_UniqueDevices"
     id: 101
@@ -252,7 +260,9 @@
   customer_id: 1
   project_id: 1
   id: 20
-  max_event_code: 4
+  metric_dimensions: {
+    max_event_code: 4
+  }
   reports: {
     report_name: "FeaturesActive_UniqueDevices"
     id: 201
@@ -270,7 +280,9 @@
   customer_id: 1
   project_id: 1
   id: 40
-  max_event_code: 4
+  metric_dimensions: {
+    max_event_code: 4
+  }
   reports: {
     report_name: "EventsOccurred_SimpleCount"
     id: 401
@@ -376,7 +388,9 @@
   customer_id: 1
   project_id: 1
   id: 10
-  max_event_code: 2
+  metric_dimensions: {
+    max_event_code: 2
+  }
   time_zone_policy: LOCAL
   reports: {
     report_name: "DeviceBoots_UniqueDevices"
@@ -393,7 +407,9 @@
   customer_id: 1
   project_id: 1
   id: 20
-  max_event_code: 2
+  metric_dimensions: {
+    max_event_code: 2
+  }
   time_zone_policy: UTC
   reports: {
     report_name: "FeaturesActive_UniqueDevices"
diff --git a/logger/logger.cc b/logger/logger.cc
index 18293e8..b382103 100644
--- a/logger/logger.cc
+++ b/logger/logger.cc
@@ -606,13 +606,13 @@
 
 /////////////// OccurrenceEventLogger method implementations ///////////////////
 
-// TODO(zmbush): Validate dimensions (for all subclasses of EventLogger).
 Status OccurrenceEventLogger::ValidateEvent(const EventRecord& event_record) {
   CHECK(event_record.event->has_occurrence_event());
   const auto& occurrence_event = event_record.event->occurrence_event();
   if (occurrence_event.event_code() > event_record.metric->max_event_code()) {
     LOG(ERROR) << "The event_code " << occurrence_event.event_code()
-               << " exceeds " << event_record.metric->max_event_code()
+               << " exceeds "
+               << event_record.metric->metric_dimensions(0).max_event_code()
                << ", the max_event_code for Metric "
                << MetricDebugString(*event_record.metric) << " in project "
                << project_context()->DebugString() << ".";
diff --git a/tools/test_app2/test_app_test.cc b/tools/test_app2/test_app_test.cc
index 2fece9a..258c79b 100644
--- a/tools/test_app2/test_app_test.cc
+++ b/tools/test_app2/test_app_test.cc
@@ -40,7 +40,9 @@
   customer_id: 1
   project_id: 1
   id: 1
-  max_event_code: 100
+  metric_dimensions: {
+    max_event_code: 100
+  }
   reports: {
     report_name: "ErrorCountsByType"
     id: 123