using System; using System.Collections.Generic; using System.Linq; using Manager.DTOs; using ManagerService.Data; using ManagerService.Data.SubSection; using ManagerService.Security; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using NSwag.Annotations; namespace ManagerService.Controllers { /// /// Les points d'intérêt d'une maquette 3D — item E7 du lot XR-4. /// /// Pourquoi un contrôleur à part. Le CRUD des points existe déjà, mais il est /// écrit sur SectionMap : OfType<SectionMap>(), MapPoints. /// Une maquette a les mêmes points — c'est le même GeoPoint — sur une autre /// collection. Élargir SectionMapController aurait mélangé deux types de /// section dans chaque méthode ; ce contrôleur-ci ne parle que de maquettes. /// /// La différence de fond tient en un champ : ici un point porte une /// LocalTransform (x, y, z dans le repère du modèle) là où une carte porte /// une géométrie PostGIS. Les deux coexistent sur l'entité, et rien n'oblige un point /// à n'en avoir qu'une. /// [Authorize(Policy = ManagerService.Service.Security.Policies.ContentEditor)] [ApiController, Route("api/[controller]")] [OpenApiTag("SectionScene3D", Description = "3D model section points of interest")] public class SectionScene3DController : ControllerBase { private readonly ILogger _logger; private readonly MyInfoMateDbContext _myInfoMateDbContext; public SectionScene3DController(ILogger logger, MyInfoMateDbContext myInfoMateDbContext) { _logger = logger; _myInfoMateDbContext = myInfoMateDbContext; } /// Get all points of interest of a 3D model section /// Section id [AllowAnonymous] [RequireAppKey] [ProducesResponseType(typeof(List), 200)] [ProducesResponseType(typeof(string), 404)] [ProducesResponseType(typeof(string), 500)] [HttpGet("{sectionId}/points")] public ObjectResult GetAllPointsFromSection(string sectionId) { try { var section = _myInfoMateDbContext.Sections .OfType() .Include(s => s.Points) .FirstOrDefault(s => s.Id == sectionId); if (section == null) throw new KeyNotFoundException("3D model section does not exist"); return new OkObjectResult( (section.Points ?? new List()).Select(p => p.ToDTO()).ToList()); } catch (KeyNotFoundException ex) { return new NotFoundObjectResult(ex.Message) { }; } catch (Exception ex) { return new ObjectResult(ex.Message) { StatusCode = 500 }; } } /// Add a point of interest to a 3D model section /// Section id /// Point to create [ProducesResponseType(typeof(GeoPointDTO), 200)] [ProducesResponseType(typeof(string), 400)] [ProducesResponseType(typeof(string), 404)] [ProducesResponseType(typeof(string), 500)] [HttpPost("{sectionId}/points")] public ObjectResult CreatePoint(string sectionId, [FromBody] GeoPointDTO geoPointDTO) { try { if (geoPointDTO == null) throw new ArgumentNullException("GeoPoint is null"); var section = _myInfoMateDbContext.Sections .OfType() .Include(s => s.Points) .FirstOrDefault(s => s.Id == sectionId); if (section == null) throw new KeyNotFoundException("3D model section does not exist"); var point = new GeoPoint { Title = geoPointDTO.title, Description = geoPointDTO.description, Contents = geoPointDTO.contents, ImageResourceId = geoPointDTO.imageResourceId, ImageUrl = geoPointDTO.imageUrl, Schedules = geoPointDTO.schedules, Prices = geoPointDTO.prices, Phone = geoPointDTO.phone, Email = geoPointDTO.email, Site = geoPointDTO.site, // Un point neuf n'a pas encore été posé sur la maquette : il arrive à // l'origine du modèle, là où l'éditeur le montrera pour qu'on le place. LocalTransform = geoPointDTO.localTransform ?? new Position3D() }; section.Points ??= new List(); section.Points.Add(point); _myInfoMateDbContext.SaveChanges(); return new OkObjectResult(point.ToDTO()); } catch (ArgumentNullException ex) { return new BadRequestObjectResult(ex.Message) { }; } catch (KeyNotFoundException ex) { return new NotFoundObjectResult(ex.Message) { }; } catch (Exception ex) { return new ObjectResult(ex.Message) { StatusCode = 500 }; } } /// Update a point of interest, position included /// Point to update [ProducesResponseType(typeof(GeoPointDTO), 200)] [ProducesResponseType(typeof(string), 400)] [ProducesResponseType(typeof(string), 404)] [ProducesResponseType(typeof(string), 500)] [HttpPut("points")] public ObjectResult UpdatePoint([FromBody] GeoPointDTO geoPointDTO) { try { if (geoPointDTO == null) throw new ArgumentNullException("GeoPoint param is null"); var point = _myInfoMateDbContext.GeoPoints .FirstOrDefault(p => p.Id == geoPointDTO.id); if (point == null) throw new KeyNotFoundException("GeoPoint does not exist"); point.Title = geoPointDTO.title ?? point.Title; point.Description = geoPointDTO.description ?? point.Description; point.Contents = geoPointDTO.contents ?? point.Contents; point.ImageResourceId = geoPointDTO.imageResourceId ?? point.ImageResourceId; point.ImageUrl = geoPointDTO.imageUrl ?? point.ImageUrl; // C'est le champ que l'éditeur 3D renvoie à chaque déplacement, et la // seule raison pour laquelle cette route est appelée souvent. if (geoPointDTO.localTransform != null) point.LocalTransform = geoPointDTO.localTransform; _myInfoMateDbContext.SaveChanges(); return new OkObjectResult(point.ToDTO()); } catch (ArgumentNullException ex) { return new BadRequestObjectResult(ex.Message) { }; } catch (KeyNotFoundException ex) { return new NotFoundObjectResult(ex.Message) { }; } catch (Exception ex) { return new ObjectResult(ex.Message) { StatusCode = 500 }; } } /// Delete a point of interest /// Point id [ProducesResponseType(typeof(string), 200)] [ProducesResponseType(typeof(string), 404)] [ProducesResponseType(typeof(string), 500)] [HttpDelete("points/{id}")] public ObjectResult DeletePoint(int id) { try { var point = _myInfoMateDbContext.GeoPoints.FirstOrDefault(p => p.Id == id); if (point == null) throw new KeyNotFoundException("GeoPoint does not exist"); _myInfoMateDbContext.GeoPoints.Remove(point); _myInfoMateDbContext.SaveChanges(); return new OkObjectResult("Point deleted"); } catch (KeyNotFoundException ex) { return new NotFoundObjectResult(ex.Message) { }; } catch (Exception ex) { return new ObjectResult(ex.Message) { StatusCode = 500 }; } } } }