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;
}