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
2 changes: 2 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ name: Build and Test Spring Boot HATEOAS
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
workflow_dispatch:

jobs:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ target/
.mvn/
mvnw
mvnw.cmd
bin/

### STS ###
.apt_generated
Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@

Demonstração do uso de HATEOAS para um projeto Springboot REST

## Frontend

### Home Message

* Indice

http://localhost:8080/

## Endpoints API

### Home Message
Expand Down
6 changes: 6 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@
<scope>runtime</scope>
</dependency>

<!-- Fronend Thymeleaf - Para demonstrar paginação (opcional) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
Expand Down
12 changes: 6 additions & 6 deletions src/main/java/com/pro/api/HomeApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public class HomeApi {

@GetMapping({ "", "/", "/index" })
public EntityModel<HomeDTO> index() {
HomeDTO homeDto = new HomeDTO("Springboot HATEOAS Home | Index");
HomeDTO homeDto = new HomeDTO("Springboot HATEOAS API Home | Index");

return EntityModel.of(homeDto,
WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(HomeApi.class).index()).withSelfRel());
Expand Down Expand Up @@ -69,10 +69,10 @@ public CollectionModel<EntityModel<HomeDTO>> getAllMessagesAssembler() {
}

@GetMapping("/messages/pag")
public CollectionModel<EntityModel<HomeDTO>> getAllMessages(@RequestParam int page, @RequestParam int size) {
public CollectionModel<EntityModel<HomeDTO>> getAllMessages(@RequestParam String searchText, @RequestParam int page, @RequestParam int size) {
Pageable pageable = PageRequest.of(page, size);

Page<HomeDTO> pageResult = homeService.getAllMessages(pageable);
Page<HomeDTO> pageResult = homeService.getAllMessages(pageable, searchText);

List<EntityModel<HomeDTO>> userResources = pageResult.stream().map(homeDTOAssembler::toModel)
.collect(Collectors.toList());
Expand All @@ -81,15 +81,15 @@ public CollectionModel<EntityModel<HomeDTO>> getAllMessages(@RequestParam int pa

//lembrar de adicionar o self a paginação tambem
collectionModel.add(WebMvcLinkBuilder.linkTo(
WebMvcLinkBuilder.methodOn(HomeApi.class).getAllMessages(page, size)).withSelfRel());
WebMvcLinkBuilder.methodOn(HomeApi.class).getAllMessages(searchText, page, size)).withSelfRel());

if (pageResult.hasNext()) {
collectionModel.add(WebMvcLinkBuilder.linkTo(
WebMvcLinkBuilder.methodOn(HomeApi.class).getAllMessages(page + 1, size)).withRel("next"));
WebMvcLinkBuilder.methodOn(HomeApi.class).getAllMessages(searchText, page + 1, size)).withRel("next"));
}
if (pageResult.hasPrevious()) {
collectionModel.add(WebMvcLinkBuilder.linkTo(
WebMvcLinkBuilder.methodOn(HomeApi.class).getAllMessages(page - 1, size)).withRel("prev"));
WebMvcLinkBuilder.methodOn(HomeApi.class).getAllMessages(searchText, page - 1, size)).withRel("prev"));
}

return collectionModel;
Expand Down
53 changes: 53 additions & 0 deletions src/main/java/com/pro/controller/HomeController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.pro.controller;

import java.util.Map;

