Skip to content
This repository was archived by the owner on Nov 7, 2024. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
6225556
feat: inicia desenvolvimento do projeto
eduardo-mior Jun 6, 2022
0edb902
feat: inicia implementação do front-end
eduardo-mior Jun 7, 2022
3d87bca
Merge branch 'master' of https://github.com/eduardo-mior/dev-hiring-c…
eduardo-mior Jun 7, 2022
7a12c75
feat: implementa backend para salvar os dados retornados da API do Gi…
eduardo-mior Jun 9, 2022
4fc5a7c
feat: implementa integração do FrontEnd com o Cache do BackEnd
eduardo-mior Jun 9, 2022
1510583
feat: implementa telas de erro 404 e 500 para o FrontEnd
eduardo-mior Jun 9, 2022
47a16f5
feat: implementa middleware para tratar erros de 404 e 500 no BackEnd
eduardo-mior Jun 9, 2022
8ba60f2
feat: implementa rota /ping para testar a conectividade da API
eduardo-mior Jun 9, 2022
f398c26
feat: implementa Sentry na aplicação para monitoramento e trackamento…
eduardo-mior Jun 9, 2022
0a51582
feat: implementa pacote para carregamento das variavéis de ambiente d…
eduardo-mior Jun 9, 2022
f246407
feat: implementa favicon no frontend
eduardo-mior Jun 9, 2022
b73f706
feat: implementa loader e notificação de sucesso e erro
eduardo-mior Jun 9, 2022
7be6064
feat: implementa swagger para a documentação da API do BackEnd
eduardo-mior Jun 9, 2022
2fc3c78
refactor: melhora organização do código routes.go
eduardo-mior Jun 9, 2022
c5e0d32
refactor: renomeia arquivo para fazer sentido o nome do arquivo com o…
eduardo-mior Jun 9, 2022
dfa28bd
test: implementa testes e2e com postman
eduardo-mior Jun 9, 2022
3797432
feat: implementa Sentry na aplicação para monitoramento e trackamento…
eduardo-mior Jun 9, 2022
2e6acab
feat: melhora responsividade do projeto no mobile
eduardo-mior Jun 9, 2022
dc9bae5
test: adiciona testes unitarios do router do BackEnd
eduardo-mior Jun 9, 2022
dc04567
test: adiciona testes unitarios do router do FrontEnd
eduardo-mior Jun 9, 2022
e0776f8
ci: adiciona action do github para build e testes unitarios
eduardo-mior Jun 9, 2022
5cbef0b
chore: atualiza dependencias
eduardo-mior Jun 9, 2022
3dff0c8
ci: remove testes unitarios automaticos com go 1.17 pois o github não…
eduardo-mior Jun 9, 2022
ce0e72c
refactor: adiciona possibilidade de passar a porta por parametro na h…
eduardo-mior Jun 10, 2022
754d209
test: corrige testes unitarios
eduardo-mior Jun 10, 2022
0d0de43
test: adiciona testes unitários para o EndPoint healthcheck
eduardo-mior Jun 10, 2022
d7bc31c
refactor: renomeia arquivo escrito errado
eduardo-mior Jun 10, 2022
78e50fe
feat: adiciona mock da interface IRepositoryGitHub para ser usada nos…
eduardo-mior Jun 10, 2022
7081bfe
feat: implementa testes unitários nos EndPoints de HealthCheck, exclu…
eduardo-mior Jun 10, 2022
18521ae
feat: implementa testes unitários no EndPoints de salvar repositórios…
eduardo-mior Jun 10, 2022
97c3fbd
fix: corrige inicialização do servidor que não estava tratando corret…
eduardo-mior Jun 11, 2022
9a1a720
docs: ajusta documenta do swagger
eduardo-mior Jun 11, 2022
6c471d3
ci: adiciona dockerfile para buildar o projeto
eduardo-mior Jun 11, 2022
f6298a0
feat: melhora utilitario de conexão com o banco de dados para possibi…
eduardo-mior Jun 11, 2022
8ad9684
ci: adiciona docker-compose para buildar o projeto juntamente com o b…
eduardo-mior Jun 11, 2022
940ea0c
feat: adiciona tratamento de erro na função que inicializa o servidor…
eduardo-mior Jun 11, 2022
bf494ac
feat: melhora função de gravarErroNoSentry para também printar o erro…
eduardo-mior Jun 11, 2022
1c62e59
fix: altera ordenação da declaração das rotas e dos middlewares para …
eduardo-mior Jun 11, 2022
6479982
ci: altera a imagem do postgresql para postgresql-alpine pelo fato de…
eduardo-mior Jun 11, 2022
c6deb72
refactor: melhora mensagens de notificação de sucesso do frontend
eduardo-mior Jun 11, 2022
fc92f88
docs: atualiza readme com informações de como rodar o projeto localmente
eduardo-mior Jun 11, 2022
aaaaf5f
docs: atualiza readme colocando de volta as informações originais do …
eduardo-mior Jun 11, 2022
01f213e
feat: implementa modal para visualização de mais detalhes do repositório
eduardo-mior Jun 13, 2022
6d3b6c4
feat: melhora estilização do modal de informações detalhadas do repos…
eduardo-mior Jun 13, 2022
8eb63b8
feat: adiciona tamanho do repositório no modal de mais informações
eduardo-mior Jun 13, 2022
444e02a
feat: ajusta para o nome do repositório ser clicavel na table e abrir…
eduardo-mior Jun 13, 2022
4652e83
feat: melhora responsividade da aplicação no mobile
eduardo-mior Jun 13, 2022
3209f0c
feat: adiciona loader que estava faltando ao buscar repositórios no c…
eduardo-mior Jun 14, 2022
998e0d3
feat: adiciona EmptyState na tabela quando ela estiver vazia
eduardo-mior Jun 14, 2022
d38b438
docs: Adiciona screenshots do projeto no README
eduardo-mior Jun 14, 2022
fae8b4a
fix: ajusta <meta> tag description do site
eduardo-mior Sep 5, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
SENTRY_DSN=https://[email protected]/8877665
GIN_MODE=release
DB_USER=postgres
DB_PASSWORD=postgres
DB_NAME=ateliware
DB_HOST=localhost
DB_PORT=5433
27 changes: 27 additions & 0 deletions .github/workflows/build_and_tests.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: Build and Tests

