<repo_name>oiagorodrigues/ds-bootcamp-java-task-1<file_sep>/README.md
# DS Client API

Simple Client REST API made as the first task of the DevSuperior Spring React Bootcamp, using Spring Boot and persiting in a memory database using Spring Data JPA.

## How to install

Clone this repo
```bash
git clone https://github.com/oiagorodrigues/dsbootcamp-java-first-task.git

or 

git clone <EMAIL>:oiagorodrigues/dsbootcamp-java-first-task.git
```

Open it in your favorite IDE. 
Recommended using [Intellij](https://www.jetbrains.com/pt-br/idea/), [STS](https://spring.io/tools) or [VSCode](https://code.visualstudio.com/).

## Learning topics

- Creating a Data Layer with Entity|Model, Repository and Service
- Creating a API layer with Controller and DTO
- Creating custom Exceptions, and StandarError and an ExceptionHandler
- Seeing the database

## Conceptual model

<img src="https://raw.githubusercontent.com/oiagorodrigues/dsbootcamp-java-first-task/main/conceptual_model.jpg" width="300">

## Technologies

- Java 11
- Spring Boot 2.4.6
- Spring Data JPA
- H2 Database

## Endpoints

### Client [[Postman docs]](https://documenter.getpostman.com/view/6767905/TzXzEcpC#cf7c6d12-e9b6-4ec5-8a8a-80e51297d65b)

#### Add a client

> POST - /clients

Example Request

```json
{
    "name": "<NAME>",
    "cpf": "12345678901",
    "income": 6500.0,
    "birthDate": "1994-07-20T10:30:00Z",
    "children": 2
}
```

Example Response

```json
// 201 CREATED
// Location: /clients/11

{
  "id": 11,
  "name": "<NAME>",
  "cpf": "12345678901",
  "income": 6500,
  "birthDate": "1994-07-20T10:30:00Z",
  "children": 2
}
```

#### Fetch Clients (Pageable)

> GET - /clients

Params

- **page** - Gets a list specified by the page number. Default = 0

- **linesPerPage** - Gets the size of the list specified. Default = 12

- **direction** - Gets the list sorted by a direction. [ASC, DESC] Default = DESC

- **orderBy** - Gets the list ordered by the fields of the Client. Default = id.
```

Example Response

```json
// 200 OK

 {
    "content": [
    {
      "id": 10,
      "name": "<NAME>",
      "cpf": "58298663900",
      "income": 20000,
      "birthDate": "1968-08-02T20:50:07Z",
      "children": 2
    },
    {
      "id": 8,
      "name": "<NAME>",
      "cpf": "38294706221",
      "income": 3000,
      "birthDate": "1953-11-09T20:50:07Z",
      "children": 1
    },
    {
      "id": 5,
      "name": "<NAME>",
      "cpf": "58093464565",
      "income": 11000,
      "birthDate": "1982-06-14T20:50:07Z",
      "children": 5
    },
    {
      "id": 6,
      "name": "<NAME>",
      "cpf": "54636645529",
      "income": 1000,
      "birthDate": "1949-09-07T20:50:07Z",
      "children": 1
    },
    {
      "id": 2,
      "name": "<NAME>",
      "cpf": "74665963398",
      "income": 1070,
      "birthDate": "1963-07-13T20:50:07Z",
      "children": 1
    },
    {
      "id": 9,
      "name": "<NAME>",
      "cpf": "81025624122",
      "income": 3200,
      "birthDate": "1989-03-22T20:50:07Z",
      "children": 4
    }
  ],
    "pageable": {
    "sort": {
      "sorted": true,
      "unsorted": false,
      "empty": false
    },
    "pageSize": 6,
    "pageNumber": 0,
    "offset": 0,
    "unpaged": false,
    "paged": true
  },
  "last": false,
  "totalPages": 2,
  "totalElements": 10,
  "sort": {
    "sorted": true,
    "unsorted": false,
    "empty": false
  },
  "first": true,
  "numberOfElements": 6,
  "size": 6,
  "number": 0,
  "empty": false
 }
```

#### Fetch Client by Id

> GET - /clients/{id}

Example Response

```json
// 200 OK

{
  "id": 10,
  "name": "<NAME>",
  "cpf": "58298663900",
  "income": 20000,
  "birthDate": "1968-08-02T20:50:07Z",
  "children": 2
}
```

#### Update Client

> PUT - /clients/{id}

Example Request

```json
{
    "name": "<NAME>",
    "cpf": "12345678901",
    "income": 6500.0,
    "birthDate": "1994-07-20T10:30:00Z",
    "children": 2
}
```

Example Response

```json
// 200 OK

{
  "id": 1,
  "name": "<NAME>",
  "cpf": "12345678901",
  "income": 6500,
  "birthDate": "1994-07-20T10:30:00Z",
  "children": 2
}
```

#### Delete Client

> DELETE - /clients/{id}

Example Response

```json
// 201 NO CONTENT
```
<file_sep>/src/main/java/dev/iagorodrigues/clientapi/services/ClientService.java
package dev.iagorodrigues.clientapi.services;

import dev.iagorodrigues.clientapi.dto.ClientDTO;
import dev.iagorodrigues.clientapi.entities.Client;
import dev.iagorodrigues.clientapi.exceptions.ResourceNotFoundException;
import dev.iagorodrigues.clientapi.repositories.ClientRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityNotFoundException;
import java.util.Optional;

@Service
public class ClientService {

    @Autowired
    private ClientRepository clientRepository;

    @Transactional(readOnly = true)
    public Page<ClientDTO> findAll(PageRequest pageRequest) {
        return clientRepository.findAll(pageRequest).map(ClientDTO::new);
    }

    @Transactional(readOnly = true)
    public ClientDTO findById(Long id) {
        return clientRepository.findById(id)
                .map(ClientDTO::new)
                .orElseThrow(() -> new ResourceNotFoundException("Client doesn't exist"));
    }

    @Transactional
    public ClientDTO insert(ClientDTO clientDTO) {
        Client client = new Client();
        mapClientDtoToEntity(clientDTO, client);
        client = clientRepository.save(client);
        return new ClientDTO(client);
    }

    @Transactional
    public ClientDTO update(Long id, ClientDTO clientDTO) {
        try {
            Client client = clientRepository.getOne(id);
            mapClientDtoToEntity(clientDTO, client);
            client = clientRepository.save(client);
            return new ClientDTO(client);
        } catch (EntityNotFoundException ex) {
            throw new ResourceNotFoundException("Client doesn't exist.");
        }
    }

    @Transactional
    public void delete(Long id) {
        try {
            clientRepository.deleteById(id);
        } catch (Exception ex) {
            throw new ResourceNotFoundException("Client doesn't exist.");
        }
    }

    private void mapClientDtoToEntity(ClientDTO clientDTO, Client client) {
        client.setName(clientDTO.getName());
        client.setChildren(clientDTO.getChildren());
        client.setCpf(clientDTO.getCpf());
        client.setIncome(clientDTO.getIncome());
        client.setBirthDate(clientDTO.getBirthDate());
    }

}
<file_sep>/src/main/java/dev/iagorodrigues/clientapi/controllers/ClientController.java
package dev.iagorodrigues.clientapi.controllers;

import dev.iagorodrigues.clientapi.dto.ClientDTO;
import dev.iagorodrigues.clientapi.services.ClientService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;

import java.net.URI;

@RestController
@RequestMapping("/clients")
public class ClientController {

    @Autowired
    private ClientService clientService;

    @GetMapping
    public ResponseEntity<Page<ClientDTO>> fetchClients(
            @RequestParam(value = "page", defaultValue = "0") Integer page,
            @RequestParam(value = "linesPerPage", defaultValue = "12") Integer linesPerPage,
            @RequestParam(value = "direction", defaultValue = "DESC") String direction,
            @RequestParam(value = "orderBy", defaultValue = "id") String orderBy
    ) {
        PageRequest pageRequest = PageRequest.of(page, linesPerPage, Sort.Direction.valueOf(direction), orderBy);
        Page<ClientDTO> clients = clientService.findAll(pageRequest);
        return ResponseEntity.ok().body(clients);
    }

    @GetMapping("/{id}")
    public ResponseEntity<ClientDTO> fetchClientById(@PathVariable Long id) {
        ClientDTO clientDTO = clientService.findById(id);
        return ResponseEntity.ok(clientDTO);
    }

    @PostMapping
    public ResponseEntity<ClientDTO> createClient(@RequestBody ClientDTO clientDTO) {
        ClientDTO createdClientDto = clientService.insert(clientDTO);

        URI uri = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}")
                .buildAndExpand(createdClientDto.getId()).toUri();

        return ResponseEntity.created(uri).body(createdClientDto);
    }

    @PutMapping("/{id}")
    public ResponseEntity<ClientDTO> updateClient(@PathVariable Long id, @RequestBody ClientDTO clientDTO) {
        ClientDTO updatedClientDto = clientService.update(id, clientDTO);
        return ResponseEntity.ok(updatedClientDto);
    }

    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteClient(@PathVariable Long id) {
        clientService.delete(id);
        return ResponseEntity.noContent().build();
    }

}