import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.client.RestTemplate;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Controller
@RequestMapping({ "", "/" })
public class HomeController {

private final RestTemplate restTemplate;

public HomeController(RestTemplateBuilder builder) {
this.restTemplate = builder.build();
}

@RequestMapping(value = { "", "/", "/home", "/index" }, method = { RequestMethod.GET, RequestMethod.POST })
public String index(Model model,
@RequestParam(defaultValue = "") String searchText,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "3") int size) {
log.info("----- Springboot HATEOAS Controller Home | Index -----");

String url = "http://localhost:8080/api/messages/pag?page="+ page + "&size=" + size + "&searchText=" + searchText;

// if (searchText != null && !searchText.isEmpty()) {
// url += "&searchText=" + searchText;
// }

ParameterizedTypeReference<Map<String, Object>> typeRef = new ParameterizedTypeReference<Map<String, Object>>() {};

ResponseEntity<Map<String, Object>> response = restTemplate.exchange(url, HttpMethod.GET ,null ,typeRef);

Map<String, Object> body = response.getBody();

model.addAttribute("apiResponse", body);
model.addAttribute("searchText", searchText);
model.addAttribute("index", true);
return "web/home/home";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@ public HomeCollectionAssembler(HomeDTOAssembler homeDTOAssembler) {
this.homeDTOAssembler = homeDTOAssembler;
}

public CollectionModel<EntityModel<HomeDTO>> toCollectionModel(Iterable<? extends HomeDTO> entities, int page, int size) {
public CollectionModel<EntityModel<HomeDTO>> toCollectionModel(Iterable<? extends HomeDTO> entities, String searchText, int page, int size) {
List<EntityModel<HomeDTO>> entityModels = StreamSupport.stream(entities.spliterator(), false)
.map(homeDTOAssembler::toModel) // Usa o assembler do item para cada item
.collect(Collectors.toList());

// Link 'self' para a coleção de recursos
return CollectionModel.of(entityModels,
WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(HomeApi.class).getAllMessages(page, size)).withSelfRel());
WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(HomeApi.class).getAllMessages(searchText, page, size)).withSelfRel());
}
}
43 changes: 17 additions & 26 deletions src/main/java/com/pro/service/HomeService.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
Expand All @@ -15,15 +16,22 @@ public class HomeService {

private List<HomeDTO> homeDtoList;

public HomeService() {
init();
}
// public HomeService() {
// init();
// }

public List<HomeDTO> getAllMessages() {
return homeDtoList;
}

public Page<HomeDTO> getAllMessages(Pageable pageable) {
public Page<HomeDTO> getAllMessages(Pageable pageable, String searchText) {
init();
if (searchText != null && !searchText.isEmpty()) {
homeDtoList = homeDtoList.stream()
.filter(homeDTO -> homeDTO.getMessage().toLowerCase().contains(searchText.toLowerCase()))
.collect(Collectors.toList());
}

int start = (int) pageable.getOffset();
int end = Math.min((start + pageable.getPageSize()), homeDtoList.size());

Expand All @@ -34,27 +42,10 @@ public Page<HomeDTO> getAllMessages(Pageable pageable) {

private void init() {
homeDtoList = new ArrayList<HomeDTO>();

HomeDTO m1 = new HomeDTO("mensagem 1");
HomeDTO m2 = new HomeDTO("mensagem 2");
HomeDTO m3 = new HomeDTO("mensagem 3");
HomeDTO m4 = new HomeDTO("mensagem 4");
HomeDTO m5 = new HomeDTO("mensagem 5");
HomeDTO m6 = new HomeDTO("mensagem 6");
HomeDTO m7 = new HomeDTO("mensagem 7");
HomeDTO m8 = new HomeDTO("mensagem 8");
HomeDTO m9 = new HomeDTO("mensagem 9");
HomeDTO m10 = new HomeDTO("mensagem 10");

homeDtoList.add(m1);
homeDtoList.add(m2);
homeDtoList.add(m3);
homeDtoList.add(m4);
homeDtoList.add(m5);
homeDtoList.add(m6);
homeDtoList.add(m7);
homeDtoList.add(m8);
homeDtoList.add(m9);
homeDtoList.add(m10);

for (int i=1; i<=10; i++) {
HomeDTO m = new HomeDTO("mensagem " + i);
homeDtoList.add(m);
}
}
}
89 changes: 89 additions & 0 deletions src/main/resources/templates/web/home/home-old.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
<meta name="description" content="Pagina Inicial da aplicação">
<meta name="keywords" content="pagina-inicial, pagina-home, inicio, home, inicial, index, ...">
<meta name="author" content="Dev Academy">
<meta name="revisit-after" content="60 days">
<meta name="ROBOTS" content="INDEX, FOLLOW">
<link rel="icon" th:href="@{/ico/favicon.ico}">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Home | Inicio</title>
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css" rel="stylesheet">
<style>
.body-align {
/* border: 2px solid #000000; */
padding-top: 2%;
padding-left: 5%;
padding-right: 5%;
text-align: center;
}
</style>
</head>
<body class="w3-light-grey">

<div class="w3-container w3-blue w3-padding">
<h2>Home | Início</h2>
</div>

<div class="w3-container w3-margin-top" style="min-height: 400px;">
<!-- Card principal -->
<div class="w3-card w3-white w3-padding w3-round-large">
<h3 class="w3-text-dark-grey">Lista de Mensagens</h3>

<!-- 🔎 Formulário de pesquisa -->
<form class="w3-row w3-margin-bottom"
th:action="@{/home}" method="get">
<div class="w3-col s9">
<input class="w3-input w3-border w3-round"
type="text"
name="searchText"
th:value="${searchText}"
placeholder="Digite o texto para buscar..."/>
</div>
<div class="w3-col s3">
<button class="w3-button w3-blue w3-round w3-block"
type="submit">
Buscar
</button>
</div>
</form>

<!-- 📄 Lista de mensagens -->
<ul class="w3-ul w3-hoverable w3-card-4 w3-round">
<li class="w3-padding"
th:each="msg : ${apiResponse['_embedded']['homeDTOList']}">
<span class="w3-text-grey"
th:text="${msg['message']}">
</span>
</li>
</ul>

<!-- ⚠ Caso não tenha mensagens -->
<div class="w3-panel w3-yellow w3-round w3-margin-top"
th:if="${#lists.isEmpty(apiResponse['_embedded']['homeDTOList'])}">
Nenhuma mensagem encontrada.
</div>

<!-- 🔁 Paginação -->
<div class="w3-center w3-margin-top">
<a class="w3-button w3-light-grey w3-round w3-margin-right"
th:if="${apiResponse['_links']['prev'] != null}"
th:href="@{/home(page=${apiResponse['_links']['prev']['href'].split('page=')[1].split('&')[0]}, searchText=${searchText})}">
◀ Anterior
</a>

<a class="w3-button w3-blue w3-round"
th:if="${apiResponse['_links']['next'] != null}"
th:href="@{/home(page=${apiResponse['_links']['next']['href'].split('page=')[1].split('&')[0]}, searchText=${searchText})}">
Próxima ▶
</a>
</div>
</div>
</div>
</body>
</html>
Loading