diff --git a/.gitignore b/.gitignore index 08bf0c26bef2..634966577363 100644 --- a/.gitignore +++ b/.gitignore @@ -9,7 +9,6 @@ _build/ _site/ -Properties/ # See #577 # Use git add -f to force override .sln when required. Not needed in most cases. @@ -227,3 +226,6 @@ __pycache__/ # Windows thumbnail cache files Thumbs.db + +# Rider +.idea/ \ No newline at end of file diff --git a/aspnetcore/fundamentals/openapi/samples/10.x/aspnet-openapi-xml-controllers/api/AdditionalFiles.xml b/aspnetcore/fundamentals/openapi/samples/10.x/aspnet-openapi-xml-controllers/api/AdditionalFiles.xml new file mode 100644 index 000000000000..c320a477b4a3 --- /dev/null +++ b/aspnetcore/fundamentals/openapi/samples/10.x/aspnet-openapi-xml-controllers/api/AdditionalFiles.xml @@ -0,0 +1,8 @@ + + + + + + + diff --git a/aspnetcore/fundamentals/openapi/samples/10.x/aspnet-openapi-xml-controllers/api/Api-remove.csproj.xml b/aspnetcore/fundamentals/openapi/samples/10.x/aspnet-openapi-xml-controllers/api/Api-remove.csproj.xml new file mode 100644 index 000000000000..dd0a59cffb2e --- /dev/null +++ b/aspnetcore/fundamentals/openapi/samples/10.x/aspnet-openapi-xml-controllers/api/Api-remove.csproj.xml @@ -0,0 +1,25 @@ + + + + net10.0 + enable + enable + true + + + + + + + + + + + + + + + + + + diff --git a/aspnetcore/fundamentals/openapi/samples/10.x/aspnet-openapi-xml-controllers/api/Api.csproj b/aspnetcore/fundamentals/openapi/samples/10.x/aspnet-openapi-xml-controllers/api/Api.csproj new file mode 100644 index 000000000000..5e5e3e2bc796 --- /dev/null +++ b/aspnetcore/fundamentals/openapi/samples/10.x/aspnet-openapi-xml-controllers/api/Api.csproj @@ -0,0 +1,19 @@ + + + + net10.0 + enable + enable + true + + + + + + + + + + + + diff --git a/aspnetcore/fundamentals/openapi/samples/10.x/aspnet-openapi-xml-controllers/api/Api.http b/aspnetcore/fundamentals/openapi/samples/10.x/aspnet-openapi-xml-controllers/api/Api.http new file mode 100644 index 000000000000..c9bb55fff877 --- /dev/null +++ b/aspnetcore/fundamentals/openapi/samples/10.x/aspnet-openapi-xml-controllers/api/Api.http @@ -0,0 +1,73 @@ +@Api_HostAddress = http://localhost:5052 + +GET {{Api_HostAddress}}/weatherforecast/ +Accept: application/json + +### Project Board Endpoints ### + +### Get all project boards +GET {{Api_HostAddress}}/api/projectboards +Accept: application/json + +### Get project board by ID +GET {{Api_HostAddress}}/api/projectboards/1 +Accept: application/json + +### Create a new project board +POST {{Api_HostAddress}}/api/projectboards +Content-Type: application/json + +{ + "name": "Website Redesign", + "description": "Tasks for redesigning the company website" +} + +### Update a project board +PUT {{Api_HostAddress}}/api/projectboards/1 +Content-Type: application/json + +{ + "name": "Website Redesign v2", + "description": "Updated tasks for redesigning the company website" +} + +### Delete a project board +DELETE {{Api_HostAddress}}/api/projectboards/1 + + +### Todo Endpoints ### + +### Get all todos for a project board +GET {{Api_HostAddress}}/api/projectboards/1/todos +Accept: application/json + +### Get a specific todo by ID +GET {{Api_HostAddress}}/api/projectboards/1/todos/1 +Accept: application/json + +### Create a new todo +POST {{Api_HostAddress}}/api/projectboards/1/todos +Content-Type: application/json + +{ + "title": "Design new homepage", + "description": "Create wireframes for the new homepage design", + "isComplete": false, + "priority": 2, + "dueDate": "2025-03-20T00:00:00Z" +} + +### Update a todo +PUT {{Api_HostAddress}}/api/projectboards/1/todos/1 +Content-Type: application/json + +{ + "title": "Design new homepage with feedback", + "description": "Update wireframes based on stakeholder feedback", + "isComplete": true, + "priority": 3, + "dueDate": "2025-03-25T00:00:00Z" +} + +### Delete a todo +DELETE {{Api_HostAddress}}/api/projectboards/1/todos/1 diff --git a/aspnetcore/fundamentals/openapi/samples/10.x/aspnet-openapi-xml-controllers/api/Controllers/ProjectBoardsController.cs b/aspnetcore/fundamentals/openapi/samples/10.x/aspnet-openapi-xml-controllers/api/Controllers/ProjectBoardsController.cs new file mode 100644 index 000000000000..bf44d9ad017d --- /dev/null +++ b/aspnetcore/fundamentals/openapi/samples/10.x/aspnet-openapi-xml-controllers/api/Controllers/ProjectBoardsController.cs @@ -0,0 +1,119 @@ +using Microsoft.AspNetCore.Mvc; +using Models; + +namespace Api.Controllers; + +/// +/// API controller for managing project boards. +/// +[ApiController] +[Route("api/[controller]")] +[Tags("Project Boards")] +public class ProjectBoardsController : ControllerBase +{ + // In-memory storage for demo purposes + internal static readonly List Boards = new(); + private static int _nextBoardId = 1; + + /// + /// Retrieves all project boards. + /// + /// A collection of all project boards. + /// Returns the list of project boards. + [HttpGet] + [ProducesResponseType(StatusCodes.Status200OK)] + public ActionResult> GetAllProjectBoards() + { + return Ok(Boards); + } + + // + /// + /// Retrieves a specific project board by ID. + /// + /// The ID of the project board to retrieve. + /// The requested project board. + /// Returns the requested project board. + /// If the project board is not found. + [HttpGet("{id}")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public ActionResult GetProjectBoardById(int id) + { + var board = Boards.FirstOrDefault(b => b.Id == id); + if (board == null) + { + return NotFound(); + } + return Ok(board); + } + // + + /// + /// Creates a new project board. + /// + /// The project board to create. + /// The newly created project board. + /// Returns the newly created project board. + /// If the project board data is invalid. + [HttpPost] + [ProducesResponseType(StatusCodes.Status201Created)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + public ActionResult CreateProjectBoard(ProjectBoard board) + { + board.Id = _nextBoardId++; + board.CreatedAt = DateTime.UtcNow; + Boards.Add(board); + + return CreatedAtAction(nameof(GetProjectBoardById), new { id = board.Id }, board); + } + + /// + /// Updates an existing project board. + /// + /// The ID of the project board to update. + /// The updated project board data. + /// No content if successful. + /// If the update was successful. + /// If the project board data is invalid. + /// If the project board is not found. + [HttpPut("{id}")] + [ProducesResponseType(StatusCodes.Status204NoContent)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public IActionResult UpdateProjectBoard(int id, ProjectBoard updatedBoard) + { + var board = Boards.FirstOrDefault(b => b.Id == id); + if (board == null) + { + return NotFound(); + } + + board.Name = updatedBoard.Name; + board.Description = updatedBoard.Description; + + return NoContent(); + } + + /// + /// Deletes a project board. + /// + /// The ID of the project board to delete. + /// No content if successful. + /// If the deletion was successful. + /// If the project board is not found. + [HttpDelete("{id}")] + [ProducesResponseType(StatusCodes.Status204NoContent)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public IActionResult DeleteProjectBoard(int id) + { + var board = Boards.FirstOrDefault(b => b.Id == id); + if (board == null) + { + return NotFound(); + } + + Boards.Remove(board); + return NoContent(); + } +} diff --git a/aspnetcore/fundamentals/openapi/samples/10.x/aspnet-openapi-xml-controllers/api/Controllers/TodosController.cs b/aspnetcore/fundamentals/openapi/samples/10.x/aspnet-openapi-xml-controllers/api/Controllers/TodosController.cs new file mode 100644 index 000000000000..1c09f518dd0d --- /dev/null +++ b/aspnetcore/fundamentals/openapi/samples/10.x/aspnet-openapi-xml-controllers/api/Controllers/TodosController.cs @@ -0,0 +1,161 @@ +using Microsoft.AspNetCore.Mvc; +using Models; +using Api.Controllers; + +namespace Api.Controllers; + +/// +/// API controller for managing todos within project boards. +/// +[ApiController] +[Route("api/projectboards/{boardId}/todos")] +[Tags("Todos")] +public class TodosController : ControllerBase +{ + private static int _nextTodoId = 1; + + /// + /// Retrieves all todos for a specific project board. + /// + /// The ID of the project board. + /// A collection of todos for the specified project board. + /// Returns the list of todos. + /// If the project board is not found. + [HttpGet] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public ActionResult> GetAllTodos(int boardId) + { + var board = ProjectBoardsController.Boards.FirstOrDefault(b => b.Id == boardId); + if (board == null) + { + return NotFound("Project board not found"); + } + + return Ok(board.Todos); + } + + /// + /// Retrieves a specific todo by ID within a project board. + /// + /// The ID of the project board. + /// The ID of the todo to retrieve. + /// The requested todo. + /// Returns the requested todo. + /// If the project board or todo is not found. + [HttpGet("{id}")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public ActionResult GetTodoById(int boardId, int id) + { + var board = ProjectBoardsController.Boards.FirstOrDefault(b => b.Id == boardId); + if (board == null) + { + return NotFound("Project board not found"); + } + + var todo = board.Todos.FirstOrDefault(t => t.Id == id); + if (todo == null) + { + return NotFound("Todo not found"); + } + + return Ok(todo); + } + + /// + /// Creates a new todo within a project board. + /// + /// The ID of the project board. + /// The todo to create. + /// The newly created todo. + /// Returns the newly created todo. + /// If the todo data is invalid. + /// If the project board is not found. + [HttpPost] + [ProducesResponseType(StatusCodes.Status201Created)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public ActionResult CreateTodo(int boardId, Todo todo) + { + var board = ProjectBoardsController.Boards.FirstOrDefault(b => b.Id == boardId); + if (board == null) + { + return NotFound("Project board not found"); + } + + todo.Id = _nextTodoId++; + todo.ProjectBoardId = boardId; + todo.CreatedAt = DateTime.UtcNow; + + board.Todos.Add(todo); + + return CreatedAtAction(nameof(GetTodoById), new { boardId, id = todo.Id }, todo); + } + + /// + /// Updates an existing todo within a project board. + /// + /// The ID of the project board. + /// The ID of the todo to update. + /// The updated todo data. + /// No content if successful. + /// If the update was successful. + /// If the todo data is invalid. + /// If the project board or todo is not found. + [HttpPut("{id}")] + [ProducesResponseType(StatusCodes.Status204NoContent)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public IActionResult UpdateTodo(int boardId, int id, Todo updatedTodo) + { + var board = ProjectBoardsController.Boards.FirstOrDefault(b => b.Id == boardId); + if (board == null) + { + return NotFound("Project board not found"); + } + + var todo = board.Todos.FirstOrDefault(t => t.Id == id); + if (todo == null) + { + return NotFound("Todo not found"); + } + + todo.Title = updatedTodo.Title; + todo.Description = updatedTodo.Description; + todo.IsComplete = updatedTodo.IsComplete; + todo.Priority = updatedTodo.Priority; + todo.DueDate = updatedTodo.DueDate; + + return NoContent(); + } + + /// + /// Deletes a todo from a project board. + /// + /// The ID of the project board. + /// The ID of the todo to delete. + /// No content if successful. + /// If the deletion was successful. + /// If the project board or todo is not found. + [HttpDelete("{id}")] + [ProducesResponseType(StatusCodes.Status204NoContent)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public IActionResult DeleteTodo(int boardId, int id) + { + var board = ProjectBoardsController.Boards.FirstOrDefault(b => b.Id == boardId); + if (board == null) + { + return NotFound("Project board not found"); + } + + var todo = board.Todos.FirstOrDefault(t => t.Id == id); + if (todo == null) + { + return NotFound("Todo not found"); + } + + board.Todos.Remove(todo); + return NoContent(); + } +} diff --git a/aspnetcore/fundamentals/openapi/samples/10.x/aspnet-openapi-xml-controllers/api/Program.cs b/aspnetcore/fundamentals/openapi/samples/10.x/aspnet-openapi-xml-controllers/api/Program.cs new file mode 100644 index 000000000000..cc1f3c2be78d --- /dev/null +++ b/aspnetcore/fundamentals/openapi/samples/10.x/aspnet-openapi-xml-controllers/api/Program.cs @@ -0,0 +1,19 @@ +using Scalar.AspNetCore; + +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddControllers(); +builder.Services.AddOpenApi(); + +var app = builder.Build(); + +if (app.Environment.IsDevelopment()) +{ + app.MapOpenApi(); + app.MapScalarApiReference(); +} + +app.Map("/", () => Results.Redirect("/scalar/v1")); +app.MapControllers(); + +app.Run(); diff --git a/aspnetcore/fundamentals/openapi/samples/10.x/aspnet-openapi-xml-controllers/api/Properties/launchSettings.json b/aspnetcore/fundamentals/openapi/samples/10.x/aspnet-openapi-xml-controllers/api/Properties/launchSettings.json new file mode 100644 index 000000000000..e9e960e175e9 --- /dev/null +++ b/aspnetcore/fundamentals/openapi/samples/10.x/aspnet-openapi-xml-controllers/api/Properties/launchSettings.json @@ -0,0 +1,25 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "scalar/v1", + "applicationUrl": "http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "scalar/v1", + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/aspnetcore/fundamentals/openapi/samples/10.x/aspnet-openapi-xml-controllers/api/appsettings.Development.json b/aspnetcore/fundamentals/openapi/samples/10.x/aspnet-openapi-xml-controllers/api/appsettings.Development.json new file mode 100644 index 000000000000..0c208ae9181e --- /dev/null +++ b/aspnetcore/fundamentals/openapi/samples/10.x/aspnet-openapi-xml-controllers/api/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/aspnetcore/fundamentals/openapi/samples/10.x/aspnet-openapi-xml-controllers/api/appsettings.json b/aspnetcore/fundamentals/openapi/samples/10.x/aspnet-openapi-xml-controllers/api/appsettings.json new file mode 100644 index 000000000000..10f68b8c8b4f --- /dev/null +++ b/aspnetcore/fundamentals/openapi/samples/10.x/aspnet-openapi-xml-controllers/api/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/aspnetcore/fundamentals/openapi/samples/10.x/aspnet-openapi-xml-controllers/global.json b/aspnetcore/fundamentals/openapi/samples/10.x/aspnet-openapi-xml-controllers/global.json new file mode 100644 index 000000000000..bc5a8a6a726a --- /dev/null +++ b/aspnetcore/fundamentals/openapi/samples/10.x/aspnet-openapi-xml-controllers/global.json @@ -0,0 +1,5 @@ +{ + "sdk": { + "version": "10.0.100-preview.2.25163.38" + } +} diff --git a/aspnetcore/fundamentals/openapi/samples/10.x/aspnet-openapi-xml-controllers/models/Models.csproj b/aspnetcore/fundamentals/openapi/samples/10.x/aspnet-openapi-xml-controllers/models/Models.csproj new file mode 100644 index 000000000000..9ed914b5ba4b --- /dev/null +++ b/aspnetcore/fundamentals/openapi/samples/10.x/aspnet-openapi-xml-controllers/models/Models.csproj @@ -0,0 +1,9 @@ + + + + net10.0 + enable + enable + + + diff --git a/aspnetcore/fundamentals/openapi/samples/10.x/aspnet-openapi-xml-controllers/models/ProjectBoard.cs b/aspnetcore/fundamentals/openapi/samples/10.x/aspnet-openapi-xml-controllers/models/ProjectBoard.cs new file mode 100644 index 000000000000..c5401c91d538 --- /dev/null +++ b/aspnetcore/fundamentals/openapi/samples/10.x/aspnet-openapi-xml-controllers/models/ProjectBoard.cs @@ -0,0 +1,32 @@ +namespace Models; + +/// +/// Represents a project board containing todos and related information. +/// +public class ProjectBoard +{ + /// + /// Unique identifier for the project board. + /// + public int Id { get; set; } + + /// + /// The name of the project board. + /// + public string Name { get; set; } = string.Empty; + + /// + /// A description of the project board and its purpose. + /// + public string? Description { get; set; } + + /// + /// The date when the project board was created. + /// + public DateTime CreatedAt { get; set; } = DateTime.UtcNow; + + /// + /// The collection of todos associated with this project board. + /// + public List Todos { get; set; } = new(); +} diff --git a/aspnetcore/fundamentals/openapi/samples/10.x/aspnet-openapi-xml-controllers/models/Todo.cs b/aspnetcore/fundamentals/openapi/samples/10.x/aspnet-openapi-xml-controllers/models/Todo.cs new file mode 100644 index 000000000000..41ab9aeb5aa7 --- /dev/null +++ b/aspnetcore/fundamentals/openapi/samples/10.x/aspnet-openapi-xml-controllers/models/Todo.cs @@ -0,0 +1,47 @@ +namespace Models; + +/// +/// Represents a todo item within a project board. +/// +public class Todo +{ + /// + /// Unique identifier for the todo item. + /// + public int Id { get; set; } + + /// + /// The title or brief description of the todo. + /// + public string Title { get; set; } = string.Empty; + + /// + /// A detailed description of the todo item. + /// + public string? Description { get; set; } + + /// + /// Indicates whether the todo has been completed. + /// + public bool IsComplete { get; set; } + + /// + /// The priority level of the todo item. + /// + public TodoPriority Priority { get; set; } = TodoPriority.Medium; + + /// + /// The date when the todo was created. + /// + public DateTime CreatedAt { get; set; } = DateTime.UtcNow; + + /// + /// The date when the todo is due to be completed. + /// + public DateTime? DueDate { get; set; } + + /// + /// The ID of the project board this todo belongs to. + /// + public int ProjectBoardId { get; set; } +} diff --git a/aspnetcore/fundamentals/openapi/samples/10.x/aspnet-openapi-xml-controllers/models/TodoPriority.cs b/aspnetcore/fundamentals/openapi/samples/10.x/aspnet-openapi-xml-controllers/models/TodoPriority.cs new file mode 100644 index 000000000000..1ec44d42963e --- /dev/null +++ b/aspnetcore/fundamentals/openapi/samples/10.x/aspnet-openapi-xml-controllers/models/TodoPriority.cs @@ -0,0 +1,27 @@ +namespace Models; + +/// +/// Represents the priority level of a todo item. +/// +public enum TodoPriority +{ + /// + /// Low priority items that can be addressed later. + /// + Low = 0, + + /// + /// Medium priority items that should be addressed in a reasonable timeframe. + /// + Medium = 1, + + /// + /// High priority items that should be addressed soon. + /// + High = 2, + + /// + /// Critical items that require immediate attention. + /// + Critical = 3 +} diff --git a/aspnetcore/fundamentals/openapi/samples/10.x/aspnet-openapi-xml/api/Api.csproj b/aspnetcore/fundamentals/openapi/samples/10.x/aspnet-openapi-xml/api/Api.csproj index a01535c1ee80..67b404f502e9 100644 --- a/aspnetcore/fundamentals/openapi/samples/10.x/aspnet-openapi-xml/api/Api.csproj +++ b/aspnetcore/fundamentals/openapi/samples/10.x/aspnet-openapi-xml/api/Api.csproj @@ -8,7 +8,7 @@ - + diff --git a/aspnetcore/fundamentals/openapi/samples/10.x/aspnet-openapi-xml/api/ProjectBoardApis.cs b/aspnetcore/fundamentals/openapi/samples/10.x/aspnet-openapi-xml/api/ProjectBoardApis.cs index 2039da1e50b4..8771edf85179 100644 --- a/aspnetcore/fundamentals/openapi/samples/10.x/aspnet-openapi-xml/api/ProjectBoardApis.cs +++ b/aspnetcore/fundamentals/openapi/samples/10.x/aspnet-openapi-xml/api/ProjectBoardApis.cs @@ -19,9 +19,11 @@ public static class ProjectBoardApis /// The endpoint route builder. public static void MapProjectBoardApis(this IEndpointRouteBuilder app) { +#pragma warning disable ASPDEPR002 var boardGroup = app.MapGroup("/api/projectboards") .WithTags("Project Boards") .WithOpenApi(); +#pragma warning restore ASPDEPR002 // Project Board endpoints boardGroup.MapGet("/", GetAllProjectBoards); diff --git a/aspnetcore/fundamentals/openapi/samples/10.x/aspnet-openapi-xml/api/Properties/launchSettings.json b/aspnetcore/fundamentals/openapi/samples/10.x/aspnet-openapi-xml/api/Properties/launchSettings.json new file mode 100644 index 000000000000..e9e960e175e9 --- /dev/null +++ b/aspnetcore/fundamentals/openapi/samples/10.x/aspnet-openapi-xml/api/Properties/launchSettings.json @@ -0,0 +1,25 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "scalar/v1", + "applicationUrl": "http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "scalar/v1", + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +}