private async Task PostAsync(IMonitoringAccount monitoringAccount, string url)
        {
            Validate(monitoringAccount);
            var settings = new JsonSerializerSettings {
                TypeNameHandling = TypeNameHandling.Auto
            };
            var serializedMonitoringAccount = JsonConvert.SerializeObject(
                monitoringAccount,
                Formatting.Indented,
                settings);

            try
            {
                await HttpClientHelper.GetResponse(
                    new Uri(url),
                    HttpMethod.Post,
                    this.httpClient,
                    monitoringAccount.Name,
                    this.operation,
                    serializedContent : serializedMonitoringAccount).ConfigureAwait(false);
            }
            catch (MetricsClientException mce)
            {
                ThrowSpecificExceptionIfPossible(mce, monitoringAccount.Name);
                throw;
            }
        }
        /// <inheritdoc />
        public async Task <IReadOnlyList <ConfigurationUpdateResultList> > SyncConfigurationAsync(
            IMonitoringAccount monitoringAccount,
            bool skipVersionCheck = false,
            bool validate         = true)
        {
            if (monitoringAccount == null)
            {
                throw new ArgumentNullException(nameof(monitoringAccount));
            }

            var namespaces =
                await this.metricReader.GetNamespacesAsync(monitoringAccount.Name).ConfigureAwait(false);

            List <ConfigurationUpdateResultList> results = new List <ConfigurationUpdateResultList>();

            foreach (var ns in namespaces)
            {
                var namespaceResults = await this.SyncConfigurationAsync(
                    monitoringAccount,
                    ns,
                    skipVersionCheck,
                    validate).ConfigureAwait(false);

                // For QOS namespace or other internal namespace, there is no configuration to
                // replicate and thus the namespace results is an empty list.
                if (namespaceResults.Count > 0)
                {
                    results.AddRange(namespaceResults);
                }
            }

            return(results);
        }
        /// <summary>
        /// Save the monitoring account configuration provided.
        /// </summary>
        /// <param name="monitoringAccount">The monitoring account configuration to save.</param>
        /// <param name="skipVersionCheck">Flag indicating whether or not the version flag should be honored.</param>
        /// <returns>A task the caller can wait on.</returns>
        public async Task SaveAsync(IMonitoringAccount monitoringAccount, bool skipVersionCheck = false)
        {
            var path =
                $"{this.monitoringAccountUrlPrefix}/{monitoringAccount.Name}/skipVersionCheck/{skipVersionCheck}";

            var uriBuilder = new UriBuilder(this.connectionInfo.GetEndpoint(monitoringAccount.Name))
            {
                Path = path
            };

            var url = uriBuilder.Uri.ToString();

            await this.PostAsync(monitoringAccount, url).ConfigureAwait(false);
        }
        /// <inheritdoc />
        public async Task <IReadOnlyList <ConfigurationUpdateResultList> > SyncConfigurationAsync(
            IMonitoringAccount monitoringAccount,
            string metricNamespace,
            bool skipVersionCheck = false,
            bool validate         = true)
        {
            if (monitoringAccount == null)
            {
                throw new ArgumentNullException(nameof(monitoringAccount));
            }

            if (string.IsNullOrEmpty(metricNamespace))
            {
                throw new ArgumentNullException(nameof(metricNamespace));
            }

            var metricNames = await this.metricReader.GetMetricNamesAsync(
                monitoringAccount.Name,
                metricNamespace).ConfigureAwait(false);

            var taskList = new List <Task <ConfigurationUpdateResultList> >(this.MaxParallelRunningTasks);

            List <ConfigurationUpdateResultList> results = new List <ConfigurationUpdateResultList>();

            foreach (var metricName in metricNames)
            {
                if (taskList.Count == this.MaxParallelRunningTasks)
                {
                    await this.WaitForSync(taskList, results).ConfigureAwait(false);

                    taskList.Clear();
                }
                else
                {
                    taskList.Add(this.SyncConfigurationAsync(monitoringAccount, metricNamespace, metricName, skipVersionCheck, validate));
                }
            }

            if (taskList.Count > 0)
            {
                await this.WaitForSync(taskList, results).ConfigureAwait(false);

                taskList.Clear();
            }

            return(results);
        }
        /// <summary>
        /// Validates that the monitoring account provided can be sent to the server to be saved.
        /// </summary>
        /// <param name="monitoringAccount">The monitoring account configuration being saved.</param>
        private static void Validate(IMonitoringAccount monitoringAccount)
        {
            if (monitoringAccount == null)
            {
                throw new ArgumentNullException(nameof(monitoringAccount));
            }

            if (string.IsNullOrWhiteSpace(monitoringAccount.Name))
            {
                throw new ArgumentException("Monitoring account name cannot be null or empty.");
            }

            if (monitoringAccount.Permissions == null || !monitoringAccount.Permissions.Any())
            {
                throw new ArgumentException("One or more permissions must be specified for this account.  These can include users, security groups, or certificates.");
            }
        }
        /// <inheritdoc />
        public async Task <IReadOnlyList <ConfigurationUpdateResult> > SyncMonitoringAccountConfigurationAsync(
            IMonitoringAccount monitoringAccount,
            bool skipVersionCheck = false)
        {
            if (monitoringAccount == null)
            {
                throw new ArgumentNullException(nameof(monitoringAccount));
            }

            var mirrorOperation = $"{this.monitoringAccountUrlPrefix}replicateConfigurationToMirrorAccounts";

            var path =
                $"{this.monitoringAccountUrlPrefix}{monitoringAccount.Name}/replicateConfigurationToMirrorAccounts/skipVersionCheck/{skipVersionCheck}";

            var uriBuilder = new UriBuilder(this.connectionInfo.GetGlobalEndpoint())
            {
                Path = path
            };

            try
            {
                var response = await HttpClientHelper.GetResponse(
                    uriBuilder.Uri,
                    HttpMethod.Post,
                    this.httpClient,
                    monitoringAccount.Name,
                    mirrorOperation).ConfigureAwait(false);

                return(JsonConvert.DeserializeObject <ConfigurationUpdateResult[]>(
                           response.Item1,
                           this.serializerSettings));
            }
            catch (MetricsClientException mce)
            {
                if (mce.ResponseStatusCode == HttpStatusCode.Unauthorized)
                {
                    var exMsg = $"Unable to sync configuration for monitoring account:{monitoringAccount.Name} as "
                                + $"user doesn't have permission to update configurations. Response:{mce.Message}";

                    throw new ConfigurationValidationException(exMsg, ValidationType.ServerSide, mce);
                }

                throw;
            }
        }
        /// <summary>
        /// Creates a monitoring account with provided configuration.
        /// </summary>
        /// <param name="monitoringAccount">The monitoring account configuration.</param>
        /// <param name="stampHostName">The stamp name such as prod3.metrics.nsatc.net as documented @ https://aka.ms/mdm-endpoints.</param>
        /// <returns>A task the caller can wait on.</returns>
        public async Task CreateAsync(IMonitoringAccount monitoringAccount, string stampHostName)
        {
            if (string.IsNullOrWhiteSpace(stampHostName))
            {
                throw new ArgumentException("value is null or empty", nameof(stampHostName));
            }

            if (this.connectionInfo.Endpoint != null)
            {
                throw new ArgumentException("'Endpoint' must not be specified in the constructor for ConnectionInfo to create monitoring accounts.");
            }

            // Check if the client has service admin permission in the global stamp; if not, try to create the account in the target stamp directly.
            var  globalStampEndpoint = ConnectionInfo.ResolveGlobalEnvironments()[(int)this.connectionInfo.MdmEnvironment];
            bool hasAccountCreationPermissionInGlobalStamp = await this.HasAccountCreationPermission(globalStampEndpoint).ConfigureAwait(false);

            var endpoint = hasAccountCreationPermissionInGlobalStamp ? globalStampEndpoint : $"https://{stampHostName}/";
            var url      = $"{endpoint}{this.monitoringAccountUrlPrefix}{monitoringAccount.Name}/stamp/{stampHostName}";

            await this.PostAsync(monitoringAccount, url).ConfigureAwait(false);
        }
        /// <inheritdoc />
        public async Task <ConfigurationUpdateResultList> SyncConfigurationAsync(
            IMonitoringAccount monitoringAccount,
            string metricNamespace,
            string metricName,
            bool skipVersionCheck = false,
            bool validate         = true)
        {
            if (monitoringAccount == null)
            {
                throw new ArgumentNullException(nameof(monitoringAccount));
            }

            if (metricNamespace == null)
            {
                throw new ArgumentNullException(nameof(metricNamespace));
            }

            if (metricName == null)
            {
                throw new ArgumentNullException(nameof(metricName));
            }

            var operation = $"{this.metricUrlPrefix}/replicateMonitorConfigurations";
            var path      =
                $"{operation}/monitoringAccount/{monitoringAccount.Name}/metricNamespace/{metricNamespace}/metricName/{metricName}/skipVersionCheck/{skipVersionCheck}/operation/Replace";

            var uriBuilder = new UriBuilder(this.connectionInfo.GetGlobalEndpoint())
            {
                Path  = path,
                Query = $"validate={validate}"
            };

            var result = new ConfigurationUpdateResultList
            {
                MonitoringAccount = monitoringAccount.Name,
                MetricNamespace   = metricNamespace,
                MetricName        = metricName
            };

            try
            {
                if (monitoringAccount.MirrorMonitoringAccountList == null || !monitoringAccount.MirrorMonitoringAccountList.Any())
                {
                    throw new Exception("MirrorAccountsList can't be null or empty while replicating monitors.");
                }

                var serializedTargetAccounts = JsonConvert.SerializeObject(monitoringAccount.MirrorMonitoringAccountList.ToList(), Formatting.Indented, this.serializerSettings);
                var response = await HttpClientHelper.GetResponse(
                    uriBuilder.Uri,
                    HttpMethod.Post,
                    this.httpClient,
                    monitoringAccount.Name,
                    operation,
                    serializedContent : serializedTargetAccounts).ConfigureAwait(false);

                result.ConfigurationUpdateResults =
                    JsonConvert.DeserializeObject <ConfigurationUpdateResult[]>(
                        response.Item1,
                        this.serializerSettings);

                foreach (var updateResult in result.ConfigurationUpdateResults)
                {
                    if (!updateResult.Success)
                    {
                        result.Success          = false;
                        result.ExceptionMessage = updateResult.Message;
                        return(result);
                    }
                }

                result.Success = true;
                return(result);
            }
            catch (MetricsClientException mce)
            {
                result.Success = false;

                if (mce.ResponseStatusCode == HttpStatusCode.Unauthorized || mce.ResponseStatusCode == HttpStatusCode.Forbidden)
                {
                    var exMsg =
                        $"Unable to sync configuration for monitoringAccount:{monitoringAccount.Name}, metricNamespace:{metricNamespace}, metricName:{metricName}"
                        + $"doesn't have permission to update configurations in mirror accounts. Response:{mce.Message}";

                    throw new ConfigurationValidationException(exMsg, ValidationType.ServerSide, mce);
                }

                result.ExceptionMessage = mce.Message;

                return(result);
            }
        }