diff --git a/CompanyApi.sln b/CompanyApi.sln index ca944aa..954c053 100644 --- a/CompanyApi.sln +++ b/CompanyApi.sln @@ -3,9 +3,11 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.7.34202.233 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CompanyApi", "CompanyApi\CompanyApi.csproj", "{AB9810FE-4FFB-4890-B2F0-FC002BFE376E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CompanyApi", "CompanyApi\CompanyApi.csproj", "{AB9810FE-4FFB-4890-B2F0-FC002BFE376E}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CompanyApiTest", "CompanyApiTest\CompanyApiTest.csproj", "{CB3506BC-1633-4CB6-A561-B39DDCF59168}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CompanyApiTest", "CompanyApiTest\CompanyApiTest.csproj", "{CB3506BC-1633-4CB6-A561-B39DDCF59168}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F21EEDA1-5E42-4F81-9909-E2E2555C5046}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/CompanyApi/Company.cs b/CompanyApi/Company.cs index 43e7373..735bc93 100644 --- a/CompanyApi/Company.cs +++ b/CompanyApi/Company.cs @@ -6,10 +6,13 @@ public Company(string name) { Id = Guid.NewGuid().ToString(); Name = name; + Employees = new List(); } public string Id { get; set; } public string Name { get; set; } + + public List Employees { get; set; } } } diff --git a/CompanyApi/Controllers/CompanyController.cs b/CompanyApi/Controllers/CompanyController.cs index 08002a0..f7d1ff3 100644 --- a/CompanyApi/Controllers/CompanyController.cs +++ b/CompanyApi/Controllers/CompanyController.cs @@ -1,4 +1,5 @@ using Microsoft.AspNetCore.Mvc; +using System.Linq; namespace CompanyApi.Controllers { @@ -22,8 +23,73 @@ public ActionResult Create(CreateCompanyRequest request) [HttpDelete] public void ClearData() - { + { companies.Clear(); } + + [HttpGet("{id}")] + public ActionResult Get(string id) + { + foreach (var company in companies.Where(company => company.Id.Equals(id))) + { + return Ok(company); + } + + return NotFound(); + } + + [HttpGet] + public ActionResult> GetInPage([FromQuery] int? pageSize, [FromQuery] int? pageIndex) + { + if (pageIndex == null||pageSize == null) + { + return Ok(companies); + } + List companiesInPage = companies.Skip(((int)pageIndex -1)* (int)pageSize).Take((int)pageSize).ToList(); + return Ok(companiesInPage); + } + + + [HttpPut("{id}")] + public ActionResult Update(string id, CreateCompanyRequest companyRequest) + { + foreach (var company in companies.Where(company => company.Id.Equals(id))) + { + company.Name = companyRequest.Name; + return NoContent(); + } + return NotFound(); + } + + [HttpPost("{id}/employees")] + public ActionResult AddEmployee(string id, CreateEmployeeRequest request) + { + Employee employeeCreated = new Employee(request.Name); + foreach (var company in companies.Where(company => company.Id.Equals(id))) + { + if (company.Employees.Exists(employee => employee.Name.Equals(request.Name))) + { + return BadRequest(); + } + company.Employees.Add(employeeCreated); + return StatusCode(StatusCodes.Status201Created, employeeCreated); + } + return NotFound(); + } + + [HttpDelete("{companyId}/employees/{employeeId}")] + public ActionResult DeleteEmployeeFromSpecificCompany(string companyId, string employeeId) + { + + foreach (var (company, employee) in from company in companies + where company.Id.Equals(companyId) + from employee in company.Employees.Where(employee => employee.Id.Equals(employeeId)) + select (company, employee)) + { + company.Employees.Remove(employee); + return NoContent() ; + } + return NotFound() ; + } } } diff --git a/CompanyApi/CreateEmployeeRequest.cs b/CompanyApi/CreateEmployeeRequest.cs new file mode 100644 index 0000000..afe8ddf --- /dev/null +++ b/CompanyApi/CreateEmployeeRequest.cs @@ -0,0 +1,7 @@ +namespace CompanyApi +{ + public class CreateEmployeeRequest + { + public required string Name { get; set; } + } +} diff --git a/CompanyApi/Employee.cs b/CompanyApi/Employee.cs new file mode 100644 index 0000000..bcf4ffb --- /dev/null +++ b/CompanyApi/Employee.cs @@ -0,0 +1,16 @@ +namespace CompanyApi +{ + public class Employee + { + public Employee(string name) + { + Id = Guid.NewGuid().ToString(); + Name = name; + } + + public string Id { get; set; } + + public string Name { get; set; } + } +} + diff --git a/CompanyApi/apis.http b/CompanyApi/apis.http index a17fa51..39496ee 100644 --- a/CompanyApi/apis.http +++ b/CompanyApi/apis.http @@ -58,4 +58,53 @@ Content-Type: application/json; charset=utf-8 ### Case1: Sucess, 204 NO Content ### Case2: 404 Company NOT Found +###################################################################################### +## AC6 Add an employee to a specific company +POST https://{{hostname}}:{{port}}/api/Companies/{companyID} +Content-Type: application/json; charset=utf-8 + +{ + "Name": Tom +} +### Response: +### Case1: Created Success, Return employee created with Status Created( 201 ) +### Case2: Employee Name Exsiting, Return 400 Bad request +### Case3: 404 company not found +### Case4: Bad Request (invalid request body) + +###################################################################################### +## AC7 Delete an employee from a specific company +DELETE https://{{hostname}}:{{port}}/api/Companies/{companyID}/employees/{employeeID} + +### Response: +### Case1: Sucess, 204 NO Content +### Case2: 404 NOT Found(Wrong Employee id) +### Case3: 404 NOT Found(Wrong Company id) + +###################################################################################### +## AC8 Get all employees from a specific company +GET https://{{hostname}}:{{port}}/api/Companies/{companyID}/employees + +### Response: OK 200 with List of Employees + +###################################################################################### +## AC9 Update the information of a specific employee from a certain company +PUT https://{{hostname}}:{{port}}/api/Companies/{companyID}/employees/{employeeID} +Content-Type: application/json; charset=utf-8 + +{ + "name": Jim +} +### Response: +### Case1: Sucess, 204 NO Content +### Case2: 404 Company NOT Found + +###################################################################################### +## AC10 Delete a specific company and remove all the employee. +DELETE https://{{hostname}}:{{port}}/api/Companies/{companyID} + +### Response: +### Case1: Sucess, 204 NO Content +### Case2: 404 Company NOT Found + ###################################################################################### \ No newline at end of file diff --git a/CompanyApiTest/CompanyApiTest.cs b/CompanyApiTest/CompanyApiTest.cs index 2f38b90..490950b 100644 --- a/CompanyApiTest/CompanyApiTest.cs +++ b/CompanyApiTest/CompanyApiTest.cs @@ -2,6 +2,9 @@ using Microsoft.AspNetCore.Mvc.Testing; using Newtonsoft.Json; using System.Net; +using System.Net.Http; +using System.Net.Http.Json; +using System.Runtime.CompilerServices; using System.Text; namespace CompanyApiTest @@ -21,17 +24,14 @@ public async Task Should_return_created_company_with_status_201_when_create_cpmo { // Given await ClearDataAsync(); - Company companyGiven = new Company("BlueSky Digital Media"); - + CreateCompanyRequest companyGiven = new CreateCompanyRequest() { Name = "BlueSky Digital Media" }; + // When - HttpResponseMessage httpResponseMessage = await httpClient.PostAsync( - "/api/companies", - SerializeObjectToContent(companyGiven) - ); + HttpResponseMessage httpResponseMessage = await httpClient.PostAsJsonAsync("/api/companies", companyGiven); // Then Assert.Equal(HttpStatusCode.Created, httpResponseMessage.StatusCode); - Company? companyCreated = await DeserializeTo(httpResponseMessage); + Company? companyCreated = await httpResponseMessage.Content.ReadFromJsonAsync(); Assert.NotNull(companyCreated); Assert.NotNull(companyCreated.Id); Assert.Equal(companyGiven.Name, companyCreated.Name); @@ -42,14 +42,12 @@ public async Task Should_return_bad_reqeust_when_create_company_given_a_existed_ { // Given await ClearDataAsync(); - Company companyGiven = new Company("BlueSky Digital Media"); + CreateCompanyRequest companyGiven = new CreateCompanyRequest() { Name = "BlueSky Digital Media" }; + await httpClient.PostAsJsonAsync("/api/companies", companyGiven); // When - await httpClient.PostAsync("/api/companies", SerializeObjectToContent(companyGiven)); - HttpResponseMessage httpResponseMessage = await httpClient.PostAsync( - "/api/companies", - SerializeObjectToContent(companyGiven) - ); + HttpResponseMessage httpResponseMessage = await httpClient.PostAsJsonAsync("/api/companies", companyGiven); + // Then Assert.Equal(HttpStatusCode.BadRequest, httpResponseMessage.StatusCode); } @@ -68,21 +66,249 @@ public async Task Should_return_bad_reqeust_when_create_company_given_a_company_ Assert.Equal(HttpStatusCode.BadRequest, httpResponseMessage.StatusCode); } - private async Task DeserializeTo(HttpResponseMessage httpResponseMessage) + private async Task ClearDataAsync() { - string response = await httpResponseMessage.Content.ReadAsStringAsync(); - T? deserializedObject = JsonConvert.DeserializeObject(response); - return deserializedObject; + await httpClient.DeleteAsync("/api/companies"); } - private static StringContent SerializeObjectToContent(T objectGiven) + [Fact] + public async Task Should_return_company_list_with_statuscode_200_when_get_given_without_company_id() { - return new StringContent(JsonConvert.SerializeObject(objectGiven), Encoding.UTF8, "application/json"); + // Given + await ClearDataAsync(); + // When + HttpResponseMessage httpResponseMessage = await httpClient.GetAsync("/api/companies"); + // Then + Assert.Equal(HttpStatusCode.OK, httpResponseMessage.StatusCode); + Assert.Equal(new List(), await httpResponseMessage.Content.ReadFromJsonAsync>()); } - private async Task ClearDataAsync() + [Fact] + public async Task Should_return_a_company_with_statuscode_200_when_get_given_company_id() { - await httpClient.DeleteAsync("/api/companies"); + // Given + await ClearDataAsync(); + Company companyGiven = new Company("BlueSky Digital Media"); + HttpResponseMessage message = await httpClient.PostAsJsonAsync("/api/companies", companyGiven); + Company company = await message.Content.ReadFromJsonAsync(); + string targetId = company.Id; + // When + HttpResponseMessage httpResponseMessage = await httpClient.GetAsync($"/api/companies/{targetId}" ); + var gotCompany = await httpResponseMessage.Content.ReadFromJsonAsync(); + // Then + Assert.Equal(HttpStatusCode.Created, message.StatusCode); + Assert.Equal(HttpStatusCode.OK, httpResponseMessage.StatusCode); + + Assert.Equal(companyGiven.Name, gotCompany.Name); + } + + [Fact] + public async Task Should_return_not_found_when_get_given_not_existing_id() + { + // Given + await ClearDataAsync(); + CreateCompanyRequest companyGiven = new CreateCompanyRequest() { Name = "BlueSky Digital Media" }; + HttpResponseMessage message = await httpClient.PostAsJsonAsync("/api/companies", companyGiven); + var deletedId = message.Content.ReadFromJsonAsync().Id.ToString(); + await httpClient.DeleteAsync(deletedId); + // When + HttpResponseMessage httpResponseMessage = await httpClient.GetAsync($"/api/companies/{deletedId}"); + + //Then + Assert.Equal(HttpStatusCode.NotFound, httpResponseMessage.StatusCode); + + } + + [Fact] + public async Task Should_return_company_list_with_statuscode_200_when_get_companies_with_paging() + { + // Given + await ClearDataAsync(); + for (int i = 0; i < 10; i++) + { + CreateCompanyRequest companyGiven = new CreateCompanyRequest() { Name = $"BlueSky Digital Media{i+1}" }; + await httpClient.PostAsJsonAsync("/api/companies", companyGiven); + } + + int pageSize = 2; + int pageIndex = 3; + + // When + HttpResponseMessage httpResponseMessage = await httpClient.GetAsync($"/api/companies/?pageSize={pageSize}&pageIndex={pageIndex}"); + + // Then + Assert.Equal(HttpStatusCode.OK, httpResponseMessage.StatusCode); + List resultPage = await httpResponseMessage.Content.ReadFromJsonAsync>(); + Assert.Equal("BlueSky Digital Media5", resultPage[0].Name); + Assert.Equal("BlueSky Digital Media6", resultPage[1].Name); + } + + [Fact] + public async Task Should_return_not_found_when_update_given_invalid_id() + { + // Given + await ClearDataAsync(); + CreateCompanyRequest companyGiven = new CreateCompanyRequest() { Name = "BlueSky Digital Media" }; + HttpResponseMessage message = await httpClient.PostAsJsonAsync("/api/companies", companyGiven); + var deletedId = message.Content.ReadFromJsonAsync().Id.ToString(); + await httpClient.DeleteAsync(deletedId); + CreateCompanyRequest companyToUpdate = new CreateCompanyRequest() { Name = "BlueSky Digital Media" }; + + // When + HttpResponseMessage httpResponseMessage = await httpClient.PutAsJsonAsync($"/api/companies/{deletedId}", companyToUpdate); + + // Then + Assert.Equal(HttpStatusCode.NotFound, httpResponseMessage.StatusCode); + } + + [Fact] + public async Task Should_return_NoContent_204_when_update_given_valid_id_and_content_to_update() + { + // Given + await ClearDataAsync(); + CreateCompanyRequest companyGiven = new CreateCompanyRequest() { Name = "BlueSky Digital Media" }; + HttpResponseMessage message = await httpClient.PostAsJsonAsync("/api/companies", companyGiven); + Company company = await message.Content.ReadFromJsonAsync(); + string targetId = company.Id; + CreateCompanyRequest companyToUpdate = new CreateCompanyRequest() { Name = "New Name" }; + + // When + HttpResponseMessage httpResponseMessage = await httpClient.PutAsJsonAsync($"/api/companies/{targetId}", companyToUpdate); + + // Then + Assert.Equal(HttpStatusCode.NoContent, httpResponseMessage.StatusCode); + HttpResponseMessage updatedMessage = await httpClient.GetAsync($"/api/companies/{targetId}"); + var updatedCompany = await updatedMessage.Content.ReadFromJsonAsync(); + Assert.Equal(companyToUpdate.Name, updatedCompany.Name); + } + + [Fact] + public async Task Should_return_created_employee_with_status_201_when_add_employee_to_company_given_a_company_and_employee() + { + // Given + await ClearDataAsync(); + CreateCompanyRequest companyGiven = new CreateCompanyRequest() { Name = "BlueSky Digital Media" }; + HttpResponseMessage message = await httpClient.PostAsJsonAsync("/api/companies", companyGiven); + var company = await message.Content.ReadFromJsonAsync(); + string companyId = company.Id; + CreateEmployeeRequest employeeGiven = new CreateEmployeeRequest() { Name = "Tom" }; + // When + HttpResponseMessage httpResponseMessage = await httpClient.PostAsJsonAsync($"/api/companies/{companyId}/employees", employeeGiven); + // Then + Assert.Equal(HttpStatusCode.Created, httpResponseMessage.StatusCode); + Employee? employeeCreated = await httpResponseMessage.Content.ReadFromJsonAsync(); + Assert.NotNull(employeeCreated); + Assert.NotNull(employeeCreated.Id); + Assert.Equal(employeeGiven.Name, employeeCreated.Name); + } + + [Fact] + public async Task Should_return_bad_reqeust_when_create_employee_given_a_existed_employee_name() + { + // Given + await ClearDataAsync(); + CreateCompanyRequest companyGiven = new CreateCompanyRequest() { Name = "BlueSky Digital Media" }; + HttpResponseMessage message = await httpClient.PostAsJsonAsync("/api/companies", companyGiven); + var company = await message.Content.ReadFromJsonAsync(); + string companyId = company.Id; + CreateEmployeeRequest employeeGiven = new CreateEmployeeRequest() { Name = "Tom" }; + await httpClient.PostAsJsonAsync($"/api/companies/{companyId}/employees", employeeGiven); + // When + HttpResponseMessage httpResponseMessage = await httpClient.PostAsJsonAsync($"/api/companies/{companyId}/employees", employeeGiven); + // Then + Assert.Equal(HttpStatusCode.BadRequest, httpResponseMessage.StatusCode); + } + [Fact] + public async Task Should_return_bad_reqeust_when_create_employee_given_a_employee_with_unknown_field() + { + // Given + await ClearDataAsync(); + CreateCompanyRequest companyGiven = new CreateCompanyRequest() { Name = "BlueSky Digital Media" }; + HttpResponseMessage message = await httpClient.PostAsJsonAsync("/api/companies", companyGiven); + var company = await message.Content.ReadFromJsonAsync(); + string companyId = company.Id; + StringContent employeeGiven = new StringContent("{\"unknownField\": \"Tim\"}", Encoding.UTF8, "application/json"); + // When + HttpResponseMessage httpResponseMessage = await httpClient.PostAsync($"/api/companies/{companyId}/employees", employeeGiven); + + // Then + Assert.Equal(HttpStatusCode.BadRequest, httpResponseMessage.StatusCode); + } + + [Fact] + public async Task Should_return_bad_reqeust_when_create_company_given_a_company_with_unknown_field2() + { + // Given + await ClearDataAsync(); + CreateCompanyRequest companyGiven = new CreateCompanyRequest() { Name = "BlueSky Digital Media" }; + HttpResponseMessage message = await httpClient.PostAsJsonAsync("/api/companies", companyGiven); + var company = await message.Content.ReadFromJsonAsync(); + string companyId = "sfejiroew"; + CreateEmployeeRequest employeeGiven = new CreateEmployeeRequest() { Name = "Tom" }; + // When + HttpResponseMessage httpResponseMessage = await httpClient.PostAsJsonAsync($"/api/companies/{companyId}/employees", employeeGiven); + // Then + Assert.Equal(HttpStatusCode.NotFound, httpResponseMessage.StatusCode); + } + + [Fact] + public async Task Should_return_204_NoContent_when_delete_employee_given_companyid_and_employeeid() + { + // Given + await ClearDataAsync(); + CreateCompanyRequest companyGiven = new CreateCompanyRequest() { Name = "BlueSky Digital Media" }; + HttpResponseMessage message = await httpClient.PostAsJsonAsync("/api/companies", companyGiven); + var company = await message.Content.ReadFromJsonAsync(); + string companyId = company.Id; + CreateEmployeeRequest employeeGiven = new CreateEmployeeRequest() { Name = "Tom" }; + HttpResponseMessage message2 = await httpClient.PostAsJsonAsync($"/api/companies/{companyId}/employees", employeeGiven); + var employee = await message2.Content.ReadFromJsonAsync(); + string employeeId = employee.Id; + + // When + HttpResponseMessage httpResponseMessage = await httpClient.DeleteAsync($"/api/companies/{companyId}/employees/{employeeId}"); + // Then + Assert.Equal(HttpStatusCode.NoContent, httpResponseMessage.StatusCode); + } + + [Fact] + public async Task Should_return_404_NotFound_when_delete_employee_given_invalid_employeeid() + { + // Given + await ClearDataAsync(); + CreateCompanyRequest companyGiven = new CreateCompanyRequest() { Name = "BlueSky Digital Media" }; + HttpResponseMessage message = await httpClient.PostAsJsonAsync("/api/companies", companyGiven); + var company = await message.Content.ReadFromJsonAsync(); + string companyId = company.Id; + CreateEmployeeRequest employeeGiven = new CreateEmployeeRequest() { Name = "Tom" }; + HttpResponseMessage message2 = await httpClient.PostAsJsonAsync($"/api/companies/{companyId}/employees", employeeGiven); + var employee = await message2.Content.ReadFromJsonAsync(); + string employeeId = employee.Id; + + // When + HttpResponseMessage httpResponseMessage = await httpClient.DeleteAsync($"/api/companies/{companyId}/employees/{employeeId+"1"}"); + // Then + Assert.Equal(HttpStatusCode.NotFound, httpResponseMessage.StatusCode); + } + + [Fact] + public async Task Should_return_404_NotFound_when_delete_employee_given_invalid_companyid() + { + // Given + await ClearDataAsync(); + CreateCompanyRequest companyGiven = new CreateCompanyRequest() { Name = "BlueSky Digital Media" }; + HttpResponseMessage message = await httpClient.PostAsJsonAsync("/api/companies", companyGiven); + var company = await message.Content.ReadFromJsonAsync(); + string companyId = company.Id; + CreateEmployeeRequest employeeGiven = new CreateEmployeeRequest() { Name = "Tom" }; + HttpResponseMessage message2 = await httpClient.PostAsJsonAsync($"/api/companies/{companyId}/employees", employeeGiven); + var employee = await message2.Content.ReadFromJsonAsync(); + string employeeId = employee.Id; + + // When + HttpResponseMessage httpResponseMessage = await httpClient.DeleteAsync($"/api/companies/{companyId+"1"}/employees/{employeeId}"); + // Then + Assert.Equal(HttpStatusCode.NotFound, httpResponseMessage.StatusCode); } } } \ No newline at end of file diff --git a/Employee.txt b/Employee.txt new file mode 100644 index 0000000..e69de29