diff --git a/Commander.csproj b/Commander.csproj index 3dfe42a..bbbf558 100644 --- a/Commander.csproj +++ b/Commander.csproj @@ -1,19 +1,21 @@ - netcoreapp3.1 + net10.0 + enable + enable - - - - - + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + diff --git a/Controllers/CommandsController.cs b/Controllers/CommandsController.cs index 7740cc3..08a7368 100644 --- a/Controllers/CommandsController.cs +++ b/Controllers/CommandsController.cs @@ -32,9 +32,9 @@ public ActionResult> GetAllCommands() //GET api/commands/{id} [HttpGet("{id}", Name = "GetCommandById")] - public ActionResult GetCommandById(int Id) + public ActionResult GetCommandById(int id) { - var commandItem = _repository.GetCommandById(Id); + var commandItem = _repository.GetCommandById(id); if (commandItem != null) { return Ok(_mapper.Map(commandItem)); @@ -45,7 +45,7 @@ public ActionResult GetCommandById(int Id) //POST api/commands [HttpPost] - public ActionResult CreateComand(CommandCreateDto command) + public ActionResult CreateCommand(CommandCreateDto command) { var commandModelRepo = _mapper.Map(command); _repository.CreateCommand(commandModelRepo); @@ -53,13 +53,13 @@ public ActionResult CreateComand(CommandCreateDto command) var commandReadDto = _mapper.Map(commandModelRepo); - return CreatedAtRoute(nameof(GetCommandById), new { Id = commandReadDto.Id }, commandReadDto); + return CreatedAtRoute(nameof(GetCommandById), new { id = commandReadDto.Id }, commandReadDto); } //PUT api/commands/{1} [HttpPut("{id}")] - public ActionResult UpdateComand(int id, CommandUpdateDto command) + public ActionResult UpdateCommand(int id, CommandUpdateDto command) { var commandModelRepo = _repository.GetCommandById(id); if (commandModelRepo == null) @@ -78,21 +78,26 @@ public ActionResult UpdateComand(int id, CommandUpdateDto comman [HttpPatch("{id}")] public ActionResult PartialCommandUpdate(int id, JsonPatchDocument patchDoc) { + if (patchDoc == null) + { + return BadRequest(); + } + var commandModelRepo = _repository.GetCommandById(id); if (commandModelRepo == null) { return NotFound(); } - var commadToPath = _mapper.Map(commandModelRepo); - patchDoc.ApplyTo(commadToPath, ModelState); + var commandToPatch = _mapper.Map(commandModelRepo); + patchDoc.ApplyTo(commandToPatch, ModelState); - if (!TryValidateModel(commadToPath)) + if (!TryValidateModel(commandToPatch)) { return ValidationProblem(ModelState); } - _mapper.Map(commadToPath, commandModelRepo); + _mapper.Map(commandToPatch, commandModelRepo); _repository.UpdateCommand(commandModelRepo); _repository.SaveChanges(); @@ -101,9 +106,9 @@ public ActionResult PartialCommandUpdate(int id, JsonPatchDocument options) : base(optio { } - public DbSet Commands { get; set; } + + public DbSet Commands => Set(); } -} \ No newline at end of file +} diff --git a/Data/ICommanderRepo.cs b/Data/ICommanderRepo.cs index ff15929..5b5ce63 100644 --- a/Data/ICommanderRepo.cs +++ b/Data/ICommanderRepo.cs @@ -9,7 +9,7 @@ public interface ICommanderRepo bool SaveChanges(); IEnumerable GetAllCommands(); - Command GetCommandById(int id); + Command? GetCommandById(int id); void CreateCommand(Command command); @@ -19,4 +19,4 @@ public interface ICommanderRepo } -} \ No newline at end of file +} diff --git a/Data/SqlCommanderRepo.cs b/Data/SqlCommanderRepo.cs index 70a93c4..9fa8370 100644 --- a/Data/SqlCommanderRepo.cs +++ b/Data/SqlCommanderRepo.cs @@ -6,7 +6,8 @@ namespace Commander.Data { public class SqlCommanderRepo : ICommanderRepo { - private CommanderContext _context; + private readonly CommanderContext _context; + public SqlCommanderRepo(CommanderContext context) { _context = context; @@ -27,7 +28,7 @@ public IEnumerable GetAllCommands() return _context.Commands.ToList(); } - public Command GetCommandById(int id) + public Command? GetCommandById(int id) { return _context.Commands.FirstOrDefault(w => w.Id == id); } @@ -55,4 +56,4 @@ public void DeleteCommand(Command command) } } -} \ No newline at end of file +} diff --git a/Dtos/CommandCreateDto.cs b/Dtos/CommandCreateDto.cs index 3d69bd4..9bda726 100644 --- a/Dtos/CommandCreateDto.cs +++ b/Dtos/CommandCreateDto.cs @@ -6,14 +6,14 @@ public class CommandCreateDto { [Required] [MaxLength(250)] - public string HowTo { get; set; } + public string HowTo { get; set; } = string.Empty; [Required] - public string Line { get; set; } + public string Line { get; set; } = string.Empty; [Required] - public string Platform { get; set; } + public string Platform { get; set; } = string.Empty; } -} \ No newline at end of file +} diff --git a/Dtos/CommandReadDto.cs b/Dtos/CommandReadDto.cs index 2392b8c..ce86ca5 100644 --- a/Dtos/CommandReadDto.cs +++ b/Dtos/CommandReadDto.cs @@ -1,15 +1,15 @@ -using System.ComponentModel.DataAnnotations; - namespace Commander.Dtos { public class CommandReadDto { public int Id { get; set; } - public string HowTo { get; set; } + public string HowTo { get; set; } = string.Empty; + + public string Line { get; set; } = string.Empty; - public string Line { get; set; } + public string Platform { get; set; } = string.Empty; } -} \ No newline at end of file +} diff --git a/Dtos/CommandUpdateDto.cs b/Dtos/CommandUpdateDto.cs index d293441..452665c 100644 --- a/Dtos/CommandUpdateDto.cs +++ b/Dtos/CommandUpdateDto.cs @@ -6,14 +6,14 @@ public class CommandUpdateDto { [Required] [MaxLength(250)] - public string HowTo { get; set; } + public string HowTo { get; set; } = string.Empty; [Required] - public string Line { get; set; } + public string Line { get; set; } = string.Empty; [Required] - public string Platform { get; set; } + public string Platform { get; set; } = string.Empty; } -} \ No newline at end of file +} diff --git a/Models/Command.cs b/Models/Command.cs index 56581cd..5f9dc44 100644 --- a/Models/Command.cs +++ b/Models/Command.cs @@ -9,14 +9,14 @@ public class Command [Required] [MaxLength(250)] - public string HowTo { get; set; } + public string HowTo { get; set; } = string.Empty; [Required] - public string Line { get; set; } + public string Line { get; set; } = string.Empty; [Required] - public string Platform { get; set; } + public string Platform { get; set; } = string.Empty; } -} \ No newline at end of file +} diff --git a/Program.cs b/Program.cs index 38d7747..eda873b 100644 --- a/Program.cs +++ b/Program.cs @@ -1,26 +1,39 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; +using Commander.Data; +using Commander.Profiles; +using Microsoft.EntityFrameworkCore; +using Newtonsoft.Json.Serialization; + +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddDbContext(options => + options.UseSqlServer(builder.Configuration.GetConnectionString("CommanderConnection"))); + +builder.Services + .AddControllers() + .AddNewtonsoftJson(options => + { + options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); + }); + +builder.Services.AddAutoMapper(_ => { }, typeof(CommandsProfile).Assembly); +builder.Services.AddScoped(); + +var app = builder.Build(); + +if (app.Environment.IsDevelopment()) +{ + app.UseDeveloperExceptionPage(); +} + +app.UseHttpsRedirection(); + +app.UseAuthorization(); + +app.MapControllers(); + +app.Run(); namespace Commander { - public class Program - { - public static void Main(string[] args) - { - CreateHostBuilder(args).Build().Run(); - } - - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder.UseStartup(); - }); - } + public partial class Program; } diff --git a/Properties/launchSettings.json b/Properties/launchSettings.json index ba15347..b0f2efa 100644 --- a/Properties/launchSettings.json +++ b/Properties/launchSettings.json @@ -12,7 +12,7 @@ "IIS Express": { "commandName": "IISExpress", "launchBrowser": true, - "launchUrl": "weatherforecast", + "launchUrl": "api/commands", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } @@ -20,7 +20,7 @@ "Commander": { "commandName": "Project", "launchBrowser": true, - "launchUrl": "weatherforecast", + "launchUrl": "api/commands", "applicationUrl": "https://localhost:5001;http://localhost:5000", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" diff --git a/README.md b/README.md index cb2da4c..c43a843 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # Commander API -> Commander API is a simple ASP.NET Core 3.1 MVC REST API that executes CRUD (Create, Retrieve, Update and Delete) commands on an MS SQL Server Database using Entity Framework Core. +> Commander API is a simple ASP.NET Core/.NET 10 LTS REST API that executes CRUD (Create, Retrieve, Update and Delete) operations on an MS SQL Server database using Entity Framework Core. ![GitHub language count](https://img.shields.io/github/languages/count/savaladaojr/CommanderAPI) ![GitHub top language](https://img.shields.io/github/languages/top/savaladaojr/CommanderAPI) @@ -10,31 +10,41 @@
## Description -The Commander API stores command line snippets (e.g. dotnet new mvc - to create a new ASP.NET Core Web Application). -It is a simple but useful tool when you need to find a specific command line to use it. So, you'll have it within your reach, quickly and without having to perform any search on Google ;). +The Commander API stores command line snippets (e.g. `dotnet new webapi` to create a new ASP.NET Core Web API). +It is a simple but useful tool when you need to find a specific command line quickly. Each command line will have the following attributes: - How to
-Description of what the command will do, e.g. Build a .NET the project +Description of what the command will do, e.g. Build a .NET project - Command line
-The actual command line snippet, e.g. dotnet build +The actual command line snippet, e.g. `dotnet build` - Platform
-Application or platform domain, e.g. .Net Core +Application or platform domain, e.g. .NET + +The API is currently built with: + +- .NET 10 LTS +- ASP.NET Core MVC Controllers +- Entity Framework Core 10 +- SQL Server / LocalDB +- AutoMapper +- Newtonsoft.Json and JSON Patch support ### Features - [X] Retrieve all commands -- [X] Retrieve a spectic command +- [X] Retrieve a specific command - [X] Create a new command -- [X] Update a command information -- [X] Delete an existent command +- [X] Update command information +- [X] Partially update command information +- [X] Delete an existing command
-As the most REST APIs, the Commander API will follow the standard set of methods to Create, Retrieve, Update, and Delete records in the database. So, each functionality could be access as listed in the table below: +As with most REST APIs, Commander API follows the standard set of methods to create, retrieve, update, and delete records in the database. Each functionality can be accessed as listed in the table below: @@ -48,7 +58,7 @@ As the most REST APIs, the Commander API will follow the standard set of methods - + @@ -68,6 +78,12 @@ As the most REST APIs, the Commander API will follow the standard set of methods + + + + + + @@ -82,10 +98,11 @@ As the most REST APIs, the Commander API will follow the standard set of methods ### Pre-requisites -1. .Net Core SDK installed; +1. .NET 10 SDK installed; 2. Visual Studio Code installed; 3. Microsoft SQL Server Local DB installed; -4. MS SQl Server - VSCode Extension - installed. +4. MS SQL Server - VSCode Extension - installed; +5. Entity Framework Core CLI tool installed or available through your .NET tooling. ### Cloning the Repository @@ -100,7 +117,7 @@ git clone https://github.com/savaladaojr/CommanderAPI #### Opening the project -1. On the director that you clone the repository, by a command prompt, execute the command to open the Visual Sutdio Code and load the project folder. +1. On the directory where you cloned the repository, open a command prompt and execute the command to open Visual Studio Code and load the project folder. You should see something like the screenshot below. ```sh @@ -120,18 +137,18 @@ dotnet restore ### Updating the Database 1. Connect on the SQL Server Local DB:
-1.1. Create a new database "CommanderDB";
-1.2. Create new user called "CommanderUser", set it password and set it as a CommanderDB owner;
+1.1. Create a new database "CommanderDB" or let Entity Framework create it from the migrations;
+1.2. Make sure your Windows user has permission to access the database when using the default trusted connection;
2. In the VSCode:
-2.1. Open the file *"appsettings.json"* and change the *"ConnectionString"* property to reflect your configuration;
+2.1. Open the file *"appsettings.json"* and change the *"CommanderConnection"* connection string to reflect your environment, if needed;
2.2. Open a new Terminal and run the command to update the database with the project's database migrations.
```sh dotnet ef database update ``` -### Building & Runnin the API +### Building & Running the API 1. Then rebuild the application; @@ -150,13 +167,38 @@ dotnet run ## Consuming the API. -To run some tests on the API, I recommend that you download [Postman](https://www.postman.com/downloads/) or also use a Google Chrome plug-in. You also have another option which is [Insomina](https://insomnia.rest/download/). Both are gonna work and have a free version. +To run some tests on the API, I recommend that you download [Postman](https://www.postman.com/downloads/) or use a Google Chrome plug-in. You also have another option, [Insomnia](https://insomnia.rest/download/). Both work well and have a free version. I'm using Postman! So I have already prepared a file with all requests for the API. You can find this file (Commands API.postman_collection.json) in the repository. -Exemple of a request to the API: +Example of a request to the API: Visual Studio Code - Project's structure +### Example payload + +```json +{ + "howTo": "Build the project", + "line": "dotnet build", + "platform": ".NET" +} +``` + +### Current response contract + +Read operations return the following fields: + +- `id` +- `howTo` +- `line` +- `platform` + +## Migration notes + +This project was migrated from ASP.NET Core 3.1 to .NET 10 LTS. The application now uses the modern hosting model in `Program.cs`; the old `Startup.cs` file was removed. The default local connection string no longer stores a username or password in the repository. + +Additional architecture and requirements details are documented in [`docs/REQUISITOS_APLICACAO.md`](docs/REQUISITOS_APLICACAO.md). +


