Skip to content

Commit 85362e3

Browse files
committed
Feat: Project Name
1 parent 2fa5e0d commit 85362e3

File tree

6 files changed

+283
-0
lines changed

6 files changed

+283
-0
lines changed

src/WebApi/Dockerfile

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base
2+
3+
LABEL maintainer "Jonathan Peris"
4+
5+
USER app
6+
WORKDIR /app
7+
8+
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
9+
10+
RUN apt-get update \
11+
&& apt-get install -y --no-install-recommends \
12+
clang zlib1g-dev
13+
14+
ARG AOT
15+
ARG TRIM
16+
ARG EXTRA_OPTIMIZE
17+
ARG BUILD_ARCHITECTURE
18+
ARG BUILD_CONFIGURATION
19+
20+
WORKDIR /src
21+
22+
COPY ["WebApi/WebApi.csproj", "WebApi/"]
23+
24+
RUN dotnet restore -r $BUILD_ARCHITECTURE "./WebApi/WebApi.csproj" -p:Configuration=${BUILD_CONFIGURATION} -p:AOT=${AOT} -p:Trim=${TRIM}
25+
26+
COPY . .
27+
28+
WORKDIR "/src/WebApi"
29+
30+
RUN dotnet build -r $BUILD_ARCHITECTURE "WebApi.csproj" -c $BUILD_CONFIGURATION -p:AOT=${AOT} -p:Trim=${TRIM} -p:ExtraOptimize=${EXTRA_OPTIMIZE} -o /app/build
31+
32+
FROM build AS publish
33+
34+
RUN dotnet publish -r $BUILD_ARCHITECTURE "WebApi.csproj" --no-restore -c $BUILD_CONFIGURATION -p:AOT=${AOT} -p:Trim=${TRIM} -p:ExtraOptimize=${EXTRA_OPTIMIZE} -o /app/publish
35+
36+
FROM base AS final
37+
38+
WORKDIR /app
39+
EXPOSE 8080
40+
41+
COPY --from=publish /app/publish .
42+
ENTRYPOINT ["./WebApi"]

