GeoSense API é uma solução RESTful desenvolvida em .NET para o gerenciamento de motos, vagas, pátios e usuários em ambientes de manutenção e estacionamento. O projeto utiliza arquitetura em camadas, Entity Framework Core, Oracle como banco de dados e documentação completa via Swagger/OpenAPI.
- Enzo Giuseppe Marsola – RM: 556310, Turma: 2TDSPK
- Rafael de Souza Pinto – RM: 555130, Turma: 2TDSPY
- Luiz Paulo F. Fernandes – RM: 555497, Turma: 2TDSPF
O domínio foi escolhido para atender à necessidade de controle eficiente do fluxo de motos em pátios de manutenção, oficinas ou estacionamentos. O sistema permite cadastro, alocação e histórico de motos, gestão de vagas, controle de usuários com diferentes permissões.
A arquitetura segue boas práticas REST, separação de responsabilidades (camadas Controller, Service, Repository), e utiliza recursos avançados como paginação, HATEOAS, DTOs e exemplos interativos no Swagger.
-
Clonar o Repositório:
git clone https://github.com/MarsoL4/geosense-api.git cd geosense-api -
Configurar o Banco de Dados:
Edite o arquivoGeoSense.API/appsettings.jsoncom sua string de conexão Oracle em"ConnectionStrings:Oracle". -
Restaurar os Pacotes e Compilar:
dotnet restore dotnet build
-
Aplicar Migrations no Banco de Dados:
dotnet ef database update --project GeoSense.API
-
Executar a API:
dotnet run --project GeoSense.API
Acesse a documentação Swagger em:
http://localhost:5194/swaggerouhttps://localhost:7150/swagger -
Rodar Testes Automatizados:
dotnet test
O projeto inclui:
- Testes unitários (xUnit) abrangendo lógica de negócio e fluxo de controle principal das entidades.
- Testes de integração reais com WebApplicationFactory garantindo funcionamento dos principais endpoints.
- Para rodar todos os testes, utilize o comando acima; não é necessário banco externo (os testes usam banco in-memory).
- Todos os endpoints (exceto
/swaggere/health) exigem a inclusão do header de API Key:
GeoSense-Api-Key: SEGREDO-GEOSENSE-123(valor configurado emappsettings.json). - Para propósitos de testes e demonstração local, a chave está mantida no arquivo de configuração do repositório.
- Endpoint de health check disponível em:
GET /health - Resposta em JSON exibindo o status da aplicação e do banco.
- Exemplo de resposta esperada:
{
"status": "Healthy",
"totalDuration": "00:00:01.1203756",
"entries": {
"Database": {
"status": "Healthy",
"duration": "00:00:01.0069991",
"tags": []
}
}
}- Endpoints versionados por URL, no padrão:
/api/v1/[controller]
- Moto: Controle de motos cadastradas, informações de placa, chassi, modelo e vaga alocada.
- Vaga: Gerenciamento de vagas disponíveis em pátios, incluindo status e tipo.
- Usuário: Cadastro de usuários do sistema, com controle de papéis (administrador, mecânico) e autenticação.
- Pátio: Cadastro e gestão dos pátios onde as vagas são distribuídas.
- GET
/api/v1/moto?page=1&pageSize=10 - Resposta:
{ "items": [ { "id": 1, "modelo": "Honda CG 160", "placa": "ABC1D23", "chassi": "9C2JC4110JR000001", "problemaIdentificado": "Motor com ruído excessivo", "risco": "ALTO", "vagaId": 1 } ], "totalCount": 1, "page": 1, "pageSize": 10, "links": [ { "rel": "self", "method": "GET", "href": "/api/v1/moto?page=1&pageSize=10" } ] }
- GET
/api/v1/moto/{id} - Resposta:
{ "id": 1, "modelo": "Honda CG 160", "placa": "ABC1D23", "chassi": "9C2JC4110JR000001", "problemaIdentificado": "Motor com ruído excessivo", "risco": "ALTO", "vagaId": 1 }
- POST
/api/v1/moto - Payload de exemplo:
{ "modelo": "Honda CG 160", "placa": "ABC1D23", "chassi": "9C2JC4110JR000001", "problemaIdentificado": "Motor com ruído excessivo", "vagaId": 1 } - Resposta:
{ "mensagem": "Moto cadastrada com sucesso.", "dados": { "id": 1, "modelo": "Honda CG 160", "placa": "ABC1D23", "chassi": "9C2JC4110JR000001", "problemaIdentificado": "Motor com ruído excessivo", "risco": "ALTO", "vagaId": 1 } }
- PUT
/api/v1/moto/{id} - Payload igual ao POST
- Resposta:
{ "mensagem": "Moto atualizada com sucesso.", "dados": { "id": 1, "modelo": "Honda CG 160", "placa": "ABC1D23", "chassi": "9C2JC4110JR000001", "problemaIdentificado": "Motor com ruído excessivo", "risco": "ALTO", "vagaId": 1 } }
- DELETE
/api/v1/moto/{id} - Resposta:
{ "mensagem": "Moto deletada com sucesso." }
- GET
/api/v1/vaga?page=1&pageSize=10 - Resposta:
{ "items": [ { "id": 1, "numero": 101, "tipo": 0, "status": 0, "patioId": 1, "motoId": 2 } ], "totalCount": 1, "page": 1, "pageSize": 10, "links": [ { "rel": "self", "method": "GET", "href": "/api/v1/vaga?page=1&pageSize=10" } ] }
- GET
/api/v1/vaga/{id} - Resposta:
{ "id": 1, "numero": 101, "tipo": 0, "status": 0, "patioId": 1, "motoId": 2 }
- POST
/api/v1/vaga - Payload de exemplo:
{ "numero": 101, "tipo": 0, "status": 0, "patioId": 1 } - Resposta:
{ "mensagem": "Vaga cadastrada com sucesso.", "dados": { "id": 1, "numero": 101, "tipo": 0, "status": 0, "patioId": 1, "motoId": null } }
- PUT
/api/v1/vaga/{id} - Payload igual ao POST
- Resposta:
{ "mensagem": "Vaga atualizada com sucesso.", "dados": { "id": 1, "numero": 101, "tipo": 0, "status": 1, "patioId": 1, "motoId": 2 } }
- DELETE
/api/v1/vaga/{id} - Resposta:
{ "mensagem": "Vaga deletada com sucesso." }
- GET
/api/v1/patio?page=1&pageSize=10 - Resposta:
{ "items": [ { "id": 1, "nome": "Pátio Central" } ], "totalCount": 1, "page": 1, "pageSize": 10, "links": [ { "rel": "self", "method": "GET", "href": "/api/v1/patio?page=1&pageSize=10" } ] }
- GET
/api/v1/patio/{id} - Resposta:
{ "id": 1, "nome": "Pátio Central", "vagas": [ { "id": 1, "numero": 101, "tipo": 0, "status": 0, "patioId": 1, "motoId": null } ] }
- POST
/api/v1/patio - Payload de exemplo:
{ "nome": "Pátio Central" } - Resposta:
{ "mensagem": "Pátio cadastrado com sucesso.", "dados": { "id": 1, "nome": "Pátio Central" } }
- PUT
/api/v1/patio/{id} - Payload igual ao POST
- Resposta:
{ "mensagem": "Pátio atualizado com sucesso.", "dados": { "id": 1, "nome": "Pátio Central" } }
- DELETE
/api/v1/patio/{id} - Resposta:
{ "mensagem": "Pátio deletado com sucesso." }
- GET
/api/v1/usuario?page=1&pageSize=10 - Resposta:
{ "items": [ { "id": 1, "nome": "Rafael de Souza Pinto", "email": "rafael.pinto@exemplo.com", "senha": "12345678", "tipo": 0 } ], "totalCount": 1, "page": 1, "pageSize": 10, "links": [ { "rel": "self", "method": "GET", "href": "/api/v1/usuario?page=1&pageSize=10" } ] }
- GET
/api/v1/usuario/{id} - Resposta:
{ "id": 1, "nome": "Rafael de Souza Pinto", "email": "rafael.pinto@exemplo.com", "senha": "12345678", "tipo": 0 }
- POST
/api/v1/usuario - Payload de exemplo:
{ "nome": "Rafael de Souza Pinto", "email": "rafael.pinto@exemplo.com", "senha": "12345678", "tipo": 0 } - Resposta:
{ "mensagem": "Usuário cadastrado com sucesso.", "dados": { "id": 1, "nome": "Rafael de Souza Pinto", "email": "rafael.pinto@exemplo.com", "senha": "12345678", "tipo": 0 } }
- PUT
/api/v1/usuario/{id} - Payload igual ao POST
- Resposta:
{ "mensagem": "Usuário atualizado com sucesso.", "dados": { "id": 1, "nome": "Rafael de Souza Pinto", "email": "rafael.pinto@exemplo.com", "senha": "12345678", "tipo": 0 } }
- DELETE
/api/v1/usuario/{id} - Resposta:
{ "mensagem": "Usuário deletado com sucesso." }
- GET
/api/v1/dashboard - Resposta:
{ "totalMotos": 10, "motosComProblema": 2, "vagasLivres": 5, "vagasOcupadas": 5, "totalVagas": 10 }
- Todos os endpoints possuem descrição, parâmetros documentados, exemplos de payload (POST/PUT) e modelos de dados.
- Acesse
/swaggerpara explorar e testar a API interativamente.