Skip to content

Commit e3233f0

Browse files
committed
docs: benahi swagger preparation agar nantinya bisa generate contract dengan example dan security scheme yang benar
1 parent 483a852 commit e3233f0

File tree

5 files changed

+132
-55
lines changed

5 files changed

+132
-55
lines changed

Inv_Backend_NET/Fitur/Autentikasi/Login/LoginController.cs

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@
22
using Inventory_Backend_NET.Database;
33
using Inventory_Backend_NET.Fitur._Logic.Services;
44
using Inventory_Backend_NET.Fitur.Autentikasi._Dto.Response;
5+
using JetBrains.Annotations;
56
using Microsoft.AspNetCore.Authorization;
67
using Microsoft.AspNetCore.Mvc;
8+
using Swashbuckle.AspNetCore.Annotations;
9+
using Swashbuckle.AspNetCore.Filters;
710

811
namespace Inventory_Backend_NET.Fitur.Autentikasi.Login
912
{
@@ -22,7 +25,11 @@ IJwtTokenService jwtTokenService
2225

2326
[HttpPost]
2427
[AllowAnonymous]
25-
public IActionResult Login([FromBody] LoginBodyDto user)
28+
[SwaggerResponse(StatusCodes.Status200OK, "Login Sukses", typeof(LoginSucceedResponse))]
29+
[SwaggerResponseExample(StatusCodes.Status200OK, typeof(LoginSucceedResponseExample))]
30+
[SwaggerResponse(StatusCodes.Status400BadRequest, "Login gagal", typeof(LoginFailedResponse))]
31+
[SwaggerResponseExample(StatusCodes.Status400BadRequest, typeof(LoginFailedResponseExample))]
32+
public IActionResult Login([FromBody] LoginRequest user)
2633
{
2734
var currentUser = _db.Users.FirstOrDefault(
2835
predicate: aUser =>
@@ -32,28 +39,56 @@ public IActionResult Login([FromBody] LoginBodyDto user)
3239

3340
if (currentUser == null)
3441
{
35-
return BadRequest(new { message = "Username atau password salah" });
42+
return BadRequest(new LoginFailedResponse(Message: "Username atau password salah" ));
3643
} else
3744
{
38-
return Ok(new
39-
{
40-
message = "Sukses",
41-
token = _jwtTokenService.GenerateNewToken(currentUser),
42-
user = GetUserDto.From(currentUser)
43-
});
45+
return Ok(new LoginSucceedResponse
46+
(
47+
Message: "Sukses",
48+
Token: _jwtTokenService.GenerateNewToken(currentUser),
49+
User: GetUserDto.From(currentUser)
50+
));
4451
}
4552
}
4653

4754

4855
}
56+
57+
internal record LoginSucceedResponse(
58+
[property: JsonPropertyName("token")][UsedImplicitly] string Token,
59+
[property: JsonPropertyName("user")][UsedImplicitly] GetUserDto User,
60+
[property: JsonPropertyName("message")][UsedImplicitly] string Message);
61+
62+
internal record LoginFailedResponse(
63+
[property: JsonPropertyName("message")][UsedImplicitly] string Message);
4964

50-
public class LoginBodyDto
65+
public class LoginRequest
5166
{
5267
[JsonPropertyName("username")]
5368
public string Username { get; set; } = null!;
5469

5570
[JsonPropertyName("password")]
5671
public string Password { get; set; } = null!;
5772
}
73+
74+
file class LoginSucceedResponseExample : IExamplesProvider<LoginSucceedResponse>
75+
{
76+
public LoginSucceedResponse GetExamples()
77+
{
78+
return new LoginSucceedResponse(
79+
Token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1laWRlbnRpZmllciI6ImFkbWluIiwiaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS93cy8yMDA4LzA2L2lkZW50aXR5L2NsYWltcy9yb2xlIjoiQWRtaW4iLCJleHAiOjE3NjgyNzE0MDEsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6NTE1NCIsImF1ZCI6Imh0dHA6Ly9sb2NhbGhvc3Q6MzAwMCJ9.H46pIQejekT9s-GuXsEauIqGjK2zu2vitYyWAhLRu8Y",
80+
User: new GetUserDto(id: 1, username: "admin", isAdmin: true),
81+
Message: "Sukses"
82+
);
83+
}
84+
}
85+
86+
file class LoginFailedResponseExample : IExamplesProvider<LoginFailedResponse>
87+
{
88+
public LoginFailedResponse GetExamples()
89+
{
90+
return new LoginFailedResponse(Message: "Username atau password salah");
91+
}
92+
}
5893
}
5994

