Iza Car

Backend

Simples como viajar

A Iza Car é uma empresa que proporciona o aluguel de veículos aos seus usuários, sem burocracia. Assim como o slogan diz, simples como viajar. Promove uma experiência única aos seus clientes através das locações para que estes se sintam satisfeitos e excedam suas expectativas.

Este projeto é uma demonstração teórica e prática no que se refere a desenvolvimento de softwares corporativos abordando a linguagem Java e suas tecnologias e frameworks.

Pré-requisitos

Ferramentas

  • Design UML
  • Postgres ou similar
  • DBeaver
  • IntelliJ ou similar
  • Github desktop

Expert

Vídeos

Aula 14 - Obtenha parte do código clicando aqui.
Aula 26 - O conteúdo deste vídeo não possui áudio

application.properties

spring.application.name=iza-car-api

spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=false

spring.datasource.url=jdbc:postgresql://localhost:5432/izacar_db
spring.datasource.username=postgres
spring.datasource.password=postgres
spring.datasource.driver-class-name=org.postgresql.Driver

spring.jpa.hibernate.ddl-auto=update

Código Aula 14

import org.springframework.cglib.proxy.UndeclaredThrowableException;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
import tec.iza.car.infra.business.BusinessException;
import tec.iza.car.infra.http.Response;
import tec.iza.car.infra.http.ResponseFactory;

@RestControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {

  @ExceptionHandler(Exception.class)
  private ResponseEntity<Object> handleGeneral(Exception e, WebRequest request) {
      String message = "";
      if (e.getClass().isAssignableFrom(UndeclaredThrowableException.class)) {
          UndeclaredThrowableException exception = (UndeclaredThrowableException) e;
          Class<? extends Throwable> exceptionClass = exception.getUndeclaredThrowable().getClass();
          return handleBusinessException((BusinessException) exception.getUndeclaredThrowable(), request);
      } else {
          BusinessMessage be = BusinessMessage.E501;
          Response error = ResponseFactory.error(Integer.valueOf(HttpStatus.INTERNAL_SERVER_ERROR.value()), be.getMessage().concat(message), be.getSuggestion());
          HttpHeaders headers = new HttpHeaders();
          headers.setContentType(MediaType.APPLICATION_JSON);
          return handleExceptionInternal(e, error, headers, HttpStatus.INTERNAL_SERVER_ERROR, request);
      }

  }
  @ExceptionHandler({BusinessException.class})
  private ResponseEntity<Object> handleBusinessException(BusinessException be, WebRequest request) {
      Response error = ResponseFactory.error(be.getId(), be.getMessage(), be.getSuggestion());
      HttpHeaders headers = new HttpHeaders();
      headers.setContentType(MediaType.APPLICATION_JSON);
      ResponseEntity response = handleExceptionInternal(be, error, headers, HttpStatus.resolve(be.getHttpStatus()), request);
      return response;
  }
}

import org.springframework.http.HttpStatus;

public enum BusinessMessage {
  E500("500", "Erro não mapeado","Contacte o Suporte Técnico"){

      @Override
      public int getHttpStatus() {
          return 500;
      }
  },
  E501("501", "Erro ao tentar acessar o recurso","Contacte o Suporte Técnico"){

      @Override
      public int getHttpStatus() {
          return 500;
      }
  }
  ;
  private final String code;
  private final String message;
  private final String suggestion;

  private int httpStatus;

  private BusinessMessage(String code, String message, String suggestion) {
      this.code = code;
      this.message = message;
      this.suggestion = suggestion;
  }

  public int getHttpStatus() {
      return HttpStatus.CONFLICT.value();
  }

  public String getSuggestion() {
      return suggestion;
  }

  public String getCode() {
      return code;
  }
  public String getMessage() {
      return message;
  }
}

public class BusinessException extends RuntimeException {
  private String id;
  private String suggestion;
  public BusinessException(String id, String message,String suggestion){
      super(message);
      this.id = id;
      this.suggestion = suggestion;
  }

  public String getSuggestion() {
      return suggestion;
  }

  public String getId() {
      return id;
  }
  public int getHttpStatus(){
      return 409;
  }
}

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;

import java.io.Serializable;
import java.time.LocalDateTime;

@Data
@Schema(name="Status da requisição", description="Representação padrão do status das respostas HTTP disponíveis na API")
public class ResponseStatus {

