{"id":31166253,"url":"https://github.com/mtlouzada/domain-inspector","last_synced_at":"2026-04-09T12:04:11.030Z","repository":{"id":312200737,"uuid":"1045784950","full_name":"mtlouzada/Desafio.Umbler","owner":"mtlouzada","description":"Desafio Técnico Fullstack para a empresa Umbler - Desenvolvedor C# / JavaScript - Jr","archived":false,"fork":false,"pushed_at":"2025-09-18T17:42:40.000Z","size":849,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-09-18T20:14:59.739Z","etag":null,"topics":["asp-net-mvc","dotnet","efcore","fullstack-development","javascript","reactjs","tests","umbler","vite"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mtlouzada.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-08-27T17:51:54.000Z","updated_at":"2025-09-17T21:40:20.000Z","dependencies_parsed_at":"2025-08-29T07:44:17.117Z","dependency_job_id":null,"html_url":"https://github.com/mtlouzada/Desafio.Umbler","commit_stats":null,"previous_names":["mtlouzada/desafio.umbler"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/mtlouzada/Desafio.Umbler","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtlouzada%2FDesafio.Umbler","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtlouzada%2FDesafio.Umbler/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtlouzada%2FDesafio.Umbler/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtlouzada%2FDesafio.Umbler/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mtlouzada","download_url":"https://codeload.github.com/mtlouzada/Desafio.Umbler/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtlouzada%2FDesafio.Umbler/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":275917699,"owners_count":25552448,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-19T02:00:09.700Z","response_time":108,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["asp-net-mvc","dotnet","efcore","fullstack-development","javascript","reactjs","tests","umbler","vite"],"created_at":"2025-09-19T09:56:44.139Z","updated_at":"2025-12-30T21:21:58.378Z","avatar_url":"https://github.com/mtlouzada.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Domain Inspector\n\nDomain Inspector é uma API web que permite consultar e consolidar informações de DNS e WHOIS de um domínio.\n\nO projeto foi desenvolvido com foco em separação de responsabilidades, testabilidade e integração com serviços externos, servindo como um estudo de caso prático em .NET.\n\nExemplo de uso: consulta de dados do domínio `example.com`\n\n**Informações retornadas:**\n- Name Servers\n- Registro A (endereço IP)\n- Dados de hospedagem do domínio\n\nAs informações são obtidas através de consultas a servidores DNS e ao protocolo WHOIS.\n\n*WHOIS é um protocolo utilizado para obter informações de registro e contato associadas a domínios.*\n\nOs dados consultados são persistidos em banco de dados e reutilizados enquanto o TTL estiver válido, evitando consultas desnecessárias e melhorando a performance da aplicação.\n\nTecnologias Backend utilizadas:\n\n- C#\n- Asp.Net Core\n- MySQL\n- Entity Framework\n\nTecnologias Frontend utilizadas:\n\n- Webpack\n- Babel\n- ES7\n\nPara rodar o projeto você vai precisar instalar:\n\n- dotnet Core SDK (https://www.microsoft.com/net/download/windows dotnet Core 6.0.201 SDK)\n- Um editor de código, acoselhamos o Visual Studio ou VisualStudio Code. (https://code.visualstudio.com/)\n- NodeJs v17.6.0 para \"buildar\" o FrontEnd (https://nodejs.org/en/)\n- Um banco de dados MySQL (vc pode rodar localmente ou criar um site PHP gratuitamente no app da Umbler https://app.umbler.com/ que lhe oferece o banco Mysql adicionamente)\n\nCom as ferramentas devidamente instaladas, basta executar os seguintes comandos:\n\nPara \"buildar\" o javascript basta executar:\n\n`npm install`\n`npm run build`\n\nPara Rodar o projeto:\n\nExecute a migration no banco mysql:\n\n`dotnet tool update --global dotnet-ef`\n`dotnet tool ef database update`\n\nE após: \n\n`dotnet run` (ou clique em \"play\" no editor do vscode)\n\n# Objetivos:\n\nSe você rodar o projeto e testar um domínio, verá que ele já está funcionando. Porém, queremos melhorar varios pontos deste projeto:\n\n# FrontEnd\n\n - Os dados retornados não estão formatados, e devem ser apresentados de uma forma legível.\n - Não há validação no frontend permitindo que seja submetido uma requsição inválida para o servidor (por exemplo, um domínio sem extensão).\n - Está sendo utilizado \"vanilla-js\" para fazer a requisição para o backend, apesar de já estar configurado o webpack. O ideal seria utilizar algum framework mais moderno como ReactJs ou Blazor.  \n\n# BackEnd\n\n - Não há validação no backend permitindo que uma requisição inválida prossiga, o que ocasiona exceptions (erro 500).\n - A complexidade ciclomática do controller está muito alta, o ideal seria utilizar uma arquitetura em camadas.\n - O DomainController está retornando a própria entidade de domínio por JSON, o que faz com que propriedades como Id, Ttl e UpdatedAt sejam mandadas para o cliente web desnecessariamente. Retornar uma ViewModel (DTO) neste caso seria mais aconselhado.\n\n# Testes\n\n - A cobertura de testes unitários está muito baixa, e o DomainController está impossível de ser testado pois não há como \"mockar\" a infraestrutura.\n - O Banco de dados já está sendo \"mockado\" graças ao InMemoryDataBase do EntityFramework, mas as consultas ao Whois e Dns não. \n\n# Dica\n\n- Este teste não tem \"pegadinha\", é algo pensado para ser simples. Aconselhamos a ler o código, e inclusive algumas dicas textuais deixadas nos testes unitários. \n- Há um teste unitário que está comentado, que obrigatoriamente tem que passar.\n- Diferencial: criar mais testes.\n\n# Entrega\n\n- Enviei o link do seu repositório com o código atualizado.\n- O repositório deve estar público.\n- Modifique Este readme adicionando informações sobre os motivos das mudanças realizadas.\n\n# Modificações:\n\nDurante o desenvolvimento e refatoração do desafio, foram realizadas as seguintes melhorias:\n\n## 1° BackEnd\n\u003cimg width=\"1209\" height=\"503\" alt=\"Image\" src=\"https://github.com/user-attachments/assets/c85b3a79-a006-4b78-8f57-2cf43a78171f\" /\u003e\n\n1. **Migrations**\n    - Criação de novas migrations, alinhada com um banco de dados local MySQL e refatoração na connectionstring.\n2. **Atualização da pasta Models e impacto**\n\n#### O que foi feito:\n\n- Atualizado o model Domain com valores padrão e [Required] nas propriedades essenciais (Name, Ip, UpdatedAt, Ttl).\n\n- Mantida a chave primária Id e o DbSet\u003cDomain\u003e no DatabaseContext.\n\n#### Impacto:\n\n- Garante integridade e consistência dos dados no banco.\n\n- Facilita testes unitários com dados padrão.\n\n- Permite que DomainService e DomainController retornem informações completas sem erros de valores nulos.\n1. **Refatoração do Controller (`DomainController`)**\n    - DomainController foi simplificado: ele apenas valida entrada e retorna HTTP adequado.\n    - Toda a lógica de consultas (DNS, Whois, banco de dados) foi movida para o DomainService, seguindo o padrão de separação de responsabilidades.\n2. **Criação do Service (`DomainService`)**\n    - Criado **wrapper para o `WhoisClient`** e injeção de `ILookupClient` para permitir mock durante testes unitários.\n    - Alterado para retornar objetos do tipo `Domain`, mantendo a lógica de persistência e consulta ao banco de dados.\n    - Inclusão de **tratamento de TTL** e atualização de registros antigos no banco de dados.\n3. **Criação de DTO (`DomainResultDto`)**\n    - Separação entre a entidade do banco (`Domain`) e os dados retornados para o front-end.\n    - Melhora na segurança, evitando expor dados desnecessários.\n    - Facilita a manipulação dos dados no frontend (Vite + React).\n    \n## 2° Frontend\n\u003cimg width=\"1085\" height=\"512\" alt=\"Image\" src=\"https://github.com/user-attachments/assets/b8079ddb-dc23-498e-bd27-24cc385c8d1c\" /\u003e\n\n\u003e OBS: Optei por criar o front usando vite + ReactJS em uma pasta diferente, seguindo as recomendações da documentação do ASP.NET. Caso não seja comum para a equipe usar build tools como o vite, também é possível buildar o frontend dentro da pasta Views. Ajustes como esse são de rápida implementação.\n1. Substituição  de vanilla JS por **React + TypeScript**.\n    \n2. Consulta à API via service dedicado (`domainService.ts`).\n    \n3. Validação de entrada do usuário (domínios inválidos não são enviados).\n    \n4. Dados retornados pelo backend são \nexibidos de forma legível no frontend.\n\n\u003e OBS: A escolha do pnpm como gerenciador de pacotes no front foi meramente perfomática, por estar lidando com uma máquina com recursos limitados.\n    \n## 3° Testes Unitários\n\u003cimg width=\"1115\" height=\"284\" alt=\"Image\" src=\"https://github.com/user-attachments/assets/ea315eac-32e8-4bbd-8ca5-821f71156c9d\" /\u003e\n    \n\u003e OBS: O testa unitário que estava comentado, não estava funcionando naquele formato. Ele dependia do WhoisClient original, que é uma classe estática e não mockável diretamente.\n\n_Para funcionar, foi preciso:_\n\n- Criar um wrapper/método mockável para o Whois (IWhoisClientWrapper).\n- Alterar o DomainService e o DomainController para usar a interface mockável em vez da classe estática.\n- Criar o teste usando Mock\u003cIWhoisClientWrapper\u003e retornando um valor fake para o domínio.\n\nDepois dessa refatoração, esse teste passou a funcionar, mas não é mais aquele código comentado, e sim uma versão adaptada usando o service mockado.\n    \n**Porém mesmo adaptado, ele exerce exatamente a mesma função que o teste original comentado**\n    \n---    \n - Configuração de **InMemoryDatabase** do Entity Framework para testar interações com o banco sem depender de um banco real.\n- Criação de mocks para `IDomainService` e wrappers de Whois/DNS.\n    - Cobertura aumentada para cenários:\n        - Domínio existente no banco.\n        - Domínio inexistente no banco.\n        - Domínio inválido.\n    - Todos os testes unitários obrigatórios passam, garantindo estabilidade da aplicação.\n\n ## 5° Extra \n- Criação de um teste unitário específico para domínio inválido, simulando a requisição de um domínio que não existe ou não é válido.\n- Habilitação de **CORS** e configuração de `Swagger` para facilitar testes e documentação da API.\n- Atualização de pacotes NuGet para resolver conflitos de versões do Entity Framework.\n- Ajustes para compatibilidade com **.NET 6**.\n\n---\n\n## Como Rodar\n\n### Backend\n\n1. Configure a ConnectionString no `appsettings.json`.\n2. Execute `dotnet build`.\n3. Atualize as Migrations `dotnet ef database update`\n4. Execute `dotnet run` para iniciar o servidor.\n\n### Frontend\n\n1. Entre na pasta `client`.\n2. Execute `pnpm i`.\n3. Execute `pnpm dev` para iniciar o frontend.\n4. Acesse [http://localhost:5173](http://localhost:5173/) (ou porta indicada pelo Vite).\n\n### Testes\n\n- Execute `dotnet test` na pasta `Desafio.Umbler.Test` para rodar todos os testes unitários.\n\n---\nfeito :)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmtlouzada%2Fdomain-inspector","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmtlouzada%2Fdomain-inspector","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmtlouzada%2Fdomain-inspector/lists"}