on: [push]

jobs:

build_and_test:

strategy:
matrix:
go-version: [1.18.x, 1.17.x]
platform: [ubuntu-latest]

runs-on: ${{ matrix.platform }}

steps:
- uses: actions/checkout@v2

- uses: actions/setup-go@v2
with:
go-version: ${{ matrix.go-version }}

- name: Build with GO ${{ matrix.go-version }}
run: go build -v ./...

- name: Test with GO ${{ matrix.go-version }}
run: go test -v ./...
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.env
postgres
111 changes: 96 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,103 @@
# Desafio técnico para desenvolvedores

Construa uma nova aplicação, utilizando o framework de sua preferência (Ruby on Rails, Elixir Phoenix, Python Django ou Flask, NodeJS Sails, Java Spring, ASP.NET ou outro), a qual deverá conectar na API do GitHub e disponibilizar as seguintes funcionalidades:

- Botão para buscar e armazenar os repositórios destaques de 5 linguagens à sua escolha;
- Listar os repositórios encontrados;
- Visualizar os detalhes de cada repositório.

Botão para buscar e armazenar os repositórios destaques de 5 linguagens à sua escolha;
Listar os repositórios encontrados;
Visualizar os detalhes de cada repositório.
Alguns requisitos:

- Deve ser uma aplicação totalmente nova;
- A solução deve estar em um repositório público do GitHub;
- A aplicação deve armazenar as informações encontradas;
- Utilizar PostgreSQL, MySQL ou SQL Server;
- O deploy deve ser realizado, preferencialmente, no Heroku, AWS ou no Azure;
- A aplicação precisa ter testes automatizados;
- Preferenciamente dockerizar a aplicação;
- Por favor atualizar o readme da aplicação com passo a passo com instrução para subir o ambiente.