  @Schema(description="Data\\Hora da resposta", nullable = false,example = "2022-06-30 16:10:21")
  @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
  @JsonSerialize(using = LocalDateTimeSerializer.class)
  @JsonDeserialize(using = LocalDateTimeDeserializer.class)
  LocalDateTime dateTime = LocalDateTime.now();

  @Schema(description="Confirmação de sucesso da resposta da requisição", nullable = false,example = "true", allowableValues ={"true","false"} )
  boolean success;
  @Schema(description="Mensagem que detalha a resposta devolvida", nullable = false,example = "Operação realizada com sucesso" )
  String message;
  @Schema(description="Código de sucesso ou baseado ao dicionário de erros da aplicação", nullable = false,example = "200" )
  Serializable code;
  @Schema(description="Mensagem que representa uma sugestão em caso de erro na requisição", nullable = false,example = "O campo: Nome é obrigatório" )
  String suggestion;
}

import org.springframework.http.HttpStatus;
import tec.iza.car.infra.business.BusinessException;
import tec.iza.car.infra.business.ConsultaSemRegistrosException;
import tec.iza.car.infra.business.RegistroNaoLocalizadoException;

import java.io.Serializable;
import java.util.Collection;
import java.util.Optional;

public class ResponseFactory {
  public static Response okOrNotFound(Object value) {
      return okOrNotFound(value, "Registro localizado com sucesso");
  }
  public static Response okOrNotFound(Optional optional) {
      return okOrNotFound(optional,"Registro localizado com sucesso");
  }
  public static Response okOrNotFound(Optional optional, String message) {
      if(optional.isPresent())
          return ok(optional.get(),message) ;
      else
          throw new RegistroNaoLocalizadoException();
  }
  public static Response okOrNotFound(Object value, String message) {
      RegistroNaoLocalizadoException exception = new RegistroNaoLocalizadoException();
      Optional.ofNullable(value).orElseThrow(() -> exception );
      return ok(value,message) ;
  }
  public static Response okOrNoContent(Object value) {
      ConsultaSemRegistrosException exception= new ConsultaSemRegistrosException();
      if(value==null)
          throw exception;

      String msg = "Consulta realizada com sucesso";
      if(value instanceof Collection){
          if(((Collection) value).isEmpty())
              throw exception;
          return ok(value,msg) ;
      }else
          return null;

  }
  public static Response ok(Object body) {
      return ok(body,"Consulta realizada com sucesso");
  }
  public static Response ok(Object body, String message) {
      return response(HttpStatus.OK.value(), body,message);
  }
  public static Response create(Object body, String message) {
      return response(HttpStatus.CREATED.value(), body,message);
  }
  private static Response response(Serializable code, Object body, String message) {
      return define(code,body,message,"",true);
  }

  public static Response error() {
      return error("Error","Contacte o Suporte Técnico");
  }
  public static Response exception(BusinessException be) {
      return error(be.getId(), be.getMessage(),be.getSuggestion());
  }
  public static Response error(String message, String suggestion) {
      return error(500,message,suggestion);
  }
  public static Response error(Serializable code,String message, String suggestion){
      return define(code,null, message, suggestion, false);
  }
  private static Response define(Serializable code,Object body, String message, String suggestion, boolean success){
      Response response = new Response();
      ResponseStatus status = new ResponseStatus();
      status.code =code;
      status.message = message;
      status.suggestion = suggestion;
      status.success = success;
      response.setStatus(status);
      response.setBody(body);
      return response;
  }
}

Código Template

Ao longo do seu envolvimento com implementações de funcionalidades de qualquer projeto de negócio, você perceberá uma singularidade e modelo de desenvolvimento. Abaixo, segue uma das abordagens muito utilizadas em projetos do dia-a-dia seguindo as representações como: Entity, DTO, Request, Response, Repository, Service, Controller e etc.
package tec.iza.car.model.marca;
import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;

@Entity
@Table(name = "tab_marca")
@Data
public class MarcaEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Setter(AccessLevel.NONE)
    private Integer id;
    @Column(nullable = false, length = 30)
    private String nome;
    @Column(nullable = true)
    private boolean excluido;
}
package tec.iza.car.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import tec.iza.car.model.marca.MarcaEntity;

import java.util.List;

public interface MarcaRepository extends JpaRepository <MarcaEntity, Integer> {
    List<MarcaEntity> findByExcluidoFalse();
}