using MassTransit; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.IdentityModel.Tokens; using OpenWarehouse.warehouse.api.Common.ItemTagManager; using OpenWarehouse.warehouse.api.Common.Kafka; using OpenWarehouse.warehouse.api.Common.Kafka.TagTree; using OpenWarehouse.warehouse.api.Common.Producer; using OpenWarehouse.warehouse.api.Common.TaskManager; using OpenWarehouse.warehouse.api.Model.Item; using ActionResult = Microsoft.AspNetCore.Mvc.ActionResult; using ItemCategoryTag = OpenWarehouse.warehouse.api.Data.ItemCategoryTag; namespace OpenWarehouse.warehouse.api.Controller; [ApiController] [Route("[controller]/[action]")] [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] public class Item(IItemManager itemManager, ILogger logger, ITopicProducer publishTagTreeEndpoint, ITaskManager taskManager, IItemTagManager itemTagManager, IProducerManager producerManager) : ControllerBase { [HttpGet("{id}")] public async Task> GetItemById([FromRoute] string id) { logger.LogInformation("GetItemById called for item id {id}", id); var item = await itemManager.FindItemByIdAsync(id); logger.LogDebug("Item retrieved: {@Item}", item); if (item == null) { logger.LogWarning("Item with ID:{id} not found", id); return NotFound(); } var result = new ItemModel() { Id = item.Id.ToString(), Name = item.Name, Claims = item.ItemClaims?.ToDictionary( q => q.ClaimType ?? $"Unknown type, {Guid.NewGuid()} ", q => new ClaimValueModel() { Issuer = q.ClaimIssuer, Value = q.ClaimValue, }), ProducerId = item.ProducerId.ToString(), ProducerName = item.Producer?.Name, }; logger.LogDebug("Returning item model: {@Result}", result); return Ok(result); } [HttpPost] public async Task> CreateItem([FromBody]CreateItemRequest request) { CreateItemRespond respond = new CreateItemRespond(); bool ready = true; if (request is { ProducerName: null, ItemName: null}) return BadRequest(); var producer = await producerManager.FindProducerByName(request.ProducerName ?? ""); if (producer == null) { ready = false; respond.Errors["Producer"] = $"Producer {request.ProducerName} is not found"; } var listOfTags = new List(); foreach (var itemTag in request.Tags) { var tag = await itemTagManager.GetTagByName(itemTag); if (tag == null) { ready = false; respond.Errors[$"Tag:{itemTag}"] = "Not found"; continue; } listOfTags.Add(tag); } if (!ready) { return BadRequest(respond); } var item = new Data.Item() { Name = request.ItemName, ItemCategoryTags = listOfTags, Producer = producer, }; var res = await itemManager.CreateAsync(item); return res.IsSuccess ? Ok(item) : BadRequest(respond); } [HttpPost] public async Task CreateTagTree([FromBody] CreateTagTreeRequest request) { Guid id = Guid.NewGuid(); logger.LogInformation("Request with id:{id} to create tag tree received", id); if (request.ItemCategoryTagsList == null || request.ItemCategoryTagsList.Count == 0) { logger.LogWarning("Request with id:{id} cannot be accepted ", id); return BadRequest("List cannot be empty!"); } logger.LogInformation("Request with id:{id} to create item tag tree accepted", id); var actor = (HttpContext.User.Identity as CaseSensitiveClaimsIdentity)? .Claims.Where(q => q.Type == ClaimTypes.NameIdentifier) .Select(q => q.Value).FirstOrDefault(); logger.LogDebug("Actor identified: {Actor}", actor); await taskManager.CreateTask(id, actor); logger.LogDebug("Task created with ID {TaskId}", id); var kafkaMess = new TagTreeKafkaMessage() { Guid = id, ItemCategoryTagsList = request.ItemCategoryTagsList, Action = Actions.Create }; logger.LogDebug("Publishing Kafka message: {@KafkaMessage}", kafkaMess); _ = publishTagTreeEndpoint.Produce(kafkaMess); return Accepted($"/status/{id}"); } [HttpPost] public async Task DeleteTagList([FromBody] CreateTagTreeRequest request) { Guid id = Guid.NewGuid(); logger.LogInformation("Request with id:{id} to delete tag list received", id); if (request.ItemCategoryTagsList == null || request.ItemCategoryTagsList.Count == 0) { logger.LogWarning("Request with id:{id} cannot be accepted ", id); return BadRequest("List cannot be empty!"); } logger.LogInformation("Request with id:{id} to delete tag list accepted", id); var actor = (HttpContext.User.Identity as CaseSensitiveClaimsIdentity)? .Claims.Where(q => q.Type == ClaimTypes.NameIdentifier) .Select(q => q.Value).FirstOrDefault(); logger.LogDebug("Actor identified: {Actor}", actor); await taskManager.CreateTask(id, actor); logger.LogDebug("Task created with ID {TaskId}", id); var kafkaMess = new TagTreeKafkaMessage() { Guid = id, ItemCategoryTagsList = request.ItemCategoryTagsList, Action = Actions.Delete }; logger.LogDebug("Publishing Kafka message: {@KafkaMessage}", kafkaMess); _ = publishTagTreeEndpoint.Produce(kafkaMess); return Accepted($"/status/{id}"); } [HttpGet("{root}")] public async Task> GetTagTree([FromRoute]string root) { logger.LogInformation("GetTagTree called for root {root}", root); var result = await itemTagManager.GetTagTree(root); logger.LogDebug("Tag tree retrieved for root {Root}: {@Result}", root, result); if (result == null) { logger.LogWarning("No tag tree found for root {Root}", root); return NotFound(); } var returnResult = GetChild(result); logger.LogDebug("Returning tag tree model: {@ReturnResult}", returnResult); return Ok(returnResult); } [HttpPost] public async Task> UpdateItemClaims([FromBody]UpdateItemClaimRequest request) { if (request.ItemId == null || request.ListOfUpdatedClaims.Count == 0) return BadRequest(); int result = 0; var item = await itemManager.FindItemByIdAsync(request.ItemId); if (item == null) return NotFound(); foreach (var claim in request.ListOfUpdatedClaims) { if (claim.ClaimType == null) continue; if (claim.ClaimValue == null) { var resultLocal = await itemManager.DeleteClaim(item, claim.ClaimType); if (resultLocal.IsSuccess) { result++; } else { logger.LogWarning("Failed to delete claim: {ClaimType}", claim.ClaimType); } } else { var resultLocal = await itemManager.UpdateClaim(item, new Claim(claim.ClaimType, claim.ClaimValue, claim.ClaimIssuer)); if (resultLocal.IsSuccess) { result++; } else { logger.LogWarning("Failed to update claim: {ClaimType}", claim.ClaimType); } } } return result> 0 ? Ok(result) : BadRequest(); } private TagTreeModel GetChild(ItemCategoryTag model) { logger.LogDebug("Building child model for tag {TagName}", model.Name); var result = new TagTreeModel(){Name = model.Name}; if (model.ListOfChildrenTag != null) foreach (var element in model.ListOfChildrenTag) { logger.LogDebug("Processing child tag {ChildTagName}", element.Name); result.Child.Add(GetChild(element)); } return result; } }