Inv_Backend_NET/Inventory_Backend_NET.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
<PackageReference Include="CSVHelper" Version="30.0.1" />
1313
<PackageReference Include="dotenv.net" Version="3.1.3" />
1414
<PackageReference Include="EntityFrameworkCore.Exceptions.SqlServer" Version="8.1.3" />
15+
<PackageReference Include="JetBrains.Annotations" Version="2025.2.4" />
1516
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.0" />
1617
<PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="8.0.0" />
1718
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.13" />
@@ -25,6 +26,7 @@
2526
<PackageReference Include="Serilog" Version="4.2.0" />
2627
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
2728
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
29+
<PackageReference Include="Swashbuckle.AspNetCore.Filters" Version="6.1.0" />
2830
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="7.0.3" />
2931
</ItemGroup>
3032

Inv_Backend_NET/Program.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public static void Main(string[] args)
1313

1414
builder
1515
.PrepareDependencyInjectionServices()
16-
.PrepareSwaggerWithJwtInputService()
16+
.PrepareSwagger()
1717
.PrepareAuthenticationServices()
1818
.PrepareCorsServices()
1919
.PrepareMonitongServices()
@@ -27,11 +27,11 @@ public static void Main(string[] args)
2727
var containsSeederKeyword = app.HandleSeedingCommandFromCli(args: args);
2828
if (containsSeederKeyword) return;
2929

30-
if (app.Environment.IsDevelopment())
30+
if (app.Environment.IsEnvironment("Local"))
3131
{
32-
app.UseSwagger();
3332
app.UseSwaggerUI();
3433
}
34+
app.UseSwagger();
3535
app.UseWebSockets();
3636
app.UseHttpsRedirection();
3737
app.UseCors();
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
using System.Reflection;
2+
using JetBrains.Annotations;
3+
using Microsoft.AspNetCore.Authentication.JwtBearer;
4+
using Microsoft.AspNetCore.Authorization;
5+
using Microsoft.AspNetCore.Mvc.Controllers;
6+
using Microsoft.OpenApi.Models;
7+
using Swashbuckle.AspNetCore.Filters;
8+
using Swashbuckle.AspNetCore.SwaggerGen;
9+
10+
namespace Inventory_Backend_NET.Startup;
11+
12+
public static class PrepareSwaggerExtension
13+
{
14+
public static WebApplicationBuilder PrepareSwagger(this WebApplicationBuilder builder)
15+
{
16+
builder.Services.AddSwaggerGen(option =>
17+
{
18+
option.SwaggerDoc("v1", new OpenApiInfo
19+
{
20+
Version = "1.0.0",
21+
Title = "Inventory System API Specification"
22+
});
23+
24+
option.AddSecurityDefinition(name: JwtBearerDefaults.AuthenticationScheme , new OpenApiSecurityScheme
25+
{
26+
In = ParameterLocation.Header,
27+
Description = "Masukkan token",
28+
Name = "Authorization",
29+
Type = SecuritySchemeType.Http,
30+
BearerFormat = "JWT",
31+
Scheme = JwtBearerDefaults.AuthenticationScheme
32+
});
33+
option.OperationFilter<AllowAnonymousOperationFilter>();
34+
35+
option.EnableAnnotations();
36+
option.ExampleFilters();
37+
option.SupportNonNullableReferenceTypes();
38+
});
39+
40+
builder.Services.AddSwaggerExamplesFromAssemblyOf<Program>();
41+
42+
return builder;
43+
}
44+
45+
46+
[UsedImplicitly]
47+
private class AllowAnonymousOperationFilter : IOperationFilter
48+
{
49+
public void Apply(OpenApiOperation operation, OperationFilterContext context)
50+
{
51+
var methodAttributes = context.ApiDescription.CustomAttributes().ToArray();
52+
var controllerAttributes = (context.ApiDescription.ActionDescriptor as ControllerActionDescriptor)
53+
?.ControllerTypeInfo
54+
.GetCustomAttributes()
55+
.ToArray();
56+
57+
bool methodAllowAnonymous = methodAttributes.Any(attr => attr is AllowAnonymousAttribute);
58+
bool controllerAllowAnonymous = controllerAttributes?.Any(attr => attr is AllowAnonymousAttribute) == true;
59+
60+
if (methodAllowAnonymous || controllerAllowAnonymous)
61+
return;
62+
63+
operation.Security =
64+
[
65+
new OpenApiSecurityRequirement
66+
{
67+
{
68+
new OpenApiSecurityScheme
69+
{
70+
Reference = new OpenApiReference
71+
{
72+
Type = ReferenceType.SecurityScheme,
73+
Id = JwtBearerDefaults.AuthenticationScheme // sesuaikan sama cheme di security definition
74+
}
75+
},
76+
[]
77+
}
78+
}
79+
];
80+
}
81+
}
82+
83+
}

Inv_Backend_NET/Startup/PrepareSwaggerWithJwtInput.cs

Lines changed: 0 additions & 43 deletions
This file was deleted.

0 commit comments

Comments
 (0)