diff --git a/Core.Inventory.DAL.API/Controllers/TagController.cs b/Core.Inventory.DAL.API/Controllers/TagController.cs new file mode 100644 index 0000000..0b1426e --- /dev/null +++ b/Core.Inventory.DAL.API/Controllers/TagController.cs @@ -0,0 +1,190 @@ +using Asp.Versioning; +using Core.Adapters.Lib; +using Core.Blueprint.Logging; +using Core.Blueprint.Mongo; +using Core.Inventory.Domain.Contexts.Inventory.Request; +using Core.Inventory.Provider.Contracts; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace Core.Inventory.DAL.API.Controllers +{ + /// + /// Handles all requests for Tag authentication. + /// + [ApiVersion(MimeTypes.ApplicationVersion)] + [Route("api/v{api-version:apiVersion}/[controller]")] + [Produces(MimeTypes.ApplicationJson)] + [Consumes(MimeTypes.ApplicationJson)] + [ApiController] + [AllowAnonymous] + public class TagController(ITagProvider service) : ControllerBase + { + /// + /// Gets all the Tags. + /// + /// The found entities. + /// The Tags found. + /// The Tags not found error. + /// The service internal error. + [HttpGet] + [Consumes(MimeTypes.ApplicationJson)] + [Produces(MimeTypes.ApplicationJson)] + [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] + public async Task GetAllTagsAsync(CancellationToken cancellationToken) + { + var result = await service.GetAllTags(cancellationToken).ConfigureAwait(false); + return Ok(result); + } + + /// + /// Gets all the Tags by Tag identifiers. + /// + /// The list of Tag identifiers. + /// The found entities. + /// The Tags found. + /// The Tags not found error. + /// The service internal error. + [HttpPost] + [Route("GetTagList")] + [Consumes(MimeTypes.ApplicationJson)] + [Produces(MimeTypes.ApplicationJson)] + [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] + public async Task GetAllTagsByList([FromBody] string[] tags, CancellationToken cancellationToken) + { + if (tags == null || !tags.Any()) + { + return BadRequest("Tag identifiers are required."); + } + + var result = await service.GetAllTagsByList(tags, cancellationToken).ConfigureAwait(false); + return Ok(result); + } + + + /// + /// Gets the Tag by identifier. + /// + /// The Tag identifier. + /// The found entity. + /// The Tag found. + /// The Tag not found error. + /// The service internal error. + [HttpGet] + [Route("{id}")] + [Consumes(MimeTypes.ApplicationJson)] + [Produces(MimeTypes.ApplicationJson)] + [ProducesResponseType(typeof(TagAdapter), StatusCodes.Status200OK)] + public async Task GetTagByIdAsync([FromRoute] string id, CancellationToken cancellationToken) + { + var result = await service.GetTagById(id, cancellationToken).ConfigureAwait(false); + + if (result == null) + { + return NotFound("Entity not found"); + } + + return Ok(result); + } + + /// + /// Creates a new Tag. + /// + /// The Tag to be added. + /// The created entity. + /// The Tag created. + /// The Tag could not be created. + /// The service internal e|ror. + [HttpPost] + [ProducesResponseType(typeof(TagAdapter), StatusCodes.Status201Created)] + public async Task CreateTagAsync([FromBody] TagRequest newTag, CancellationToken cancellationToken) + { + var result = await service.CreateTag(newTag, cancellationToken).ConfigureAwait(false); + return Created("CreatedWithIdAsync", result); + } + + /// + /// Updates a full Tag by identifier. + /// + /// The Tag to update. + /// The Tag identifier. + /// The updated entity. + /// The Tag updated. + /// The Tag not found. + /// The Tag could not be updated. + /// The service internal error. + [HttpPut] + [Route("{id}")] + [Consumes(MimeTypes.ApplicationJson)] + [Produces(MimeTypes.ApplicationJson)] + [ProducesResponseType(typeof(TagAdapter), StatusCodes.Status200OK)] + public async Task UpdateTagAsync([FromRoute] string id, TagAdapter entity, CancellationToken cancellationToken) + { + if (id != entity.Id?.ToString()) + { + return BadRequest("Tag ID mismatch"); + } + + var result = await service.UpdateTag(entity, cancellationToken).ConfigureAwait(false); + + return Ok(result); + } + + /// + /// Changes the status of the Tag. + /// + /// The Tag identifier. + /// The new status of the Tag. + /// The updated entity. + /// The Tag updates. + /// The Tag not found. + /// The Tag could not be deleted. + /// The service internal error. + [HttpPatch] + [Route("{id}/{newStatus}/ChangeStatus")] + [Consumes(MimeTypes.ApplicationJson)] + [Produces(MimeTypes.ApplicationJson)] + [ProducesResponseType(typeof(TagAdapter), StatusCodes.Status200OK)] + public async Task ChangeTagStatus([FromRoute] string id, [FromRoute] StatusEnum newStatus, CancellationToken cancellationToken) + { + var result = await service.ChangeTagStatus(id, newStatus, cancellationToken).ConfigureAwait(false); + return Ok(result); + } + + /// + /// Adds a parentTag to the tag. + /// + /// The tag identifier. + /// The parentTag identifier to add. + /// The updated entity. + /// The tag with the updated parentTags. + /// The tag or parentTag not found. + /// The service internal error. + [HttpPost] + [Route("{tagId}/ParentTags/{parentTagId}/Add")] + [ProducesResponseType(typeof(TagAdapter), StatusCodes.Status200OK)] + public async Task AddParentTagAsync([FromRoute] string tagId, [FromRoute] string parentTagId, CancellationToken cancellationToken) + { + var result = await service.AddParentTag(tagId, parentTagId, cancellationToken).ConfigureAwait(false); + return Ok(result); + } + + /// + /// Remove a parentTag to the tag. + /// + /// The tag identifier. + /// The parentTag identifier to remove. + /// The updated entity. + /// The tag with the updated parentTags. + /// The tag or parentTag not found. + /// The service internal error. + [HttpDelete] + [Route("{tagId}/ParentTags/{parentTagId}/Remove")] + [ProducesResponseType(typeof(TagAdapter), StatusCodes.Status200OK)] + public async Task RemoveParentTagAsync([FromRoute] string tagId, [FromRoute] string parentTagId, CancellationToken cancellationToken) + { + var result = await service.RemoveParentTag(tagId, parentTagId, cancellationToken).ConfigureAwait(false); ; + return Ok(result); + } + } +} diff --git a/Core.Inventory.Domain/Contexts/Inventory/Request/TagRequest.cs b/Core.Inventory.Domain/Contexts/Inventory/Request/TagRequest.cs new file mode 100644 index 0000000..bcbe71d --- /dev/null +++ b/Core.Inventory.Domain/Contexts/Inventory/Request/TagRequest.cs @@ -0,0 +1,67 @@ +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using System.Text.Json.Serialization; + +namespace Core.Inventory.Domain.Contexts.Inventory.Request +{ + /// + /// Data transfer object (DTO) for adding Tag. + /// + public class TagRequest + { + /// + /// Gets or sets the tenantId of the Tag. + /// + [BsonElement("tenantId")] + [BsonRepresentation(BsonType.String)] + [JsonPropertyName("tenantId")] + public string TenantId { get; set; } = null!; + + /// + /// Gets or sets the name of the Tag. + /// + [BsonElement("tagName")] + [BsonRepresentation(BsonType.String)] + [JsonPropertyName("tagName")] + public string TagName { get; set; } = null!; + + /// + /// Gets or sets the typeId of the Tag. + /// + [BsonElement("typeId")] + [BsonRepresentation(BsonType.String)] + [JsonPropertyName("typeId")] + public string TypeId { get; set; } = null!; + + /// + /// Gets or sets the parentTagId of the Tag. + /// + [BsonElement("parentTagId")] + [JsonPropertyName("parentTagId")] + public string[] ParentTagId { get; set; } = null!; + + /// + /// Gets or sets the slug of the Tag. + /// + [BsonElement("slug")] + [BsonRepresentation(BsonType.String)] + [JsonPropertyName("slug")] + public string Slug { get; set; } = null!; + + /// + /// Gets or sets the displayOrder of the Tag. + /// + [BsonElement("displayOrder")] + [BsonRepresentation(BsonType.String)] + [JsonPropertyName("displayOrder")] + public int DisplayOrder { get; set; } + + /// + /// Gets or sets the icon of the Tag. + /// + [BsonElement("icon")] + [BsonRepresentation(BsonType.String)] + [JsonPropertyName("icon")] + public string Icon { get; set; } = null!; + } +} diff --git a/Core.Inventory.Provider/Contracts/ITagProvider.cs b/Core.Inventory.Provider/Contracts/ITagProvider.cs new file mode 100644 index 0000000..10c3108 --- /dev/null +++ b/Core.Inventory.Provider/Contracts/ITagProvider.cs @@ -0,0 +1,75 @@ +using Core.Adapters.Lib; +using Core.Blueprint.Mongo; +using Core.Inventory.Domain.Contexts.Inventory.Request; + +namespace Core.Inventory.Provider.Contracts +{ + public interface ITagProvider + { + /// + /// Creates a new Tag. + /// + /// The Tag to be created. + /// A representing + /// the asynchronous execution of the service. + ValueTask CreateTag(TagRequest newTag, CancellationToken cancellationToken); + + /// + /// Gets an Tag by identifier. + /// + /// The Tag identifier. + /// A representing + /// the asynchronous execution of the service. + ValueTask GetTagById(string _id, CancellationToken cancellationToken); + + /// + /// Gets all the Tags. + /// + /// A representing + /// the asynchronous execution of the service. + ValueTask> GetAllTags(CancellationToken cancellationToken); + + /// + /// Gets all the Tags by Tags identifier list. + /// + /// The list of Tags identifiers. + /// A representing + /// the asynchronous execution of the service. + ValueTask> GetAllTagsByList(string[] Tags, CancellationToken cancellationToken); + + /// + /// Changes the status of the Tag. + /// + /// The Tag identifier. + /// The new status of the Tag. + /// The updated entity. + /// A representing + /// the asynchronous execution of the service. + ValueTask ChangeTagStatus(string id, StatusEnum newStatus, CancellationToken cancellationToken); + + /// + /// Updates a Tag by id. + /// + /// The Tag to be updated. + /// The Tag identifier. + /// A representing + /// the asynchronous execution of the service. + ValueTask UpdateTag(TagAdapter entity, CancellationToken cancellationToken); + + /// + /// Adds a parentTag to the tag. + /// + /// The identifier of the tag to whom the parentTag will be added. + /// The identifier of the parentTag to add. + /// A representing the asynchronous operation, with the updated tag object. + ValueTask AddParentTag(string tagId, string parentTagId, CancellationToken cancellationToken); + + /// + /// Removes a parentTag from the tag. + /// + /// The identifier of the tag to whom the parentTag will be added. + /// The identifier of the parentTag to add. + /// A representing the asynchronous operation, with the updated tag object. + ValueTask RemoveParentTag(string tagId, string parentTagId, CancellationToken cancellationToken); + } +} diff --git a/Core.Inventory.Provider/Core.Inventory.Provider.csproj b/Core.Inventory.Provider/Core.Inventory.Provider.csproj index 963ec5e..2e45719 100644 --- a/Core.Inventory.Provider/Core.Inventory.Provider.csproj +++ b/Core.Inventory.Provider/Core.Inventory.Provider.csproj @@ -7,7 +7,7 @@ - + diff --git a/Core.Inventory.Provider/Providers/Inventory/TagProvider.cs b/Core.Inventory.Provider/Providers/Inventory/TagProvider.cs new file mode 100644 index 0000000..fd8faa6 --- /dev/null +++ b/Core.Inventory.Provider/Providers/Inventory/TagProvider.cs @@ -0,0 +1,192 @@ +using Core.Adapters.Lib; +using Core.Blueprint.Mongo; +using Core.Blueprint.Redis; +using Core.Blueprint.Redis.Helpers; +using Core.Inventory.Domain.Contexts.Inventory.Request; +using Core.Inventory.Provider.Contracts; +using Mapster; +using Microsoft.Extensions.Options; +using MongoDB.Driver; + +namespace Core.Inventory.Provider.Providers.Inventory +{ + /// + /// Handles all services and business rules related to . + /// + public class TagProvider : ITagProvider + { + private readonly CollectionRepository repository; + private readonly CacheSettings cacheSettings; + private readonly IRedisCacheProvider cacheProvider; + + public TagProvider(CollectionRepository repository, + IRedisCacheProvider cacheProvider, + IOptions cacheSettings) + { + this.repository = repository; + this.repository.CollectionInitialization(); + this.cacheSettings = cacheSettings.Value; + this.cacheProvider = cacheProvider; + } + + /// + /// Creates a new Tag. + /// + /// The Tag to be created. + /// A representing + /// the asynchronous execution of the service. + public async ValueTask CreateTag(TagRequest newTag, CancellationToken cancellationToken) + { + var tagCollection = newTag.Adapt(); + + await repository.InsertOneAsync(tagCollection); + + return tagCollection; + } + + /// + /// Gets an Tag by identifier. + /// + /// The Tag identifier. + /// A representing + /// the asynchronous execution of the service.0 + public async ValueTask GetTagById(string _id, CancellationToken cancellationToken) + { + var cacheKey = CacheKeyHelper.GenerateCacheKey(this, "GetTagById", _id); + var cachedData = await cacheProvider.GetAsync(cacheKey); + + if (cachedData is not null) { return cachedData; } + + var tag = await repository.FindByIdAsync(_id); + + await cacheProvider.SetAsync(cacheKey, tag); + + return tag; + } + + /// + /// Gets all the Tags. + /// + /// A representing + /// the asynchronous execution of the service. + public async ValueTask> GetAllTags(CancellationToken cancellationToken) + { + var cacheKey = CacheKeyHelper.GenerateCacheKey(this, "GetTags"); + var cachedData = await cacheProvider.GetAsync>(cacheKey) ?? []; + + if (cachedData.Any()) return cachedData; + + var tags = await repository.AsQueryable(); + + await cacheProvider.SetAsync(cacheKey, tags); + + return tags; + } + + /// + /// Gets all the Tags by Tags identifier list. + /// + /// The list of Tags identifiers. + /// A representing + /// the asynchronous execution of the service. + public async ValueTask> GetAllTagsByList(string[] tags, CancellationToken cancellationToken) + { + var cacheKey = CacheKeyHelper.GenerateCacheKey(this, "GetAllTagsByList", tags); + + var cachedData = await cacheProvider.GetAsync>(cacheKey) ?? []; + + if (cachedData.Any()) return cachedData; + + var builder = Builders.Filter; + var filters = new List>(); + + if (tags != null || !tags.Any()) + { + filters.Add(builder.In(x => x._Id, tags)); + } + + var finalFilter = filters.Any() ? builder.And(filters) : builder.Empty; + + var TagsList = await repository.FilterByMongoFilterAsync(finalFilter); + + await cacheProvider.SetAsync(cacheKey, TagsList); + + return TagsList; + } + + + /// + /// Changes the status of the Tag. + /// + /// The Tag identifier. + /// The new status of the Tag. + /// A representing + /// the asynchronous execution of the service. + public async ValueTask ChangeTagStatus(string id, StatusEnum newStatus, CancellationToken cancellationToken) + { + var entity = await repository.FindByIdAsync(id); + entity.Status = newStatus; + + await repository.ReplaceOneAsync(entity); + + return entity; + } + + /// + /// Updates a Tag by id. + /// + /// The Tag to be updated. + /// The Tag identifier. + /// A representing + /// the asynchronous execution of the service. + public async ValueTask UpdateTag(TagAdapter entity, CancellationToken cancellationToken) + { + await repository.ReplaceOneAsync(entity); + + return entity; + } + + /// + /// Adds a parentTag to the tag. + /// + /// The identifier of the tag to whom the parentTag will be added. + /// The identifier of the parentTag to add. + /// A representing the asynchronous operation, with the updated tag object. + public async ValueTask AddParentTag(string tagId, string parentTagId, CancellationToken cancellationToken) + { + var tag = await repository.FindOneAsync( + u => u._Id == tagId && + u.Status == StatusEnum.Active); + + var updatedParentTags = tag.ParentTagId.Append(parentTagId).Distinct().ToArray(); + tag.ParentTagId = updatedParentTags; + + await repository.ReplaceOneAsync(tag); + + return tag; + } + + /// + /// Remove a parentTag to the tag. + /// + /// The identifier of the tag to whom the parentTag will be removed. + /// The identifier of the parentTag to add. + /// A representing the asynchronous operation, with the updated tag object. + public async ValueTask RemoveParentTag(string tagId, string parentTagId, CancellationToken cancellationToken) + { + var tag = await repository.FindOneAsync( + u => u._Id == tagId && + u.Status == StatusEnum.Active); + + var updatedParentTags = tag.ParentTagId + ?.Where(c => c != parentTagId) + .ToArray(); + + tag.ParentTagId = updatedParentTags; + + await repository.ReplaceOneAsync(tag); + + return tag; + } + } +} diff --git a/Core.Inventory.Provider/ServiceCollectionExtensions.cs b/Core.Inventory.Provider/ServiceCollectionExtensions.cs index 4c408a2..30faf2d 100644 --- a/Core.Inventory.Provider/ServiceCollectionExtensions.cs +++ b/Core.Inventory.Provider/ServiceCollectionExtensions.cs @@ -20,6 +20,9 @@ namespace Core.Inventory.Provider services.AddScoped(); services.AddScoped>(); + services.AddScoped(); + services.AddScoped>(); + return services; }