Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fullstack Developer Protean Task Solution #5

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions DeveloperTest.sln
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29806.167
# Visual Studio Version 17
VisualStudioVersion = 17.1.32319.34
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DeveloperTest", "DeveloperTest\DeveloperTest.csproj", "{1D4CF36B-FE89-404A-867A-4986AB960BD1}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DeveloperTest", "DeveloperTest\DeveloperTest.csproj", "{1D4CF36B-FE89-404A-867A-4986AB960BD1}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests", "UnitTests\UnitTests.csproj", "{11383945-67C6-4F7A-8C98-93FAB13BA22E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand All @@ -15,6 +17,10 @@ Global
{1D4CF36B-FE89-404A-867A-4986AB960BD1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1D4CF36B-FE89-404A-867A-4986AB960BD1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1D4CF36B-FE89-404A-867A-4986AB960BD1}.Release|Any CPU.Build.0 = Release|Any CPU
{11383945-67C6-4F7A-8C98-93FAB13BA22E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{11383945-67C6-4F7A-8C98-93FAB13BA22E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{11383945-67C6-4F7A-8C98-93FAB13BA22E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{11383945-67C6-4F7A-8C98-93FAB13BA22E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
57 changes: 57 additions & 0 deletions DeveloperTest/Business/CustomerService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
using AutoMapper;
using DeveloperTest.Business.Interfaces;
using DeveloperTest.Database;
using DeveloperTest.Database.Models;
using DeveloperTest.Models;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace DeveloperTest.Business
{
public class CustomerService : ICustomerService
{
private readonly ApplicationDbContext context;
private readonly IMapper mapper;

public CustomerService(ApplicationDbContext context, IMapper mapper)
{
this.context = context;
this.mapper = mapper;
}

public async Task<IEnumerable<CustomerModel>> GetCustomersAsync(CancellationToken token)
{
return mapper.Map<IEnumerable<CustomerModel>>(await context.Customers.Include(x => x.CustomerType).ToListAsync(token));
}

public async Task<CustomerModel> GetCustomerAsync(int id, CancellationToken token)
{
return mapper.Map<CustomerModel>(await context.Customers.Include(x => x.CustomerType).SingleOrDefaultAsync(x => x.Id == id, token));
}

public async Task<CustomerModel> CreateCustomerAsync(BaseCustomerModel model, CancellationToken token)
{
var addedCustomer = await context.Customers.AddAsync(mapper.Map<Customer>(model));

await context.SaveChangesAsync();

return mapper.Map<CustomerModel>(await context.Customers.Include(x => x.CustomerType).SingleOrDefaultAsync(x => x.Id == addedCustomer.Entity.Id, token));
}

//Added for manual testing purposes
public async Task<bool> DeleteCustomer(int id, CancellationToken token)
{
var customerToDelete = await context.Customers.SingleOrDefaultAsync(x => x.Id == id, token);
if (customerToDelete is null)
{
return false;
}

context.Customers.Remove(customerToDelete);
await context.SaveChangesAsync(token);
return true;
}
}
}
28 changes: 28 additions & 0 deletions DeveloperTest/Business/CustomerTypeService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using AutoMapper;
using DeveloperTest.Business.Interfaces;
using DeveloperTest.Database;
using DeveloperTest.Models;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace DeveloperTest.Business
{
public class CustomerTypeService : ICustomerTypeService
{
private readonly ApplicationDbContext context;
private readonly IMapper mapper;

public CustomerTypeService(ApplicationDbContext context, IMapper mapper)
{
this.context = context;
this.mapper = mapper;
}

public async Task<IEnumerable<CustomerTypeModel>> GetCustomerTypesAsync(CancellationToken token)
{
return mapper.Map<IEnumerable<CustomerTypeModel>>(await context.CustomerTypes.ToListAsync(token));
}
}
}
17 changes: 17 additions & 0 deletions DeveloperTest/Business/Interfaces/ICustomerService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using DeveloperTest.Models;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace DeveloperTest.Business.Interfaces
{
public interface ICustomerService
{
Task<IEnumerable<CustomerModel>> GetCustomersAsync(CancellationToken token);
Task<CustomerModel> GetCustomerAsync(int id, CancellationToken token);
Task<CustomerModel> CreateCustomerAsync(BaseCustomerModel model, CancellationToken token);

//Added for manual testing purposes
Task<bool> DeleteCustomer(int id, CancellationToken token);
}
}
12 changes: 12 additions & 0 deletions DeveloperTest/Business/Interfaces/ICustomerTypeService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using DeveloperTest.Models;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace DeveloperTest.Business.Interfaces
{
public interface ICustomerTypeService
{
Task<IEnumerable<CustomerTypeModel>> GetCustomerTypesAsync(CancellationToken token);
}
}
22 changes: 15 additions & 7 deletions DeveloperTest/Business/JobService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@
using DeveloperTest.Database;
using DeveloperTest.Database.Models;
using DeveloperTest.Models;
using Microsoft.EntityFrameworkCore;

namespace DeveloperTest.Business
{
public class JobService : IJobService
{
private readonly ApplicationDbContext context;
private readonly string unknown = "Unknown";

public JobService(ApplicationDbContext context)
{
Expand All @@ -17,11 +19,12 @@ public JobService(ApplicationDbContext context)

public JobModel[] GetJobs()
{
return context.Jobs.Select(x => new JobModel
return context.Jobs.Include(x => x.Customer).Select(x => new JobModel
{
JobId = x.JobId,
Engineer = x.Engineer,
When = x.When
When = x.When,
CustomerName = x.Customer.Name ?? unknown
}).ToArray();
}

Expand All @@ -31,7 +34,9 @@ public JobModel GetJob(int jobId)
{
JobId = x.JobId,
Engineer = x.Engineer,
When = x.When
When = x.When,
CustomerName = x.Customer.Name ?? unknown,
CustomerType = x.Customer.Type.ToString()
}).SingleOrDefault();
}

Expand All @@ -40,16 +45,19 @@ public JobModel CreateJob(BaseJobModel model)
var addedJob = context.Jobs.Add(new Job
{
Engineer = model.Engineer,
When = model.When
When = model.When,
CustomerId = model.CustomerId
});

context.SaveChanges();
var job = context.Jobs.Include(x => x.Customer).SingleOrDefault(x => x.JobId == addedJob.Entity.JobId);

return new JobModel
{
JobId = addedJob.Entity.JobId,
Engineer = addedJob.Entity.Engineer,
When = addedJob.Entity.When
JobId = job.JobId,
Engineer = job.Engineer,
When = job.When,
CustomerName = job.Customer.Name ?? unknown
};
}
}
Expand Down
69 changes: 69 additions & 0 deletions DeveloperTest/Controllers/CustomerController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
using DeveloperTest.Business.Interfaces;
using DeveloperTest.Models;
using Microsoft.AspNetCore.Mvc;
using System.Threading;
using System.Threading.Tasks;

namespace DeveloperTest.Controllers
{
[ApiController, Route("[controller]")]
public class CustomerController : ControllerBase
{
private readonly ICustomerService _customerService;

public CustomerController(ICustomerService customerService)
{
_customerService = customerService;
}

[HttpGet]
public async Task<IActionResult> Get(CancellationToken token)
{
var customers = await _customerService.GetCustomersAsync(token);

if (customers is null)
return NotFound();

return Ok(customers);
}

[HttpGet("{id}")]
public async Task<IActionResult> Get([FromRoute]int id, CancellationToken token)
{
var customers = await _customerService.GetCustomerAsync(id, token);

if (customers is null)
return NotFound();

return Ok(customers);
}

[HttpPost]
public async Task<IActionResult> Create([FromBody] BaseCustomerModel model, CancellationToken token)
{
var created = await _customerService.CreateCustomerAsync(model, token);

if (created is null)
return BadRequest(new { error = "Unable to create customer" });

return Created($"{GetBaseUrl()}/customer/{created.Id}", created);
}

//Added for manual testing purposes
[HttpDelete("{id}")]
public async Task<IActionResult> Delete([FromRoute] int id, CancellationToken token)
{
var deleted = await _customerService.DeleteCustomer(id, token);

if (deleted)
return NoContent();

return NotFound();
}

private string GetBaseUrl()
{
return $"{HttpContext.Request.Scheme}://{HttpContext.Request.Host.ToUriComponent()}";
}
}
}
29 changes: 29 additions & 0 deletions DeveloperTest/Controllers/CustomerTypeController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using DeveloperTest.Business.Interfaces;
using Microsoft.AspNetCore.Mvc;
using System.Threading;
using System.Threading.Tasks;

namespace DeveloperTest.Controllers
{
[ApiController, Route("[controller]")]
public class CustomerTypeController : ControllerBase
{
private readonly ICustomerTypeService _customerTypeService;

public CustomerTypeController(ICustomerTypeService customerTypeService)
{
_customerTypeService = customerTypeService;
}

[HttpGet]
public async Task<IActionResult> Get(CancellationToken token)
{
var customerTypes = await _customerTypeService.GetCustomerTypesAsync(token);

if (customerTypes is null)
return NotFound();

return Ok(customerTypes);
}
}
}
17 changes: 9 additions & 8 deletions DeveloperTest/Controllers/JobController.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc;
using DeveloperTest.Business.Interfaces;
using DeveloperTest.Models;

Expand Down Expand Up @@ -37,14 +36,16 @@ public IActionResult Get(int id)
[HttpPost]
public IActionResult Create(BaseJobModel model)
{
if (model.When.Date < DateTime.Now.Date)
{
return BadRequest("Date cannot be in the past");
}

var job = jobService.CreateJob(model);

return Created($"job/{job.JobId}", job);
if (job is null)
return BadRequest(new { error = "Unable to create job" });

return Created($"{GetBaseUrl}/job/{job.JobId}", job);
}
private string GetBaseUrl()
{
return $"{HttpContext.Request.Scheme}://{HttpContext.Request.Host.ToUriComponent()}";
}
}
}
16 changes: 15 additions & 1 deletion DeveloperTest/Database/ApplicationDbContext.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
using System;
using Microsoft.EntityFrameworkCore;
using DeveloperTest.Database.Models;
using DeveloperTest.Enums;
using System.Linq;

namespace DeveloperTest.Database
{
public class ApplicationDbContext : DbContext
{
public DbSet<Job> Jobs { get; set; }

public DbSet<Customer> Customers { get; set; }
public DbSet<CustomerType> CustomerTypes { get; set; }
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
{

Expand All @@ -31,6 +34,17 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
Engineer = "Test",
When = new DateTime(2022, 2, 1, 12, 0, 0)
});

foreach (CustomerTypeEnum cte in Enum.GetValues(typeof(CustomerTypeEnum)).Cast<CustomerTypeEnum>())
{
CustomerType ct = new CustomerType
{
Id = cte,
Name = cte.ToString(),
};

modelBuilder.Entity<CustomerType>().HasData(ct);
}
}
}
}
19 changes: 19 additions & 0 deletions DeveloperTest/Database/Models/Customer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using DeveloperTest.Enums;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace DeveloperTest.Database.Models
{
public class Customer
{
[Key]
public int Id { get; set; }

public string Name { get; set; }

[Required]
public CustomerTypeEnum Type { get; set; }
[ForeignKey("Type")]
public CustomerType CustomerType { get; set; }
}
}
Loading