src/WebApi/Program.cs

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
using Microsoft.AspNetCore.Mvc;
2+
using Npgsql;
3+
#if !EXTRAOPTIMIZE
4+
using OpenTelemetry.Metrics;
5+
using OpenTelemetry.ResourceDetectors.Container;
6+
using OpenTelemetry.ResourceDetectors.Host;
7+
using OpenTelemetry.Resources;
8+
using OpenTelemetry.Trace;
9+
#endif
10+
using System.Text.Json;
11+
using System.Text.Json.Serialization;
12+
13+
var builder = WebApplication.CreateSlimBuilder(args);
14+
15+
builder.Services.ConfigureHttpJsonOptions(options =>
16+
{
17+
options.SerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower;
18+
options.SerializerOptions.TypeInfoResolverChain.Insert(0, SourceGenerationContext.Default);
19+
});
20+
21+
#if !EXTRAOPTIMIZE
22+
Action<ResourceBuilder> appResourceBuilder =
23+
resource => resource
24+
.AddDetector(new ContainerResourceDetector())
25+
.AddDetector(new HostDetector());
26+
27+
builder.Services.AddOpenTelemetry()
28+
.ConfigureResource(appResourceBuilder)
29+
.WithTracing(tracerBuilder => tracerBuilder
30+
.AddAspNetCoreInstrumentation()
31+
.AddHttpClientInstrumentation())
32+
.WithMetrics(meterBuilder => meterBuilder
33+
.AddProcessInstrumentation()
34+
.AddRuntimeInstrumentation()
35+
.AddAspNetCoreInstrumentation()
36+
.AddPrometheusExporter());
37+
#endif
38+
39+
builder.Services.AddNpgsqlDataSource(
40+
builder.Configuration.GetConnectionString("DefaultConnection")!
41+
);
42+
43+
var app = builder.Build();
44+
45+
#if !EXTRAOPTIMIZE
46+
app.MapPrometheusScrapingEndpoint();
47+
#endif
48+
49+
var clientes = new Dictionary<int, int>
50+
{
51+
{ 1, 100000 },
52+
{ 2, 80000 },
53+
{ 3, 1000000 },
54+
{ 4, 10000000 },
55+
{ 5, 500000 }
56+
};
57+
58+
#if !EXTRAOPTIMIZE
59+
app.MapHealthChecks("/healthz");
60+
#endif
61+
62+
app.MapGet("/clientes/{id:int}/extrato", async (int id, [FromServices] NpgsqlDataSource dataSource) =>
63+
{
64+
if (!clientes.TryGetValue(id, out _))
65+
return Results.NotFound();
66+
67+
await using (var cmd = dataSource.CreateCommand())
68+
{
69+
cmd.CommandText = "SELECT * FROM GetSaldoClienteById($1)";
70+
cmd.Parameters.AddWithValue(id);
71+
72+
using var reader = await cmd.ExecuteReaderAsync();
73+
74+
if (!await reader.ReadAsync())
75+
return Results.NotFound();
76+
77+
var saldo = new SaldoDto(reader.GetInt32(0), reader.GetInt32(1), reader.GetDateTime(2));
78+
var jsonDoc = reader.GetFieldValue<JsonDocument>(3);
79+
var ultimasTransacoes = JsonSerializer.Deserialize<List<TransacaoDto>>(jsonDoc.RootElement, SourceGenerationContext.Default.ListTransacaoDto.Options);
80+
81+
var extrato = new ExtratoDto(saldo, ultimasTransacoes);
82+
83+
return Results.Ok(extrato);
84+
}
85+
});
86+
87+
app.MapPost("/clientes/{id:int}/transacoes", async (int id, [FromBody] TransacaoDto transacao, [FromServices] NpgsqlDataSource dataSource) =>
88+
{
89+
if (!clientes.TryGetValue(id, out int limite))
90+
return Results.NotFound();
91+
92+
if (!IsTransacaoValid(transacao))
93+
return Results.UnprocessableEntity();
94+
95+
await using (var cmd = dataSource.CreateCommand())
96+
{
97+
cmd.CommandText = "SELECT InsertTransacao($1, $2, $3, $4)";
98+
cmd.Parameters.AddWithValue(id);
99+
cmd.Parameters.AddWithValue(transacao.Valor);
100+
cmd.Parameters.AddWithValue(transacao.Tipo);
101+
cmd.Parameters.AddWithValue(transacao.Descricao);
102+
103+
using var reader = await cmd.ExecuteReaderAsync();
104+
105+
if (!await reader.ReadAsync())
106+
return Results.UnprocessableEntity();
107+
108+
var updatedSaldo = reader.GetInt32(0);
109+
110+
return Results.Ok(new ClienteDto(id, limite, updatedSaldo));
111+
}
112+
});
113+
114+
app.Run();
115+
116+
static bool IsTransacaoValid(TransacaoDto transacao)
117+
{
118+
ReadOnlySpan<char> tipoC = "c";
119+
ReadOnlySpan<char> tipoD = "d";
120+
121+
return (transacao.Tipo.AsSpan().SequenceEqual(tipoC) || transacao.Tipo.AsSpan().SequenceEqual(tipoD))
122+
&& !string.IsNullOrEmpty(transacao.Descricao)
123+
&& transacao.Descricao.Length <= 10
124+
&& transacao.Valor > 0;
125+
}
126+
127+
[JsonSerializable(typeof(ClienteDto))]
128+
[JsonSerializable(typeof(ExtratoDto))]
129+
[JsonSerializable(typeof(SaldoDto))]
130+
[JsonSerializable(typeof(TransacaoDto))]
131+
[JsonSerializable(typeof(List<TransacaoDto>))]
132+
internal partial class SourceGenerationContext : JsonSerializerContext { }
133+
134+
internal readonly record struct ClienteDto(int Id, int Limite, int Saldo);
135+
internal readonly record struct ExtratoDto(SaldoDto Saldo, List<TransacaoDto>? ultimas_transacoes);
136+
internal readonly record struct SaldoDto(int Total, int Limite, DateTime data_extrato);
137+
internal readonly record struct TransacaoDto(int Valor, string Tipo, string Descricao);
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"profiles": {
3+
"http": {
4+
"commandName": "Project",
5+
"environmentVariables": {
6+
"ASPNETCORE_ENVIRONMENT": "Development"
7+
},
8+
"dotnetRunMessages": true,
9+
"applicationUrl": "http://localhost:9999"
10+
},
11+
"Docker": {
12+
"commandName": "Docker",
13+
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}",
14+
"environmentVariables": {
15+
"ASPNETCORE_HTTP_PORTS": "8080"
16+
},
17+
"publishAllPorts": true
18+
}
19+
},
20+
"$schema": "http://json.schemastore.org/launchsettings.json"
21+
}

