One API , multiple clients but different result sets.

This issue arose when I got a requirement in office to build API for clients but a tricky one indeed as my philosophy is to always build systems that are highly configurable rather than a system that is subject to code changes every time a new requirement arises.

Hence I took my time to analyze and plan carefully how to come up with a system that can handle the scenario on the ground.

The problem statement is to build a web API that will be used by the various client but each of them requires a different level of data exposure.

Imagine a system to display customer information but the business team has decided that data expose to the client should be based on an agreement signed eg.
Client A can get [Name,Age,Sex] , Client B can get [Name,Age] only , Client C can get [Name] only.

Let us get into the raw action.

First, let us create a sample repository

public class DataRepository
    {
        public static IList<Data> GetData()
        {
            return new List<Data>()
            {
                new Data(){ Age = 45, Sex = "Male" , Name = "Adeyinka Oluwaseun", PhoneNumber = "1111"},
                new Data(){ Age = 23, Sex = "Female" , Name = "Doyin Solomon", PhoneNumber = "2222"},
                new Data(){ Age = 56, Sex = "Male" , Name = "John Doe", PhoneNumber = "3333"},
            };
        }
    }

    public class Data
    {
        public string Name { get; set; }
        public string Sex { get; set; }
        public int Age { get; set; }
        public string PhoneNumber { get; set; }
    }

Next is to create our custom response class which is a dictionary with an indexer to store the retrieved information from our repository.

public class Response
    {
        private readonly Dictionary<string, string> _store;

        public Response()
        {
            _store = new Dictionary<string, string>();
        }

        public string this[string key]
        {
            get => _store[key];
            set => _store[key] = value;
        }

        public Dictionary<string, string> Get()
        {
            return _store;
        }
    }

Next is to configure the clients in the appsettings.json where will configure the clients and the data that will be available to them based on the business agreement signed.

{
  "Logging": {
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "AllowedHosts": "*",
  "Clients": {
    "Banks": "CLIENTA:Name,Sex|CLIENTB:Name|CLIENTC:Name,Age,Sex"
  } 
}

Next is our controller class

[Route("api/[controller]")]
    public class InformationController : Controller
    {
        private static Clients _clients;

        public InformationController(IOptions<Clients> clients)
        {
            _clients = clients.Value;
        }

        [HttpGet()]
        public IActionResult Get([FromQuery]string number , [FromQuery]string clientName)
        {
            var response = new Response();

            foreach (var info in ClientData())
            {
                if (info.Key == clientName)
                {
                    var props = info.Value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);

                    var result = DataRepository.GetData().FirstOrDefault(c => c.PhoneNumber == number);

                    foreach (var prop in props)
                    {
                        if (result != null)
                        {
                            response[prop] = GetPropertyValue(result, prop);
                        }
                    }
                }
            }

            
            return new ObjectResult(JsonConvert.SerializeObject(response.Get(), Formatting.Indented));
        }

        private static Dictionary<string, string> ClientData()
        {

            var clients = _clients.Banks.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
            var clientInfo = new Dictionary<string, string>();

            foreach (var client in clients)
            {
                var split = client.Split(new[] { ':' }, StringSplitOptions.RemoveEmptyEntries);
                clientInfo.Add(split.First(), split.Last());
            }

            return clientInfo;
        }

        private static string GetPropertyValue(Data.Data data, string property)
        {
            return data.GetType().GetProperty(property)?.GetValue(data, null).ToString() ?? string.Empty;
        }
    }

The model to bind to the appsettings.json

public class Clients
    {
        public string Banks { get; set; }
    }

Finally in the startup.cs where we bind the Clients in the appsettings.json to the Model class ie. Clients

public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

            services.Configure<Clients>(settings => Configuration.GetSection("Clients").Bind(settings));

        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseHsts();
            }

            app.UseHttpsRedirection();
            app.UseMvc();
        }
    }

Result screenshot from the test created out on different clients using POSTMAN.

CLIENT A

Client A

CLIENT C

Client C

CLIENT B

Cleint B

CLICK TO DOWNLOAD SOURCE CODE FROM GITHUB

Advertisement

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s