using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using RobotNet10.MapEditor.Shared.DTOs.Node; using RobotNet10.MapEditor.Shared.DTOs.Requests; using RobotNet10.MapEditor.Shared.DTOs.Responses; using RobotNet10.MapManager.Services; namespace RobotNet10.MapManager.Controllers; /// /// Controller for managing nodes /// [ApiController] [Route("api/nodes")] [Authorize] public class NodesController( INodeService nodeService, ILogger logger) : ControllerBase { private readonly INodeService _nodeService = nodeService; private readonly ILogger _logger = logger; /// /// Get all nodes for a layout level /// /// Layout level ID /// List of nodes with vehicle properties [HttpGet("level/{layoutLevelId}")] [ProducesResponseType(typeof(List), 200)] public async Task>> GetNodesByLevel(Guid layoutLevelId) { var nodes = await _nodeService.GetNodesByLevelAsync(layoutLevelId, includeVehicleProperties: true); var dtos = nodes.Select(MapToDto).ToList(); return Ok(dtos); } /// /// Get node by ID /// /// Node database ID /// Node details with vehicle properties [HttpGet("{nodeId}")] [ProducesResponseType(typeof(NodeDto), 200)] [ProducesResponseType(404)] public async Task> GetNode(Guid nodeId) { var node = await _nodeService.GetByIdAsync(nodeId, includeVehicleProperties: true); if (node == null) { return NotFound(CreateErrorResponse($"Node with ID '{nodeId}' not found", "NODE_NOT_FOUND")); } return Ok(MapToDto(node)); } /// /// Update node /// /// Node database ID /// Update request /// Updated node [HttpPut("{nodeId}")] [ProducesResponseType(typeof(NodeDto), 200)] [ProducesResponseType(400)] [ProducesResponseType(404)] public async Task> UpdateNode( Guid nodeId, [FromBody] UpdateNodeRequest request) { try { var node = await _nodeService.UpdateAsync(nodeId, request); return Ok(MapToDto(node)); } catch (InvalidOperationException ex) { if (_logger.IsEnabled(LogLevel.Warning)) _logger.LogWarning(ex, "Failed to update node: {NodeId}", nodeId); if (ex.Message.Contains("not found")) return NotFound(CreateErrorResponse(ex.Message, "NODE_NOT_FOUND")); return BadRequest(CreateErrorResponse(ex.Message, "VALIDATION_ERROR")); } } // Helper method to map entity to DTO private static NodeDto MapToDto(Data.Node node) { return new NodeDto { Id = node.Id, LevelId = node.LevelId, NodeId = node.NodeId, NodeName = node.NodeName, NodeDescription = node.NodeDescription, MapId = node.MapId, X = node.X, Y = node.Y, VehicleProperties = node.VehicleProperties?.Select(vp => new NodeVehiclePropertyDto { Id = vp.Id, NodeId = vp.NodeId, VehicleTypeId = vp.VehicleTypeId, VehicleTypeIdString = vp.VehicleType?.VehicleTypeId, Theta = vp.Theta, Actions = vp.Actions, AllowedDeviationXY = vp.AllowedDeviationXY, AllowedDeviationTheta = vp.AllowedDeviationTheta }).ToList() }; } private static ErrorResponseDto CreateErrorResponse( string error, string? errorCode = null, Dictionary? details = null) { return new ErrorResponseDto { Error = error, ErrorCode = errorCode, Details = details }; } }