docs(api): add API documentation
This commit is contained in:
parent
d417a46a06
commit
fb0ddf391f
@ -26,13 +26,6 @@
|
|||||||
<scope>runtime</scope>
|
<scope>runtime</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- Swagger/OpenAPI -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springdoc</groupId>
|
|
||||||
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
|
||||||
<version>${springdoc.version}</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.slf4j</groupId>
|
<groupId>org.slf4j</groupId>
|
||||||
<artifactId>slf4j-api</artifactId>
|
<artifactId>slf4j-api</artifactId>
|
||||||
|
@ -74,6 +74,14 @@
|
|||||||
<version>1.39.0</version>
|
<version>1.39.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Swagger/OpenAPI -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springdoc</groupId>
|
||||||
|
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
||||||
|
<version>${springdoc.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.google.apis</groupId>
|
<groupId>com.google.apis</groupId>
|
||||||
<artifactId>google-api-services-gmail</artifactId>
|
<artifactId>google-api-services-gmail</artifactId>
|
||||||
|
@ -0,0 +1,46 @@
|
|||||||
|
package com.pablotj.restemailbridge.infrastructure.config;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.models.OpenAPI;
|
||||||
|
import io.swagger.v3.oas.models.info.Contact;
|
||||||
|
import io.swagger.v3.oas.models.info.Info;
|
||||||
|
import io.swagger.v3.oas.models.info.License;
|
||||||
|
import io.swagger.v3.oas.models.parameters.Parameter;
|
||||||
|
import io.swagger.v3.oas.models.media.StringSchema;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springdoc.core.customizers.OpenApiCustomizer;
|
||||||
|
@Configuration
|
||||||
|
public class OpenApiConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public OpenAPI customOpenAPI() {
|
||||||
|
return new OpenAPI()
|
||||||
|
.info(new Info()
|
||||||
|
.title("Rest Email Bridge API")
|
||||||
|
.version("v1")
|
||||||
|
.description("API for sending and managing emails")
|
||||||
|
.license(new License().name("Apache 2.0").url("https://www.apache.org/licenses/LICENSE-2.0"))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public OpenApiCustomizer globalHeaderCustomizer() {
|
||||||
|
return openApi -> openApi.getPaths().values().forEach(pathItem ->
|
||||||
|
pathItem.readOperations().forEach(operation ->
|
||||||
|
operation.addParametersItem(
|
||||||
|
new Parameter()
|
||||||
|
.in("header")
|
||||||
|
.name("Accept-Language")
|
||||||
|
.description("Language for messages (en, es, gl)")
|
||||||
|
.required(false)
|
||||||
|
.schema(new StringSchema()
|
||||||
|
._default("en")
|
||||||
|
.addEnumItem("en")
|
||||||
|
.addEnumItem("es")
|
||||||
|
.addEnumItem("gl"))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,13 @@ package com.pablotj.restemailbridge.infrastructure.rest;
|
|||||||
import com.pablotj.restemailbridge.application.dto.EmailDTO;
|
import com.pablotj.restemailbridge.application.dto.EmailDTO;
|
||||||
import com.pablotj.restemailbridge.application.usecase.SendEmailUseCase;
|
import com.pablotj.restemailbridge.application.usecase.SendEmailUseCase;
|
||||||
import com.pablotj.restemailbridge.infrastructure.rest.dto.SendMailRequest;
|
import com.pablotj.restemailbridge.infrastructure.rest.dto.SendMailRequest;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Content;
|
||||||
|
import io.swagger.v3.oas.annotations.media.ExampleObject;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||||
|
import io.swagger.v3.oas.annotations.responses.ApiResponses;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import jakarta.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
@ -10,18 +17,76 @@ import org.springframework.web.bind.annotation.RequestBody;
|
|||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REST controller responsible for handling email-related requests.
|
||||||
|
* <p>
|
||||||
|
* Exposes endpoints under {@code /v1/mail} to send emails through the system.
|
||||||
|
* Delegates business logic to the {@link SendEmailUseCase}.
|
||||||
|
*/
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/v1/mail")
|
@RequestMapping("/v1/mail")
|
||||||
|
@Tag(name = "Mail API", description = "Endpoints for sending emails")
|
||||||
public class MailController {
|
public class MailController {
|
||||||
|
|
||||||
private final SendEmailUseCase sendEmailUseCase;
|
private final SendEmailUseCase sendEmailUseCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@link MailController} instance.
|
||||||
|
*
|
||||||
|
* @param sendEmailUseCase the use case responsible for sending emails
|
||||||
|
*/
|
||||||
public MailController(SendEmailUseCase sendEmailUseCase) {
|
public MailController(SendEmailUseCase sendEmailUseCase) {
|
||||||
this.sendEmailUseCase = sendEmailUseCase;
|
this.sendEmailUseCase = sendEmailUseCase;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a new email using the provided request data.
|
||||||
|
* <p>
|
||||||
|
* The request payload is validated using {@link jakarta.validation.Valid}.
|
||||||
|
*
|
||||||
|
* @param request the email request containing sender, subject and body
|
||||||
|
* @return {@link ResponseEntity} with HTTP 200 (OK) if the email is sent successfully
|
||||||
|
*/
|
||||||
@PostMapping
|
@PostMapping
|
||||||
|
@Operation(
|
||||||
|
summary = "Send an email",
|
||||||
|
description = "Sends an email using the provided sender, subject, and body.",
|
||||||
|
requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(
|
||||||
|
required = true,
|
||||||
|
content = @Content(
|
||||||
|
mediaType = "application/json",
|
||||||
|
examples = @ExampleObject(
|
||||||
|
name = "Basic email",
|
||||||
|
value = "{ \"from\": \"user@example.com\", \"subject\": \"Hello\", \"body\": \"Hi there!\" }"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
@ApiResponses(value = {
|
||||||
|
@ApiResponse(responseCode = "200", description = "Email sent successfully"),
|
||||||
|
@ApiResponse(responseCode = "400", description = "Invalid request payload"),
|
||||||
|
@ApiResponse(
|
||||||
|
responseCode = "401",
|
||||||
|
description = "Unauthorized – missing or invalid authentication token",
|
||||||
|
content = @Content(schema = @Schema(hidden = true))
|
||||||
|
),
|
||||||
|
@ApiResponse(
|
||||||
|
responseCode = "403",
|
||||||
|
description = "Forbidden – the authenticated user cannot send to the specified recipient",
|
||||||
|
content = @Content(schema = @Schema(hidden = true))
|
||||||
|
),
|
||||||
|
@ApiResponse(
|
||||||
|
responseCode = "422",
|
||||||
|
description = "Unprocessable Entity – domain validation failed (e.g. invalid email address, business rule violation)",
|
||||||
|
content = @Content(
|
||||||
|
mediaType = "application/json",
|
||||||
|
schema = @Schema(
|
||||||
|
example = "{ \"error\": \"Invalid recipient domain\" }"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
@ApiResponse(responseCode = "500", description = "Unexpected server error")
|
||||||
|
})
|
||||||
public ResponseEntity<Void> send(@Valid @RequestBody SendMailRequest request) {
|
public ResponseEntity<Void> send(@Valid @RequestBody SendMailRequest request) {
|
||||||
sendEmailUseCase.handle(new EmailDTO(request.from(), request.subject(), request.body()));
|
sendEmailUseCase.handle(new EmailDTO(request.from(), request.subject(), request.body()));
|
||||||
return ResponseEntity.ok().build();
|
return ResponseEntity.ok().build();
|
||||||
|
@ -1,12 +1,26 @@
|
|||||||
package com.pablotj.restemailbridge.infrastructure.rest.dto;
|
package com.pablotj.restemailbridge.infrastructure.rest.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import jakarta.validation.constraints.Email;
|
import jakarta.validation.constraints.Email;
|
||||||
import jakarta.validation.constraints.NotBlank;
|
import jakarta.validation.constraints.NotBlank;
|
||||||
import org.hibernate.validator.constraints.Length;
|
import org.hibernate.validator.constraints.Length;
|
||||||
|
|
||||||
|
@Schema(description = "Request payload to send an email")
|
||||||
public record SendMailRequest(
|
public record SendMailRequest(
|
||||||
@NotBlank @Email @Length(min = 4, max = 100) String from,
|
@NotBlank(message = "{email.from.blank}")
|
||||||
@NotBlank @Length(min=1, max = 30) String subject,
|
@Email(message = "{email.from.invalid}")
|
||||||
@NotBlank @Length(min=1, max = 4000) String body
|
@Length(min = 4, max = 100, message = "email.from.length}")
|
||||||
|
@Schema(description = "Sender email address", example = "user@example.com")
|
||||||
|
String from,
|
||||||
|
|
||||||
|
@NotBlank(message = "{email.subject.blank}")
|
||||||
|
@Length(min=1, max = 30, message = "{email.subject.length}")
|
||||||
|
@Schema(description = "Email subject", example = "Welcome to RestEmailBridge")
|
||||||
|
String subject,
|
||||||
|
|
||||||
|
@NotBlank(message = "{email.body.blank}")
|
||||||
|
@Length(min=1, max = 4000, message = "{email.body.length}")
|
||||||
|
@Schema(description = "Email body content", example = "Hello, thanks for signing up!")
|
||||||
|
String body
|
||||||
) {
|
) {
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user