Skip to content
Merged
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
36 changes: 24 additions & 12 deletions .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,40 @@ name: .NET Build and Test

on:
push:
branches: [ "**" ]
branches: [main]
pull_request:
branches: [ "**" ]
branches: [main]
workflow_dispatch:

permissions:
contents: read

concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

env:
DOTNET_NOLOGO: true
DOTNET_CLI_TELEMETRY_OPTOUT: true

jobs:
build:
runs-on: ubuntu-latest

steps:
- name: Checkout do código
uses: actions/checkout@v4
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Instalar .NET
uses: actions/setup-dotnet@v4
- name: Setup .NET
uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
with:
dotnet-version: 8.0.x

- name: Restaurar dependências
run: dotnet restore
- name: Restore dependencies
run: dotnet restore GoodHamburger.sln

- name: Build da solução
run: dotnet build --configuration Release --no-restore
- name: Build solution
run: dotnet build GoodHamburger.sln --configuration Release --no-restore -p:ContinuousIntegrationBuild=true

- name: Rodar Testes
run: dotnet test tests/GoodHamburger.Tests/GoodHamburger.Tests.csproj --no-build --configuration Release --verbosity normal --collect:"XPlat Code Coverage"
- name: Run tests
run: dotnet test GoodHamburger.sln --configuration Release --no-build --collect:"XPlat Code Coverage"
7 changes: 7 additions & 0 deletions GoodHamburger.sln
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GoodHamburger.Tests", "test
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GoodHamburger.Api", "src\GoodHamburger.Api\GoodHamburger.Api.csproj", "{EAD5639C-4A37-C848-61BC-3741C1588FA5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GoodHamburger.Web", "src\GoodHamburger.Web\GoodHamburger.Web.csproj", "{55C4CF04-B068-45EB-9460-3844DE4763B2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -43,6 +45,10 @@ Global
{EAD5639C-4A37-C848-61BC-3741C1588FA5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EAD5639C-4A37-C848-61BC-3741C1588FA5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EAD5639C-4A37-C848-61BC-3741C1588FA5}.Release|Any CPU.Build.0 = Release|Any CPU
{55C4CF04-B068-45EB-9460-3844DE4763B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{55C4CF04-B068-45EB-9460-3844DE4763B2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{55C4CF04-B068-45EB-9460-3844DE4763B2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{55C4CF04-B068-45EB-9460-3844DE4763B2}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -53,6 +59,7 @@ Global
{06F9B59D-FDBD-4AEC-83E7-3CEA03454DD5} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
{B9A370B3-AF31-1FD3-BC9D-2A2F666345DE} = {947BC999-37A5-4ED0-BDD8-B116FDCBF35C}
{EAD5639C-4A37-C848-61BC-3741C1588FA5} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
{55C4CF04-B068-45EB-9460-3844DE4763B2} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E5AE4DB7-B2DB-4608-8A96-C90B49C45BA9}
Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
[![Build Status](https://github.com/louresb/GoodHamburgerAPI/actions/workflows/build-and-test.yml/badge.svg)](https://github.com/louresb/GoodHamburgerAPI/actions/workflows/build-and-test.yml)
![Status](https://img.shields.io/badge/Status-Concluded-green)

This project is a backend-focused solution built with C# and .NET 8 to simulate an order management system for a hamburger restaurant, applying specific business and discount rules in a real-world scenario.
This repository contains a backend-focused technical challenge built with C# and .NET 8. It implements an order management system for a hamburger restaurant, including the required business and discount rules and an optional Blazor WebAssembly interface.

## 🎯 Project Objective

Expand Down Expand Up @@ -132,7 +132,7 @@ An optional frontend was created using **Blazor WebAssembly** to enhance the pre
| `/orders/edit/{id}`| Edit an existing order |
| `/orders/details/{id}` | View full order details |

The frontend communicates with the API using `HttpClient`, dynamically resolving the base address (localhost or Docker alias `http://api/`).
The frontend communicates with the API using `HttpClient` through the API port exposed by Docker Compose.

---

Expand All @@ -142,12 +142,12 @@ The frontend communicates with the API using `HttpClient`, dynamically resolving
- Full CRUD for orders with business rules enforced
- API tested via Swagger and Postman
- Extra Blazor frontend to visualize the solution
- CI/CD configured with GitHub Actions
- CI pipeline for the API, Blazor frontend and tests
- Fully Dockerized for local testing and presentation

---

### ✅ Final Notes

- This solution was developed with care to fully meet the challenge requirements, while also showcasing practical development skills using modern .NET technologies.
- The extra frontend is intended to enrich the experience and demonstrate full-stack integration.
- This repository is preserved as a concluded technical challenge and reflects its original requirements.
- The optional frontend extends the required API with a simple interface for exercising the order flow.
2 changes: 1 addition & 1 deletion src/GoodHamburger.Infra/GoodHamburger.Infra.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="9.0.4" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="8.0.1" />
</ItemGroup>

</Project>
4 changes: 2 additions & 2 deletions src/GoodHamburger.Web/GoodHamburger.Web.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.15" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.15" PrivateAssets="all" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.30" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.30" PrivateAssets="all" />
</ItemGroup>

</Project>
11 changes: 6 additions & 5 deletions src/GoodHamburger.Web/Services/OrderService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@ public async Task<OrderResponse> CreateAsync(OrderRequest request)
{
var response = await _http.PostAsJsonAsync("api/orders", request);
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync<OrderResponse>();
return await response.Content.ReadFromJsonAsync<OrderResponse>()
?? throw new InvalidOperationException("The API returned an empty order response.");
}

public async Task<List<OrderResponse>> GetAllAsync()
{
return await _http.GetFromJsonAsync<List<OrderResponse>>("api/orders");
return await _http.GetFromJsonAsync<List<OrderResponse>>("api/orders") ?? [];
}

public async Task DeleteAsync(int id)
Expand All @@ -27,17 +28,17 @@ public async Task DeleteAsync(int id)
response.EnsureSuccessStatusCode();
}

// auxiliar para mostrar nomes dos produtos
public async Task<List<Product>> GetAllProductsAsync()
{
return await _http.GetFromJsonAsync<List<Product>>("api/products");
return await _http.GetFromJsonAsync<List<Product>>("api/products") ?? [];
}

public async Task<OrderResponse> GetByIdAsync(int id)
{
var response = await _http.GetAsync($"api/orders/{id}");
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync<OrderResponse>();
return await response.Content.ReadFromJsonAsync<OrderResponse>()
?? throw new InvalidOperationException("The API returned an empty order response.");
}

public async Task UpdateAsync(int id, OrderRequest request)
Expand Down
2 changes: 1 addition & 1 deletion src/GoodHamburger.Web/Services/ProductService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ public ProductService(HttpClient http)

public async Task<List<Product>> GetAllAsync()
{
return await _http.GetFromJsonAsync<List<Product>>("api/products");
return await _http.GetFromJsonAsync<List<Product>>("api/products") ?? [];
}
}
16 changes: 11 additions & 5 deletions tests/GoodHamburger.Tests/GoodHamburger.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,18 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="9.0.4" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="coverlet.collector" Version="6.0.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="8.0.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.9.0" />
<PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="xunit" Version="2.5.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
Expand Down