Cox Automotive API Challenge [C#]

Introduction

This project was about implementing Cox’s API coding challenge. One of my coworkers recently applied for a position here and they tasked him with this. He mentioned it to me and I was interested and I decided to attempt it in C# since this is the new language I’ve been using for work. Here we go!

Prerequisites

  • .NET 5.0
    • Newtonsoft.Json NuGet package
  • Internet access to their API server
  • Source code
    • https://bitbucket.org/97WaterPolo/cox-api-challenge/src/master/

Background

In this challenge we are given REST API endpoints, and our goal is to retrieve a bunch of different information, format it all and then send it back to the API in this new format. In order to do this we have to follow the following logic

  1. Generate a new DataSetID
  2. Retrieve all the VehicleIDs in a DataSetID
  3. Retrieve all the VehicleInfo for each VehicleID
  4. Retrieve all the DealerInfo for each DealerId (found within Vehicle Info)
  5. Format the relevant information into desire format
  6. Send back the information and ensure we got the success on operation

The way that we do this is by hitting their REST API endpoints in a certain order

  1. GET /api/datasetId – Creates new dataset and return its ID
  2. GET /api/{datasetId}/vehicles – Get a list of all vehicle ids in a dataset
  3. GET /api/{datasetId}/vehicles/{vehicleId} – Get specific information about vehicle
  4. GET /api/{datasetId}/dealers/{dealerId} – Gets specific information about dealer
  5. POST /api/{datasetId}/answer – Send information back and see if it is correct

Code

To start I am going to explain how I mapped the Request and Response Models. Every single REST call generally returns a JSON formatted object. What I did was recreate all of their response models as C# objects. That way when I retrieve the response from the REST API I am able to use the build JSON Serialize to create the object and have all the properties matched. All the models can be found here

Here is an example response for the Vehicle Info Response. Note, that the “[JsonProperty(“”)]” attribute denotes the name of the field in the json payload. Most of the times you don’t need it as long property name matches the json response but I put it here just for reference.

Vehicle Response (SPOILER)
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CoxApi.Models
{
    class VehicleResponse
    {
        [JsonProperty("vehicleId")]
        public int VehicleId { get; set; }
        [JsonProperty("year")]
        public int Year { get; set; }
        [JsonProperty("make")]
        public string Make { get; set; }
        [JsonProperty("model")]
        public string model { get; set; }
        [JsonProperty("dealerId")]
        public int DealerId { get; set; }
    }
}

Another piece of code that you need is the Proxy class. This is a quick implementation using System.Net.Http library that is part of the .Net framework. This is a generic class that you can use on any proxy to retrieve info using the specified method. In this case we only needed POST and GET but if you wanted to add the other methods you could.

Base Proxy (SPOILER)
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;

namespace CoxApi
{
    class BaseProxy
    {
        private HttpClient _client;
        public BaseProxy()
        {
            _client = new HttpClient();
        }

        public async Task GetAsync(string rootUrl)
        {
            var response = await _client.GetAsync(rootUrl);
            return JsonConvert.DeserializeObject(await response.Content.ReadAsStringAsync());

        }

        public async Task PostAsync(string rootUrl, U data)
        {
            var jsonPayload = JsonConvert.SerializeObject(data);

            var response = await _client.PostAsync(rootUrl, PrepJsonForPost(jsonPayload));

            return JsonConvert.DeserializeObject(await response.Content.ReadAsStringAsync());

        }

        private StringContent PrepJsonForPost(string jsonObj)
        {
            return new StringContent(jsonObj, Encoding.UTF8, "application/json");
        }
    }
}

After we created our base proxy, we need to create a class that inherits it and actually does the information retrieval. I created my CoxService class and its one job is to generate the catalog and post it back.

Base Proxy (SPOILER)
using CoxApi.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CoxApi
{
    class CoxService : BaseProxy
    {
        private readonly string BaseUrl = "https://api.coxauto-interview.com";
        private readonly string GetApiDatasetId = "/api/datasetId";
        private readonly string PostAnswer = "/api/{0}/answer";
        private readonly string GetDealers = "/api/{0}/dealers/{1}";
        private readonly string GetVehicles = "/api/{0}/vehicles";
        private readonly string GetSpecificVehicle= "/api/{0}/vehicles/{1}";

        public async Task GenerateCoxAutoCatalog()
        {
            //Create a new dataset object
            var dataSetIdResponse = await GetAsync(BaseUrl + GetApiDatasetId);

            //Obtain all of the vehicles
            var vehiclesResponse = await GetAsync(BaseUrl + string.Format(GetVehicles, dataSetIdResponse.DatasetId));
            var vehicles = await GetAllObject(GetSpecificVehicle, dataSetIdResponse.DatasetId, vehiclesResponse.VehicleIds);//await GetAllVehiclesInfo(dataSetIdResponse.DatasetId, vehiclesResponse.VehicleIds);

            //Obtain all the dealers
            var dealerIds = vehicles.Select(x => x.DealerId);
            var dealers = await GetAllObject(GetDealers, dataSetIdResponse.DatasetId, dealerIds);//await GetAllDealers(dataSetIdResponse.DatasetId, dealerIds);

            //Mapping all the objects above to the correct Answer format
            var aDealers = dealers.GroupBy(x => x.DealerId).Select(x => x.First()).Select(dealer => new Dealer()
            {
                DealerId = dealer.DealerId,
                Name = dealer.Name,
                Vehicles = vehicles.Where(x => x.DealerId.Equals(dealer.DealerId)).Select(x => new Vehicle()
                {
                    Make = x.Make,
                    Model = x.model,
                    VehicleId = x.VehicleId,
                    Year = x.Year
                })
            });

            //Post back our answer and see if it is correct!
            return await PostAsync(BaseUrl + string.Format(PostAnswer, dataSetIdResponse.DatasetId), new AnswerRequest() { Dealers = aDealers });
        }

        private async Task> GetAllObject(string endpoint, string datasetId, IEnumerable ids)
        {
            var tasks = new List>();
            foreach (var id in ids)
            {
                tasks.Add(GetAsync(BaseUrl + string.Format(endpoint, datasetId, id)));
            }
            return await Task.WhenAll(tasks);
        }

    }
}

The GenerateCoxAutoCatalog() retrieves all the information, and then does formatting at the end to get it into the correct format for it to be posted back. It then returns an AnswerResponse (mapped model from their REST API) that tells you if you got the call correctly.

Conclusion

Overall this was a very fun project that gave me some hands on experience with REST API and writing code to obtain and send information in C#. One of the things that I really had to pay attention too when doing this was the Async calls, in particular take a look at the GetAllObject<T>(string, string, IEnumerable<int>) function in the Cox Service. All those queries had to be run in parallel otherwise the built in delay would’ve put it pass our 30 second time limit. We utilized a Task List and awaited till they were all done running in parallel.

One thought on “Cox Automotive API Challenge [C#]

Leave a Reply

Your email address will not be published.