csharp/codecov/codecov-exe/Source/Codecov/Url/Query.cs

Query.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using Codecov.Services;
using Codecov.Services.ContinuousIntegrationServers;
using Codecov.Utilities;
using Codecov.Yaml;

namespace Codecov.Url
{
    internal clast Query : IQuery
    {
        private readonly IEnviornmentVariables _environmentVariables;
        private readonly Lazy _getQuery;

        public Query(IQueryOptions options, IEnumerable repositories, IBuild build, IYaml yaml, IEnviornmentVariables environmentVariables)
        {
            Options = options;
            Repositories = repositories;
            Build = build;
            Yaml = yaml;
            _environmentVariables = environmentVariables;
            SetQueryParameters();
            _getQuery = new Lazy(() => string.Join("&", QueryParameters.Select(x => $"{x.Key}={x.Value ?? string.Empty}")));
        }

        public string GetQuery => _getQuery.Value;

        private IBuild Build { get; }

        private IQueryOptions Options { get; }

        private IDictionary QueryParameters { get; set; }

        private IEnumerable Repositories { get; }

        private IYaml Yaml { get; }

        private static string EscapeKnownProblematicCharacters(string data)
        {
            var knownChars = new Dictionary
            {
                { '#', "%23" },
            };

            var result = new StringBuilder();

            foreach (var c in data)
            {
                if (knownChars.ContainsKey(c))
                {
                    result.Append(knownChars[c]);
                }
                else
                {
                    result.Append(c);
                }
            }

            return result.ToString();
        }

        private void OverrideIfNotEmptyOrNull(string key, string value, bool escapeValue = false)
        {
            if (!string.IsNullOrWhiteSpace(value))
            {
                QueryParameters[key] = escapeValue ?
                    Uri.EscapeDataString(value.RemoveAllWhiteSpace()) :
                    value.RemoveAllWhiteSpace();
            }
        }

        private void SetBranch()
        {
            QueryParameters["branch"] = string.Empty;

            foreach (var repository in Repositories)
            {
                if (!string.IsNullOrWhiteSpace(repository.Branch))
                {
                    // We also need to take into account that '#' needs to be escaped for parameters
                    var escapedBranch = EscapeKnownProblematicCharacters(Uri.EscapeDataString(repository.Branch));
                    QueryParameters["branch"] = escapedBranch;
                    break;
                }
            }

            OverrideIfNotEmptyOrNull("branch", Options.Branch);
        }

        private void SetBuild()
        {
            var escapedBuild = Uri.EscapeDataString(Build.Build);

            QueryParameters["build"] = escapedBuild;
            OverrideIfNotEmptyOrNull("build", Options.Build);
        }

        private void SetBuildUrl()
            => QueryParameters["build_url"] = Build.BuildUrl.Replace("?", "%3F");

        private void SetCommit()
        {
            QueryParameters["commit"] = string.Empty;

            foreach (var repository in Repositories)
            {
                if (!string.IsNullOrWhiteSpace(repository.Commit))
                {
                    QueryParameters["commit"] = repository.Commit;
                    break;
                }
            }

            OverrideIfNotEmptyOrNull("commit", Options.Commit);
        }

        private void SetFlags()
        {
            var flags = Options.Flags;
            if (string.IsNullOrWhiteSpace(flags))
            {
                QueryParameters["flags"] = string.Empty;
                return;
            }

            var flagsSeperatedByCommasAndNoExtraWhiteSpace = string.Join(",", flags.Split(',').Select(x => x.Trim()));
            QueryParameters["flags"] = flagsSeperatedByCommasAndNoExtraWhiteSpace;
        }

        private void SetJob()
        {
            var escapedJob = Uri.EscapeDataString(Build.Job);

            // Due to the + sign being escaped, we need to unescape that character
            escapedJob = escapedJob.Replace("%2B", "%252B");

            QueryParameters["job"] = escapedJob;
        }

        private void SetName()
        {
            QueryParameters["name"] = string.Empty;
            OverrideIfNotEmptyOrNull("name", Options.Name);
        }

        private void SetPackage()
            => QueryParameters["package"] = About.Version;

        private void SetPr()
        {
            QueryParameters["pr"] = string.Empty;

            foreach (var repository in Repositories)
            {
                if (!string.IsNullOrWhiteSpace(repository.Pr))
                {
                    QueryParameters["pr"] = repository.Pr;
                    break;
                }
            }

            OverrideIfNotEmptyOrNull("pr", Options.Pr);
        }

        private void SetProjectAndServerUri()
        {
            foreach (var repository in Repositories)
            {
                if (!string.IsNullOrEmpty(repository.Project) && !string.IsNullOrEmpty(repository.ServerUri))
                {
                    QueryParameters["project"] = repository.Project;
                    QueryParameters["server_uri"] = repository.ServerUri;
                    break;
                }
            }
        }

        private void SetQueryParameters()
        {
            QueryParameters = new Dictionary();
            SetBranch();
            SetCommit();
            SetBuild();
            SetTag();
            SetPr();
            SetName();
            SetFlags();
            SetSlug();
            SetToken();
            SetPackage();
            SetBuildUrl();
            SetYaml();
            SetJob();
            SetService();
            SetProjectAndServerUri();
        }

        private void SetService()
            => QueryParameters["service"] = Build.Service;

        private void SetSlug()
        {
            QueryParameters["slug"] = string.Empty;

            foreach (var repository in Repositories)
            {
                if (!string.IsNullOrWhiteSpace(repository.Slug))
                {
                    QueryParameters["slug"] = WebUtility.UrlEncode(repository.Slug);
                    break;
                }
            }

            var slugEnv = _environmentVariables.GetEnvironmentVariable("CODECOV_SLUG");
            if (!string.IsNullOrWhiteSpace(slugEnv))
            {
                QueryParameters["slug"] = WebUtility.UrlEncode(slugEnv.Trim());
            }

            if (!string.IsNullOrWhiteSpace(Options.Slug))
            {
                QueryParameters["slug"] = WebUtility.UrlEncode(Options.Slug.Trim());
            }
        }

        private void SetTag()
        {
            QueryParameters["tag"] = string.Empty;

            foreach (var repository in Repositories)
            {
                if (!string.IsNullOrWhiteSpace(repository.Tag))
                {
                    QueryParameters["tag"] = repository.Tag;
                    break;
                }
            }

            OverrideIfNotEmptyOrNull("tag", Options.Tag);
        }

        private void SetToken()
        {
            QueryParameters["token"] = string.Empty;

            var tokenEnv = _environmentVariables.GetEnvironmentVariable("CODECOV_TOKEN");
            if (!string.IsNullOrWhiteSpace(tokenEnv))
            {
                QueryParameters["token"] = tokenEnv.RemoveAllWhiteSpace();
            }

            if (Guid.TryParse(Options.Token, out var _))
            {
                QueryParameters["token"] = Options.Token.RemoveAllWhiteSpace();
            }
        }

        private void SetYaml()
            => QueryParameters["yaml"] = Yaml.FileName;
    }
}