1
\$\begingroup\$

Given a Tax type and city, access a REST API to get the total amount of taxes with the given type and at the given city. This information is to be grouped by users and ordered ascending by numerical id. To access the log of taxes, I perform an HTTP GET request to https://api.doit.com/taxes/search?taxType={0}&page={1} where the taxType is either 'd' or 'c'and the page is the page of the results, as an integer. The response to a request is a JSON ith the following 5 fields:

public class PageResult
{
    public string page { get; set; }
    public int per_page { get; set; }
    public int total { get; set; }
    public int total_pages { get; set; }
    public TaxData[] data { get; set; }
}

Each Tax Record has the following schema:

public class TaxData
{
    public int id { get; set; }
    public int userId { get; set; }
    public string userName { get; set; }
    public long timestamp { get; set; }
    public string txnType { get; set; }
    public string amount { get; set; }
    public City city { get; set; }
    public string ip { get; set; }
}

The city object has the following scheme:

public class City
{
    public int id { get; set; }
    public string address { get; set; }
    public string city { get; set; }
    public int zipCode { get; set; }
}

I need to return a 2d array of integers with two columns, where the first column contains user ids of all users those pay taxes in the given city using the given tax type. The second column for a row corresponding to some user must contain the total amount that this user pays in dollars.

the following code that I want to review, especially the API consuming way.

class Program
{
    private static readonly string apiUrl = "https://api.doit.com/taxes/search?taxType={0}&page={1}";
    static void Main(string[] args)
    {
        var c = GetTaxesTotals(1, "d");
        Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(c));
        Console.ReadLine();
    }
    public static List<List<int>> GetTaxesTotals(int cityId, string taxType)
    {
        List<TaxData> taxes = GetTaxes(cityId, taxType);
        var transactionGroupsPreUser = taxes.OrderBy(x => x.userId).GroupBy(x => x.userId);
        var result = new List<List<int>>();
        result = transactionGroupsPreUser.Select(g => new List<int> { g.Key,
            (int)g.Sum(x => decimal.Parse(x.amount, NumberStyles.Currency)) }).ToList();
        if (result.Count == 0)
        {
            result = new List<List<int>> { new List<int> { -1, -1 } };
        }
        return result;
    }

    private static PageResult GetDataPage(string taxType, int page)
    {
        if (!string.Equals(taxType, "c", StringComparison.OrdinalIgnoreCase) && !string.Equals(taxType, "d", StringComparison.OrdinalIgnoreCase))
        {
            throw new ArgumentException("Unexpected tax type the allowed values: 'c', 'd'");
        }

        var uri = string.Format(apiUrl, taxType, page);
        System.Net.HttpWebRequest request = (System.Net.HttpWebRequest)System.Net.WebRequest.Create(uri);
        request.AutomaticDecompression = System.Net.DecompressionMethods.GZip | System.Net.DecompressionMethods.Deflate;
        var jsonResult = string.Empty;
        using (System.Net.HttpWebResponse response = (System.Net.HttpWebResponse)request.GetResponse())
        using (Stream stream = response.GetResponseStream())
        using (StreamReader reader = new StreamReader(stream))
        {
            jsonResult = reader.ReadToEnd();
        }
        return Newtonsoft.Json.JsonConvert.DeserializeObject<PageResult>(jsonResult);
    }

    private static List<TaxData> GetTaxes(int city, string taxType)
    {
        PageResult page = null;
        var taxes = new List<TaxData>();
        var currentPageIndex = 0;
        do
        {
            currentPageIndex++;
            page = GetDataPage(taxType, currentPageIndex);
            if (page?.data != null)
            {
                taxes.AddRange(page.data.Where(x => x.city.id == city));
            }
        } while (currentPageIndex < page.total_pages);

        return taxes;
    }
}
\$\endgroup\$

1 Answer 1

3
\$\begingroup\$

Welcome to Code Review. I'm new to this site too so I hope that this is what you are looking for.

Looking at the API call it seems that GetDataPage does more than one action:

  • validates the inputs used to form the URL
  • forms the URL
  • create a HttpWebRequest instance
  • makes the call
  • formats the response

I would suggest refactoring GetDataPage into 5 smaller methods to make it more self-documenting.

In terms of calling the API there does not seem to be any handling for cases other than the happy path, eg,

  • if the server is down
  • if the server hangs, ie, you make the request and get no response back. In this case I suspect your code will hang as the call to the API is not async
  • if the server returns a response other than the expected one (I guess a 200)

Adding code to address these would make your code more robust. If there is one suggestion over all the others it would be to make the HTTP request async so that the application is not blocked while awaiting the response.

I notice that there is no exception handling but I am assuming that this is a prototype.

I hope that helps.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.