// Calling this based on a grouping (in SasMetricRetriever) should guarantee that it will have metric names specified (cannot have empty group)
        internal override async Task<MetricListResponse> GetMetricsInternalAsync(MetricFilter filter, MetricLocation location, string invocationId)
        {
            if (filter == null)
            {
                throw new ArgumentNullException("filter");
            }

            if (location == null)
            {
                throw new ArgumentNullException("location");
            }

            // This is called based on the definitions no the dimension portion of the filter should never be null or empty
            if (filter.DimensionFilters == null || !filter.DimensionFilters.Any())
            {
                throw new ArgumentNullException("filter.DimensionFilters");
            }

            // Separate out capacity metrics and transaction metrics into two groups
            IEnumerable<string> capacityMetrics = filter.DimensionFilters.Select(df => df.Name).Where(StorageConstants.MetricNames.IsCapacityMetric);
            IEnumerable<string> transactionMetrics = filter.DimensionFilters.Select(df => df.Name).Where(n => !StorageConstants.MetricNames.IsCapacityMetric(n));

            List<Task<IEnumerable<Metric>>> queryTasks = new List<Task<IEnumerable<Metric>>>(); 

            // Add task to get capacity metrics (if any)
            if (capacityMetrics.Any())
            {
                MetricTableInfo capacityTableInfo = location.TableInfo.FirstOrDefault(ti => StorageConstants.IsCapacityMetricsTable(ti.TableName));
                if (capacityTableInfo == null)
                {
                    throw new InvalidOperationException("Definitions for capacity metrics must contain table info for capacity metrics table");
                }

                queryTasks.Add(GetCapacityMetricsAsync(filter, GetTableReference(location, capacityTableInfo), capacityMetrics, invocationId));
            }

            // Add tasks to get transaction metrics (if any)
            if (transactionMetrics.Any())
            {
                IEnumerable<MetricTableInfo> transactionTableInfos = location.TableInfo.Where(ti => !StorageConstants.IsCapacityMetricsTable(ti.TableName));
                if (!transactionTableInfos.Any())
                {
                    throw new InvalidOperationException("Definitions for transaction metrics must contain table info for transaction metrics table");
                }

                queryTasks.AddRange(transactionTableInfos
                    .Select(info => GetTransactionMetricsAsync(filter, GetTableReference(location, info), transactionMetrics, invocationId)));
            }

            // Collect results and wrap
            return new MetricListResponse()
            {
                RequestId = invocationId,
                StatusCode = HttpStatusCode.OK,
                MetricCollection = new MetricCollection()
                {
                    Value = (await CollectResultsAsync(queryTasks)).ToList()
                }
            };
        }
 internal override async Task<MetricListResponse> GetMetricsInternalAsync(MetricFilter filter, MetricLocation location, string invocationId)
 {
     return await ShoeboxClient.GetMetricsInternalAsync(filter, location, invocationId);
 }
 private static void AreEqual(MetricLocation exp, MetricLocation act)
 {
     if (exp != null)
     {
         Assert.Equal(exp.PartitionKey, act.PartitionKey);
         Assert.Equal(exp.TableEndpoint, act.TableEndpoint);
         AreEqual(exp.TableInfo, act.TableInfo);
     }
 }
 private static CloudTable GetTableReference(MetricLocation location, MetricTableInfo tableInfo)
 {
     return new CloudTableClient(new Uri(location.TableEndpoint), new StorageCredentials(tableInfo.SasToken)).GetTableReference(tableInfo.TableName);
 }