diff --git a/Startup.cs b/Startup.cs deleted file mode 100644 index 255c861..0000000 --- a/Startup.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using AutoMapper; -using Commander.Data; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.HttpsPolicy; -using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using Newtonsoft.Json.Serialization; - -namespace Commander -{ - public class Startup - { - - public IConfiguration Configuration { get; } - - public Startup(IConfiguration configuration) - { - Configuration = configuration; - } - - public void ConfigureServices(IServiceCollection services) - { - services.AddDbContext(options => - options.UseSqlServer(Configuration.GetConnectionString("CommanderConnection")) - ); - - services.AddControllers().AddNewtonsoftJson(options => - { - options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); - }); - - services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies()); - services.AddScoped(); - } - - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - - app.UseHttpsRedirection(); - - app.UseRouting(); - - app.UseAuthorization(); - - app.UseEndpoints(endpoints => - { - endpoints.MapControllers(); - }); - } - } -} diff --git a/appsettings.json b/appsettings.json index 80f0322..c010538 100644 --- a/appsettings.json +++ b/appsettings.json @@ -8,6 +8,6 @@ }, "AllowedHosts": "*", "ConnectionStrings": { - "CommanderConnection": "Server=(localdb)\\MSSQLLocalDB;Database=CommanderDB;User ID=CommanderUser;Password=p455w0rd!1;Trusted_Connection=True;MultipleActiveResultSets=true" + "CommanderConnection": "Server=(localdb)\\MSSQLLocalDB;Database=CommanderDB;Trusted_Connection=True;MultipleActiveResultSets=true" } } diff --git a/docs/REQUISITOS_APLICACAO.md b/docs/REQUISITOS_APLICACAO.md new file mode 100644 index 0000000..6207eb2 --- /dev/null +++ b/docs/REQUISITOS_APLICACAO.md @@ -0,0 +1,327 @@ +# Requisitos da Aplicação Commander API + +## 1. Visão geral + +A Commander API é uma API REST construída com ASP.NET Core/.NET 10 LTS para cadastrar, consultar, atualizar e remover snippets de linha de comando. A aplicação persiste os dados em SQL Server por meio do Entity Framework Core e expõe recursos HTTP no caminho `/api/commands`. + +O domínio principal da aplicação é o recurso **Command**, que representa um comando reutilizável com descrição de uso, linha de comando e plataforma relacionada. + +## 2. Arquitetura do projeto + +A aplicação segue uma arquitetura em camadas simples, típica de uma API MVC/REST em ASP.NET Core: + +```text +Cliente HTTP/Postman + | + v +Program.cs + Controllers/CommandsController.cs + | + v +DTOs + AutoMapper + | + v +Data/ICommanderRepo.cs +Data/SqlCommanderRepo.cs + | + v +Data/CommanderContext.cs + | + v +SQL Server / Tabela Commands +``` + +### 2.1 Camada de apresentação/API + +- **Responsabilidade:** receber requisições HTTP, validar entrada via model binding/DataAnnotations, acionar o repositório e retornar respostas REST. +- **Componentes principais:** `Program.cs`, com o bootstrap moderno via `WebApplicationBuilder`, e `CommandsController`. +- **Rota base:** `/api/commands`. +- **Formatos:** JSON com serialização em camelCase, configurada via Newtonsoft.Json para manter compatibilidade com JSON Patch. + +### 2.2 Camada de contrato/DTOs + +- **Responsabilidade:** separar os contratos públicos da API do modelo de persistência. +- **DTOs existentes:** + - `CommandCreateDto`: payload para criação. + - `CommandReadDto`: resposta de leitura. + - `CommandUpdateDto`: payload para atualização total e parcial. + +### 2.3 Camada de mapeamento + +- **Responsabilidade:** converter objetos entre modelo de domínio e DTOs. +- **Componente principal:** `CommandsProfile`, usando AutoMapper. + +### 2.4 Camada de acesso a dados + +- **Responsabilidade:** encapsular operações de persistência e consulta. +- **Componentes:** + - `ICommanderRepo`: contrato do repositório. + - `SqlCommanderRepo`: implementação baseada em Entity Framework Core. + - `CommanderContext`: `DbContext` da aplicação. + +### 2.5 Camada de persistência + +- **Banco de dados:** Microsoft SQL Server/LocalDB. +- **ORM:** Entity Framework Core. +- **Entidade/tabela:** `Commands`. +- **Migração inicial:** cria a tabela `Commands` com os campos `Id`, `HowTo`, `Line` e `Platform`. + +## 3. Componentes e tecnologias utilizadas + +| Componente | Uso no projeto | +| --- | --- | +| ASP.NET Core/.NET 10 LTS | Framework web para hospedar a API REST em uma versão LTS suportada. | +| ASP.NET Core MVC Controllers | Definição dos endpoints HTTP. | +| Entity Framework Core 10.0.8 | ORM para mapeamento e persistência em SQL Server. | +| EF Core SQL Server Provider 10.0.8 | Provider de acesso ao SQL Server. | +| EF Core Design 10.0.8 | Suporte a migrations e ferramentas de design-time. | +| AutoMapper 16.1.1 | Configuração de mapeamentos entre DTOs e entidades. | +| Microsoft.AspNetCore.JsonPatch 10.0.8 | Suporte a atualização parcial via HTTP PATCH. | +| Microsoft.AspNetCore.Mvc.NewtonsoftJson 10.0.8 | Serialização JSON com Newtonsoft.Json e suporte ao JsonPatch. | +| Newtonsoft.Json CamelCasePropertyNamesContractResolver | Padronização de propriedades JSON em camelCase. | +| SQL Server LocalDB | Banco esperado no ambiente de desenvolvimento. | +| Postman Collection | Coleção de requisições para consumo/teste manual da API. | + +## 4. Modelo de dados + +### 4.1 Entidade Command + +| Campo | Tipo | Obrigatório | Regras | Observações | +| --- | --- | --- | --- | --- | +| `Id` | `int` | Sim | Chave primária, identity | Gerado pelo banco. | +| `HowTo` | `string` | Sim | Máximo de 250 caracteres | Descreve o que o comando faz. | +| `Line` | `string` | Sim | Não possui limite explícito no modelo | Linha de comando/snippet. | +| `Platform` | `string` | Sim | Não possui limite explícito no modelo | Plataforma, ferramenta ou domínio relacionado. | + +### 4.2 Contratos de entrada e saída + +#### Criação (`CommandCreateDto`) + +Campos obrigatórios: + +- `howTo` +- `line` +- `platform` + +#### Leitura (`CommandReadDto`) + +Campos retornados: + +- `id` +- `howTo` +- `line` +- `platform` + +#### Atualização (`CommandUpdateDto`) + +Campos obrigatórios: + +- `howTo` +- `line` +- `platform` + +## 5. Funcionalidades atuais + +### 5.1 Listar comandos + +- **Método:** `GET` +- **Endpoint:** `/api/commands` +- **Resposta esperada:** `200 OK` com lista de comandos no formato `CommandReadDto`. +- **Requisito funcional:** o sistema deve retornar todos os comandos cadastrados. + +### 5.2 Consultar comando por ID + +- **Método:** `GET` +- **Endpoint:** `/api/commands/{id}` +- **Resposta esperada:** + - `200 OK` quando o comando existir. + - `404 Not Found` quando o comando não existir. +- **Requisito funcional:** o sistema deve permitir consultar um único comando por identificador. + +### 5.3 Criar comando + +- **Método:** `POST` +- **Endpoint:** `/api/commands` +- **Payload:** `CommandCreateDto`. +- **Resposta esperada:** `201 Created` com rota para consulta do recurso criado. +- **Requisito funcional:** o sistema deve criar um novo comando válido e persisti-lo no banco. + +### 5.4 Atualizar comando integralmente + +- **Método:** `PUT` +- **Endpoint:** `/api/commands/{id}` +- **Payload:** `CommandUpdateDto`. +- **Resposta esperada:** + - `204 No Content` quando atualizado com sucesso. + - `404 Not Found` quando o comando não existir. +- **Requisito funcional:** o sistema deve substituir os dados de um comando existente. + +### 5.5 Atualizar comando parcialmente + +- **Método:** `PATCH` +- **Endpoint:** `/api/commands/{id}` +- **Payload:** `JsonPatchDocument`. +- **Resposta esperada:** + - `204 No Content` quando atualizado com sucesso. + - `404 Not Found` quando o comando não existir. + - resposta de validação quando o patch produzir um modelo inválido. +- **Requisito funcional:** o sistema deve permitir alterações parciais em comandos existentes. + +### 5.6 Remover comando + +- **Método:** `DELETE` +- **Endpoint:** `/api/commands/{id}` +- **Resposta esperada:** + - `204 No Content` quando removido com sucesso. + - `404 Not Found` quando o comando não existir. +- **Requisito funcional:** o sistema deve remover comandos cadastrados. + +## 6. Requisitos funcionais + +| ID | Requisito | Status no repositório | +| --- | --- | --- | +| RF-001 | A API deve listar todos os comandos cadastrados. | Implementado. | +| RF-002 | A API deve consultar um comando por ID. | Implementado. | +| RF-003 | A API deve criar comandos com `howTo`, `line` e `platform`. | Implementado. | +| RF-004 | A API deve validar campos obrigatórios na criação. | Implementado via DataAnnotations/ApiController. | +| RF-005 | A API deve atualizar integralmente um comando por ID. | Implementado. | +| RF-006 | A API deve atualizar parcialmente um comando por ID. | Implementado via JSON Patch. | +| RF-007 | A API deve excluir um comando por ID. | Implementado. | +| RF-008 | A API deve retornar `404 Not Found` para comandos inexistentes em operações por ID. | Implementado. | +| RF-009 | A API deve retornar `201 Created` após criação, incluindo rota para o recurso criado. | Implementado. | +| RF-010 | A API deve expor o campo `platform` nas respostas de leitura quando o cliente precisar dessa informação. | Implementado. | +| RF-011 | A API deve oferecer filtros por plataforma, busca por texto e/ou paginação para listagens maiores. | Pendente. | +| RF-012 | A API deve documentar endpoints em Swagger/OpenAPI. | Pendente. | + +## 7. Requisitos não funcionais + +| ID | Requisito | Status no repositório | +| --- | --- | --- | +| RNF-001 | A aplicação deve executar em ASP.NET Core/.NET 10 LTS. | Implementado. | +| RNF-002 | A persistência deve usar SQL Server configurado por connection string. | Implementado. | +| RNF-003 | A aplicação deve usar HTTPS redirection. | Implementado. | +| RNF-004 | As respostas JSON devem usar propriedades em camelCase. | Implementado. | +| RNF-005 | O acesso a dados deve ser abstraído por interface para facilitar testes e troca de implementação. | Implementado. | +| RNF-006 | O projeto deve possuir testes automatizados de unidade e/ou integração. | Pendente. | +| RNF-007 | A aplicação deve evitar credenciais sensíveis em arquivos versionados. | Parcialmente implementado: a connection string versionada usa autenticação integrada local, mas ambientes reais ainda devem usar variáveis de ambiente, user-secrets ou secret manager. | +| RNF-008 | A aplicação deve ter observabilidade mínima com logs estruturados, health checks e tratamento de erros padronizado. | Pendente. | +| RNF-009 | A API deve possuir autenticação/autorização caso seja exposta fora de ambiente local/de aprendizagem. | Pendente. | +| RNF-010 | O projeto deve usar uma versão suportada do .NET para receber correções de segurança. | Implementado com .NET 10 LTS. | + +## 8. Requisitos de ambiente e execução + +### 8.1 Ambiente de desenvolvimento + +- SDK do .NET 10. +- SQL Server LocalDB ou SQL Server acessível. +- Banco `CommanderDB` criado ou provisionado por migrations. +- Usuário/permissões compatíveis com a connection string configurada. +- Ferramenta para chamadas HTTP, como Postman, Insomnia ou curl. + +### 8.2 Configuração + +A aplicação espera a connection string `CommanderConnection` na seção `ConnectionStrings`. Essa configuração é lida no `Program.cs` para registrar o `CommanderContext` com provider SQL Server. + +### 8.3 Banco de dados + +Para provisionar a base, o fluxo esperado é: + +1. Configurar a connection string. +2. Executar migrations do Entity Framework Core. +3. Iniciar a API. + +Comandos esperados em ambiente com SDK instalado: + +```bash +dotnet restore +dotnet ef database update +dotnet build +dotnet run +``` + +Após a migração para .NET 10, a aplicação usa o modelo de hosting moderno em `Program.cs`; o arquivo `Startup.cs` foi removido. + +## 9. Regras de validação e comportamento + +- `howTo`, `line` e `platform` são obrigatórios na criação e atualização. +- `howTo` deve ter no máximo 250 caracteres. +- Requisições inválidas devem ser rejeitadas automaticamente pelo comportamento de `[ApiController]`. +- Operações de atualização e exclusão devem verificar a existência do recurso antes de persistir alterações. +- A criação deve rejeitar payload nulo ou inválido. +- A exclusão deve rejeitar recurso nulo antes de remover do contexto. + +## 10. Pontos de atenção identificados + +1. **Migrations existentes foram criadas em EF Core 3.1:** o projeto compila em EF Core 10, mas novas migrations devem ser geradas/validadas em ambiente com banco SQL Server antes de deploy. +2. **Configuração de secrets:** a connection string principal não contém mais senha, mas ambientes reais ainda precisam externalizar credenciais e parâmetros sensíveis. +3. **Ausência de testes automatizados:** não há projeto de testes no repositório. +4. **Ausência de Swagger/OpenAPI:** dificulta descoberta e documentação interativa da API. +5. **DTO de leitura agora retorna `platform`:** clientes antigos que assumiam apenas `id`, `howTo` e `line` receberão um campo adicional. +6. **Sem paginação/filtros:** `GET /api/commands` retorna todos os registros, o que pode ser inadequado com crescimento da base. +7. **Sem autenticação/autorização:** adequado para estudo/local, mas insuficiente para exposição pública. +8. **Sem tratamento global de erros:** exceções inesperadas não têm contrato padronizado de erro. +9. **LaunchSettings corrigido:** o launch URL agora aponta para `api/commands`. +10. **Nomes com pequenos typos corrigidos:** métodos como `CreateComand` e `UpdateComand` foram padronizados para `CreateCommand` e `UpdateCommand`. + +## 11. Próximos passos recomendados + +### 11.1 Prioridade alta + +1. **Validar banco e migrations em ambiente real** + - Executar `dotnet ef database update` com SQL Server/LocalDB disponível. + - Gerar nova migration somente se houver alteração intencional de schema. + - Validar manualmente os endpoints CRUD com a base migrada. + +2. **Externalizar configuração sensível por ambiente** + - Substituir secrets por variáveis de ambiente, user-secrets ou secret manager do ambiente de deploy. + - Manter no repositório apenas exemplos seguros, como `appsettings.Example.json`. + +3. **Adicionar testes automatizados** + - Criar testes unitários para repositório/mapeamentos quando aplicável. + - Criar testes de integração para endpoints REST. + - Usar banco em memória, SQLite em memória ou container SQL Server para testes. + +4. **Adicionar Swagger/OpenAPI** + - Documentar endpoints, contratos, status codes e exemplos de payload. + - Facilitar testes manuais e integração com clientes. + +### 11.2 Prioridade média + +1. **Implementar paginação, filtros e busca** + - Paginar `GET /api/commands`. + - Permitir filtro por `platform`. + - Permitir busca textual por `howTo` e/ou `line`. + +2. **Criar tratamento global de exceções** + - Padronizar respostas de erro. + - Evitar exposição de detalhes internos em produção. + +3. **Melhorar observabilidade** + - Adicionar health checks. + - Padronizar logs. + - Definir métricas mínimas de requisições, erros e latência. + +### 11.3 Prioridade baixa/evolutiva + +1. **Autenticação e autorização** + - Necessário se a API for usada por múltiplos usuários ou exposta publicamente. + +2. **Versionamento de API** + - Adicionar `/api/v1/commands` antes de mudanças incompatíveis nos contratos. + +3. **CI/CD** + - Criar pipeline para restore, build, testes, análise estática e publicação. + +4. **Containerização** + - Adicionar `Dockerfile` e `docker-compose` para API + SQL Server em ambiente local. + +5. **Seed de dados** + - Criar carga inicial opcional de comandos úteis para desenvolvimento e demonstração. + +## 12. Critérios de aceite sugeridos + +- A API deve compilar e iniciar com .NET 10 LTS. +- Migrations devem criar a tabela `Commands` corretamente. +- Todos os endpoints CRUD devem possuir testes cobrindo sucesso e erro. +- A documentação OpenAPI deve listar todos os endpoints, DTOs e status codes. +- Nenhum segredo real deve permanecer em arquivos versionados. +- `GET /api/commands` deve suportar paginação antes de uso com volume significativo de dados.
GET /api/commands RetrieveRetrieve all command lineRetrieve all command lines
GETUpdate Update a single command line by Id
PATCH/api/commands/{id}Partial updatePartially update a single command line by Id using JSON Patch
DELETE /api/commands/{id}