src/WebApi/WebApi.csproj

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<Project Sdk="Microsoft.NET.Sdk.Web">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net9.0</TargetFramework>
5+
<Nullable>enable</Nullable>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
8+
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
9+
<DockerfileContext>..\..</DockerfileContext>
10+
<DockerComposeProjectPath>..\..\docker-compose.dcproj</DockerComposeProjectPath>
11+
</PropertyGroup>
12+
13+
<PropertyGroup Condition="'$(AOT)' == 'true'">
14+
<PublishAot>true</PublishAot>
15+
<OptimizationPreference>Speed</OptimizationPreference>
16+
</PropertyGroup>
17+
18+
<PropertyGroup Condition="'$(Trim)' == 'true'">
19+
<PublishReadyToRun>true</PublishReadyToRun>
20+
<PublishReadyToRunComposite>true</PublishReadyToRunComposite>
21+
<PublishSingleFile>true</PublishSingleFile>
22+
<SelfContained>true</SelfContained>
23+
</PropertyGroup>
24+
25+
<PropertyGroup Condition="'$(ExtraOptimize)' == 'true'">
26+
<TrimmerRemoveSymbols>true</TrimmerRemoveSymbols>
27+
<DebuggerSupport>false</DebuggerSupport>
28+
<EnableUnsafeBinaryFormatterSerialization>false</EnableUnsafeBinaryFormatterSerialization>
29+
<EnableUnsafeUTF7Encoding>false</EnableUnsafeUTF7Encoding>
30+
<EventSourceSupport>false</EventSourceSupport>
31+
<HttpActivityPropagationSupport>false</HttpActivityPropagationSupport>
32+
<InvariantGlobalization>true</InvariantGlobalization>
33+
<MetadataUpdaterSupport>false</MetadataUpdaterSupport>
34+
<StackTraceSupport>false</StackTraceSupport>
35+
<UseSystemResourceKeys>true</UseSystemResourceKeys>
36+
<DefineConstants>$(DefineConstants);EXTRAOPTIMIZE</DefineConstants>
37+
</PropertyGroup>
38+
39+
<ItemGroup>
40+
<PackageReference Include="Npgsql" Version="9.0.3" />
41+
<PackageReference Include="Npgsql.DependencyInjection" Version="9.0.3" />
42+
</ItemGroup>
43+
44+
<ItemGroup Condition="'$(ExtraOptimize)' == 'false'">
45+
<PackageReference Include="OpenTelemetry.Exporter.Prometheus.AspnetCore" Version="1.7.0-rc.1" />
46+
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.7.0" />
47+
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.7.1" />
48+
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.7.1" />
49+
<PackageReference Include="OpenTelemetry.Instrumentation.Process" Version="0.5.0-beta.4" />
50+
<PackageReference Include="OpenTelemetry.Instrumentation.Runtime" Version="1.7.0" />
51+
<PackageReference Include="OpenTelemetry.ResourceDetectors.Container" Version="1.0.0-beta.6" />
52+
<PackageReference Include="OpenTelemetry.ResourceDetectors.Host" Version="0.1.0-alpha.2" />
53+
</ItemGroup>
54+
55+
</Project>

src/WebApi/WebApi.http

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
@HostAddress = http://localhost:9999
2+
3+
POST {{HostAddress}}/clientes/1/transacoes
4+
Content-Type: application/json
5+
6+
{
7+
"valor": 1000,
8+
"tipo" : "d",
9+
"descricao" : "1"
10+
}
11+
12+
###
13+
14+
GET {{HostAddress}}/clientes/1/extrato
15+
Accept: application/json
16+
17+
###

src/WebApi/appsettings.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"Logging": {
3+
"LogLevel": {
4+
"Default": "Information",
5+
"Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware": "Warning",
6+
"Microsoft.AspNetCore": "Warning",
7+
"Microsoft.EntityFrameworkCore": "Warning"
8+
}
9+
},
10+
"AllowedHosts": "*"
11+
}

0 commit comments

Comments
 (0)