Deve ser uma aplicação totalmente nova;
A solução deve estar em um repositório público do GitHub;
A aplicação deve armazenar as informações encontradas;
Utilizar PostgreSQL, MySQL ou SQL Server;
O deploy deve ser realizado, preferencialmente, no Heroku, AWS ou no Azure;
A aplicação precisa ter testes automatizados;
Preferenciamente dockerizar a aplicação;
Por favor atualizar o readme da aplicação com passo a passo com instrução para subir o ambiente.
Quando terminar, faça um Pull Request neste repo e avise-nos por email.

**IMPORTANTE:** se você não conseguir finalizar o teste, por favor nos diga o motivo e descreva quais foram as suas dificuldades. Você pode também sugerir uma outra abordagem para avaliarmos seus skills técnicos, vender seu peixe, mostrar-nos do que é capaz.
IMPORTANTE: se você não conseguir finalizar o teste, por favor nos diga o motivo e descreva quais foram as suas dificuldades. Você pode também sugerir uma outra abordagem para avaliarmos seus skills técnicos, vender seu peixe, mostrar-nos do que é capaz.

# Implementação

## Ferramentas


Para o desenvolvimento do BackEnd do projeto foi utilizada a linguagem [GO](https://go.dev/) com as seguintes ferramentas:
- [GIN](https://github.com/gin-gonic/gin): Framework WEB para tratamento das requisições HTTP.
- [Swaggo](github.com/swaggo/gin-swagger): Geração automática e disponibilização da documentação no padrão OpenAPI com Swagger.
- [Testify](https://github.com/stretchr/testify): Conjunto de pacotes e funções utilitárias que ajudam no desenvolvimento dos testes unitários.
- [Mockery](https://github.com/vektra/mockery): Gerador de Mocks para facilitar o desenvolvimento dos testes unitários.
- [Postman](https://www.postman.com/): Ferramenda usada para criar os testes de ponta a ponta (e2e).
- [Sentry](https://sentry.io/): Plataforma para trackeamento e monitoramento de erros.

Para o desenvolvimento do FrontEnd do projeto foi utilizado a linguagem [GO](https://go.dev/) para inializar o servidor e server de proxy para as requisições HTTP, e para a parte do ClientSide foram utilizadas técnologias nativas da WEB, sendo elas, [HTML](https://developer.mozilla.org/pt-BR/docs/Web/HTML), [JavaScript](https://developer.mozilla.org/pt-BR/docs/Web/JavaScript) e [CSS](https://developer.mozilla.org/pt-BR/docs/Web/CSS), além disso foram usadas também as seguintes ferramentas:
- [GIN](https://github.com/gin-gonic/gin): Framework WEB ServerSide para tratamento das requisições HTTP.
- [Bootstrap](https://getbootstrap.com/): Biblioteca de utilitários e componentes CSS para desenvolvimento de interfaces responsivas e amigáveis.
- [jQuery](https://jquery.com/): Biblioteca de utilitários e facilitadores para JavaScript que simplifica e agiliza o desenvolvimento de aplicações para Web.
- [Sentry](https://sentry.io/): Plataforma para trackeamento e monitoramento de erros.


## Arquitetura
A arquitetura implantada no projeto usa a linguagem GO como base para o desenvolvimento do projeto, ela atua tanto como servidor BackEnd quanto FrontEnd. Para o armazenamento dos dados o banco de dados escolhido foi [PostgreSQL](https://www.postgresql.org/) e para a conteinerização da aplicação foi utilizado [Docker](https://www.docker.com/) juntamente com [DockerCompose](https://docs.docker.com/compose/) para a gerência de míltiplos containers.

Todo o tráfego de dados entre o FrontEnd e o BackEnd ocorre no padrão `application/json`.


## Como acessar/executar o projeto
Para executar o projeto localmente, você deve primeiro clonar o projeto usando:
```bash
$ git clone https://github.com/eduardo-mior/dev-hiring-challenge.git
```


Atenção! Antes de tentar inicializar o projeto, certifique-se de que as portas 80, 8080 e 5432 estão disponíveis na sua maquina!


Após ter clonado o projeto você precisará criar um arquivo `.env` na raiz do projeto, esse arquivo irá conter as mesmas chaves e valores do `.env.example`.

Após ter criado o arquivo `.env` você pode inicializar o servidor de três maneiras diferentes:


### 1) Usando docker-compose
Se você tiver o `docker-compose` instalado basta executar seguinte comando:
```bash
$ docker-compose up -d
```


### 2) Usando docker
Se você não tiver o `docker-compose` instalado porém tiver o `docker` instalado, basta executar os seguintes comandos:
```bash
$ docker build . --tag desafio_ateliware:latest
```
```bash
$ docker run -d --name postgres --volume "${PWD}/postgres:/var/lib/postgresql/data/" --volume "${PWD}/backend/sql/init.sql:/docker-entrypoint-initdb.d/init.sql" -p 5432:5432 --env POSTGRES_USER=postgres --env POSTGRES_PASSWORD=masterkey postgres:9.6-alpine
```
```bash
$ docker run -d --name desafio-ateliware --env-file .env --link postgres -p 80:80 -p 8080:8080 desafio_ateliware
```
Atenção! Dependendo do terminal que você estiver rodando os comandos e dependendo do seu sistema operacional talvez você precise trocar o `${PWD}` por `$(pwd)` ou `%cd%`. Para mais informações [clique aqui](https://stackoverflow.com/questions/41485217/mount-current-directory-as-a-volume-in-docker-on-windows-10).


### 3) Usando o próprio GO
Se você tiver o GO e o PostgreSQL insalado na sua maquina você pode executar manualmente o SQL do arquivo `init.sql` e depois executar o arquivo `main.go`:
```bash
$ go run main.go
```


## Screenshots
Veja abaixo algumas screenshots do projeto:
### ⁃⁃⁃⁃⁃⁃⁃⁃⁃
![image](https://user-images.githubusercontent.com/32434418/173470350-10621e49-1a93-476a-a674-646553a984ff.png)
### ⁃⁃⁃⁃⁃⁃⁃⁃⁃
![image](https://user-images.githubusercontent.com/32434418/173470366-0eecac31-b2ce-4e62-bffe-7b48f81e9b42.png)
### ⁃⁃⁃⁃⁃⁃⁃⁃⁃
![image](https://user-images.githubusercontent.com/32434418/173470396-8e4915a7-cdc3-40b2-b6d1-29d81e5c4396.png)
### ⁃⁃⁃⁃⁃⁃⁃⁃⁃
![image](https://user-images.githubusercontent.com/32434418/173470414-fa7e799c-6427-4afa-8eca-fb78bd3972e9.png)
### ⁃⁃⁃⁃⁃⁃⁃⁃⁃
![image](https://user-images.githubusercontent.com/32434418/173469985-ecb0f5c6-f652-422e-b0f2-7c61b3f4b56a.png)
![image](https://user-images.githubusercontent.com/32434418/173470045-95ed2d0f-6397-44bd-ae91-54d278cfac2b.png)

37 changes: 37 additions & 0 deletions backend/controllers/buscarRepositoriosController.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package controllers

import (
"desafio_ateliware/backend/domain"
"desafio_ateliware/backend/models"
"desafio_ateliware/backend/util"

"github.com/gin-gonic/gin"
)

// @Tags GitHub
// @Summary Buscar todos os repositórios de uma linguagem
// @Description Busca todos os repositórios de uma linguagem de programação.
// @Produce json
// @Param linguagem query string true "Linguagem"
// @Success 200 {array} models.Repositorio
// @Failure 400,500 {object} models.Error
// @Router /repositorios [get]
func BuscarRepositorios(repositoryGitHub domain.IRepoitoryGitHub) gin.HandlerFunc {
return func(c *gin.Context) {

linguagem := c.Query("linguagem")
if linguagem == "" {
c.AbortWithStatusJSON(400, models.Error{Error: "Você deve informar a linguagem de programação de que você quer buscar os repositórios"})
return
}

repositorios, err := repositoryGitHub.BuscarRepositorios(linguagem)
if err != nil {
util.GravarErroNoSentry(err, c)
c.AbortWithStatusJSON(500, models.Error{Error: "Ocorreu um erro interno ao tentar buscar os repositórios da linguagem"})
return
}

c.JSON(200, repositorios)
}
}
77 changes: 77 additions & 0 deletions backend/controllers/buscarRepositoriosController.go_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package controllers

import (
"desafio_ateliware/backend/models"
"desafio_ateliware/backend/repositories/mocks"
"encoding/json"
"errors"
"net/http"
"net/http/httptest"
"testing"

"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
)

func TestBuscarRepositoriosSucesso(t *testing.T) {

response := httptest.NewRecorder()
c, _ := gin.CreateTestContext(response)
c.Request, _ = http.NewRequest("GET", "http://localhost/repositorios?linguagem=java", nil)

expectedResponseRepositorios := []models.Repositorio{{Nome: "Teste", Linguagem: "java", Tamanho: 9000, Estrelas: 100, Forks: 20}}

repositoryGitHub := new(mocks.IRepoitoryGitHub)
repositoryGitHub.On("BuscarRepositorios", "java").Return(expectedResponseRepositorios, nil)
BuscarRepositorios(repositoryGitHub)(c)

responseBody := []models.Repositorio{}
err := json.NewDecoder(response.Body).Decode(&responseBody)
assert.Equal(t, nil, err)

assert.Equal(t, 200, response.Code)
assert.Equal(t, expectedResponseRepositorios, responseBody)

}

func TestBuscarRepositoriosErroLinguagemNaoInformada(t *testing.T) {

response := httptest.NewRecorder()
c, _ := gin.CreateTestContext(response)
c.Request, _ = http.NewRequest("GET", "http://localhost/repositorios?yyy=xxx", nil)

expectedResponseError := models.Error{Error: "Você deve informar a linguagem de programação de que você quer buscar os repositórios"}

BuscarRepositorios(nil)(c)

responseBody := models.Error{}
err := json.NewDecoder(response.Body).Decode(&responseBody)
assert.Equal(t, nil, err)

assert.Equal(t, 400, response.Code)
assert.Equal(t, expectedResponseError, responseBody)

}

func TestBuscarRepositoriosErroInesperado(t *testing.T) {

response := httptest.NewRecorder()
c, _ := gin.CreateTestContext(response)
c.Request, _ = http.NewRequest("GET", "http://localhost/repositorios?linguagem=java", nil)

errMsg := "Ocorreu um erro interno ao tentar buscar os repositórios da linguagem"
err := errors.New(errMsg)
expectedResponseError := models.Error{Error: errMsg}

repositoryGitHub := new(mocks.IRepoitoryGitHub)
repositoryGitHub.On("BuscarRepositorios", "java").Return(nil, err)
BuscarRepositorios(repositoryGitHub)(c)

responseBody := models.Error{}
err = json.NewDecoder(response.Body).Decode(&responseBody)
assert.Equal(t, nil, err)

assert.Equal(t, 500, response.Code)
assert.Equal(t, expectedResponseError, responseBody)

}
37 changes: 37 additions & 0 deletions backend/controllers/excluirRepositoriosController.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package controllers

import (
"desafio_ateliware/backend/domain"
"desafio_ateliware/backend/models"
"desafio_ateliware/backend/util"

"github.com/gin-gonic/gin"
)

// @Tags GitHub
// @Summary Excluir todos os repositórios de uma linguagem
// @Description Exclui todos os repositórios de uma linguagem de programação.
// @Produce json
// @Param linguagem query string true "Linguagem"
// @Success 200 {object} models.Success
// @Failure 400,500 {object} models.Error
// @Router /repositorios [delete]
func ExcluirRepositorios(repositoryGitHub domain.IRepoitoryGitHub) gin.HandlerFunc {
return func(c *gin.Context) {

linguagem := c.Query("linguagem")
if linguagem == "" {
c.AbortWithStatusJSON(400, models.Error{Error: "Você deve informar a linguagem de programação de que você quer excluir os repositórios"})
return
}

err := repositoryGitHub.ExcluirRepositorios(linguagem)
if err != nil {
util.GravarErroNoSentry(err, c)
c.AbortWithStatusJSON(500, models.Error{Error: "Ocorreu um erro interno ao tentar excluir os repositórios da linguagem"})
return
}

c.JSON(200, models.Success{Message: "Repositórios excluídos com sucesso"})
}
}
77 changes: 77 additions & 0 deletions backend/controllers/excluirRepositoriosController_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package controllers

import (
"desafio_ateliware/backend/models"
"desafio_ateliware/backend/repositories/mocks"
"encoding/json"
"errors"
"net/http"
"net/http/httptest"
"testing"

"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
)

func TestExcluirRepositoriosSucesso(t *testing.T) {

response := httptest.NewRecorder()
c, _ := gin.CreateTestContext(response)
c.Request, _ = http.NewRequest("DELETE", "http://localhost/repositorios?linguagem=java", nil)

expectedResponseRepositorios := models.Success{Message: "Repositórios excluídos com sucesso"}

repositoryGitHub := new(mocks.IRepoitoryGitHub)
repositoryGitHub.On("ExcluirRepositorios", "java").Return(nil)
ExcluirRepositorios(repositoryGitHub)(c)

responseBody := models.Success{}
err := json.NewDecoder(response.Body).Decode(&responseBody)
assert.Equal(t, nil, err)

assert.Equal(t, 200, response.Code)
assert.Equal(t, expectedResponseRepositorios, responseBody)

}

func TestExcluirRepositoriosErroLinguagemNaoInformada(t *testing.T) {

response := httptest.NewRecorder()
c, _ := gin.CreateTestContext(response)
c.Request, _ = http.NewRequest("DELETE", "http://localhost/repositorios?yyy=xxx", nil)

expectedResponseError := models.Error{Error: "Você deve informar a linguagem de programação de que você quer excluir os repositórios"}

ExcluirRepositorios(nil)(c)

responseBody := models.Error{}
err := json.NewDecoder(response.Body).Decode(&responseBody)
assert.Equal(t, nil, err)

assert.Equal(t, 400, response.Code)
assert.Equal(t, expectedResponseError, responseBody)

}

func TestExcluirRepositoriosErroInesperado(t *testing.T) {

response := httptest.NewRecorder()
c, _ := gin.CreateTestContext(response)
c.Request, _ = http.NewRequest("DELETE", "http://localhost/repositorios?linguagem=java", nil)

errMsg := "Ocorreu um erro interno ao tentar excluir os repositórios da linguagem"
err := errors.New(errMsg)
expectedResponseError := models.Error{Error: errMsg}

repositoryGitHub := new(mocks.IRepoitoryGitHub)
repositoryGitHub.On("ExcluirRepositorios", "java").Return(err)
ExcluirRepositorios(repositoryGitHub)(c)

responseBody := models.Error{}
err = json.NewDecoder(response.Body).Decode(&responseBody)
assert.Equal(t, nil, err)

assert.Equal(t, 500, response.Code)
assert.Equal(t, expectedResponseError, responseBody)

}
Loading