diff --git a/metricflow-semantics/metricflow_semantics/test_helpers/snapshot_helpers.py b/metricflow-semantics/metricflow_semantics/test_helpers/snapshot_helpers.py index 296e600571..c2c68adc33 100644 --- a/metricflow-semantics/metricflow_semantics/test_helpers/snapshot_helpers.py +++ b/metricflow-semantics/metricflow_semantics/test_helpers/snapshot_helpers.py @@ -361,8 +361,8 @@ def assert_linkable_spec_set_snapshot_equal( # noqa: D103 def assert_object_snapshot_equal( # type: ignore[misc] request: FixtureRequest, mf_test_configuration: SnapshotConfiguration, - obj_id: str, obj: Any, + obj_id: str = "result", ) -> None: """For tests to compare large objects, this can be used to snapshot a text representation of the object.""" assert_snapshot_text_equal( @@ -375,7 +375,7 @@ def assert_object_snapshot_equal( # type: ignore[misc] ) -def assert_str_snapshot_equal( # type: ignore[misc] +def assert_str_snapshot_equal( # noqa: D103 request: FixtureRequest, mf_test_configuration: SnapshotConfiguration, snapshot_id: str, diff --git a/metricflow-semantics/tests_metricflow_semantics/query/test_query_parser.py b/metricflow-semantics/tests_metricflow_semantics/query/test_query_parser.py index 022a4490cd..b1059525d6 100644 --- a/metricflow-semantics/tests_metricflow_semantics/query/test_query_parser.py +++ b/metricflow-semantics/tests_metricflow_semantics/query/test_query_parser.py @@ -5,6 +5,7 @@ from typing import List import pytest +from _pytest.fixtures import FixtureRequest from dbt_semantic_interfaces.parsing.dir_to_model import parse_yaml_files_to_validation_ready_semantic_manifest from dbt_semantic_interfaces.parsing.objects import YamlConfigFile from dbt_semantic_interfaces.protocols import SemanticManifest @@ -13,7 +14,6 @@ from dbt_semantic_interfaces.type_enums.date_part import DatePart from dbt_semantic_interfaces.type_enums.time_granularity import TimeGranularity from dbt_semantic_interfaces.validations.semantic_manifest_validator import SemanticManifestValidator -from metricflow_semantics.filters.time_constraint import TimeRangeConstraint from metricflow_semantics.model.semantic_manifest_lookup import SemanticManifestLookup from metricflow_semantics.query.query_exceptions import InvalidQueryException from metricflow_semantics.query.query_parser import MetricFlowQueryParser @@ -25,12 +25,10 @@ ) from metricflow_semantics.specs.spec_classes import ( DimensionSpec, - EntitySpec, - MetricSpec, - OrderBySpec, - TimeDimensionSpec, ) +from metricflow_semantics.test_helpers.config_helpers import MetricFlowTestConfiguration from metricflow_semantics.test_helpers.metric_time_dimension import MTD +from metricflow_semantics.test_helpers.snapshot_helpers import assert_object_snapshot_equal from tests_metricflow_semantics.model.example_project_configuration import ( EXAMPLE_PROJECT_CONFIGURATION_YAML_CONFIG_FILE, @@ -195,61 +193,37 @@ def revenue_query_parser() -> MetricFlowQueryParser: # noqa return query_parser_from_yaml([EXAMPLE_PROJECT_CONFIGURATION_YAML_CONFIG_FILE, revenue_yaml_file]) -def test_query_parser(bookings_query_parser: MetricFlowQueryParser) -> None: # noqa: D103 - query_spec = bookings_query_parser.parse_and_validate_query( +def test_query_parser( # noqa: D103 + request: FixtureRequest, + mf_test_configuration: MetricFlowTestConfiguration, + bookings_query_parser: MetricFlowQueryParser, +) -> None: + result = bookings_query_parser.parse_and_validate_query( metric_names=["bookings"], group_by_names=["booking__is_instant", "listing", MTD], order_by_names=[MTD, "-bookings"], - ).query_spec - - assert query_spec.metric_specs == (MetricSpec(element_name="bookings"),) - assert query_spec.dimension_specs == ( - DimensionSpec(element_name="is_instant", entity_links=(EntityReference("booking"),)), - ) - assert query_spec.time_dimension_specs == ( - TimeDimensionSpec(element_name=MTD, entity_links=(), time_granularity=TimeGranularity.DAY), - ) - assert query_spec.entity_specs == (EntitySpec(element_name="listing", entity_links=()),) - assert query_spec.order_by_specs == ( - OrderBySpec( - instance_spec=TimeDimensionSpec(element_name=MTD, entity_links=(), time_granularity=TimeGranularity.DAY), - descending=False, - ), - OrderBySpec( - instance_spec=MetricSpec(element_name="bookings"), - descending=True, - ), ) + assert_object_snapshot_equal(request=request, mf_test_configuration=mf_test_configuration, obj=result) -def test_query_parser_case_insensitivity(bookings_query_parser: MetricFlowQueryParser) -> None: # noqa: D103 - # String params - query_spec = bookings_query_parser.parse_and_validate_query( +def test_query_parser_case_insensitivity_with_names( # noqa: D103 + request: FixtureRequest, + mf_test_configuration: MetricFlowTestConfiguration, + bookings_query_parser: MetricFlowQueryParser, +) -> None: + result = bookings_query_parser.parse_and_validate_query( metric_names=["BOOKINGS"], group_by_names=["BOOKING__IS_INSTANT", "LISTING", MTD.upper()], order_by_names=[MTD.upper(), "-BOOKINGS"], - ).query_spec - - assert query_spec.metric_specs == (MetricSpec(element_name="bookings"),) - assert query_spec.dimension_specs == ( - DimensionSpec(element_name="is_instant", entity_links=(EntityReference("booking"),)), - ) - assert query_spec.time_dimension_specs == ( - TimeDimensionSpec(element_name=MTD, entity_links=(), time_granularity=TimeGranularity.DAY), - ) - assert query_spec.entity_specs == (EntitySpec(element_name="listing", entity_links=()),) - assert query_spec.order_by_specs == ( - OrderBySpec( - instance_spec=TimeDimensionSpec(element_name=MTD, entity_links=(), time_granularity=TimeGranularity.DAY), - descending=False, - ), - OrderBySpec( - instance_spec=MetricSpec(element_name="bookings"), - descending=True, - ), ) + assert_object_snapshot_equal(request=request, mf_test_configuration=mf_test_configuration, obj=result) - # Object params + +def test_query_parser_case_insensitivity_with_parameter_objects( # noqa: D103 + request: FixtureRequest, + mf_test_configuration: MetricFlowTestConfiguration, + bookings_query_parser: MetricFlowQueryParser, +) -> None: metric = MetricParameter(name="BOOKINGS") group_by = ( DimensionOrEntityParameter("BOOKING__IS_INSTANT"), @@ -260,35 +234,20 @@ def test_query_parser_case_insensitivity(bookings_query_parser: MetricFlowQueryP OrderByParameter(order_by=TimeDimensionParameter(MTD.upper())), OrderByParameter(order_by=MetricParameter("BOOKINGS"), descending=True), ) - query_spec = bookings_query_parser.parse_and_validate_query( - metrics=[metric], group_by=group_by, order_by=order_by - ).query_spec - assert query_spec.metric_specs == (MetricSpec(element_name="bookings"),) - assert query_spec.dimension_specs == ( - DimensionSpec(element_name="is_instant", entity_links=(EntityReference("booking"),)), - ) - assert query_spec.time_dimension_specs == ( - TimeDimensionSpec(element_name=MTD, entity_links=(), time_granularity=TimeGranularity.DAY), - ) - assert query_spec.entity_specs == (EntitySpec(element_name="listing", entity_links=()),) - assert query_spec.order_by_specs == ( - OrderBySpec( - instance_spec=TimeDimensionSpec(element_name=MTD, entity_links=(), time_granularity=TimeGranularity.DAY), - descending=False, - ), - OrderBySpec( - instance_spec=MetricSpec(element_name="bookings"), - descending=True, - ), - ) + result = bookings_query_parser.parse_and_validate_query(metrics=[metric], group_by=group_by, order_by=order_by) + assert_object_snapshot_equal(request=request, mf_test_configuration=mf_test_configuration, obj=result) def test_query_parser_invalid_group_by(bookings_query_parser: MetricFlowQueryParser) -> None: # noqa: D103 with pytest.raises(InvalidQueryException): - bookings_query_parser.parse_and_validate_query(group_by_names=["random_stuff"]).query_spec + bookings_query_parser.parse_and_validate_query(group_by_names=["random_stuff"]) -def test_query_parser_with_object_params(bookings_query_parser: MetricFlowQueryParser) -> None: # noqa: D103 +def test_query_parser_with_object_params( # noqa: D103 + request: FixtureRequest, + mf_test_configuration: MetricFlowTestConfiguration, + bookings_query_parser: MetricFlowQueryParser, +) -> None: metric = MetricParameter(name="bookings") group_by = ( DimensionOrEntityParameter("booking__is_instant"), @@ -299,30 +258,14 @@ def test_query_parser_with_object_params(bookings_query_parser: MetricFlowQueryP OrderByParameter(order_by=TimeDimensionParameter(MTD)), OrderByParameter(order_by=MetricParameter("bookings"), descending=True), ) - query_spec = bookings_query_parser.parse_and_validate_query( - metrics=[metric], group_by=group_by, order_by=order_by - ).query_spec - assert query_spec.metric_specs == (MetricSpec(element_name="bookings"),) - assert query_spec.dimension_specs == ( - DimensionSpec(element_name="is_instant", entity_links=(EntityReference("booking"),)), - ) - assert query_spec.time_dimension_specs == ( - TimeDimensionSpec(element_name=MTD, entity_links=(), time_granularity=TimeGranularity.DAY), - ) - assert query_spec.entity_specs == (EntitySpec(element_name="listing", entity_links=()),) - assert query_spec.order_by_specs == ( - OrderBySpec( - instance_spec=TimeDimensionSpec(element_name=MTD, entity_links=(), time_granularity=TimeGranularity.DAY), - descending=False, - ), - OrderBySpec( - instance_spec=MetricSpec(element_name="bookings"), - descending=True, - ), - ) + result = bookings_query_parser.parse_and_validate_query(metrics=[metric], group_by=group_by, order_by=order_by) + assert_object_snapshot_equal(request=request, mf_test_configuration=mf_test_configuration, obj=result) -def test_order_by_granularity_conversion() -> None: +def test_order_by_granularity_conversion( + request: FixtureRequest, + mf_test_configuration: MetricFlowTestConfiguration, +) -> None: """Test that the granularity of the primary time dimension in the order by is returned appropriately. In the case where the primary time dimension is specified in the order by without a granularity suffix, the order @@ -335,34 +278,27 @@ def test_order_by_granularity_conversion() -> None: query_parser = query_parser_from_yaml( [EXAMPLE_PROJECT_CONFIGURATION_YAML_CONFIG_FILE, bookings_yaml_file, revenue_yaml_file] ) - query_spec = query_parser.parse_and_validate_query( + result = query_parser.parse_and_validate_query( metric_names=["bookings", "revenue"], group_by_names=[MTD], order_by_names=[f"-{MTD}"] - ).query_spec - - # The lowest common granularity is MONTH, so we expect the PTD in the order by to have that granularity. - assert ( - OrderBySpec( - instance_spec=TimeDimensionSpec(element_name=MTD, entity_links=(), time_granularity=TimeGranularity.MONTH), - descending=True, - ), - ) == query_spec.order_by_specs + ) + assert_object_snapshot_equal(request=request, mf_test_configuration=mf_test_configuration, obj=result) -def test_order_by_granularity_no_conversion(bookings_query_parser: MetricFlowQueryParser) -> None: # noqa: D103 - query_spec = bookings_query_parser.parse_and_validate_query( +def test_order_by_granularity_no_conversion( # noqa: D103 + request: FixtureRequest, + mf_test_configuration: MetricFlowTestConfiguration, + bookings_query_parser: MetricFlowQueryParser, +) -> None: + result = bookings_query_parser.parse_and_validate_query( metric_names=["bookings"], group_by_names=[MTD], order_by_names=[MTD] - ).query_spec - - # The only granularity is DAY, so we expect the PTD in the order by to have that granularity. - assert ( - OrderBySpec( - instance_spec=TimeDimensionSpec(element_name=MTD, entity_links=(), time_granularity=TimeGranularity.DAY), - descending=False, - ), - ) == query_spec.order_by_specs + ) + assert_object_snapshot_equal(request=request, mf_test_configuration=mf_test_configuration, obj=result) -def test_time_range_constraint_conversion() -> None: +def test_time_range_constraint_conversion( + request: FixtureRequest, + mf_test_configuration: MetricFlowTestConfiguration, +) -> None: """Test that the returned time constraint in the query spec is adjusted to match the granularity of the query.""" bookings_yaml_file = YamlConfigFile(filepath="inline_for_test_1", contents=BOOKINGS_YAML) revenue_yaml_file = YamlConfigFile(filepath="inline_for_test_2", contents=REVENUE_YAML) @@ -372,19 +308,20 @@ def test_time_range_constraint_conversion() -> None: ) # "bookings" has a granularity of DAY, "revenue" has a granularity of MONTH - query_spec = query_parser.parse_and_validate_query( + result = query_parser.parse_and_validate_query( metric_names=["bookings", "revenue"], group_by_names=[MTD], time_constraint_start=as_datetime("2020-01-15"), time_constraint_end=as_datetime("2020-02-15"), - ).query_spec - - assert ( - TimeRangeConstraint(start_time=as_datetime("2020-01-01"), end_time=as_datetime("2020-02-29")) - ) == query_spec.time_range_constraint + ) + assert_object_snapshot_equal(request=request, mf_test_configuration=mf_test_configuration, obj=result) -def test_parse_and_validate_where_constraint_dims(bookings_query_parser: MetricFlowQueryParser) -> None: +def test_parse_and_validate_where_constraint_dims( + request: FixtureRequest, + mf_test_configuration: MetricFlowTestConfiguration, + bookings_query_parser: MetricFlowQueryParser, +) -> None: """Test that the returned time constraint in the query spec is adjusted to match the granularity of the query.""" # check constraint on invalid_dim raises InvalidQueryException with pytest.raises(InvalidQueryException, match="does not match any of the available"): @@ -394,7 +331,7 @@ def test_parse_and_validate_where_constraint_dims(bookings_query_parser: MetricF time_constraint_start=as_datetime("2020-01-15"), time_constraint_end=as_datetime("2020-02-15"), where_constraint_str="{{ Dimension('booking__invalid_dim') }} = '1'", - ).query_spec + ) with pytest.raises(InvalidQueryException, match="Error parsing where filter"): bookings_query_parser.parse_and_validate_query( @@ -403,22 +340,26 @@ def test_parse_and_validate_where_constraint_dims(bookings_query_parser: MetricF time_constraint_start=as_datetime("2020-01-15"), time_constraint_end=as_datetime("2020-02-15"), where_constraint_str="{{ Dimension('invalid_format') }} = '1'", - ).query_spec + ) - query_spec = bookings_query_parser.parse_and_validate_query( + result = bookings_query_parser.parse_and_validate_query( metric_names=["bookings"], group_by_names=[MTD], time_constraint_start=as_datetime("2020-01-15"), time_constraint_end=as_datetime("2020-02-15"), where_constraint_str="{{ Dimension('booking__is_instant') }} = '1'", - ).query_spec + ) + assert_object_snapshot_equal(request=request, mf_test_configuration=mf_test_configuration, obj=result) assert ( DimensionSpec(element_name="is_instant", entity_links=(EntityReference("booking"),)) - not in query_spec.dimension_specs + not in result.query_spec.dimension_specs ) -def test_parse_and_validate_where_constraint_metric_time() -> None: +def test_parse_and_validate_where_constraint_metric_time( + request: FixtureRequest, + mf_test_configuration: MetricFlowTestConfiguration, +) -> None: """Test that granularity of metric_time reference in where constraint is at least that of the ds dimension.""" revenue_yaml_file = YamlConfigFile(filepath="inline_for_test_2", contents=REVENUE_YAML) @@ -428,10 +369,13 @@ def test_parse_and_validate_where_constraint_metric_time() -> None: metric_names=["revenue"], group_by_names=[MTD], where_constraint_str="{{ TimeDimension('metric_time', 'day') }} > '2020-01-15'", - ).query_spec + ) -def test_parse_and_validate_metric_constraint_dims() -> None: +def test_parse_and_validate_metric_constraint_dims( + request: FixtureRequest, + mf_test_configuration: MetricFlowTestConfiguration, +) -> None: """Test that the returned time constraint in the query spec is adjusted to match the granularity of the query. TODO: This test doesn't do what it says it does. @@ -446,10 +390,13 @@ def test_parse_and_validate_metric_constraint_dims() -> None: group_by_names=[MTD], time_constraint_start=as_datetime("2020-01-15"), time_constraint_end=as_datetime("2020-02-15"), - ).query_spec + ) -def test_cumulative_metric_no_time_dimension_validation() -> None: +def test_cumulative_metric_no_time_dimension_validation( + request: FixtureRequest, + mf_test_configuration: MetricFlowTestConfiguration, +) -> None: """Test that queries for cumulative metrics fail if no time dimensions are selected. This is a test of validation enforcement to ensure users don't get incorrect results due to current @@ -465,10 +412,13 @@ def test_cumulative_metric_no_time_dimension_validation() -> None: with pytest.raises(InvalidQueryException, match="do not include 'metric_time'"): query_parser.parse_and_validate_query( metric_names=["revenue_cumulative"], - ).query_spec + ) -def test_cumulative_metric_wrong_time_dimension_validation() -> None: +def test_cumulative_metric_wrong_time_dimension_validation( + request: FixtureRequest, + mf_test_configuration: MetricFlowTestConfiguration, +) -> None: """Test that queries for cumulative metrics fail if the agg_time_dimension is not selected. Our current behavior for cases where a different time dimension is selected by the agg_time_dimension is @@ -490,10 +440,13 @@ def test_cumulative_metric_wrong_time_dimension_validation() -> None: query_parser.parse_and_validate_query( metric_names=["revenue_cumulative"], group_by_names=["revenue_instance__loaded_at"], - ).query_spec + ) -def test_cumulative_metric_agg_time_dimension_name_validation() -> None: +def test_cumulative_metric_agg_time_dimension_name_validation( + request: FixtureRequest, + mf_test_configuration: MetricFlowTestConfiguration, +) -> None: """Test that queries for cumulative metrics succeed if the agg_time_dimension is selected by name.""" bookings_yaml_file = YamlConfigFile(filepath="inline_for_test_1", contents=BOOKINGS_YAML) metrics_yaml_file = YamlConfigFile(filepath="inline_for_test_1", contents=METRICS_YAML) @@ -502,12 +455,16 @@ def test_cumulative_metric_agg_time_dimension_name_validation() -> None: [EXAMPLE_PROJECT_CONFIGURATION_YAML_CONFIG_FILE, bookings_yaml_file, revenue_yaml_file, metrics_yaml_file] ) - query_parser.parse_and_validate_query( + result = query_parser.parse_and_validate_query( metric_names=["revenue_cumulative"], group_by_names=["revenue_instance__ds"] - ).query_spec + ) + assert_object_snapshot_equal(request=request, mf_test_configuration=mf_test_configuration, obj=result) -def test_derived_metric_query_parsing() -> None: +def test_derived_metric_query_parsing( + request: FixtureRequest, + mf_test_configuration: MetricFlowTestConfiguration, +) -> None: """Test derived metric inputs are properly validated.""" bookings_yaml_file = YamlConfigFile(filepath="inline_for_test_1", contents=BOOKINGS_YAML) metrics_yaml_file = YamlConfigFile(filepath="inline_for_test_1", contents=METRICS_YAML) @@ -520,23 +477,26 @@ def test_derived_metric_query_parsing() -> None: query_parser.parse_and_validate_query( metric_names=["revenue_sub_10"], group_by_names=[], - ).query_spec + ) # Attempt to query with non-time dimension with pytest.raises(InvalidQueryException, match="does not match any of the available"): query_parser.parse_and_validate_query( metric_names=["revenue_sub_10"], group_by_names=["country"], - ).query_spec + ) # Query with time dimension query_parser.parse_and_validate_query( metric_names=["revenue_sub_10"], group_by_names=[MTD], - ).query_spec + ) -def test_derived_metric_with_offset_parsing() -> None: +def test_derived_metric_with_offset_parsing( + request: FixtureRequest, + mf_test_configuration: MetricFlowTestConfiguration, +) -> None: """Test that querying derived metrics with a time offset requires a time dimension.""" revenue_yaml_file = YamlConfigFile(filepath="inline_for_test_1", contents=REVENUE_YAML) metrics_yaml_file = YamlConfigFile(filepath="inline_for_test_1", contents=METRICS_YAML) @@ -548,23 +508,27 @@ def test_derived_metric_with_offset_parsing() -> None: query_parser.parse_and_validate_query( metric_names=["revenue_growth_2_weeks"], group_by_names=[], - ).query_spec + ) # Attempt to query with non-time dimension with pytest.raises(InvalidQueryException, match="do not include 'metric_time'"): query_parser.parse_and_validate_query( metric_names=["revenue_growth_2_weeks"], group_by_names=["revenue_instance__country"], - ).query_spec + ) # Query with time dimension - query_parser.parse_and_validate_query( + result = query_parser.parse_and_validate_query( metric_names=["revenue_growth_2_weeks"], group_by_names=[MTD], - ).query_spec + ) + assert_object_snapshot_equal(request=request, mf_test_configuration=mf_test_configuration, obj=result) -def test_date_part_parsing() -> None: +def test_date_part_parsing( + request: FixtureRequest, + mf_test_configuration: MetricFlowTestConfiguration, +) -> None: """Test that querying with a date_part verifies compatibility with time_granularity.""" revenue_yaml_file = YamlConfigFile(filepath="inline_for_test_1", contents=REVENUE_YAML) metrics_yaml_file = YamlConfigFile(filepath="inline_for_test_1", contents=METRICS_YAML) @@ -577,21 +541,21 @@ def test_date_part_parsing() -> None: query_parser.parse_and_validate_query( metric_names=["revenue"], group_by=(TimeDimensionParameter(name="metric_time", date_part=DatePart.DOW),), - ).query_spec + ) # Can't query date part for cumulative metrics with pytest.raises(InvalidQueryException, match="does not match any of the available"): query_parser.parse_and_validate_query( metric_names=["revenue_cumulative"], group_by=(TimeDimensionParameter(name="metric_time", date_part=DatePart.YEAR),), - ).query_spec + ) # Can't query date part for metrics with offset to grain with pytest.raises(InvalidQueryException, match="does not allow group-by-items with a date part in the query"): query_parser.parse_and_validate_query( metric_names=["revenue_since_start_of_year"], group_by=(TimeDimensionParameter(name="metric_time", date_part=DatePart.MONTH),), - ).query_spec + ) # Requested granularity doesn't match resolved granularity with pytest.raises(InvalidQueryException, match="does not match any of the available"): @@ -600,26 +564,34 @@ def test_date_part_parsing() -> None: group_by=( TimeDimensionParameter(name="metric_time", grain=TimeGranularity.YEAR, date_part=DatePart.MONTH), ), - ).query_spec + ) # Date part is compatible query_parser.parse_and_validate_query( metric_names=["revenue"], group_by=(TimeDimensionParameter(name="metric_time", date_part=DatePart.MONTH),), - ).query_spec + ) -def test_duplicate_metric_query(bookings_query_parser: MetricFlowQueryParser) -> None: # noqa: D103 +def test_duplicate_metric_query( # noqa: D103 + request: FixtureRequest, + mf_test_configuration: MetricFlowTestConfiguration, + bookings_query_parser: MetricFlowQueryParser, +) -> None: with pytest.raises(InvalidQueryException, match="duplicate metrics"): bookings_query_parser.parse_and_validate_query( metric_names=["bookings", "bookings"], group_by_names=[MTD], - ).query_spec + ) -def test_no_metrics_or_group_by(bookings_query_parser: MetricFlowQueryParser) -> None: # noqa: D103 +def test_no_metrics_or_group_by( # noqa: D103 + request: FixtureRequest, + mf_test_configuration: MetricFlowTestConfiguration, + bookings_query_parser: MetricFlowQueryParser, +) -> None: with pytest.raises(InvalidQueryException, match="no metrics or group by items"): - bookings_query_parser.parse_and_validate_query().query_spec + bookings_query_parser.parse_and_validate_query() def test_offset_metric_with_diff_agg_time_dims_error() -> None: # noqa: D103 @@ -632,7 +604,7 @@ def test_offset_metric_with_diff_agg_time_dims_error() -> None: # noqa: D103 query_parser.parse_and_validate_query( metric_names=["monthly_revenue_last_7_days"], group_by_names=["revenue___ds"], - ).query_spec + ) def query_parser_from_yaml(yaml_contents: List[YamlConfigFile]) -> MetricFlowQueryParser: