├── .mvn └── wrapper │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── docs ├── Slide API RESTful com Spring Boot.pdf ├── Ferramentas e configuração do ambiente.pdf └── ToDo Simple App.postman_collection.json ├── .dockerignore ├── .vscode ├── settings.json └── extensions.json ├── .env ├── src ├── main │ ├── java │ │ └── com │ │ │ └── lucasangelo │ │ │ └── todosimple │ │ │ ├── models │ │ │ ├── projection │ │ │ │ └── TaskProjection.java │ │ │ ├── dto │ │ │ │ ├── UserUpdateDTO.java │ │ │ │ └── UserCreateDTO.java │ │ │ ├── enums │ │ │ │ └── ProfileEnum.java │ │ │ ├── Task.java │ │ │ └── User.java │ │ │ ├── TodosimpleApplication.java │ │ │ ├── repositories │ │ │ ├── UserRepository.java │ │ │ └── TaskRepository.java │ │ │ ├── services │ │ │ ├── exceptions │ │ │ │ ├── AuthorizationException.java │ │ │ │ ├── ObjectNotFoundException.java │ │ │ │ └── DataBindingViolationException.java │ │ │ ├── UserDetailsServiceImpl.java │ │ │ ├── TaskService.java │ │ │ └── UserService.java │ │ │ ├── configs │ │ │ ├── WebConfig.java │ │ │ └── SecurityConfig.java │ │ │ ├── exceptions │ │ │ ├── ErrorResponse.java │ │ │ └── GlobalExceptionHandler.java │ │ │ ├── security │ │ │ ├── UserSpringSecurity.java │ │ │ ├── JWTUtil.java │ │ │ ├── JWTAuthorizationFilter.java │ │ │ └── JWTAuthenticationFilter.java │ │ │ └── controllers │ │ │ ├── UserController.java │ │ │ └── TaskController.java │ └── resources │ │ ├── application-prod.properties │ │ ├── application-dev.properties │ │ └── application.properties └── test │ └── java │ └── com │ └── lucasangelo │ └── todosimple │ └── TodosimpleApplicationTests.java ├── .gitignore ├── Dockerfile ├── CITATION.cff ├── script.sh ├── view ├── scripts │ ├── signup.js │ ├── login.js │ └── script.js ├── index.html ├── login.html └── signup.html ├── docker-compose.yml ├── .github └── workflows │ └── deploy.yml ├── pom.xml ├── README.md ├── mvnw.cmd └── mvnw /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lucas-Angelo/todosimple-api/HEAD/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /docs/Slide API RESTful com Spring Boot.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lucas-Angelo/todosimple-api/HEAD/docs/Slide API RESTful com Spring Boot.pdf -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | **/target/ 2 | **/docs/ 3 | **/view/ 4 | **/.git 5 | **/README.md 6 | **/CITATION.cff 7 | **/LICENSE 8 | **/.vscode 9 | **/.editorconfig -------------------------------------------------------------------------------- /docs/Ferramentas e configuração do ambiente.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lucas-Angelo/todosimple-api/HEAD/docs/Ferramentas e configuração do ambiente.pdf -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "terminal.integrated.defaultProfile.windows": "Command Prompt", 3 | "java.configuration.updateBuildConfiguration": "interactive" 4 | } 5 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | MYSQLDB_USER=root 2 | MYSQLDB_ROOT_PASSWORD=root 3 | MYSQLDB_DATABASE=todosimple 4 | MYSQLDB_LOCAL_PORT=3307 5 | MYSQLDB_DOCKER_PORT=3306 6 | 7 | SPRING_LOCAL_PORT=8080 8 | SPRING_DOCKER_PORT=8080 -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "vscjava.vscode-java-pack", 4 | "vmware.vscode-boot-dev-pack", 5 | "sohibe.java-generate-setters-getters", 6 | "ritwickdey.liveserver" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.6/apache-maven-3.8.6-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar 3 | -------------------------------------------------------------------------------- /src/main/java/com/lucasangelo/todosimple/models/projection/TaskProjection.java: -------------------------------------------------------------------------------- 1 | package com.lucasangelo.todosimple.models.projection; 2 | 3 | public interface TaskProjection { 4 | 5 | public Long getId(); 6 | 7 | public String getDescription(); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/main/resources/application-prod.properties: -------------------------------------------------------------------------------- 1 | # Database config 2 | spring.datasource.url=${SPRING_DATASOURCE_URL} 3 | spring.datasource.username=${SPRING_DATASOURCE_USERNAME} 4 | spring.datasource.password=${SPRING_DATASOURCE_PASSWORD} 5 | spring.jpa.hibernate.ddl-auto=update 6 | spring.jpa.show-sql=false -------------------------------------------------------------------------------- /src/main/resources/application-dev.properties: -------------------------------------------------------------------------------- 1 | # Database config 2 | spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver 3 | spring.datasource.url=jdbc:mysql://localhost:3306/todosimple?useSSL=false&createDatabaseIfNotExist=true 4 | spring.datasource.username=root 5 | spring.datasource.password=root 6 | -------------------------------------------------------------------------------- /src/test/java/com/lucasangelo/todosimple/TodosimpleApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.lucasangelo.todosimple; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class TodosimpleApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/lucasangelo/todosimple/TodosimpleApplication.java: -------------------------------------------------------------------------------- 1 | package com.lucasangelo.todosimple; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class TodosimpleApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(TodosimpleApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM maven:3.8.3-openjdk-17 2 | 3 | ENV PROJECT_HOME /usr/src/todosimpleapp 4 | ENV JAR_NAME todosimpleapp.jar 5 | 6 | # Create destination directory 7 | RUN mkdir -p $PROJECT_HOME 8 | WORKDIR $PROJECT_HOME 9 | 10 | # Bundle app source 11 | COPY . . 12 | 13 | # Package the application as a JAR file 14 | RUN mvn clean package -DskipTests 15 | 16 | # Move file 17 | RUN mv $PROJECT_HOME/target/$JAR_NAME $PROJECT_HOME/ 18 | 19 | ENTRYPOINT ["java", "-jar", "-Dspring.profiles.active=prod", "todosimpleapp.jar"] -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.profiles.active=dev 2 | 3 | spring.output.ansi.enabled=ALWAYS 4 | 5 | # Database config 6 | spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver 7 | spring.datasource.url= 8 | spring.datasource.username= 9 | spring.datasource.password= 10 | spring.jpa.hibernate.ddl-auto=update 11 | spring.jpa.show-sql=true 12 | 13 | server.error.include-exception=true 14 | 15 | jwt.secret=yg0y7v9uSsN06Hyr1uMb0FNH6BoXZWPIIbiyHkWLGgjIpB2MrTLiCrUmDGEw5Bn 16 | jwt.expiration=86400000 -------------------------------------------------------------------------------- /src/main/java/com/lucasangelo/todosimple/models/dto/UserUpdateDTO.java: -------------------------------------------------------------------------------- 1 | package com.lucasangelo.todosimple.models.dto; 2 | 3 | import javax.validation.constraints.NotBlank; 4 | import javax.validation.constraints.Size; 5 | 6 | import lombok.AllArgsConstructor; 7 | import lombok.Data; 8 | import lombok.NoArgsConstructor; 9 | 10 | @AllArgsConstructor 11 | @NoArgsConstructor 12 | @Data 13 | public class UserUpdateDTO { 14 | 15 | private Long id; 16 | 17 | @NotBlank 18 | @Size(min = 8, max = 60) 19 | private String password; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/lucasangelo/todosimple/repositories/UserRepository.java: -------------------------------------------------------------------------------- 1 | package com.lucasangelo.todosimple.repositories; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | import org.springframework.stereotype.Repository; 5 | import org.springframework.transaction.annotation.Transactional; 6 | 7 | import com.lucasangelo.todosimple.models.User; 8 | 9 | @Repository 10 | public interface UserRepository extends JpaRepository { 11 | 12 | @Transactional(readOnly = true) 13 | User findByUsername(String username); 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/lucasangelo/todosimple/services/exceptions/AuthorizationException.java: -------------------------------------------------------------------------------- 1 | package com.lucasangelo.todosimple.services.exceptions; 2 | 3 | import org.springframework.http.HttpStatus; 4 | import org.springframework.security.access.AccessDeniedException; 5 | import org.springframework.web.bind.annotation.ResponseStatus; 6 | 7 | @ResponseStatus(value = HttpStatus.FORBIDDEN) 8 | public class AuthorizationException extends AccessDeniedException { 9 | 10 | public AuthorizationException(String message) { 11 | super(message); 12 | } 13 | 14 | } -------------------------------------------------------------------------------- /src/main/java/com/lucasangelo/todosimple/services/exceptions/ObjectNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.lucasangelo.todosimple.services.exceptions; 2 | 3 | import javax.persistence.EntityNotFoundException; 4 | 5 | import org.springframework.http.HttpStatus; 6 | import org.springframework.web.bind.annotation.ResponseStatus; 7 | 8 | @ResponseStatus(value = HttpStatus.NOT_FOUND) 9 | public class ObjectNotFoundException extends EntityNotFoundException { 10 | 11 | public ObjectNotFoundException(String message) { 12 | super(message); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/lucasangelo/todosimple/services/exceptions/DataBindingViolationException.java: -------------------------------------------------------------------------------- 1 | package com.lucasangelo.todosimple.services.exceptions; 2 | 3 | import org.springframework.dao.DataIntegrityViolationException; 4 | import org.springframework.http.HttpStatus; 5 | import org.springframework.web.bind.annotation.ResponseStatus; 6 | 7 | @ResponseStatus(value = HttpStatus.NOT_FOUND) 8 | public class DataBindingViolationException extends DataIntegrityViolationException { 9 | 10 | public DataBindingViolationException(String message) { 11 | super(message); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/lucasangelo/todosimple/models/dto/UserCreateDTO.java: -------------------------------------------------------------------------------- 1 | package com.lucasangelo.todosimple.models.dto; 2 | 3 | import javax.validation.constraints.NotBlank; 4 | import javax.validation.constraints.Size; 5 | 6 | import lombok.AllArgsConstructor; 7 | import lombok.Data; 8 | import lombok.NoArgsConstructor; 9 | 10 | @AllArgsConstructor 11 | @NoArgsConstructor 12 | @Data 13 | public class UserCreateDTO { 14 | 15 | @NotBlank 16 | @Size(min = 2, max = 100) 17 | private String username; 18 | 19 | @NotBlank 20 | @Size(min = 8, max = 60) 21 | private String password; 22 | 23 | } 24 | -------------------------------------------------------------------------------- /CITATION.cff: -------------------------------------------------------------------------------- 1 | cff-version: 1.2.0 2 | title: Curso de monitoria ensinando a criar uma API RESTful com Spring Boot 3 | message: >- 4 | If you use this software, please cite it using the 5 | metadata from this file. 6 | type: software 7 | authors: 8 | - given-names: Lucas 9 | family-names: Ângelo Oliveira Martins Rocha 10 | - name-suffix: Professor 11 | given-names: Hugo 12 | family-names: Bastos De Paula 13 | affiliation: PUC Minas 14 | repository-code: >- 15 | https://github.com/ICEI-PUC-Minas-PPLES-TI/PLF-ES-2022-2-MON-CursoAPIJava 16 | keywords: 17 | - spring boot 18 | - monitoria 19 | - curso 20 | - api 21 | license: MIT License 22 | -------------------------------------------------------------------------------- /src/main/java/com/lucasangelo/todosimple/configs/WebConfig.java: -------------------------------------------------------------------------------- 1 | package com.lucasangelo.todosimple.configs; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | import org.springframework.web.servlet.config.annotation.CorsRegistry; 5 | import org.springframework.web.servlet.config.annotation.EnableWebMvc; 6 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 7 | 8 | @Configuration 9 | @EnableWebMvc 10 | public class WebConfig implements WebMvcConfigurer { 11 | 12 | public void addCorsMappings(CorsRegistry registry) { 13 | registry.addMapping("/**") 14 | .allowedOrigins("*") 15 | .allowedHeaders("*") 16 | .allowedMethods("GET", "PUT", "POST", "PATCH", "DELETE", "OPTIONS"); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/lucasangelo/todosimple/models/enums/ProfileEnum.java: -------------------------------------------------------------------------------- 1 | package com.lucasangelo.todosimple.models.enums; 2 | 3 | import java.util.Objects; 4 | 5 | import lombok.AllArgsConstructor; 6 | import lombok.Getter; 7 | 8 | @AllArgsConstructor 9 | @Getter 10 | public enum ProfileEnum { 11 | 12 | ADMIN(1, "ROLE_ADMIN"), 13 | USER(2, "ROLE_USER"); 14 | 15 | private Integer code; 16 | private String description; 17 | 18 | public static ProfileEnum toEnum(Integer code) { 19 | if (Objects.isNull(code)) 20 | return null; 21 | 22 | for (ProfileEnum x : ProfileEnum.values()) { 23 | if (code.equals(x.getCode())) 24 | return x; 25 | } 26 | 27 | throw new IllegalArgumentException("Invalid code: " + code); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/lucasangelo/todosimple/repositories/TaskRepository.java: -------------------------------------------------------------------------------- 1 | package com.lucasangelo.todosimple.repositories; 2 | 3 | import java.util.List; 4 | 5 | import org.springframework.data.jpa.repository.JpaRepository; 6 | import org.springframework.stereotype.Repository; 7 | 8 | import com.lucasangelo.todosimple.models.Task; 9 | import com.lucasangelo.todosimple.models.projection.TaskProjection; 10 | 11 | @Repository 12 | public interface TaskRepository extends JpaRepository { 13 | 14 | List findByUser_Id(Long id); 15 | 16 | // @Query(value = "SELECT t FROM Task t WHERE t.user.id = :id") 17 | // List findByUser_Id(@Param("id") Long id); 18 | 19 | // @Query(value = "SELECT * FROM task t WHERE t.user_id = :id", nativeQuery = 20 | // true) 21 | // List findByUser_Id(@Param("id") Long id); 22 | 23 | } 24 | -------------------------------------------------------------------------------- /script.sh: -------------------------------------------------------------------------------- 1 | # install docker 2 | sudo apt -y update 3 | sudo apt -y install apt-transport-https ca-certificates curl software-properties-common 4 | curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - 5 | sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu focal stable" 6 | sudo apt update 7 | apt-cache policy docker-ce 8 | sudo apt -y install docker-ce 9 | sudo chmod 666 /var/run/docker.sock 10 | docker ps 11 | 12 | # install docker-compose 13 | sudo curl -L "https://github.com/docker/compose/releases/download/1.27.4/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose 14 | sudo chmod +x /usr/local/bin/docker-compose 15 | docker-compose --version 16 | 17 | # key 18 | cd ~/.ssh 19 | ssh-keygen -t rsa -b 4096 -C "your_email@example.com" 20 | ls 21 | cat id_rsa.pub >> ~/.ssh/authorized_keys 22 | cat id_rsa 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /view/scripts/signup.js: -------------------------------------------------------------------------------- 1 | async function signup() { 2 | let username = document.getElementById("username").value; 3 | let password = document.getElementById("password").value; 4 | 5 | console.log(username, password); 6 | 7 | const response = await fetch("http://localhost:8080/user", { 8 | method: "POST", 9 | headers: new Headers({ 10 | "Content-Type": "application/json; charset=utf8", 11 | Accept: "application/json", 12 | }), 13 | body: JSON.stringify({ 14 | username: username, 15 | password: password, 16 | }), 17 | }); 18 | 19 | if (response.ok) { 20 | showToast("#okToast"); 21 | } else { 22 | showToast("#errorToast"); 23 | } 24 | } 25 | 26 | function showToast(id) { 27 | var toastElList = [].slice.call(document.querySelectorAll(id)); 28 | var toastList = toastElList.map(function (toastEl) { 29 | return new bootstrap.Toast(toastEl); 30 | }); 31 | toastList.forEach((toast) => toast.show()); 32 | } 33 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.8" 2 | 3 | services: 4 | mysqldb: 5 | image: mysql:5.7 6 | restart: unless-stopped 7 | env_file: ./.env 8 | environment: 9 | - MYSQL_ROOT_PASSWORD=$MYSQLDB_ROOT_PASSWORD 10 | - MYSQL_DATABASE=$MYSQLDB_DATABASE 11 | ports: 12 | - $MYSQLDB_LOCAL_PORT:$MYSQLDB_DOCKER_PORT 13 | volumes: 14 | - mysqlvolume:/var/lib/mysql 15 | app: 16 | depends_on: 17 | - mysqldb 18 | build: 19 | context: ./ 20 | dockerfile: Dockerfile 21 | working_dir: /usr/src/todosimpleapp 22 | container_name: todosimpleapp 23 | restart: on-failure 24 | env_file: ./.env 25 | ports: 26 | - $SPRING_LOCAL_PORT:$SPRING_DOCKER_PORT 27 | environment: 28 | - SPRING_DATASOURCE_URL=jdbc:mysql://mysqldb:$MYSQLDB_DOCKER_PORT/$MYSQLDB_DATABASE?useSSL=false&createDatabaseIfNotExist=true 29 | - SPRING_DATASOURCE_USERNAME=$MYSQLDB_USER 30 | - SPRING_DATASOURCE_PASSWORD=$MYSQLDB_ROOT_PASSWORD 31 | volumes: 32 | - .m2:/root/.m2 33 | stdin_open: true 34 | tty: true 35 | 36 | volumes: 37 | mysqlvolume: 38 | -------------------------------------------------------------------------------- /view/scripts/login.js: -------------------------------------------------------------------------------- 1 | async function login() { 2 | let username = document.getElementById("username").value; 3 | let password = document.getElementById("password").value; 4 | 5 | console.log(username, password); 6 | 7 | const response = await fetch("http://localhost:8080/login", { 8 | method: "POST", 9 | headers: new Headers({ 10 | "Content-Type": "application/json; charset=utf8", 11 | Accept: "application/json", 12 | }), 13 | body: JSON.stringify({ 14 | username: username, 15 | password: password, 16 | }), 17 | }); 18 | 19 | let key = "Authorization"; 20 | let token = response.headers.get(key); 21 | window.localStorage.setItem(key, token); 22 | 23 | if (response.ok) { 24 | showToast("#okToast"); 25 | } else { 26 | showToast("#errorToast"); 27 | } 28 | 29 | window.setTimeout(function () { 30 | window.location = "/view/index.html"; 31 | }, 2000); 32 | } 33 | 34 | function showToast(id) { 35 | var toastElList = [].slice.call(document.querySelectorAll(id)); 36 | var toastList = toastElList.map(function (toastEl) { 37 | return new bootstrap.Toast(toastEl); 38 | }); 39 | toastList.forEach((toast) => toast.show()); 40 | } 41 | -------------------------------------------------------------------------------- /view/scripts/script.js: -------------------------------------------------------------------------------- 1 | const tasksEndpoint = "http://localhost:8080/task/user"; 2 | 3 | function hideLoader() { 4 | document.getElementById("loading").style.display = "none"; 5 | } 6 | 7 | function show(tasks) { 8 | let tab = ` 9 | # 10 | Description 11 | `; 12 | 13 | for (let task of tasks) { 14 | tab += ` 15 | 16 | ${task.id} 17 | ${task.description} 18 | 19 | `; 20 | } 21 | 22 | document.getElementById("tasks").innerHTML = tab; 23 | } 24 | 25 | async function getTasks() { 26 | let key = "Authorization"; 27 | const response = await fetch(tasksEndpoint, { 28 | method: "GET", 29 | headers: new Headers({ 30 | Authorization: localStorage.getItem(key), 31 | }), 32 | }); 33 | 34 | var data = await response.json(); 35 | console.log(data); 36 | if (response) hideLoader(); 37 | show(data); 38 | } 39 | 40 | document.addEventListener("DOMContentLoaded", function (event) { 41 | if (!localStorage.getItem("Authorization")) 42 | window.location = "/view/login.html"; 43 | }); 44 | 45 | getTasks(); 46 | -------------------------------------------------------------------------------- /src/main/java/com/lucasangelo/todosimple/models/Task.java: -------------------------------------------------------------------------------- 1 | package com.lucasangelo.todosimple.models; 2 | 3 | import javax.persistence.Column; 4 | import javax.persistence.Entity; 5 | import javax.persistence.GeneratedValue; 6 | import javax.persistence.GenerationType; 7 | import javax.persistence.Id; 8 | import javax.persistence.JoinColumn; 9 | import javax.persistence.ManyToOne; 10 | import javax.persistence.Table; 11 | import javax.validation.constraints.NotBlank; 12 | import javax.validation.constraints.Size; 13 | 14 | import lombok.AllArgsConstructor; 15 | import lombok.Data; 16 | import lombok.NoArgsConstructor; 17 | 18 | @Entity 19 | @Table(name = Task.TABLE_NAME) 20 | @AllArgsConstructor 21 | @NoArgsConstructor 22 | @Data 23 | public class Task { 24 | public static final String TABLE_NAME = "task"; 25 | 26 | @Id 27 | @Column(name = "id", unique = true) 28 | @GeneratedValue(strategy = GenerationType.IDENTITY) 29 | private Long id; 30 | 31 | @ManyToOne 32 | @JoinColumn(name = "user_id", nullable = false, updatable = false) 33 | private User user; 34 | 35 | @Column(name = "description", length = 255, nullable = false) 36 | @Size(min = 1, max = 255) 37 | @NotBlank 38 | private String description; 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/lucasangelo/todosimple/exceptions/ErrorResponse.java: -------------------------------------------------------------------------------- 1 | package com.lucasangelo.todosimple.exceptions; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.Objects; 6 | 7 | import com.fasterxml.jackson.annotation.JsonInclude; 8 | 9 | import lombok.Getter; 10 | import lombok.RequiredArgsConstructor; 11 | import lombok.Setter; 12 | 13 | @Getter 14 | @Setter 15 | @RequiredArgsConstructor 16 | @JsonInclude(JsonInclude.Include.NON_NULL) 17 | public class ErrorResponse { 18 | private final int status; 19 | private final String message; 20 | private String stackTrace; 21 | private List errors; 22 | 23 | @Getter 24 | @Setter 25 | @RequiredArgsConstructor 26 | private static class ValidationError { 27 | private final String field; 28 | private final String message; 29 | } 30 | 31 | public void addValidationError(String field, String message) { 32 | if (Objects.isNull(errors)) { 33 | this.errors = new ArrayList<>(); 34 | } 35 | this.errors.add(new ValidationError(field, message)); 36 | } 37 | 38 | public String toJson() { 39 | return "{\"status\": " + getStatus() + ", " + 40 | "\"message\": \"" + getMessage() + "\"}"; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/lucasangelo/todosimple/services/UserDetailsServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.lucasangelo.todosimple.services; 2 | 3 | import java.util.Objects; 4 | 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.security.core.userdetails.UserDetails; 7 | import org.springframework.security.core.userdetails.UserDetailsService; 8 | import org.springframework.security.core.userdetails.UsernameNotFoundException; 9 | import org.springframework.stereotype.Service; 10 | 11 | import com.lucasangelo.todosimple.models.User; 12 | import com.lucasangelo.todosimple.repositories.UserRepository; 13 | import com.lucasangelo.todosimple.security.UserSpringSecurity; 14 | 15 | @Service 16 | public class UserDetailsServiceImpl implements UserDetailsService { 17 | 18 | @Autowired 19 | private UserRepository userRepository; 20 | 21 | @Override 22 | public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { 23 | User user = this.userRepository.findByUsername(username); 24 | if (Objects.isNull(user)) 25 | throw new UsernameNotFoundException("Usuário não encontrado: " + username); 26 | return new UserSpringSecurity(user.getId(), user.getUsername(), user.getPassword(), user.getProfiles()); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /view/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | ToDo Simple App 8 | 9 | 15 | 16 | 17 |
18 |
19 |
20 | 21 |
22 |

Suas tarefas

23 | 30 |
31 | 32 | 33 | 34 |
35 | 36 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /src/main/java/com/lucasangelo/todosimple/security/UserSpringSecurity.java: -------------------------------------------------------------------------------- 1 | package com.lucasangelo.todosimple.security; 2 | 3 | import java.util.Collection; 4 | import java.util.Set; 5 | import java.util.stream.Collectors; 6 | 7 | import org.springframework.security.core.GrantedAuthority; 8 | import org.springframework.security.core.authority.SimpleGrantedAuthority; 9 | import org.springframework.security.core.userdetails.UserDetails; 10 | 11 | import com.lucasangelo.todosimple.models.enums.ProfileEnum; 12 | 13 | import lombok.Getter; 14 | import lombok.NoArgsConstructor; 15 | 16 | @NoArgsConstructor 17 | @Getter 18 | public class UserSpringSecurity implements UserDetails { 19 | 20 | private Long id; 21 | private String username; 22 | private String password; 23 | private Collection authorities; 24 | 25 | public UserSpringSecurity(Long id, String username, String password, Set profileEnums) { 26 | this.id = id; 27 | this.username = username; 28 | this.password = password; 29 | this.authorities = profileEnums.stream().map(x -> new SimpleGrantedAuthority(x.getDescription())) 30 | .collect(Collectors.toList()); 31 | } 32 | 33 | @Override 34 | public boolean isAccountNonExpired() { 35 | return true; 36 | } 37 | 38 | @Override 39 | public boolean isAccountNonLocked() { 40 | return true; 41 | } 42 | 43 | @Override 44 | public boolean isCredentialsNonExpired() { 45 | return true; 46 | } 47 | 48 | @Override 49 | public boolean isEnabled() { 50 | return true; 51 | } 52 | 53 | public boolean hasRole(ProfileEnum profileEnum) { 54 | return getAuthorities().contains(new SimpleGrantedAuthority(profileEnum.getDescription())); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy 2 | 3 | on: 4 | workflow_dispatch: {} 5 | 6 | jobs: 7 | todosimpleapp-deploy: 8 | name: Deploy | Docker-compose 9 | runs-on: ${{ matrix.os }} 10 | 11 | strategy: 12 | matrix: 13 | os: [ubuntu-latest] 14 | 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@v2 18 | 19 | - name: Copy ToDoSimpleApp to VPS 20 | uses: appleboy/scp-action@master 21 | with: 22 | host: ${{ secrets.SSH_HOST }} 23 | username: ${{ secrets.SSH_USER }} 24 | port: ${{ secrets.SSH_PORT }} 25 | key: ${{ secrets.SSH_KEY }} 26 | # Github path 27 | source: "**" 28 | # VPS path 29 | target: "~/todosimpleapp" 30 | 31 | - name: Create ToDoSimpleApp .env file 32 | uses: appleboy/ssh-action@master 33 | with: 34 | host: ${{ secrets.SSH_HOST }} 35 | username: ${{ secrets.SSH_USER }} 36 | port: ${{ secrets.SSH_PORT }} 37 | key: ${{ secrets.SSH_KEY }} 38 | script: | 39 | cd ~/todosimpleapp 40 | rm .env 41 | touch .env 42 | echo MYSQLDB_USER=${{ secrets.MYSQLDB_USER }} >> .env 43 | echo MYSQLDB_ROOT_PASSWORD=${{ secrets.MYSQLDB_ROOT_PASSWORD }} >> .env 44 | echo MYSQLDB_DATABASE=${{ secrets.MYSQLDB_DATABASE }} >> .env 45 | echo MYSQLDB_LOCAL_PORT=${{ secrets.MYSQLDB_LOCAL_PORT }} >> .env 46 | echo MYSQLDB_DOCKER_PORT=${{ secrets.MYSQLDB_DOCKER_PORT }} >> .env 47 | echo SPRING_LOCAL_PORT=${{ secrets.SPRING_LOCAL_PORT }} >> .env 48 | echo SPRING_DOCKER_PORT=${{ secrets.SPRING_DOCKER_PORT }} >> .env 49 | 50 | - name: Build and Start Docker-compose 51 | uses: appleboy/ssh-action@master 52 | with: 53 | host: ${{ secrets.SSH_HOST }} 54 | username: ${{ secrets.SSH_USER }} 55 | port: ${{ secrets.SSH_PORT }} 56 | key: ${{ secrets.SSH_KEY }} 57 | script: | 58 | cd ~/todosimpleapp 59 | docker-compose build --no-cache app 60 | docker-compose up -d app 61 | -------------------------------------------------------------------------------- /src/main/java/com/lucasangelo/todosimple/security/JWTUtil.java: -------------------------------------------------------------------------------- 1 | package com.lucasangelo.todosimple.security; 2 | 3 | import java.util.Date; 4 | import java.util.Objects; 5 | 6 | import javax.crypto.SecretKey; 7 | 8 | import org.springframework.beans.factory.annotation.Value; 9 | import org.springframework.stereotype.Component; 10 | 11 | import io.jsonwebtoken.Claims; 12 | import io.jsonwebtoken.Jwts; 13 | import io.jsonwebtoken.security.Keys; 14 | 15 | @Component 16 | public class JWTUtil { 17 | 18 | @Value("${jwt.secret}") 19 | private String secret; 20 | 21 | @Value("${jwt.expiration}") 22 | private Long expiration; 23 | 24 | public String generateToken(String username) { 25 | SecretKey key = getKeyBySecret(); 26 | return Jwts.builder() 27 | .setSubject(username) 28 | .setExpiration(new Date(System.currentTimeMillis() + this.expiration)) 29 | .signWith(key) 30 | .compact(); 31 | } 32 | 33 | private SecretKey getKeyBySecret() { 34 | SecretKey key = Keys.hmacShaKeyFor(this.secret.getBytes()); 35 | return key; 36 | } 37 | 38 | public boolean isValidToken(String token) { 39 | Claims claims = getClaims(token); 40 | if (Objects.nonNull(claims)) { 41 | String username = claims.getSubject(); 42 | Date expirationDate = claims.getExpiration(); 43 | Date now = new Date(System.currentTimeMillis()); 44 | if (Objects.nonNull(username) && Objects.nonNull(expirationDate) && now.before(expirationDate)) 45 | return true; 46 | } 47 | return false; 48 | } 49 | 50 | public String getUsername(String token) { 51 | Claims claims = getClaims(token); 52 | if (Objects.nonNull(claims)) 53 | return claims.getSubject(); 54 | return null; 55 | } 56 | 57 | private Claims getClaims(String token) { 58 | SecretKey key = getKeyBySecret(); 59 | try { 60 | return Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token).getBody(); 61 | } catch (Exception e) { 62 | return null; 63 | } 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/com/lucasangelo/todosimple/models/User.java: -------------------------------------------------------------------------------- 1 | package com.lucasangelo.todosimple.models; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashSet; 5 | import java.util.List; 6 | import java.util.Set; 7 | import java.util.stream.Collectors; 8 | 9 | import javax.persistence.CollectionTable; 10 | import javax.persistence.Column; 11 | import javax.persistence.ElementCollection; 12 | import javax.persistence.Entity; 13 | import javax.persistence.FetchType; 14 | import javax.persistence.GeneratedValue; 15 | import javax.persistence.GenerationType; 16 | import javax.persistence.Id; 17 | import javax.persistence.OneToMany; 18 | import javax.persistence.Table; 19 | import javax.validation.constraints.NotBlank; 20 | import javax.validation.constraints.Size; 21 | 22 | import com.fasterxml.jackson.annotation.JsonProperty; 23 | import com.fasterxml.jackson.annotation.JsonProperty.Access; 24 | import com.lucasangelo.todosimple.models.enums.ProfileEnum; 25 | 26 | import lombok.AllArgsConstructor; 27 | import lombok.Data; 28 | import lombok.NoArgsConstructor; 29 | 30 | @Entity 31 | @Table(name = User.TABLE_NAME) 32 | @AllArgsConstructor 33 | @NoArgsConstructor 34 | @Data 35 | public class User { 36 | 37 | public static final String TABLE_NAME = "user"; 38 | 39 | @Id 40 | @Column(name = "id", unique = true) 41 | @GeneratedValue(strategy = GenerationType.IDENTITY) 42 | private Long id; 43 | 44 | @Column(name = "username", length = 100, nullable = false, unique = true) 45 | @Size(min = 2, max = 100) 46 | @NotBlank 47 | private String username; 48 | 49 | @Column(name = "password", length = 60, nullable = false) 50 | @JsonProperty(access = Access.WRITE_ONLY) 51 | @Size(min = 8, max = 60) 52 | @NotBlank 53 | private String password; 54 | 55 | @OneToMany(mappedBy = "user") 56 | @JsonProperty(access = Access.WRITE_ONLY) 57 | private List tasks = new ArrayList(); 58 | 59 | @Column(name = "profile", nullable = false) 60 | @ElementCollection(fetch = FetchType.EAGER) 61 | @CollectionTable(name = "user_profile") 62 | @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) 63 | private Set profiles = new HashSet<>(); 64 | 65 | public Set getProfiles() { 66 | return this.profiles.stream().map(x -> ProfileEnum.toEnum(x)).collect(Collectors.toSet()); 67 | } 68 | 69 | public void addProfile(ProfileEnum profileEnum) { 70 | this.profiles.add(profileEnum.getCode()); 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/com/lucasangelo/todosimple/controllers/UserController.java: -------------------------------------------------------------------------------- 1 | package com.lucasangelo.todosimple.controllers; 2 | 3 | import java.net.URI; 4 | 5 | import javax.validation.Valid; 6 | 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.http.ResponseEntity; 9 | import org.springframework.validation.annotation.Validated; 10 | import org.springframework.web.bind.annotation.DeleteMapping; 11 | import org.springframework.web.bind.annotation.GetMapping; 12 | import org.springframework.web.bind.annotation.PathVariable; 13 | import org.springframework.web.bind.annotation.PostMapping; 14 | import org.springframework.web.bind.annotation.PutMapping; 15 | import org.springframework.web.bind.annotation.RequestBody; 16 | import org.springframework.web.bind.annotation.RequestMapping; 17 | import org.springframework.web.bind.annotation.RestController; 18 | import org.springframework.web.servlet.support.ServletUriComponentsBuilder; 19 | 20 | import com.lucasangelo.todosimple.models.User; 21 | import com.lucasangelo.todosimple.models.dto.UserCreateDTO; 22 | import com.lucasangelo.todosimple.models.dto.UserUpdateDTO; 23 | import com.lucasangelo.todosimple.services.UserService; 24 | 25 | @RestController 26 | @RequestMapping("/user") 27 | @Validated 28 | public class UserController { 29 | 30 | @Autowired 31 | private UserService userService; 32 | 33 | @GetMapping("/{id}") 34 | public ResponseEntity findById(@PathVariable Long id) { 35 | User obj = this.userService.findById(id); 36 | return ResponseEntity.ok().body(obj); 37 | } 38 | 39 | @PostMapping 40 | public ResponseEntity create(@Valid @RequestBody UserCreateDTO obj) { 41 | User user = this.userService.fromDTO(obj); 42 | User newUser = this.userService.create(user); 43 | URI uri = ServletUriComponentsBuilder.fromCurrentRequest() 44 | .path("/{id}").buildAndExpand(newUser.getId()).toUri(); 45 | return ResponseEntity.created(uri).build(); 46 | } 47 | 48 | @PutMapping("/{id}") 49 | public ResponseEntity update(@Valid @RequestBody UserUpdateDTO obj, @PathVariable Long id) { 50 | obj.setId(id); 51 | User user = this.userService.fromDTO(obj); 52 | this.userService.update(user); 53 | return ResponseEntity.noContent().build(); 54 | } 55 | 56 | @DeleteMapping("/{id}") 57 | public ResponseEntity delete(@PathVariable Long id) { 58 | this.userService.delete(id); 59 | return ResponseEntity.noContent().build(); 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/lucasangelo/todosimple/security/JWTAuthorizationFilter.java: -------------------------------------------------------------------------------- 1 | package com.lucasangelo.todosimple.security; 2 | 3 | import java.io.IOException; 4 | import java.util.Objects; 5 | 6 | import javax.servlet.FilterChain; 7 | import javax.servlet.ServletException; 8 | import javax.servlet.http.HttpServletRequest; 9 | import javax.servlet.http.HttpServletResponse; 10 | 11 | import org.springframework.security.authentication.AuthenticationManager; 12 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 13 | import org.springframework.security.core.context.SecurityContextHolder; 14 | import org.springframework.security.core.userdetails.UserDetails; 15 | import org.springframework.security.core.userdetails.UserDetailsService; 16 | import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; 17 | 18 | public class JWTAuthorizationFilter extends BasicAuthenticationFilter { 19 | 20 | private JWTUtil jwtUtil; 21 | 22 | private UserDetailsService userDetailsService; 23 | 24 | public JWTAuthorizationFilter(AuthenticationManager authenticationManager, JWTUtil jwtUtil, 25 | UserDetailsService userDetailsService) { 26 | super(authenticationManager); 27 | this.jwtUtil = jwtUtil; 28 | this.userDetailsService = userDetailsService; 29 | } 30 | 31 | @Override 32 | protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) 33 | throws IOException, ServletException { 34 | 35 | String authorizationHeader = request.getHeader("Authorization"); 36 | if (Objects.nonNull(authorizationHeader) && authorizationHeader.startsWith("Bearer ")) { 37 | String token = authorizationHeader.substring(7); 38 | UsernamePasswordAuthenticationToken auth = getAuthentication(token); 39 | if (Objects.nonNull(auth)) 40 | SecurityContextHolder.getContext().setAuthentication(auth); 41 | } 42 | filterChain.doFilter(request, response); 43 | } 44 | 45 | private UsernamePasswordAuthenticationToken getAuthentication(String token) { 46 | if (this.jwtUtil.isValidToken(token)) { 47 | String username = this.jwtUtil.getUsername(token); 48 | UserDetails user = this.userDetailsService.loadUserByUsername(username); 49 | UsernamePasswordAuthenticationToken authenticatedUser = new UsernamePasswordAuthenticationToken(user, null, 50 | user.getAuthorities()); 51 | return authenticatedUser; 52 | } 53 | return null; 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/lucasangelo/todosimple/controllers/TaskController.java: -------------------------------------------------------------------------------- 1 | package com.lucasangelo.todosimple.controllers; 2 | 3 | import java.net.URI; 4 | import java.util.List; 5 | 6 | import javax.validation.Valid; 7 | 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.http.ResponseEntity; 10 | import org.springframework.validation.annotation.Validated; 11 | import org.springframework.web.bind.annotation.DeleteMapping; 12 | import org.springframework.web.bind.annotation.GetMapping; 13 | import org.springframework.web.bind.annotation.PathVariable; 14 | import org.springframework.web.bind.annotation.PostMapping; 15 | import org.springframework.web.bind.annotation.PutMapping; 16 | import org.springframework.web.bind.annotation.RequestBody; 17 | import org.springframework.web.bind.annotation.RequestMapping; 18 | import org.springframework.web.bind.annotation.RestController; 19 | import org.springframework.web.servlet.support.ServletUriComponentsBuilder; 20 | 21 | import com.lucasangelo.todosimple.models.Task; 22 | import com.lucasangelo.todosimple.models.projection.TaskProjection; 23 | import com.lucasangelo.todosimple.services.TaskService; 24 | 25 | @RestController 26 | @RequestMapping("/task") 27 | @Validated 28 | public class TaskController { 29 | 30 | @Autowired 31 | private TaskService taskService; 32 | 33 | @GetMapping("/{id}") 34 | public ResponseEntity findById(@PathVariable Long id) { 35 | Task obj = this.taskService.findById(id); 36 | return ResponseEntity.ok(obj); 37 | } 38 | 39 | @GetMapping("/user") 40 | public ResponseEntity> findAllByUser() { 41 | List objs = this.taskService.findAllByUser(); 42 | return ResponseEntity.ok().body(objs); 43 | } 44 | 45 | @PostMapping 46 | @Validated 47 | public ResponseEntity create(@Valid @RequestBody Task obj) { 48 | this.taskService.create(obj); 49 | URI uri = ServletUriComponentsBuilder.fromCurrentRequest() 50 | .path("/{id}").buildAndExpand(obj.getId()).toUri(); 51 | return ResponseEntity.created(uri).build(); 52 | } 53 | 54 | @PutMapping("/{id}") 55 | @Validated 56 | public ResponseEntity update(@Valid @RequestBody Task obj, @PathVariable Long id) { 57 | obj.setId(id); 58 | this.taskService.update(obj); 59 | return ResponseEntity.noContent().build(); 60 | } 61 | 62 | @DeleteMapping("/{id}") 63 | public ResponseEntity delete(@PathVariable Long id) { 64 | this.taskService.delete(id); 65 | return ResponseEntity.noContent().build(); 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/com/lucasangelo/todosimple/security/JWTAuthenticationFilter.java: -------------------------------------------------------------------------------- 1 | package com.lucasangelo.todosimple.security; 2 | 3 | import java.io.IOException; 4 | import java.util.ArrayList; 5 | 6 | import javax.servlet.FilterChain; 7 | import javax.servlet.ServletException; 8 | import javax.servlet.http.HttpServletRequest; 9 | import javax.servlet.http.HttpServletResponse; 10 | 11 | import org.springframework.security.authentication.AuthenticationManager; 12 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 13 | import org.springframework.security.core.Authentication; 14 | import org.springframework.security.core.AuthenticationException; 15 | import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; 16 | 17 | import com.fasterxml.jackson.databind.ObjectMapper; 18 | import com.lucasangelo.todosimple.exceptions.GlobalExceptionHandler; 19 | import com.lucasangelo.todosimple.models.User; 20 | 21 | public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter { 22 | 23 | private AuthenticationManager authenticationManager; 24 | 25 | private JWTUtil jwtUtil; 26 | 27 | public JWTAuthenticationFilter(AuthenticationManager authenticationManager, JWTUtil jwtUtil) { 28 | setAuthenticationFailureHandler(new GlobalExceptionHandler()); 29 | this.authenticationManager = authenticationManager; 30 | this.jwtUtil = jwtUtil; 31 | } 32 | 33 | @Override 34 | public Authentication attemptAuthentication(HttpServletRequest request, 35 | HttpServletResponse response) throws AuthenticationException { 36 | try { 37 | User userCredentials = new ObjectMapper().readValue(request.getInputStream(), User.class); 38 | 39 | UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken( 40 | userCredentials.getUsername(), userCredentials.getPassword(), new ArrayList<>()); 41 | 42 | Authentication authentication = this.authenticationManager.authenticate(authToken); 43 | return authentication; 44 | } catch (IOException e) { 45 | throw new RuntimeException(e); 46 | } 47 | } 48 | 49 | @Override 50 | protected void successfulAuthentication(HttpServletRequest request, 51 | HttpServletResponse response, FilterChain filterChain, Authentication authentication) 52 | throws IOException, ServletException { 53 | UserSpringSecurity userSpringSecurity = (UserSpringSecurity) authentication.getPrincipal(); 54 | String username = userSpringSecurity.getUsername(); 55 | String token = this.jwtUtil.generateToken(username); 56 | response.addHeader("Authorization", "Bearer " + token); 57 | response.addHeader("access-control-expose-headers", "Authorization"); 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/com/lucasangelo/todosimple/services/TaskService.java: -------------------------------------------------------------------------------- 1 | package com.lucasangelo.todosimple.services; 2 | 3 | import java.util.List; 4 | import java.util.Objects; 5 | 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.stereotype.Service; 8 | import org.springframework.transaction.annotation.Transactional; 9 | 10 | import com.lucasangelo.todosimple.models.Task; 11 | import com.lucasangelo.todosimple.models.User; 12 | import com.lucasangelo.todosimple.models.enums.ProfileEnum; 13 | import com.lucasangelo.todosimple.models.projection.TaskProjection; 14 | import com.lucasangelo.todosimple.repositories.TaskRepository; 15 | import com.lucasangelo.todosimple.security.UserSpringSecurity; 16 | import com.lucasangelo.todosimple.services.exceptions.AuthorizationException; 17 | import com.lucasangelo.todosimple.services.exceptions.DataBindingViolationException; 18 | import com.lucasangelo.todosimple.services.exceptions.ObjectNotFoundException; 19 | 20 | @Service 21 | public class TaskService { 22 | 23 | @Autowired 24 | private TaskRepository taskRepository; 25 | 26 | @Autowired 27 | private UserService userService; 28 | 29 | public Task findById(Long id) { 30 | Task task = this.taskRepository.findById(id).orElseThrow(() -> new ObjectNotFoundException( 31 | "Tarefa não encontrada! Id: " + id + ", Tipo: " + Task.class.getName())); 32 | 33 | UserSpringSecurity userSpringSecurity = UserService.authenticated(); 34 | if (Objects.isNull(userSpringSecurity) 35 | || !userSpringSecurity.hasRole(ProfileEnum.ADMIN) && !userHasTask(userSpringSecurity, task)) 36 | throw new AuthorizationException("Acesso negado!"); 37 | 38 | return task; 39 | } 40 | 41 | public List findAllByUser() { 42 | UserSpringSecurity userSpringSecurity = UserService.authenticated(); 43 | if (Objects.isNull(userSpringSecurity)) 44 | throw new AuthorizationException("Acesso negado!"); 45 | 46 | List tasks = this.taskRepository.findByUser_Id(userSpringSecurity.getId()); 47 | return tasks; 48 | } 49 | 50 | @Transactional 51 | public Task create(Task obj) { 52 | UserSpringSecurity userSpringSecurity = UserService.authenticated(); 53 | if (Objects.isNull(userSpringSecurity)) 54 | throw new AuthorizationException("Acesso negado!"); 55 | 56 | User user = this.userService.findById(userSpringSecurity.getId()); 57 | obj.setId(null); 58 | obj.setUser(user); 59 | obj = this.taskRepository.save(obj); 60 | return obj; 61 | } 62 | 63 | @Transactional 64 | public Task update(Task obj) { 65 | Task newObj = findById(obj.getId()); 66 | newObj.setDescription(obj.getDescription()); 67 | return this.taskRepository.save(newObj); 68 | } 69 | 70 | public void delete(Long id) { 71 | findById(id); 72 | try { 73 | this.taskRepository.deleteById(id); 74 | } catch (Exception e) { 75 | throw new DataBindingViolationException("Não é possível excluir pois há entidades relacionadas!"); 76 | } 77 | } 78 | 79 | private Boolean userHasTask(UserSpringSecurity userSpringSecurity, Task task) { 80 | return task.getUser().getId().equals(userSpringSecurity.getId()); 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /view/login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Login Page 7 | 11 | 12 | 13 | 14 | 17 | 18 | 19 | 20 | 21 | 41 | 42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |

50 | To-Do Simple App - Login 51 |

52 |

Please enter your login and password!

53 |
54 | 55 | 61 |
62 |
63 | 64 | 71 |
72 |
73 | 80 |
81 |
82 |
83 |

84 | Don't have an account? 85 | Sign Up 88 |

89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 | 97 | 98 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.springframework.boot 8 | spring-boot-starter-parent 9 | 2.7.2 10 | 11 | 12 | com.lucasangelo 13 | todosimple 14 | 0.0.1-SNAPSHOT 15 | todosimple 16 | Spring Boot API for To Do App 17 | 18 | 17 19 | 20 | jar 21 | 22 | 23 | 24 | org.springframework.boot 25 | spring-boot-maven-plugin 26 | 27 | 28 | todosimpleapp 29 | 30 | 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-starter-web 35 | 2.7.3 36 | 37 | 38 | 39 | org.springframework.boot 40 | spring-boot-devtools 41 | 2.7.3 42 | runtime 43 | true 44 | 45 | 46 | 47 | org.springframework.boot 48 | spring-boot-starter-test 49 | 2.7.3 50 | test 51 | 52 | 53 | 54 | org.springframework.boot 55 | spring-boot-starter-data-jpa 56 | 2.7.3 57 | 58 | 59 | 60 | org.springframework.boot 61 | spring-boot-starter-validation 62 | 2.7.3 63 | 64 | 65 | 66 | com.h2database 67 | h2 68 | 2.1.214 69 | runtime 70 | 71 | 72 | 73 | mysql 74 | mysql-connector-java 75 | 8.0.30 76 | runtime 77 | 78 | 79 | 80 | org.projectlombok 81 | lombok 82 | 1.18.24 83 | provided 84 | 85 | 86 | 87 | org.apache.commons 88 | commons-lang3 89 | 3.12.0 90 | 91 | 92 | 93 | org.springframework.boot 94 | spring-boot-starter-security 95 | 2.7.3 96 | 97 | 98 | 99 | io.jsonwebtoken 100 | jjwt-api 101 | 0.11.5 102 | 103 | 104 | 105 | io.jsonwebtoken 106 | jjwt-impl 107 | 0.11.5 108 | runtime 109 | 110 | 111 | 112 | io.jsonwebtoken 113 | jjwt-jackson 114 | 0.11.5 115 | runtime 116 | 117 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /src/main/java/com/lucasangelo/todosimple/services/UserService.java: -------------------------------------------------------------------------------- 1 | package com.lucasangelo.todosimple.services; 2 | 3 | import java.util.Objects; 4 | import java.util.Optional; 5 | import java.util.stream.Collectors; 6 | import java.util.stream.Stream; 7 | 8 | import javax.validation.Valid; 9 | 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.security.core.context.SecurityContextHolder; 12 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 13 | import org.springframework.stereotype.Service; 14 | import org.springframework.transaction.annotation.Transactional; 15 | 16 | import com.lucasangelo.todosimple.repositories.UserRepository; 17 | import com.lucasangelo.todosimple.security.UserSpringSecurity; 18 | import com.lucasangelo.todosimple.services.exceptions.AuthorizationException; 19 | import com.lucasangelo.todosimple.services.exceptions.DataBindingViolationException; 20 | import com.lucasangelo.todosimple.services.exceptions.ObjectNotFoundException; 21 | import com.lucasangelo.todosimple.models.User; 22 | import com.lucasangelo.todosimple.models.dto.UserCreateDTO; 23 | import com.lucasangelo.todosimple.models.dto.UserUpdateDTO; 24 | import com.lucasangelo.todosimple.models.enums.ProfileEnum; 25 | 26 | @Service 27 | public class UserService { 28 | 29 | @Autowired 30 | private BCryptPasswordEncoder bCryptPasswordEncoder; 31 | 32 | @Autowired 33 | private UserRepository userRepository; 34 | 35 | public User findById(Long id) { 36 | UserSpringSecurity userSpringSecurity = authenticated(); 37 | if (!Objects.nonNull(userSpringSecurity) 38 | || !userSpringSecurity.hasRole(ProfileEnum.ADMIN) && !id.equals(userSpringSecurity.getId())) 39 | throw new AuthorizationException("Acesso negado!"); 40 | 41 | Optional user = this.userRepository.findById(id); 42 | return user.orElseThrow(() -> new ObjectNotFoundException( 43 | "Usuário não encontrado! Id: " + id + ", Tipo: " + User.class.getName())); 44 | } 45 | 46 | @Transactional 47 | public User create(User obj) { 48 | obj.setId(null); 49 | obj.setPassword(this.bCryptPasswordEncoder.encode(obj.getPassword())); 50 | obj.setProfiles(Stream.of(ProfileEnum.USER.getCode()).collect(Collectors.toSet())); 51 | obj = this.userRepository.save(obj); 52 | return obj; 53 | } 54 | 55 | @Transactional 56 | public User update(User obj) { 57 | User newObj = findById(obj.getId()); 58 | newObj.setPassword(obj.getPassword()); 59 | newObj.setPassword(this.bCryptPasswordEncoder.encode(obj.getPassword())); 60 | return this.userRepository.save(newObj); 61 | } 62 | 63 | public void delete(Long id) { 64 | findById(id); 65 | try { 66 | this.userRepository.deleteById(id); 67 | } catch (Exception e) { 68 | throw new DataBindingViolationException("Não é possível excluir pois há entidades relacionadas!"); 69 | } 70 | } 71 | 72 | public static UserSpringSecurity authenticated() { 73 | try { 74 | return (UserSpringSecurity) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); 75 | } catch (Exception e) { 76 | return null; 77 | } 78 | } 79 | 80 | public User fromDTO(@Valid UserCreateDTO obj) { 81 | User user = new User(); 82 | user.setUsername(obj.getUsername()); 83 | user.setPassword(obj.getPassword()); 84 | return user; 85 | } 86 | 87 | public User fromDTO(@Valid UserUpdateDTO obj) { 88 | User user = new User(); 89 | user.setId(obj.getId()); 90 | user.setPassword(obj.getPassword()); 91 | return user; 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /view/signup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | SignUp Page 7 | 11 | 12 | 13 | 14 | 17 | 18 | 19 | 20 | 21 | 39 | 40 | 58 | 59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |

67 | To-Do Simple App - SignUp 68 |

69 |

70 | Please enter your username and password to create your 71 | account! 72 |

73 |
74 | 75 | 81 |
82 |
83 | 84 | 91 |
92 |
93 | 101 |
102 |
103 |
104 |

105 | Already have an account? 106 | Login 107 |

108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 | 116 | 117 | -------------------------------------------------------------------------------- /src/main/java/com/lucasangelo/todosimple/configs/SecurityConfig.java: -------------------------------------------------------------------------------- 1 | package com.lucasangelo.todosimple.configs; 2 | 3 | import java.util.Arrays; 4 | 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.http.HttpMethod; 9 | import org.springframework.security.authentication.AuthenticationManager; 10 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 11 | import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; 12 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 13 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 14 | import org.springframework.security.config.http.SessionCreationPolicy; 15 | import org.springframework.security.core.userdetails.UserDetailsService; 16 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 17 | import org.springframework.security.web.SecurityFilterChain; 18 | import org.springframework.web.cors.CorsConfiguration; 19 | import org.springframework.web.cors.CorsConfigurationSource; 20 | import org.springframework.web.cors.UrlBasedCorsConfigurationSource; 21 | 22 | import com.lucasangelo.todosimple.security.JWTAuthenticationFilter; 23 | import com.lucasangelo.todosimple.security.JWTAuthorizationFilter; 24 | import com.lucasangelo.todosimple.security.JWTUtil; 25 | 26 | @Configuration 27 | @EnableWebSecurity 28 | @EnableGlobalMethodSecurity(prePostEnabled = true) 29 | public class SecurityConfig { 30 | 31 | private AuthenticationManager authenticationManager; 32 | 33 | @Autowired 34 | private UserDetailsService userDetailsService; 35 | 36 | @Autowired 37 | private JWTUtil jwtUtil; 38 | 39 | private static final String[] PUBLIC_MATCHERS = { 40 | "/" 41 | }; 42 | private static final String[] PUBLIC_MATCHERS_POST = { 43 | "/user", 44 | "/login" 45 | }; 46 | 47 | @Bean 48 | public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { 49 | 50 | http.cors().and().csrf().disable(); 51 | 52 | AuthenticationManagerBuilder authenticationManagerBuilder = http 53 | .getSharedObject(AuthenticationManagerBuilder.class); 54 | authenticationManagerBuilder.userDetailsService(this.userDetailsService) 55 | .passwordEncoder(bCryptPasswordEncoder()); 56 | this.authenticationManager = authenticationManagerBuilder.build(); 57 | 58 | http.authorizeRequests() 59 | .antMatchers(HttpMethod.POST, PUBLIC_MATCHERS_POST).permitAll() 60 | .antMatchers(PUBLIC_MATCHERS).permitAll() 61 | .anyRequest().authenticated().and() 62 | .authenticationManager(authenticationManager); 63 | 64 | http.addFilter(new JWTAuthenticationFilter(this.authenticationManager, this.jwtUtil)); 65 | http.addFilter(new JWTAuthorizationFilter(this.authenticationManager, this.jwtUtil, 66 | this.userDetailsService)); 67 | 68 | http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); 69 | 70 | return http.build(); 71 | } 72 | 73 | @Bean 74 | CorsConfigurationSource corsConfigurationSource() { 75 | CorsConfiguration configuration = new CorsConfiguration().applyPermitDefaultValues(); 76 | configuration.setAllowedMethods(Arrays.asList("POST", "GET", "PUT", "DELETE")); 77 | final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); 78 | source.registerCorsConfiguration("/**", configuration); 79 | return source; 80 | } 81 | 82 | @Bean 83 | public BCryptPasswordEncoder bCryptPasswordEncoder() { 84 | return new BCryptPasswordEncoder(); 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

PLF-ES-2022-2-MON-CursoAPIJava

2 | 3 |
4 | 5 |

6 | Curso de monitoria 7 |
8 | Como criar uma API RESTful com Spring Boot 9 |

10 |

11 | Sobre   |    12 | Monitor   |    13 | Professor responsável   |    14 | Tecnologias   |    15 | Instruções de utilização   |    16 | Licença 17 |

18 |

19 | 20 |
21 | 22 | ## 🔖 Sobre 23 | 24 | Curso de programação para apoio da disciplina Trabalho Interdisciplinar Aplicações para Processos de Negócios do 2º período de Engenharia de Software, PucMinas Praça da Liberdade 2022/2. 25 | 26 | Neste curso é abordado como criar uma API Restful utilizando o framework Spring Boot da linguagem Java. 27 | Conteúdo inclui vídeo aulas de explicações teóricas, instalação de ferramentas, prática focada no código e como consumir a API pelo Postman. Além disso, é apresentado como criar uma simples interface web em HTML, CSS e JavaScript (também com a biblioteca Booststrap) que consume esta API. 28 | 29 | Esta é uma simples aplicação onde usuário podem se cadastrar e registrarem tarefas para sua conta. 30 | 31 | --- 32 | 33 | ## 👨‍💻 Monitor 34 | 35 | * [Lucas Ângelo Oliveira Martins Rocha](https://lucasangelo.com) 36 | 37 | --- 38 | 39 | ## 👩‍🏫 Professor responsável 40 | 41 | * Hugo Bastos De Paula 42 | 43 | --- 44 | 45 | ## 🚀 Tecnologias 46 | 47 | - Frontend: 48 | - [HTML 5](https://www.w3schools.com/howto/howto_make_a_website.asp/) 49 | - [CSS 3](https://www.w3schools.com/css/css_website_layout.asp/) 50 | - [JavaScript](https://www.javascript.com/) 51 | - [Bootstrap 5](https://getbootstrap.com/) 52 | - Backend: 53 | - [Java 17](http://www.oracle.com/java/technologies/javase-downloads.html) 54 | - [Apache Maven >=3.8.6](https://maven.apache.org/download.cgi/) 55 | - Database: 56 | - [MySQL Server](https://dev.mysql.com/downloads/mysql/) 57 | - Ferramenta 58 | - [Visual Studio Code (VSCode)](https://code.visualstudio.com) 59 | - [Postman](http://www.postman.com/downloads/) 60 | - [Git](https://git-scm.com/downloads) 61 | - [Docker](https://docs.docker.com/desktop/install/windows-install/) 62 | 63 | --- 64 | 65 | ## ⤵ Instruções de utilização 66 | 67 | Essas instruções vão te levar a uma cópia do projeto rodando em sua máquina local para propósitos de testes, desenvolvimento e aprendizagem. 68 | 69 | Pré-requisitos: 70 | - Ter instalado todas as ferramentas e dependências ensinadas no vídeo [Aula 02 - Instalação de todas as ferramentas e configuração de ambiente](https://youtu.be/WHJvBUADvCE) 71 | - Java 72 | - Maven 73 | - MySQL 74 | - Docker (Docker-Compose) 75 | 76 |
77 | 78 | - Passo 1: Clonar o repositório: 79 | ```bash 80 | $ git clone https://github.com/ICEI-PUC-Minas-PPLES-TI/PLF-ES-2022-2-MON-CursoAPIJava.git 81 | ``` 82 | 83 |
84 | 85 | - Passo 2: Configurar e iniciar a API Spring Boot (backend) 86 | 87 | - Passo 2.1: Entrar no arquivo application.properties: 88 | ```bash 89 | $ vi PLF-ES-2022-2-MON-CursoAPIJava\src\main\resources\application-dev.properties 90 | ``` 91 | 92 | - Passo 2.2: Configurar as credenciais de banco de dados de acordo com sua instalação do MySQL Server: 93 | ```proprieties 94 | # Database config 95 | spring.datasource.url=jdbc:mysql://localhost:3306/todosimple?createDatabaseIfNotExist=true 96 | spring.datasource.username=root 97 | spring.datasource.password=root 98 | ``` 99 | 100 | - Passo 2.3: Voltar para a pasta raíz do projeto: 101 | ```bash 102 | $ cd PLF-ES-2022-2-MON-CursoAPIJava\ 103 | ``` 104 | 105 | - Passo 2.4: Iniciar a aplicação Spring Boot: 106 | ```bash 107 | $ mvn clean install 108 | ``` 109 | 110 | - Passo 2.4.1: Iniciar a aplicação Spring Boot utilizando o Maven: 111 | ```bash 112 | $ mvn spring-boot:run 113 | ``` 114 | 115 | ou 116 | 117 | - Passo 2.4.1: Iniciar a aplicação utilizando Docker-Compose: 118 | ```bash 119 | $ docker-compose up 120 | ``` 121 | 122 | - API estará rodando em http://localhost:8080/ 123 | 124 |
125 | 126 | - Passo 3: Entrar na aplicação frontend após subir a API 127 | 128 | - Passo 3.1: Entrar na pasta raíz do projeto: 129 | ```bash 130 | $ cd PLF-ES-2022-2-MON-CursoAPIJava\ 131 | ``` 132 | 133 | - Passo 3.2: Abrir o arquivo index.html diretamente ou pela extensão Live Server do VsCode: 134 | ```bash 135 | $ cd PLF-ES-2022-2-MON-CursoAPIJava\view\login.html 136 | ``` 137 | 138 | - Frontend estará rodando em http://127.0.0.1:5500/view/login.html caso iniciado com Live Server. 139 | 140 | --- 141 | 142 | ## 🔗 Links do projeto 143 | 144 | - [Playlist do Youtube com vídeos do curso](https://www.youtube.com/watch?v=YcO-Q6yozmU&list=PLiXotHlANc8ptwP6wajo73OZo9Nh5i597) 145 | - [Slide de apresentação](docs/Slide%20API%20RESTful%20com%20Spring%20Boot.pdf) 146 | - [Documento de ferramentas e configuração de ambiente](docs/Ferramentas%20e%20configura%C3%A7%C3%A3o%20do%20ambiente.pdf) 147 | 148 | --- 149 | 150 | ## 📝 Licença 151 | 152 | Esse projeto está sob a licença MIT License. Veja o arquivo [LICENSE](LICENSE) para mais detalhes. 153 | 154 | --- 155 | -------------------------------------------------------------------------------- /mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM https://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM set title of command window 39 | title %0 40 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' 41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 42 | 43 | @REM set %HOME% to equivalent of $HOME 44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 45 | 46 | @REM Execute a user defined script before this one 47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 49 | if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* 50 | if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* 51 | :skipRcPre 52 | 53 | @setlocal 54 | 55 | set ERROR_CODE=0 56 | 57 | @REM To isolate internal variables from possible post scripts, we use another setlocal 58 | @setlocal 59 | 60 | @REM ==== START VALIDATION ==== 61 | if not "%JAVA_HOME%" == "" goto OkJHome 62 | 63 | echo. 64 | echo Error: JAVA_HOME not found in your environment. >&2 65 | echo Please set the JAVA_HOME variable in your environment to match the >&2 66 | echo location of your Java installation. >&2 67 | echo. 68 | goto error 69 | 70 | :OkJHome 71 | if exist "%JAVA_HOME%\bin\java.exe" goto init 72 | 73 | echo. 74 | echo Error: JAVA_HOME is set to an invalid directory. >&2 75 | echo JAVA_HOME = "%JAVA_HOME%" >&2 76 | echo Please set the JAVA_HOME variable in your environment to match the >&2 77 | echo location of your Java installation. >&2 78 | echo. 79 | goto error 80 | 81 | @REM ==== END VALIDATION ==== 82 | 83 | :init 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 122 | 123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" 124 | 125 | FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 126 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B 127 | ) 128 | 129 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 130 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 131 | if exist %WRAPPER_JAR% ( 132 | if "%MVNW_VERBOSE%" == "true" ( 133 | echo Found %WRAPPER_JAR% 134 | ) 135 | ) else ( 136 | if not "%MVNW_REPOURL%" == "" ( 137 | SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" 138 | ) 139 | if "%MVNW_VERBOSE%" == "true" ( 140 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 141 | echo Downloading from: %DOWNLOAD_URL% 142 | ) 143 | 144 | powershell -Command "&{"^ 145 | "$webclient = new-object System.Net.WebClient;"^ 146 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ 147 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ 148 | "}"^ 149 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ 150 | "}" 151 | if "%MVNW_VERBOSE%" == "true" ( 152 | echo Finished downloading %WRAPPER_JAR% 153 | ) 154 | ) 155 | @REM End of extension 156 | 157 | @REM Provide a "standardized" way to retrieve the CLI args that will 158 | @REM work with both Windows and non-Windows executions. 159 | set MAVEN_CMD_LINE_ARGS=%* 160 | 161 | %MAVEN_JAVA_EXE% ^ 162 | %JVM_CONFIG_MAVEN_PROPS% ^ 163 | %MAVEN_OPTS% ^ 164 | %MAVEN_DEBUG_OPTS% ^ 165 | -classpath %WRAPPER_JAR% ^ 166 | "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ 167 | %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 168 | if ERRORLEVEL 1 goto error 169 | goto end 170 | 171 | :error 172 | set ERROR_CODE=1 173 | 174 | :end 175 | @endlocal & set ERROR_CODE=%ERROR_CODE% 176 | 177 | if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost 178 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 179 | if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" 180 | if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" 181 | :skipRcPost 182 | 183 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 184 | if "%MAVEN_BATCH_PAUSE%"=="on" pause 185 | 186 | if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% 187 | 188 | cmd /C exit /B %ERROR_CODE% 189 | -------------------------------------------------------------------------------- /src/main/java/com/lucasangelo/todosimple/exceptions/GlobalExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.lucasangelo.todosimple.exceptions; 2 | 3 | import java.io.IOException; 4 | 5 | import javax.servlet.ServletException; 6 | import javax.servlet.http.HttpServletRequest; 7 | import javax.servlet.http.HttpServletResponse; 8 | import javax.validation.ConstraintViolationException; 9 | 10 | import org.apache.commons.lang3.exception.ExceptionUtils; 11 | import org.springframework.beans.factory.annotation.Value; 12 | import org.springframework.dao.DataIntegrityViolationException; 13 | import org.springframework.http.HttpHeaders; 14 | import org.springframework.http.HttpStatus; 15 | import org.springframework.http.ResponseEntity; 16 | import org.springframework.security.access.AccessDeniedException; 17 | import org.springframework.security.core.AuthenticationException; 18 | import org.springframework.security.web.authentication.AuthenticationFailureHandler; 19 | import org.springframework.validation.FieldError; 20 | import org.springframework.web.bind.MethodArgumentNotValidException; 21 | import org.springframework.web.bind.annotation.ExceptionHandler; 22 | import org.springframework.web.bind.annotation.ResponseStatus; 23 | import org.springframework.web.bind.annotation.RestControllerAdvice; 24 | import org.springframework.web.context.request.WebRequest; 25 | import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; 26 | 27 | import com.lucasangelo.todosimple.services.exceptions.AuthorizationException; 28 | import com.lucasangelo.todosimple.services.exceptions.DataBindingViolationException; 29 | import com.lucasangelo.todosimple.services.exceptions.ObjectNotFoundException; 30 | 31 | import lombok.extern.slf4j.Slf4j; 32 | 33 | @Slf4j(topic = "GLOBAL_EXCEPTION_HANDLER") 34 | @RestControllerAdvice 35 | public class GlobalExceptionHandler extends ResponseEntityExceptionHandler implements AuthenticationFailureHandler { 36 | 37 | @Value("${server.error.include-exception}") 38 | private boolean printStackTrace; 39 | 40 | @Override 41 | @ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY) 42 | protected ResponseEntity handleMethodArgumentNotValid( 43 | MethodArgumentNotValidException methodArgumentNotValidException, 44 | HttpHeaders headers, 45 | HttpStatus status, 46 | WebRequest request) { 47 | ErrorResponse errorResponse = new ErrorResponse( 48 | HttpStatus.UNPROCESSABLE_ENTITY.value(), 49 | "Validation error. Check 'errors' field for details."); 50 | for (FieldError fieldError : methodArgumentNotValidException.getBindingResult().getFieldErrors()) { 51 | errorResponse.addValidationError(fieldError.getField(), fieldError.getDefaultMessage()); 52 | } 53 | return ResponseEntity.unprocessableEntity().body(errorResponse); 54 | } 55 | 56 | @ExceptionHandler(Exception.class) 57 | @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) 58 | public ResponseEntity handleAllUncaughtException( 59 | Exception exception, 60 | WebRequest request) { 61 | final String errorMessage = "Unknown error occurred"; 62 | log.error(errorMessage, exception); 63 | return buildErrorResponse( 64 | exception, 65 | errorMessage, 66 | HttpStatus.INTERNAL_SERVER_ERROR, 67 | request); 68 | } 69 | 70 | @ExceptionHandler(DataIntegrityViolationException.class) 71 | @ResponseStatus(HttpStatus.CONFLICT) 72 | public ResponseEntity handleDataIntegrityViolationException( 73 | DataIntegrityViolationException dataIntegrityViolationException, 74 | WebRequest request) { 75 | String errorMessage = dataIntegrityViolationException.getMostSpecificCause().getMessage(); 76 | log.error("Failed to save entity with integrity problems: " + errorMessage, dataIntegrityViolationException); 77 | return buildErrorResponse( 78 | dataIntegrityViolationException, 79 | errorMessage, 80 | HttpStatus.CONFLICT, 81 | request); 82 | } 83 | 84 | @ExceptionHandler(ConstraintViolationException.class) 85 | @ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY) 86 | public ResponseEntity handleConstraintViolationException( 87 | ConstraintViolationException constraintViolationException, 88 | WebRequest request) { 89 | log.error("Failed to validate element", constraintViolationException); 90 | return buildErrorResponse( 91 | constraintViolationException, 92 | HttpStatus.UNPROCESSABLE_ENTITY, 93 | request); 94 | } 95 | 96 | @ExceptionHandler(ObjectNotFoundException.class) 97 | @ResponseStatus(HttpStatus.NOT_FOUND) 98 | public ResponseEntity handleObjectNotFoundException( 99 | ObjectNotFoundException objectNotFoundException, 100 | WebRequest request) { 101 | log.error("Failed to find the requested element", objectNotFoundException); 102 | return buildErrorResponse( 103 | objectNotFoundException, 104 | HttpStatus.NOT_FOUND, 105 | request); 106 | } 107 | 108 | @ExceptionHandler(DataBindingViolationException.class) 109 | @ResponseStatus(HttpStatus.CONFLICT) 110 | public ResponseEntity handleDataBindingViolationException( 111 | DataBindingViolationException dataBindingViolationException, 112 | WebRequest request) { 113 | log.error("Failed to save entity with associated data", dataBindingViolationException); 114 | return buildErrorResponse( 115 | dataBindingViolationException, 116 | HttpStatus.CONFLICT, 117 | request); 118 | } 119 | 120 | @ExceptionHandler(AuthenticationException.class) 121 | @ResponseStatus(HttpStatus.UNAUTHORIZED) 122 | public ResponseEntity handleAuthenticationException( 123 | AuthenticationException authenticationException, 124 | WebRequest request) { 125 | log.error("Authentication error ", authenticationException); 126 | return buildErrorResponse( 127 | authenticationException, 128 | HttpStatus.UNAUTHORIZED, 129 | request); 130 | } 131 | 132 | @ExceptionHandler(AccessDeniedException.class) 133 | @ResponseStatus(HttpStatus.FORBIDDEN) 134 | public ResponseEntity handleAccessDeniedException( 135 | AccessDeniedException accessDeniedException, 136 | WebRequest request) { 137 | log.error("Authorization error ", accessDeniedException); 138 | return buildErrorResponse( 139 | accessDeniedException, 140 | HttpStatus.FORBIDDEN, 141 | request); 142 | } 143 | 144 | @ExceptionHandler(AuthorizationException.class) 145 | @ResponseStatus(HttpStatus.FORBIDDEN) 146 | public ResponseEntity handleAuthorizationException( 147 | AuthorizationException authorizationException, 148 | WebRequest request) { 149 | log.error("Authorization error ", authorizationException); 150 | return buildErrorResponse( 151 | authorizationException, 152 | HttpStatus.FORBIDDEN, 153 | request); 154 | } 155 | 156 | private ResponseEntity buildErrorResponse( 157 | Exception exception, 158 | HttpStatus httpStatus, 159 | WebRequest request) { 160 | return buildErrorResponse(exception, exception.getMessage(), httpStatus, request); 161 | } 162 | 163 | private ResponseEntity buildErrorResponse( 164 | Exception exception, 165 | String message, 166 | HttpStatus httpStatus, 167 | WebRequest request) { 168 | ErrorResponse errorResponse = new ErrorResponse(httpStatus.value(), message); 169 | if (this.printStackTrace) { 170 | errorResponse.setStackTrace(ExceptionUtils.getStackTrace(exception)); 171 | } 172 | return ResponseEntity.status(httpStatus).body(errorResponse); 173 | } 174 | 175 | @Override 176 | public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, 177 | AuthenticationException exception) throws IOException, ServletException { 178 | Integer status = HttpStatus.UNAUTHORIZED.value(); 179 | response.setStatus(status); 180 | response.setContentType("application/json"); 181 | ErrorResponse errorResponse = new ErrorResponse(status, "Username or password are invalid"); 182 | response.getWriter().append(errorResponse.toJson()); 183 | } 184 | 185 | } 186 | -------------------------------------------------------------------------------- /docs/ToDo Simple App.postman_collection.json: -------------------------------------------------------------------------------- 1 | { 2 | "info": { 3 | "_postman_id": "00da9252-a3f6-4afc-884f-998035b8b61c", 4 | "name": "ToDo Simple App", 5 | "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", 6 | "_exporter_id": "11631363" 7 | }, 8 | "item": [ 9 | { 10 | "name": "User", 11 | "item": [ 12 | { 13 | "name": "Create", 14 | "request": { 15 | "method": "POST", 16 | "header": [ 17 | { 18 | "key": "Authorization", 19 | "value": "{{authorization_token}}", 20 | "type": "text" 21 | } 22 | ], 23 | "body": { 24 | "mode": "raw", 25 | "raw": "{\r\n \"username\": \"lucas\",\r\n \"password\": \"supersenha\"\r\n}", 26 | "options": { 27 | "raw": { 28 | "language": "json" 29 | } 30 | } 31 | }, 32 | "url": { 33 | "raw": "{{host}}/user", 34 | "host": [ 35 | "{{host}}" 36 | ], 37 | "path": [ 38 | "user" 39 | ] 40 | } 41 | }, 42 | "response": [] 43 | }, 44 | { 45 | "name": "Update", 46 | "event": [ 47 | { 48 | "listen": "prerequest", 49 | "script": { 50 | "exec": [ 51 | "" 52 | ], 53 | "type": "text/javascript" 54 | } 55 | } 56 | ], 57 | "request": { 58 | "method": "PUT", 59 | "header": [ 60 | { 61 | "key": "Authorization", 62 | "value": "{{authorization_token}}", 63 | "type": "text" 64 | } 65 | ], 66 | "body": { 67 | "mode": "raw", 68 | "raw": "{\r\n \"username\": \"lucas\",\r\n \"password\": \"supersenha\"\r\n}", 69 | "options": { 70 | "raw": { 71 | "language": "json" 72 | } 73 | } 74 | }, 75 | "url": { 76 | "raw": "{{host}}/user/3", 77 | "host": [ 78 | "{{host}}" 79 | ], 80 | "path": [ 81 | "user", 82 | "3" 83 | ] 84 | } 85 | }, 86 | "response": [] 87 | }, 88 | { 89 | "name": "Delete", 90 | "request": { 91 | "method": "DELETE", 92 | "header": [ 93 | { 94 | "key": "Authorization", 95 | "value": "{{authorization_token}}", 96 | "type": "text" 97 | } 98 | ], 99 | "url": { 100 | "raw": "{{host}}/user/1", 101 | "host": [ 102 | "{{host}}" 103 | ], 104 | "path": [ 105 | "user", 106 | "1" 107 | ] 108 | } 109 | }, 110 | "response": [] 111 | }, 112 | { 113 | "name": "Find", 114 | "request": { 115 | "method": "GET", 116 | "header": [ 117 | { 118 | "key": "Authorization", 119 | "value": "{{authorization_token}}", 120 | "type": "text" 121 | } 122 | ], 123 | "url": { 124 | "raw": "{{host}}/user/2", 125 | "host": [ 126 | "{{host}}" 127 | ], 128 | "path": [ 129 | "user", 130 | "2" 131 | ] 132 | } 133 | }, 134 | "response": [] 135 | } 136 | ] 137 | }, 138 | { 139 | "name": "Task", 140 | "item": [ 141 | { 142 | "name": "Create", 143 | "request": { 144 | "method": "POST", 145 | "header": [ 146 | { 147 | "key": "Authorization", 148 | "value": "{{authorization_token}}", 149 | "type": "text" 150 | } 151 | ], 152 | "body": { 153 | "mode": "raw", 154 | "raw": "{\r\n \"description\": \"trabalho da faculdade 2\"\r\n}", 155 | "options": { 156 | "raw": { 157 | "language": "json" 158 | } 159 | } 160 | }, 161 | "url": { 162 | "raw": "{{host}}/task", 163 | "host": [ 164 | "{{host}}" 165 | ], 166 | "path": [ 167 | "task" 168 | ] 169 | } 170 | }, 171 | "response": [] 172 | }, 173 | { 174 | "name": "Update", 175 | "request": { 176 | "method": "PUT", 177 | "header": [ 178 | { 179 | "key": "Authorization", 180 | "value": "{{authorization_token}}", 181 | "type": "text" 182 | } 183 | ], 184 | "body": { 185 | "mode": "raw", 186 | "raw": "{\r\n \"description\": \"trabalho que nao é do admin atualizado pelo admin\"\r\n}", 187 | "options": { 188 | "raw": { 189 | "language": "json" 190 | } 191 | } 192 | }, 193 | "url": { 194 | "raw": "{{host}}/task/2", 195 | "host": [ 196 | "{{host}}" 197 | ], 198 | "path": [ 199 | "task", 200 | "2" 201 | ] 202 | } 203 | }, 204 | "response": [] 205 | }, 206 | { 207 | "name": "Find", 208 | "request": { 209 | "method": "GET", 210 | "header": [ 211 | { 212 | "key": "Authorization", 213 | "value": "{{authorization_token}}", 214 | "type": "text" 215 | } 216 | ], 217 | "url": { 218 | "raw": "{{host}}/task/3", 219 | "host": [ 220 | "{{host}}" 221 | ], 222 | "path": [ 223 | "task", 224 | "3" 225 | ], 226 | "query": [ 227 | { 228 | "key": "", 229 | "value": null, 230 | "disabled": true 231 | } 232 | ] 233 | } 234 | }, 235 | "response": [] 236 | }, 237 | { 238 | "name": "Find by user", 239 | "request": { 240 | "method": "GET", 241 | "header": [ 242 | { 243 | "key": "Authorization", 244 | "value": "{{authorization_token}}", 245 | "type": "text" 246 | } 247 | ], 248 | "url": { 249 | "raw": "{{host}}/task/user", 250 | "host": [ 251 | "{{host}}" 252 | ], 253 | "path": [ 254 | "task", 255 | "user" 256 | ] 257 | } 258 | }, 259 | "response": [] 260 | }, 261 | { 262 | "name": "Delete", 263 | "request": { 264 | "method": "DELETE", 265 | "header": [ 266 | { 267 | "key": "Authorization", 268 | "value": "{{authorization_token}}", 269 | "type": "text" 270 | } 271 | ], 272 | "url": { 273 | "raw": "{{host}}/task/3", 274 | "host": [ 275 | "{{host}}" 276 | ], 277 | "path": [ 278 | "task", 279 | "3" 280 | ], 281 | "query": [ 282 | { 283 | "key": "", 284 | "value": null, 285 | "disabled": true 286 | } 287 | ] 288 | } 289 | }, 290 | "response": [] 291 | } 292 | ] 293 | }, 294 | { 295 | "name": "Login", 296 | "request": { 297 | "method": "POST", 298 | "header": [], 299 | "body": { 300 | "mode": "raw", 301 | "raw": "{\r\n \"username\": \"lucasf\",\r\n \"password\": \"supersenha\"\r\n}", 302 | "options": { 303 | "raw": { 304 | "language": "json" 305 | } 306 | } 307 | }, 308 | "url": { 309 | "raw": "{{host}}/login", 310 | "host": [ 311 | "{{host}}" 312 | ], 313 | "path": [ 314 | "login" 315 | ] 316 | } 317 | }, 318 | "response": [] 319 | }, 320 | { 321 | "name": "/", 322 | "protocolProfileBehavior": { 323 | "disableBodyPruning": true 324 | }, 325 | "request": { 326 | "method": "GET", 327 | "header": [], 328 | "body": { 329 | "mode": "raw", 330 | "raw": "", 331 | "options": { 332 | "raw": { 333 | "language": "json" 334 | } 335 | } 336 | }, 337 | "url": { 338 | "raw": "{{host}}/", 339 | "host": [ 340 | "{{host}}" 341 | ], 342 | "path": [ 343 | "" 344 | ] 345 | } 346 | }, 347 | "response": [] 348 | } 349 | ], 350 | "event": [ 351 | { 352 | "listen": "prerequest", 353 | "script": { 354 | "type": "text/javascript", 355 | "exec": [ 356 | "let host = pm.collectionVariables.get(\"host\");", 357 | "let username = pm.collectionVariables.get(\"username\");", 358 | "let password = pm.collectionVariables.get(\"password\");", 359 | "", 360 | "let body = JSON.stringify({", 361 | " \"username\": username,", 362 | " \"password\": password", 363 | "})", 364 | "", 365 | "const postRequest = {", 366 | " url: host + '/login',", 367 | " method: 'POST',", 368 | " timeout: 0,", 369 | " header: {", 370 | " \"Content-Type\": \"application/json\",", 371 | " \"Accept\": \"*/*\"", 372 | " },", 373 | " body: body", 374 | "};", 375 | "pm.sendRequest(postRequest, function (err, res) {", 376 | " console.log(\"Login Response: \", res);", 377 | " if (err) console.log(\"Error: \", err)", 378 | " let authorization = res.headers.get('Authorization')", 379 | " pm.collectionVariables.set('authorization_token', authorization);", 380 | "});" 381 | ] 382 | } 383 | }, 384 | { 385 | "listen": "test", 386 | "script": { 387 | "type": "text/javascript", 388 | "exec": [ 389 | "" 390 | ] 391 | } 392 | } 393 | ], 394 | "variable": [ 395 | { 396 | "key": "host", 397 | "value": "localhost:8080", 398 | "type": "string" 399 | }, 400 | { 401 | "key": "authorization_token", 402 | "value": "", 403 | "type": "string" 404 | }, 405 | { 406 | "key": "username", 407 | "value": "lucas", 408 | "type": "string" 409 | }, 410 | { 411 | "key": "password", 412 | "value": "supersenha", 413 | "type": "string" 414 | } 415 | ] 416 | } -------------------------------------------------------------------------------- /mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # https://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /usr/local/etc/mavenrc ] ; then 40 | . /usr/local/etc/mavenrc 41 | fi 42 | 43 | if [ -f /etc/mavenrc ] ; then 44 | . /etc/mavenrc 45 | fi 46 | 47 | if [ -f "$HOME/.mavenrc" ] ; then 48 | . "$HOME/.mavenrc" 49 | fi 50 | 51 | fi 52 | 53 | # OS specific support. $var _must_ be set to either true or false. 54 | cygwin=false; 55 | darwin=false; 56 | mingw=false 57 | case "`uname`" in 58 | CYGWIN*) cygwin=true ;; 59 | MINGW*) mingw=true;; 60 | Darwin*) darwin=true 61 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 62 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 63 | if [ -z "$JAVA_HOME" ]; then 64 | if [ -x "/usr/libexec/java_home" ]; then 65 | export JAVA_HOME="`/usr/libexec/java_home`" 66 | else 67 | export JAVA_HOME="/Library/Java/Home" 68 | fi 69 | fi 70 | ;; 71 | esac 72 | 73 | if [ -z "$JAVA_HOME" ] ; then 74 | if [ -r /etc/gentoo-release ] ; then 75 | JAVA_HOME=`java-config --jre-home` 76 | fi 77 | fi 78 | 79 | if [ -z "$M2_HOME" ] ; then 80 | ## resolve links - $0 may be a link to maven's home 81 | PRG="$0" 82 | 83 | # need this for relative symlinks 84 | while [ -h "$PRG" ] ; do 85 | ls=`ls -ld "$PRG"` 86 | link=`expr "$ls" : '.*-> \(.*\)$'` 87 | if expr "$link" : '/.*' > /dev/null; then 88 | PRG="$link" 89 | else 90 | PRG="`dirname "$PRG"`/$link" 91 | fi 92 | done 93 | 94 | saveddir=`pwd` 95 | 96 | M2_HOME=`dirname "$PRG"`/.. 97 | 98 | # make it fully qualified 99 | M2_HOME=`cd "$M2_HOME" && pwd` 100 | 101 | cd "$saveddir" 102 | # echo Using m2 at $M2_HOME 103 | fi 104 | 105 | # For Cygwin, ensure paths are in UNIX format before anything is touched 106 | if $cygwin ; then 107 | [ -n "$M2_HOME" ] && 108 | M2_HOME=`cygpath --unix "$M2_HOME"` 109 | [ -n "$JAVA_HOME" ] && 110 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 111 | [ -n "$CLASSPATH" ] && 112 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 113 | fi 114 | 115 | # For Mingw, ensure paths are in UNIX format before anything is touched 116 | if $mingw ; then 117 | [ -n "$M2_HOME" ] && 118 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 119 | [ -n "$JAVA_HOME" ] && 120 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 121 | fi 122 | 123 | if [ -z "$JAVA_HOME" ]; then 124 | javaExecutable="`which javac`" 125 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 126 | # readlink(1) is not available as standard on Solaris 10. 127 | readLink=`which readlink` 128 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 129 | if $darwin ; then 130 | javaHome="`dirname \"$javaExecutable\"`" 131 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 132 | else 133 | javaExecutable="`readlink -f \"$javaExecutable\"`" 134 | fi 135 | javaHome="`dirname \"$javaExecutable\"`" 136 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 137 | JAVA_HOME="$javaHome" 138 | export JAVA_HOME 139 | fi 140 | fi 141 | fi 142 | 143 | if [ -z "$JAVACMD" ] ; then 144 | if [ -n "$JAVA_HOME" ] ; then 145 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 146 | # IBM's JDK on AIX uses strange locations for the executables 147 | JAVACMD="$JAVA_HOME/jre/sh/java" 148 | else 149 | JAVACMD="$JAVA_HOME/bin/java" 150 | fi 151 | else 152 | JAVACMD="`\\unset -f command; \\command -v java`" 153 | fi 154 | fi 155 | 156 | if [ ! -x "$JAVACMD" ] ; then 157 | echo "Error: JAVA_HOME is not defined correctly." >&2 158 | echo " We cannot execute $JAVACMD" >&2 159 | exit 1 160 | fi 161 | 162 | if [ -z "$JAVA_HOME" ] ; then 163 | echo "Warning: JAVA_HOME environment variable is not set." 164 | fi 165 | 166 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 167 | 168 | # traverses directory structure from process work directory to filesystem root 169 | # first directory with .mvn subdirectory is considered project base directory 170 | find_maven_basedir() { 171 | 172 | if [ -z "$1" ] 173 | then 174 | echo "Path not specified to find_maven_basedir" 175 | return 1 176 | fi 177 | 178 | basedir="$1" 179 | wdir="$1" 180 | while [ "$wdir" != '/' ] ; do 181 | if [ -d "$wdir"/.mvn ] ; then 182 | basedir=$wdir 183 | break 184 | fi 185 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 186 | if [ -d "${wdir}" ]; then 187 | wdir=`cd "$wdir/.."; pwd` 188 | fi 189 | # end of workaround 190 | done 191 | echo "${basedir}" 192 | } 193 | 194 | # concatenates all lines of a file 195 | concat_lines() { 196 | if [ -f "$1" ]; then 197 | echo "$(tr -s '\n' ' ' < "$1")" 198 | fi 199 | } 200 | 201 | BASE_DIR=`find_maven_basedir "$(pwd)"` 202 | if [ -z "$BASE_DIR" ]; then 203 | exit 1; 204 | fi 205 | 206 | ########################################################################################## 207 | # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 208 | # This allows using the maven wrapper in projects that prohibit checking in binary data. 209 | ########################################################################################## 210 | if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then 211 | if [ "$MVNW_VERBOSE" = true ]; then 212 | echo "Found .mvn/wrapper/maven-wrapper.jar" 213 | fi 214 | else 215 | if [ "$MVNW_VERBOSE" = true ]; then 216 | echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." 217 | fi 218 | if [ -n "$MVNW_REPOURL" ]; then 219 | jarUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" 220 | else 221 | jarUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" 222 | fi 223 | while IFS="=" read key value; do 224 | case "$key" in (wrapperUrl) jarUrl="$value"; break ;; 225 | esac 226 | done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" 227 | if [ "$MVNW_VERBOSE" = true ]; then 228 | echo "Downloading from: $jarUrl" 229 | fi 230 | wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" 231 | if $cygwin; then 232 | wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` 233 | fi 234 | 235 | if command -v wget > /dev/null; then 236 | if [ "$MVNW_VERBOSE" = true ]; then 237 | echo "Found wget ... using wget" 238 | fi 239 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 240 | wget "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" 241 | else 242 | wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" 243 | fi 244 | elif command -v curl > /dev/null; then 245 | if [ "$MVNW_VERBOSE" = true ]; then 246 | echo "Found curl ... using curl" 247 | fi 248 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 249 | curl -o "$wrapperJarPath" "$jarUrl" -f 250 | else 251 | curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f 252 | fi 253 | 254 | else 255 | if [ "$MVNW_VERBOSE" = true ]; then 256 | echo "Falling back to using Java to download" 257 | fi 258 | javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" 259 | # For Cygwin, switch paths to Windows format before running javac 260 | if $cygwin; then 261 | javaClass=`cygpath --path --windows "$javaClass"` 262 | fi 263 | if [ -e "$javaClass" ]; then 264 | if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 265 | if [ "$MVNW_VERBOSE" = true ]; then 266 | echo " - Compiling MavenWrapperDownloader.java ..." 267 | fi 268 | # Compiling the Java class 269 | ("$JAVA_HOME/bin/javac" "$javaClass") 270 | fi 271 | if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 272 | # Running the downloader 273 | if [ "$MVNW_VERBOSE" = true ]; then 274 | echo " - Running MavenWrapperDownloader.java ..." 275 | fi 276 | ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") 277 | fi 278 | fi 279 | fi 280 | fi 281 | ########################################################################################## 282 | # End of extension 283 | ########################################################################################## 284 | 285 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} 286 | if [ "$MVNW_VERBOSE" = true ]; then 287 | echo $MAVEN_PROJECTBASEDIR 288 | fi 289 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 290 | 291 | # For Cygwin, switch paths to Windows format before running java 292 | if $cygwin; then 293 | [ -n "$M2_HOME" ] && 294 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 295 | [ -n "$JAVA_HOME" ] && 296 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 297 | [ -n "$CLASSPATH" ] && 298 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 299 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 300 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 301 | fi 302 | 303 | # Provide a "standardized" way to retrieve the CLI args that will 304 | # work with both Windows and non-Windows executions. 305 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" 306 | export MAVEN_CMD_LINE_ARGS 307 | 308 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 309 | 310 | exec "$JAVACMD" \ 311 | $MAVEN_OPTS \ 312 | $MAVEN_DEBUG_OPTS \ 313 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 314 | "-Dmaven.home=${M2_HOME}" \ 315 | "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 316 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 317 | --------------------------------------------------------------------------------