first commit

This commit is contained in:
2025-02-26 23:42:19 +01:00
commit 777ea98be1
283 changed files with 88266 additions and 0 deletions
@@ -0,0 +1,37 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using OpenWarehouse.auth.Common.ApiInterfeiceLib;
using OpenWarehouse.auth.lib;
namespace OpenWarehouse.warehouse.api.Common.CustimIdentifyManager;
public class HasPrivilage : Attribute, IAsyncAuthorizationFilter
{
public ServicePrivilage Privilage;
public HasPrivilage(ServicePrivilage privilage)
{
Privilage = privilage;
}
public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
{
context.HttpContext.Request.Headers.TryGetValue("Authorization", out var token);
var sToken = token.ToString();
if (sToken == "") context.Result = new UnauthorizedResult();
var config = context.HttpContext.RequestServices.GetRequiredService<IConfiguration>();
var handler = new AuthTokenHandler(sToken);
var httpClient = new HttpClient(handler);
var client = new OpenWarehouseAuthClient(config["Auth:Url"], httpClient);
var privilage = await client.GetUserPrivilagesAsync();
if (privilage is {Count: 0} && !privilage.Any(s => s.Equals(Privilage.GetName())))
{
context.Result = new ForbidResult();
}
return;
}
}
@@ -0,0 +1,7 @@
namespace OpenWarehouse.warehouse.api.Common.CustimIdentifyManager;
public enum ServicePrivilage
{
Admin, ServiceAdmin, ManageWarehouse, EditWarehouse, AddWarehouse, DeleteWarehouse, ManageItems, EditItems,
AddItems, DeleteItems, ManageInvetory, EditInventory, AddItemToInventory, SeeInventory, ManageServicePrivilage, ServiceUser
}
@@ -0,0 +1,132 @@
namespace OpenWarehouse.warehouse.api.Common.CustimIdentifyManager;
public static class ServicePrivilageClass
{
private static readonly string PrivilagePrefix = "WarehouseApi";
private static readonly Dictionary<ServicePrivilage, string> PrivilageNameMap = new()
{
{ ServicePrivilage.Admin, "Admin" },
{ ServicePrivilage.ServiceAdmin, $"{PrivilagePrefix}-Admin" },
{ ServicePrivilage.ManageWarehouse, $"{PrivilagePrefix}-Warehouse-Manage" },
{ ServicePrivilage.EditWarehouse, $"{PrivilagePrefix}-Warehouse-Edit" },
{ ServicePrivilage.AddWarehouse, $"{PrivilagePrefix}-Warehouse-Add" },
{ ServicePrivilage.DeleteWarehouse, $"{PrivilagePrefix}-Warehouse-Delete" },
{ ServicePrivilage.ManageItems, $"{PrivilagePrefix}-Items-Manage" },
{ ServicePrivilage.EditItems, $"{PrivilagePrefix}-Items-Edit" },
{ ServicePrivilage.AddItems, $"{PrivilagePrefix}-Items-Add" },
{ ServicePrivilage.DeleteItems, $"{PrivilagePrefix}-Items-Delete" },
{ ServicePrivilage.ManageInvetory, $"{PrivilagePrefix}-Inventory-mange"},
{ ServicePrivilage.EditInventory, $"{PrivilagePrefix}-Inventory-Edit"},
{ ServicePrivilage.AddItemToInventory, $"{PrivilagePrefix}-Inventory-Add-Item"},
{ ServicePrivilage.SeeInventory, $"{PrivilagePrefix}-Inventory-See"},
{ ServicePrivilage.ServiceUser, $"{PrivilagePrefix}-User"}
};
private static readonly Dictionary<ServicePrivilage, List<ServicePrivilage>> PrivilageMap = new()
{
{ ServicePrivilage.Admin, [ServicePrivilage.Admin] },
{ ServicePrivilage.ServiceAdmin, [ServicePrivilage.Admin, ServicePrivilage.ServiceAdmin] },
{
ServicePrivilage.ManageWarehouse,
[ServicePrivilage.Admin, ServicePrivilage.ServiceAdmin, ServicePrivilage.ManageWarehouse]
},
{
ServicePrivilage.EditWarehouse,
[
ServicePrivilage.Admin, ServicePrivilage.ServiceAdmin, ServicePrivilage.ManageWarehouse,
ServicePrivilage.EditWarehouse
]
},
{
ServicePrivilage.AddWarehouse,
[
ServicePrivilage.Admin, ServicePrivilage.ServiceAdmin, ServicePrivilage.ManageWarehouse,
ServicePrivilage.AddWarehouse
]
},
{
ServicePrivilage.DeleteWarehouse,
[
ServicePrivilage.Admin, ServicePrivilage.ServiceAdmin, ServicePrivilage.ManageWarehouse,
ServicePrivilage.DeleteWarehouse
]
},
{
ServicePrivilage.ManageItems,
[ServicePrivilage.Admin, ServicePrivilage.ServiceAdmin, ServicePrivilage.ManageItems]
},
{
ServicePrivilage.EditItems,
[
ServicePrivilage.Admin, ServicePrivilage.ServiceAdmin, ServicePrivilage.ManageItems,
ServicePrivilage.EditItems
]
},
{
ServicePrivilage.AddItems,
[
ServicePrivilage.Admin, ServicePrivilage.ServiceAdmin, ServicePrivilage.ManageItems,
ServicePrivilage.AddItems
]
},
{
ServicePrivilage.DeleteItems,
[
ServicePrivilage.Admin, ServicePrivilage.ServiceAdmin, ServicePrivilage.ManageItems,
ServicePrivilage.DeleteItems
]
},
{
ServicePrivilage.ManageInvetory,
[ServicePrivilage.Admin, ServicePrivilage.ServiceAdmin, ServicePrivilage.ManageInvetory]
},
{
ServicePrivilage.EditInventory,
[
ServicePrivilage.Admin, ServicePrivilage.ServiceAdmin, ServicePrivilage.ManageInvetory,
ServicePrivilage.EditInventory
]
},
{
ServicePrivilage.AddItemToInventory,
[
ServicePrivilage.Admin, ServicePrivilage.ServiceAdmin, ServicePrivilage.ManageInvetory,
ServicePrivilage.AddItemToInventory
]
},
{
ServicePrivilage.SeeInventory,
[
ServicePrivilage.Admin, ServicePrivilage.ServiceAdmin, ServicePrivilage.ManageInvetory,
ServicePrivilage.SeeInventory
]
},
{ ServicePrivilage.ServiceUser, GetAllPrivilageList() }
};
public static List<ServicePrivilage> GetAllPrivilageList()
{
var list = PrivilageNameMap.Select(pair => pair.Key).ToList();
return list;
}
public static List<ServicePrivilage> GetServicePrivilageList()
{
var list = PrivilageNameMap.Where(pair => pair.Value.Contains(PrivilagePrefix))
.Select(pair => pair.Key).ToList();
return list;
}
public static List<string> GetServicePrivilageNameList()
{
var list = PrivilageNameMap.Where(pair => pair.Value.Contains(PrivilagePrefix))
.Select(pair => pair.Value).ToList();
return list;
}
public static string GetName(this ServicePrivilage servicePrivilage)
{
var result = PrivilageNameMap.TryGetValue(servicePrivilage, out var name);
return (result ? name : $"Uknow-{servicePrivilage}") ?? $"Role {servicePrivilage} Uknow";
}
}
@@ -0,0 +1,97 @@
using System.Net.Http.Headers;
using OpenWarehouse.auth.Common.ApiInterfeiceLib;
using OpenWarehouse.auth.lib;
using OpenWarehouse.auth.lib.Model.Role;
using OpenWarehouse.auth.lib.Model.Service;
using OpenWarehouse.warehouse.api.Common.CustimIdentifyManager;
using OpenWarehouse.warehouse.api.Common.ServiceClaims;
namespace OpenWarehouse.warehouse.api.Common.DefaultServiceAccountAndRole;
public class DefaultIdentify(ILogger<DefaultIdentify> logger, IConfiguration configuration,
IServiceClaimsManager serviceClaimsManager)
{
private List<RoleModel> _listOfRole = new()
{
new RoleModel()
{
RoleName = ServicePrivilage.ServiceAdmin.GetName(),
Privilage = new()
{
ServicePrivilage.ServiceAdmin.GetName(),
ServicePrivilage.ServiceUser.GetName(),
}
},
new RoleModel()
{
RoleName = ServicePrivilage.ServiceUser.GetName(),
Privilage = new()
{
ServicePrivilage.ServiceUser.GetName(),
}
}
};
public async Task<bool> TestAuth(string token)
{
var handler = new AuthTokenHandler(token);
var httpClient = new HttpClient(handler);
var client = new OpenWarehouseAuthClient(configuration["Auth:Url"], httpClient);
try
{
return await client.TestServiceTokenAsync();
}
catch (ApiException e)
{
if (e.StatusCode == 401) return false;
throw;
}
}
public async Task<CreateServiceTokenReturnModel> SeedAsync(string adminToken)
{
var httpClient = new HttpClient();
var token = adminToken.Split(" ");
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(token[0], token[1]);
var client = new OpenWarehouseAuthClient(configuration["Auth:Url"], httpClient);
try
{
var service = new CreateServiceTokenModel()
{
ServiceName = "Warehouse",
ServicePrivilages = ServicePrivilageClass.GetServicePrivilageNameList(),
ServiceRoles = _listOfRole
};
var result = await client.GetTokenForServiceAsync(service);
logger.LogInformation($"Auth service initialized: \n " +
$"Is success: {result.IsSuccess} \n " +
$"Service username: {result.ServiceUsername} \n " +
$"Service allerady exist: {result.UserAllereadyExist}");
return result;
}
catch (ApiException e)
{
logger.LogWarning($"ApiException with code {e.StatusCode} and nessage {e.Message}");
throw;
}
catch (Exception e)
{
logger.LogWarning(e.Message);
throw;
}
throw new Exception("This exemption shoudnt happen");
}
}
@@ -0,0 +1,12 @@
namespace OpenWarehouse.warehouse.api.Common.Inventory;
public interface IInventoryManger
{
public Task<List<ItemInWarehouse>> GetListOfItemInWarehouse(Data.Item item);
public Task<ItemInWarehouse?> GetPositionById(string id);
public Task<List<ItemInWarehouse>> GetPositionByPosition(string position);
public Task<bool> CreateAsync(ItemInWarehouse itemInWarehouse);
public Task<bool> AddClaims(ItemInWarehouse itemInWarehouse, List<Claim> claims);
public Task<bool> AddClaim(ItemInWarehouse itemInWarehouse, Claim claim);
public Task<bool> DeleteClaim(ItemInWarehouse itemInWarehouse, string claimType);
}
@@ -0,0 +1,88 @@
using System.Data;
using OpenWarehouse.warehouse.api.Common.Warehouse;
namespace OpenWarehouse.warehouse.api.Common.Inventory;
public class InventoryManger(ILogger<InventoryManger> logger,
ApplicationDbContext context, IWarehouseManager warehouseManager,
IItemManager itemManager) : IInventoryManger
{
public async Task<List<ItemInWarehouse>> GetListOfItemInWarehouse(Data.Item item)
{
return await context.ItemInWarehouses
.Where(q => q.Item == item)
.ToListAsync();
}
public async Task<ItemInWarehouse?> GetPositionById(string id)
{
return await context.ItemInWarehouses
.Where(q => q.Id == Guid.Parse(id))
.FirstOrDefaultAsync();
}
public async Task<List<ItemInWarehouse>> GetPositionByPosition(string position)
{
return await context.ItemInWarehouses
.Where(q => q.Positions == position)
.ToListAsync();
}
public async Task<bool> CreateAsync(ItemInWarehouse itemInWarehouse)
{
context.ItemInWarehouses.Add(itemInWarehouse);
var res = await context.SaveChangesAsync();
return res > 0;
}
public async Task<bool> AddClaims(ItemInWarehouse itemInWarehouse, List<Claim> claims)
{
foreach (Claim claim in claims)
{
if (itemInWarehouse.ItemInWarehouseClaims.Any(q => q.ClaimType == claim.Type))
{
var itemInWarehouseClaims = itemInWarehouse.ItemInWarehouseClaims
.FirstOrDefault(warehouseClaims => warehouseClaims.ClaimType == claim.Type);
if (itemInWarehouseClaims == null) return false;
itemInWarehouseClaims.ClaimValue = claim.Value;
}
else
{
itemInWarehouse.ItemInWarehouseClaims.Add(new ItemInWarehouseClaims(){ClaimType = claim.Type, ClaimValue = claim.Value});
}
}
var res = await context.SaveChangesAsync();
return res == claims.Count;
}
public async Task<bool> AddClaim(ItemInWarehouse itemInWarehouse, Claim claim)
{
if (itemInWarehouse.ItemInWarehouseClaims.Any(q => q.ClaimType == claim.Type))
{
var itemInWarehouseClaims = itemInWarehouse.ItemInWarehouseClaims
.FirstOrDefault(warehouseClaims => warehouseClaims.ClaimType == claim.Type);
if (itemInWarehouseClaims == null) return false;
itemInWarehouseClaims.ClaimValue = claim.Value;
}
else
{
itemInWarehouse.ItemInWarehouseClaims.Add(new ItemInWarehouseClaims(){ClaimType = claim.Type, ClaimValue = claim.Value});
}
var res = await context.SaveChangesAsync();
return res > 0;
}
public async Task<bool> DeleteClaim(ItemInWarehouse itemInWarehouse, string claimType)
{
var res = await context.ItemInWarehousesClaims
.Where(q => q.ClaimType == claimType && q.Item == itemInWarehouse)
.ExecuteDeleteAsync();
if (res > 1) throw new DataException("Deleted more than one record!!");
return res == 1;
}
}
@@ -0,0 +1,14 @@
namespace OpenWarehouse.warehouse.api.Common.Item;
public interface IItemManager
{
public Task<Data.Item?> FindItemByIdAsync(string id);
public Task<Data.Item?> FindItemByNameAsync(string producerName, string itemName);
public Task<Data.Item?> FindItemByNameAsync(Producers producers, string itemName);
public Task<ActionResult> CreateAsync(Data.Item item);
public Task<List<Claim>> GetListOfClaims(Data.Item item);
public Task<ActionResult> AddClaim(Data.Item item, Claim claim);
public Task<ActionResult> UpdateClaim(Data.Item item, Claim claim);
public Task<ActionResult> DeleteClaim(Data.Item item, string claimType);
public Task<ActionResult> EditClaim(Data.Item item, string claimType, string newValue);
}
@@ -0,0 +1,6 @@
namespace OpenWarehouse.warehouse.api.Common.Item;
public enum ItemClaimsType
{
Exp, DateOfProduction, Description, Price, ManufactoreContry
}
@@ -0,0 +1,52 @@
namespace OpenWarehouse.warehouse.api.Common.Item;
public static class ItemClaimsTypeClass
{
private static readonly Dictionary<ItemClaimsType, string> ItemClaimType = new()
{
{ ItemClaimsType.Exp, "Exp" },
{ ItemClaimsType.DateOfProduction, "Date_of_production" },
{ ItemClaimsType.Price, "Price" },
{ ItemClaimsType.Description, "Description" },
{ ItemClaimsType.ManufactoreContry, "Manufactore_Country" },
};
public static string GetName(this ItemClaimsType itemClaim)
{
var result = ItemClaimType.TryGetValue(itemClaim, out var name);
if (!result || name == null) throw new Exception("Key not found");
return name;
}
public static Claim GerClaims(this ItemClaimsType itemClaim, string value, string? issuer = null)
{
var result = ItemClaimType.TryGetValue(itemClaim, out var name);
if (!result || name == null) throw new Exception("Key not found");
Claim claim;
if (issuer != null) claim = new Claim(name, value, issuer);
else claim = new Claim(name, value);
return claim;
}
public static List<string> GetAllClaimName()
{
return ItemClaimType
.Select(q => q.Value)
.ToList();
}
public static bool IsDefaultClaimType(string val)
{
var res = ItemClaimType
.Any(q => q.Value == val);
return res;
}
}
@@ -0,0 +1,194 @@
using OpenWarehouse.warehouse.api.Common.Producer;
namespace OpenWarehouse.warehouse.api.Common.Item;
public class ItemManager(ApplicationDbContext context, IProducerManager producerManager) : IItemManager
{
public async Task<Data.Item?> FindItemByIdAsync(string id)
{
var result = await context.Items
.Include(item => item.ItemCategoryTags)
.Include(item => item.ItemClaims)
.FirstOrDefaultAsync(item => item.Id != null && item.Id.Equals(id));
return result;
}
public async Task<Data.Item?> FindItemByNameAsync(string producerName, string itemName)
{
var producer = await producerManager.FindProducerByName(producerName);
if (producer != null)
{
return await FindItemByNameAsync(producer, itemName);
}
return null;
}
public async Task<Data.Item?> FindItemByNameAsync(Producers producers, string itemName)
{
return await context.Items
.FirstOrDefaultAsync(item => item.Name != null &&
item.Producer == producers && item.Name.Equals(itemName));
}
public async Task<ActionResult> CreateAsync(Data.Item item)
{
context.Items.Add(item);
ActionResult? res = null;
var error = new Dictionary<string, string>();
try
{
res= new ActionResult((await context.SaveChangesAsync()) == 1);
}
catch (OperationCanceledException e)
{
error[e.Source ?? "Unknown"] = e.Message;
}
catch (DbUpdateConcurrencyException e)
{
error[e.Source ?? "Unknown"] = e.Message;
}
catch (DbUpdateException e)
{
error[e.Source ?? "Unknown"] = e.Message;
}
return res ?? new ActionResult(false, error);
}
public async Task<List<Claim>> GetListOfClaims(Data.Item item)
{
var result = await context.ItemClaims.Where(claims => claims.Item == item)
.Select(claims => new Claim(claims.ClaimType ?? "Uknow", claims.ClaimValue ?? "Uknow"))
.ToListAsync();
return result;
}
public async Task<ActionResult> AddClaim(Data.Item item, Claim claim)
{
var newClaim = new ItemClaims(item, claim);
context.ItemClaims.Add(newClaim);
ActionResult? res = null;
var error = new Dictionary<string, string>();
try
{
res= new ActionResult((await context.SaveChangesAsync()) == 1);
}
catch (OperationCanceledException e)
{
error[e.Source ?? "Unknown"] = e.Message;
}
catch (DbUpdateConcurrencyException e)
{
error[e.Source ?? "Unknown"] = e.Message;
}
catch (DbUpdateException e)
{
error[e.Source ?? "Unknown"] = e.Message;
}
return res ?? new ActionResult(false, error);
}
public async Task<ActionResult> UpdateClaim(Data.Item item, Claim claim)
{
var claimF = await context.ItemClaims
.FirstOrDefaultAsync(q => q.ClaimType.Equals(claim.Type) && q.ClaimIssuer.Equals(claim.Issuer)
&& q.Item == item);
var result = 0;
if (claimF == null)
{
result = (await AddClaim(item, claim)).IsSuccess ? 0 : 1;
}
else
{
claimF.ClaimValue = claim.Value;
result= await context.SaveChangesAsync();
}
return result > 0 ? new ActionResult(true, null) : new ActionResult(false, null);
}
public async Task<ActionResult> DeleteClaim(Data.Item item, string claimType)
{
ActionResult? res = null;
var error = new Dictionary<string, string>();
int delCount = 0;
try
{
delCount = await context.ItemClaims
.Where(claims => claims.ClaimType != null && claims.Item == item && claims.ClaimType.Equals(claimType))
.ExecuteDeleteAsync();
}
catch (OperationCanceledException e)
{
error[e.Source ?? "Unknown"] = e.Message;
}
catch (DbUpdateConcurrencyException e)
{
error[e.Source ?? "Unknown"] = e.Message;
}
catch (DbUpdateException e)
{
error[e.Source ?? "Unknown"] = e.Message;
}
if (res == null)
{
res = new ActionResult(delCount == 1, error);
}
return res;
}
public async Task<ActionResult> EditClaim(Data.Item item, string claimType, string newValue)
{
ActionResult? res = null;
var error = new Dictionary<string, string>();
int editCount = 0;
try
{
var claim = await context.ItemClaims.FirstOrDefaultAsync(claims =>
claims.ClaimType != null && claims.Item == item && claims.ClaimType.Equals(claimType));
if (claim != null) claim.ClaimValue = newValue;
else
{
error["Database"] = $"Claim {item.Name}-{claimType} not found.";
}
editCount = await context.SaveChangesAsync();
}
catch (OperationCanceledException e)
{
error[e.Source ?? "Unknown"] = e.Message;
}
catch (DbUpdateConcurrencyException e)
{
error[e.Source ?? "Unknown"] = e.Message;
}
catch (DbUpdateException e)
{
error[e.Source ?? "Unknown"] = e.Message;
}
if (res == null)
{
res = new ActionResult(editCount == 1, error);
}
return res;
}
}
@@ -0,0 +1,20 @@
using OpenWarehouse.warehouse.api.Services;
namespace OpenWarehouse.warehouse.api.Common.ItemTagManager;
public interface IItemTagManager
{
public Task<bool> CreateTag(string name, Guid? parentId);
public Task<ItemCategoryTag?> GetTagByName(string name);
public Task<ItemCategoryTag?> GetTagById(Guid id);
public Task<ItemCategoryTag?> GetTagById(string id);
public Task<bool> DeleteTag(ItemCategoryTag tag);
public Task<bool> DeleteTag(Guid id);
public Task<int> DeleteTagsByNames(List<Model.Item.ItemCategoryTag> names);
public Task<bool> DeleteTag(String id);
public Task<int> CreateTagTree(List<Model.Item.ItemCategoryTag> itemCategoryTags);
public Task<bool> DeleteAllChild(Guid id);
public Task<bool> DeleteAllChild(String id);
public Task<ItemCategoryTag?> GetTagTree(string root);
public Task<List<ItemCategoryTag>> GetFullTagTree();
}
@@ -0,0 +1,339 @@
namespace OpenWarehouse.warehouse.api.Common.ItemTagManager;
public class ItemTagManager(ApplicationDbContext applicationDbContext, ILogger<ItemManager> logger) : IItemTagManager
{
public async Task<bool> CreateTag(string name, Guid? parentId)
{
logger.LogDebug("Attempting to create a new tag with name {TagName} and parent ID {ParentId}", name, parentId);
ItemCategoryTag? parent = null;
if (parentId != null)
{
parent = await applicationDbContext.ItemCategoryTags
.FirstOrDefaultAsync(q => q.Id == parentId);
if (parent == null)
{
logger.LogWarning("Parent tag with ID {ParentId} not found.", parentId);
return false;
}
}
var newTag = new ItemCategoryTag()
{
Name = name,
ParentTag = parent
};
applicationDbContext.ItemCategoryTags.Add(newTag);
bool isSaved = (await applicationDbContext.SaveChangesAsync()) > 0;
if (isSaved)
{
logger.LogInformation("Successfully created tag {Name} with ID {TagId}.", name, newTag.Id);
}
else
{
logger.LogError("Failed to save the new tag {Name}.", name);
}
return isSaved;
}
public async Task<ItemCategoryTag?> GetTagByName(string name)
{
logger.LogDebug("Fetching tags with name {TagName}.", name);
var tags = await applicationDbContext.ItemCategoryTags
.Where(tag => tag.Name != null && tag.Name.Equals(name)).FirstOrDefaultAsync();
if (tags == null)
{
logger.LogWarning("No tags found with name {Name}.", name);
}
return tags;
}
public async Task<ItemCategoryTag?> GetTagById(Guid id)
{
logger.LogDebug("Fetching tag with ID {TagId}.", id);
var tag = await applicationDbContext.ItemCategoryTags
.Where(tag => tag.Id == id).FirstOrDefaultAsync();
if (tag == null)
{
logger.LogWarning("No tag found with ID {Id}.", id);
}
return tag;
}
public async Task<ItemCategoryTag?> GetTagById(string id)
{
logger.LogDebug("Fetching tag with string ID {TagId}.", id);
return await GetTagById(Guid.Parse(id));
}
public async Task<bool> DeleteTag(ItemCategoryTag tag)
{
logger.LogDebug("Attempting to delete tag with object reference {Tag}.", tag);
return await DeleteTag(tag.Id);
}
public async Task<bool> DeleteTag(Guid id)
{
logger.LogDebug("Attempting to delete tag with ID {TagId}.", id);
bool isDeleted = await applicationDbContext.ItemCategoryTags.Where(q => q.Id == id)
.ExecuteDeleteAsync() > 0;
if (isDeleted)
{
logger.LogInformation("Successfully deleted tag with ID {TagId}.", id);
}
else
{
logger.LogError("Failed to delete tag with ID {TagId}.", id);
}
return isDeleted;
}
public async Task<int> DeleteTagsByNames(List<Model.Item.ItemCategoryTag> names)
{
logger.LogDebug("Attempting to delete tags by names.");
if (!names.Any())
{
logger.LogWarning("No tags provided for deletion.");
return 0;
}
try
{
var res = 0;
foreach (var tag in names)
{
logger.LogDebug("Deleting tag with name {TagName}.", tag.Name);
res += await applicationDbContext.ItemCategoryTags
.Where(q => q.Name == tag.Name)
.ExecuteDeleteAsync();
}
logger.LogInformation("{Count} tags were successfully deleted.", res);
return res;
}
catch (Exception ex)
{
logger.LogError(ex, "An error occurred while deleting tags.");
throw;
}
}
public async Task<bool> DeleteTag(string id)
{
logger.LogDebug("Attempting to delete tag with string ID {TagId}.", id);
return await DeleteTag(Guid.Parse(id));
}
public async Task<int> CreateTagTree(List<Model.Item.ItemCategoryTag> itemCategoryTags)
{
logger.LogDebug("Attempting to create a tag tree.");
var result = 0;
try
{
if (itemCategoryTags.All(q => q.ParentName != null))
{
logger.LogError("Tag tree must have at least one root node.");
throw new InvalidOperationException("Tag tree must have at least one root node.");
}
var tempParent = new List<ItemCategoryTag>();
while (itemCategoryTags.Count > 0)
{
var currentLevel = itemCategoryTags
.Where(q => q.ParentName == null || itemCategoryTags.All(qq => qq.Name != q.ParentName))
.ToList();
itemCategoryTags.RemoveAll(q => currentLevel.Contains(q));
foreach (var tag in currentLevel)
{
logger.LogDebug("Processing tag {TagName} with parent {ParentName}.", tag.Name, tag.ParentName);
var exists = await applicationDbContext.ItemCategoryTags
.AnyAsync(t => t.Name == tag.Name && t.ParentTag != null && (t.ParentTag.Name == tag.ParentName ||
(t.ParentTag == null && tag.ParentName == null)));
if (exists)
{
logger.LogInformation("Tag with name {TagName} already exists.", tag.Name);
continue;
}
ItemCategoryTag? parent = null;
if (tag.ParentName != null)
{
parent = await applicationDbContext.ItemCategoryTags
.Where(q => q.Name == tag.ParentName).FirstOrDefaultAsync();
if (parent == null)
{
if (tempParent.Any(q => q.Name == tag.ParentName))
{
parent = tempParent.FirstOrDefault(q => q.Name == tag.ParentName);
}
else
{
logger.LogWarning("Parent tag with name {ParentName} not found for child {Name}.",
tag.ParentName, tag.Name);
continue;
}
}
}
var tagNew = new ItemCategoryTag()
{
Name = tag.Name,
ParentTag = parent
};
applicationDbContext.ItemCategoryTags.Add(tagNew);
tempParent.Add(tagNew);
logger.LogDebug("Added new tag {TagName} with parent {ParentName}.", tagNew.Name,
parent?.Name);
}
result += await applicationDbContext.SaveChangesAsync();
}
logger.LogInformation("Successfully created tag tree with {Changes} changes.", result);
return result;
}
catch (Exception ex)
{
logger.LogError(ex, "An error occurred while creating the tag tree.");
throw;
}
}
public async Task<bool> DeleteAllChild(Guid id)
{
logger.LogDebug("Attempting to delete all children for tag ID {TagId}.", id);
var parentTag = await GetTagById(id);
if (parentTag == null)
{
logger.LogWarning("Parent tag with ID {Id} not found.", id);
return false;
}
var childTags = await applicationDbContext.ItemCategoryTags
.Where(q => q.ParentTagId == id).ToListAsync();
if (!childTags.Any())
{
logger.LogInformation("No child tags found for parent tag with ID {Id}.", id);
return true;
}
applicationDbContext.ItemCategoryTags.RemoveRange(childTags);
bool isDeleted = (await applicationDbContext.SaveChangesAsync()) > 0;
if (isDeleted)
{
logger.LogInformation("Successfully deleted all child tags for parent tag with ID {Id}.", id);
}
else
{
logger.LogError("Failed to delete child tags for parent tag with ID {Id}.", id);
}
return isDeleted;
}
public async Task<bool> DeleteAllChild(string id)
{
logger.LogDebug("Attempting to delete all children for tag string ID {TagId}.", id);
return await DeleteAllChild(Guid.Parse(id));
}
public async Task<ItemCategoryTag?> GetTagTree(string root)
{
logger.LogDebug("Fetching tag tree starting from root {RootName}.", root);
var rootElement = await applicationDbContext.ItemCategoryTags
.Where(q => q.Name != null && q.Name.Equals(root))
.AsNoTracking()
.FirstOrDefaultAsync();
if (rootElement == null)
{
logger.LogWarning("Root tag with name {RootName} not found.", root);
return null;
}
await LoadChildren(rootElement);
return rootElement;
}
public async Task<List<ItemCategoryTag>> GetFullTagTree()
{
logger.LogDebug("Fetching the full tag tree.");
var allTags = await applicationDbContext.ItemCategoryTags
.AsNoTracking()
.ToListAsync();
var tagDictionary = allTags.ToDictionary(tag => tag.Id);
var rootTags = new List<ItemCategoryTag>();
foreach (var tag in allTags)
{
if (tag.ParentTagId == null)
{
rootTags.Add(tag);
}
else if (tagDictionary.TryGetValue(tag.ParentTagId.Value, out var parentTag))
{
if (parentTag.ListOfChildrenTag == null)
{
parentTag.ListOfChildrenTag = new List<ItemCategoryTag>();
}
parentTag.ListOfChildrenTag.Add(tag);
}
}
logger.LogInformation("Successfully fetched the full tag tree with {Count} root nodes.", rootTags.Count);
return rootTags;
}
private async Task LoadChildren(ItemCategoryTag parentTag)
{
logger.LogDebug("Loading children for tag {TagName} with ID {TagId}.", parentTag.Name, parentTag.Id);
parentTag.ListOfChildrenTag = await applicationDbContext.ItemCategoryTags
.Where(child => child.ParentTagId == parentTag.Id)
.AsNoTracking()
.ToListAsync();
foreach (var child in parentTag.ListOfChildrenTag)
{
await LoadChildren(child);
}
}
}
@@ -0,0 +1,6 @@
namespace OpenWarehouse.warehouse.api.Common.Kafka;
public enum Actions
{
Create, Update, Delete, CheckIntegrality
}
@@ -0,0 +1,6 @@
namespace OpenWarehouse.warehouse.api.Common.Kafka.Item;
public interface IItemKafkaMessage
{
}
@@ -0,0 +1,6 @@
namespace OpenWarehouse.warehouse.api.Common.Kafka.Item;
public class ItemKafkaMessage
{
}
@@ -0,0 +1,7 @@
namespace OpenWarehouse.warehouse.api.Common.Kafka;
public class KafkaConfig
{
public string? BootstrapServers { get; set; }
public string? TopicRoot { get; set; }
}
@@ -0,0 +1,10 @@
using ItemCategoryTag = OpenWarehouse.warehouse.api.Model.Item.ItemCategoryTag;
namespace OpenWarehouse.warehouse.api.Common.Kafka.TagTree;
public interface ITagTreeKafkaMessage
{
public Guid Guid { get; init; }
public Actions Action { get; init; }
public List<ItemCategoryTag> ItemCategoryTagsList { get; init; }
}
@@ -0,0 +1,11 @@
using OpenWarehouse.warehouse.api.Model.Item;
using ItemCategoryTag = OpenWarehouse.warehouse.api.Model.Item.ItemCategoryTag;
namespace OpenWarehouse.warehouse.api.Common.Kafka.TagTree;
public class TagTreeKafkaMessage : ITagTreeKafkaMessage
{
public Guid Guid { get; init; }
public Actions Action { get; init; }
public List<ItemCategoryTag> ItemCategoryTagsList { get; init; } = new();
}
@@ -0,0 +1,15 @@
namespace OpenWarehouse.warehouse.api.Common.Producer;
public interface IProducerManager
{
public Task<Producers?> FindProducerByName(string name);
public Task<Producers?> FindProducerById(Guid? guid);
public Task<List<Producers>> GetProducerList();
public Task<bool> DeleteProducerByName(string name);
public Task<List<Claim>> GetClaims(Producers producers);
public Task<ActionResult> CreateAsync(Producers producers);
public Task<ActionResult> AddClaim(Producers producers, Claim claim);
public Task<ActionResult> DeleteClaim(Producers producers, string claimType);
public Task<ActionResult> UpdateClaim(Producers producers, Claim claim);
}
@@ -0,0 +1,158 @@
namespace OpenWarehouse.warehouse.api.Common.Producer;
public class ProducerManager(ApplicationDbContext context) : IProducerManager
{
public async Task<Producers?> FindProducerByName(string name)
{
return await context.Producers
.Include(q => q.Claims)
.AsNoTracking()
.FirstOrDefaultAsync(producer1 => producer1.Name != null &&
producer1.Name.Equals(name));
}
public async Task<Producers?> FindProducerById(Guid? guid)
{
return await context.Producers
.AsNoTracking()
.FirstOrDefaultAsync(producer1 => producer1.Name != null &&
producer1.Id == guid);
}
public async Task<List<Producers>> GetProducerList()
{
return await context.Producers.Include(q => q.Claims).AsNoTracking().ToListAsync();
}
public async Task<bool> DeleteProducerByName(string name)
{
var res = await context.Producers
.Where(q => q.Name != null && q.Name.Equals(name))
.Include(q => q.Claims)
.AsNoTracking()
.ExecuteDeleteAsync();
return res > 0;
}
public async Task<List<Claim>> GetClaims(Producers producers)
{
return await context.ProducerClaims.Where(claims => claims.Producer == producers)
.Select(claims => new Claim(claims.ClaimType ?? "Unknown", claims.ClaimValue ?? "Unknown"))
.AsNoTracking()
.ToListAsync();
}
public async Task<ActionResult> CreateAsync(Producers producers)
{
context.Producers.Add(producers);
ActionResult? res = null;
var error = new Dictionary<string, string>();
try
{
res= new ActionResult((await context.SaveChangesAsync()) == 1);
}
catch (OperationCanceledException e)
{
error[e.Source ?? "Unknown"] = e.Message;
}
catch (DbUpdateConcurrencyException e)
{
error[e.Source ?? "Unknown"] = e.Message;
}
catch (DbUpdateException e)
{
error[e.Source ?? "Unknown"] = e.Message;
}
return res ?? new ActionResult(false, error);
}
public async Task<ActionResult> AddClaim(Producers producers, Claim claim)
{
context.ProducerClaims.Add(new ProducerClaims(producers, claim));
ActionResult? res = null;
var error = new Dictionary<string, string>();
try
{
res= new ActionResult((await context.SaveChangesAsync()) == 1);
}
catch (OperationCanceledException e)
{
error[e.Source ?? "Unknown"] = e.Message;
}
catch (DbUpdateConcurrencyException e)
{
error[e.Source ?? "Unknown"] = e.Message;
}
catch (DbUpdateException e)
{
error[e.Source ?? "Unknown"] = e.Message;
}
return res ?? new ActionResult(false, error);
}
public async Task<ActionResult> DeleteClaim(Producers producers, string claimType)
{
ActionResult? res = null;
var error = new Dictionary<string, string>();
try
{
var delRes = await context.ProducerClaims
.Where(claims => claims.ClaimType != null &&
claims.Producer == producers && claims.ClaimType.Equals(claimType))
.ExecuteDeleteAsync();
res= new ActionResult((delRes) == 1);
}
catch (OperationCanceledException e)
{
error[e.Source ?? "Unknown"] = e.Message;
}
catch (DbUpdateConcurrencyException e)
{
error[e.Source ?? "Unknown"] = e.Message;
}
catch (DbUpdateException e)
{
error[e.Source ?? "Unknown"] = e.Message;
}
if (res == null)
{
res = new ActionResult(false, error);
}
return res;
}
public async Task<ActionResult> UpdateClaim(Producers producers, Claim claim1)
{
var claim = await context.ProducerClaims
.Where(claims => claims.ClaimType != null &&
claims.Producer == producers && claims.ClaimType.Equals(claim1.Type)
&& claims.ClaimIssuer == claim1.Issuer).FirstOrDefaultAsync();
var res = 0;
if (claim == null)
{
res = (await AddClaim(producers, claim1)).IsSuccess ? 1 : 0;
}
else
{
claim.ClaimValue = claim1.Value;
res = await context.SaveChangesAsync();
}
return new ActionResult(res > 0);
}
}
@@ -0,0 +1,9 @@
namespace OpenWarehouse.warehouse.api.Common.ServiceClaims;
public interface IServiceClaimsManager
{
public Task<Claim> GetServiceClaims(ServiceClaim claim);
public Task<List<Claim>> GetListServiceClaims();
public Task<bool> UpdateClaim(ServiceClaim claim, string value);
public Task<bool> ClaimIsSet(ServiceClaim claim);
}
@@ -0,0 +1,6 @@
namespace OpenWarehouse.warehouse.api.Common.ServiceClaims;
public enum ServiceClaim
{
ServiceAcoountUsername, ServiceAcoountToken, SericeIsnitialized,
}
@@ -0,0 +1,29 @@
namespace OpenWarehouse.warehouse.api.Common.ServiceClaims;
public static class ServiceClaimsClass
{
private static readonly Dictionary<ServiceClaim, string> ServiceClaimsMap = new()
{
{ ServiceClaim.ServiceAcoountToken, "Service_account_token" },
{ ServiceClaim.ServiceAcoountUsername, "Service_account_username" }
};
public static string GetName(this ServiceClaim serviceClaim)
{
var result = ServiceClaimsMap.TryGetValue(serviceClaim, out var name);
return (result ? name : $"Uknow-{serviceClaim}") ?? $"Role {serviceClaim} Uknow";
}
public static Claim GerClaims(this ServiceClaim serviceClaim, string value)
{
var result = ServiceClaimsMap.TryGetValue(serviceClaim, out var name);
if (!result || value == null) throw new Exception("Claims type not implement");
return new Claim(name ?? throw new NullReferenceException("Claim name excemption"), value);
}
public static Data.ServiceClaims CreateServiceClaims(this ServiceClaim serviceClaim, string value)
{
return new Data.ServiceClaims() { ClaimType = serviceClaim.GetName(), ClaimValue = value };
}
}
@@ -0,0 +1,50 @@
namespace OpenWarehouse.warehouse.api.Common.ServiceClaims;
public class ServiceClaimsManager(ApplicationDbContext context) : IServiceClaimsManager
{
public async Task<Claim> GetServiceClaims(ServiceClaim claim)
{
var result = await context.ServiceClaims
.Where(claims => claims.ClaimType == claim.GetName())
.FirstOrDefaultAsync();
if (result == null)
{
return new Claim(claim.GetName(), "");
}
return result.Claim;
}
public async Task<List<Claim>> GetListServiceClaims()
{
return await context.ServiceClaims.Select(q => new Claim(q.ClaimType ?? "Uknow", q.ClaimValue ?? "")).ToListAsync();
}
public async Task<bool> UpdateClaim(ServiceClaim claim, string value)
{
var result = await context.ServiceClaims
.Where(claims => claims.ClaimType == claim.GetName())
.FirstOrDefaultAsync();
if (result == null)
{
Data.ServiceClaims claims = claim.CreateServiceClaims(value);
context.ServiceClaims.Add(claims);
}
else
{
result.ClaimValue = value;
}
var res = await context.SaveChangesAsync();
return res > 0;
}
public async Task<bool> ClaimIsSet(ServiceClaim claim)
{
var result = await context.ServiceClaims
.Where(q => q.ClaimType == claim.GetName()).AnyAsync();
return result;
}
}
@@ -0,0 +1,8 @@
namespace OpenWarehouse.warehouse.api.Common.TaskManager;
public interface ITaskManager
{
public Task<string?> GetTaskStatus(Guid id);
public Task<bool?> CreateTask(Guid id, string? actorId);
public Task<bool> UpdateTaskStatus(Guid id, TaskStatus status);
}
@@ -0,0 +1,57 @@
namespace OpenWarehouse.warehouse.api.Common.TaskManager;
public class TaskManager(ApplicationDbContext context, ILogger<TaskManager> logger) : ITaskManager
{
public async Task<string?> GetTaskStatus(Guid id)
{
logger.LogInformation("Get status of task with id:{id}", id);
var result = await context.TaskStatus
.FirstOrDefaultAsync(status => status.TaskId == id);
if (result == null)
{
logger.LogWarning("Task with id:{id} not found", id);
return null;
}
return result.Status;
}
public async Task<bool?> CreateTask(Guid id, string? actorId)
{
logger.LogInformation("Creating new task with id {id}", id);
var newTask = new TaskStatusTable()
{
TaskId = id,
Actor = actorId,
CreatedAt = DateTime.UtcNow,
UpdatedAt = DateTime.UtcNow,
Status = TaskStatus.Accepted.ToString()
};
context.TaskStatus.Add(newTask);
var result = await context.SaveChangesAsync();
return result > 0;
}
public async Task<bool> UpdateTaskStatus(Guid id, TaskStatus status)
{
logger.LogInformation("Updating task with id:{id} to status:{status}", id, status.ToString());
var task = await context.TaskStatus
.FirstOrDefaultAsync(q => q.TaskId == id);
if (task == null)
{
logger.LogWarning("Task with id:{id} not found", id);
return false;
}
task.Status = status.ToString();
task.UpdatedAt = DateTime.UtcNow;
return (await context.SaveChangesAsync()) > 0;
}
}
@@ -0,0 +1,6 @@
namespace OpenWarehouse.warehouse.api.Common.TaskManager;
public enum TaskStatus
{
Crated, Accepted, Processing, Error, Success, NotFullSuccess
}
@@ -0,0 +1,21 @@
namespace OpenWarehouse.warehouse.api.Common.TaskManager;
public static class TaskStatusClass
{
private static readonly Dictionary<TaskStatus, string?> TaskStatusDictionary = new()
{
{ TaskStatus.Accepted, "Accepted" },
{ TaskStatus.Crated, "Created" },
{ TaskStatus.Error, "Error" },
{ TaskStatus.Processing, "Processing" },
{ TaskStatus.Success, "Success" },
{ TaskStatus.NotFullSuccess, "Not full sucess"}
};
public static string ToString(this TaskStatus status)
{
if (!TaskStatusDictionary.TryGetValue(status, out string? value) && value == "")
throw new NotImplementedException();
return value ?? "";
}
}
@@ -0,0 +1,11 @@
namespace OpenWarehouse.warehouse.api.Common.Warehouse;
public interface IWarehouseManager
{
public Task<Data.Warehouse?> GetWarehouse(string id);
public Task<ActionResult> EditWarehouse(Data.Warehouse warehouse, string? newName, string? newFiestLineAdres, string? newSecondLineAdress, Image? image);
public Task<ActionResult> CreateWarehouse(Data.Warehouse warehouse);
public Task<ActionResult> AddWarehouseClaim(Data.Warehouse warehouse, Claim claim);
public Task<ActionResult> AddWarehouseClaims(Data.Warehouse warehouse, List<Claim> claims);
public Task<List<Claim>> GetWarehouseClaim(Data.Warehouse warehouse);
}
@@ -0,0 +1,6 @@
namespace OpenWarehouse.warehouse.api.Common.Warehouse;
public enum WarehouseClaimsType
{
Company, Email, Phone, OwnerId, OwnerUsername, WarehousePrivilageManage, WarehouseManageInfo, WarehouseAdmin,
}
@@ -0,0 +1,39 @@
namespace OpenWarehouse.warehouse.api.Common.Warehouse;
public static class WarehouseClaimsTypeClass
{
private static Dictionary<WarehouseClaimsType, string> _warehouseClaimsString = new()
{
{ WarehouseClaimsType.Company, "Company" },
{ WarehouseClaimsType.Email, "Email" },
{ WarehouseClaimsType.Phone, "Phone" },
{ WarehouseClaimsType.OwnerId, "OwnerID" },
{ WarehouseClaimsType.OwnerUsername, "OwnerUsername" },
{ WarehouseClaimsType.WarehouseAdmin, "WarehouseManagePrivilage" },
{ WarehouseClaimsType.WarehouseManageInfo, "WarehouseManagePrivilageInfo" },
{ WarehouseClaimsType.WarehousePrivilageManage, "WarehouseAdminPrivilage" }
};
public static string ToString(this WarehouseClaimsType warehouseClaimsType)
{
var result = _warehouseClaimsString.TryGetValue(warehouseClaimsType, out var claim);
if (!result) throw new NotImplementedException("Claim tyoe not implemented");
return claim ?? throw new NotImplementedException("Claim tyoe not implemented");
}
public static Claim ToClaim(this WarehouseClaimsType warehouseClaimsType, string value, string? issuer = null)
{
var result = _warehouseClaimsString.TryGetValue(warehouseClaimsType, out var claim);
if (!result || claim == null) throw new NotImplementedException("Claim tyoe not implemented");
if (issuer != null)
{
return new Claim(claim, value);
}
else
{
return new Claim(claim, value, issuer);
}
}
}
@@ -0,0 +1,193 @@
namespace OpenWarehouse.warehouse.api.Common.Warehouse;
public class WarehouseManager(ApplicationDbContext context) : IWarehouseManager
{
public async Task<Data.Warehouse?> GetWarehouse(string id)
{
var warehouse = await context.Warehouses
.Include(warehouse1 => warehouse1.Image)
.FirstOrDefaultAsync(warehouse1 => warehouse1.Id != null && warehouse1.Id.Equals(id));
return warehouse;
}
public async Task<ActionResult> EditWarehouse(Data.Warehouse warehouse, string? newName = null,
string? newFiestLineAdres = null, string? newSecondLineAdress = null,
Image? image = null)
{
ActionResult? res = null;
var error = new Dictionary<string, string>();
var warehouseEntry = await context.Warehouses
.Include(warehouse1 => warehouse.Image)
.FirstOrDefaultAsync(warehouse1 => warehouse1 == warehouse);
if (warehouseEntry == null)
{
error["Database"] = "Not found";
return new ActionResult(false, error);
}
if (newName != null)
{
warehouseEntry.Name = newName;
}
if (newFiestLineAdres != null)
{
warehouse.FiestLineAdress = newFiestLineAdres;
}
if (newSecondLineAdress != null)
{
warehouse.SecondLineAdress = newSecondLineAdress;
}
if (image != null)
{
var imageToDel = warehouse.Image;
warehouse.Image = image;
if (imageToDel != null)
{
await context.Images.Where(image1 => image1 == imageToDel).ExecuteDeleteAsync();
}
}
try
{
res= new ActionResult((await context.SaveChangesAsync()) > 0);
}
catch (OperationCanceledException e)
{
error[e.Source ?? "Uknow"] = e.Message;
}
catch (DbUpdateConcurrencyException e)
{
error[e.Source ?? "Uknow"] = e.Message;
}
catch (DbUpdateException e)
{
error[e.Source ?? "Uknow"] = e.Message;
}
if (res == null)
{
res = new ActionResult(false, error);
}
return res;
}
public async Task<ActionResult> CreateWarehouse(Data.Warehouse warehouse)
{
ActionResult? res = null;
var error = new Dictionary<string, string>();
context.Warehouses
.Add(warehouse);
try
{
res= new ActionResult((await context.SaveChangesAsync()) == 1);
}
catch (OperationCanceledException e)
{
error[e.Source ?? "Uknow"] = e.Message;
}
catch (DbUpdateConcurrencyException e)
{
error[e.Source ?? "Uknow"] = e.Message;
}
catch (DbUpdateException e)
{
error[e.Source ?? "Uknow"] = e.Message;
}
if (res == null)
{
res = new ActionResult(false, error);
}
return res;
}
public async Task<ActionResult> AddWarehouseClaim(Data.Warehouse warehouse, Claim claim)
{
ActionResult? res = null;
var error = new Dictionary<string, string>();
var claimData = new WarehouseClaims(warehouse, claim);
context.WarehouseClaims.Add(claimData);
try
{
res = new ActionResult((await context.SaveChangesAsync()) == 1);
}
catch (OperationCanceledException e)
{
error[e.Source ?? "Uknow"] = e.Message;
}
catch (DbUpdateConcurrencyException e)
{
error[e.Source ?? "Uknow"] = e.Message;
}
catch (DbUpdateException e)
{
error[e.Source ?? "Uknow"] = e.Message;
}
if (res == null)
{
res = new ActionResult(false, error);
}
return res;
}
public async Task<ActionResult> AddWarehouseClaims(Data.Warehouse warehouse, List<Claim> claims)
{
ActionResult? res = null;
var error = new Dictionary<string, string>();
foreach (var claim in claims)
{
var claimData = new WarehouseClaims(warehouse, claim);
context.WarehouseClaims.Add(claimData);
}
try
{
res = new ActionResult((await context.SaveChangesAsync()) == 1);
}
catch (OperationCanceledException e)
{
error[e.Source ?? "Uknow"] = e.Message;
}
catch (DbUpdateConcurrencyException e)
{
error[e.Source ?? "Uknow"] = e.Message;
}
catch (DbUpdateException e)
{
error[e.Source ?? "Uknow"] = e.Message;
}
if (res == null)
{
res = new ActionResult(false, error);
}
return res;
}
public async Task<List<Claim>> GetWarehouseClaim(Data.Warehouse warehouse)
{
var claims = await context.WarehouseClaims
.Where(warehouseClaims => warehouseClaims.Warehouse == warehouse)
.Select(warehouseClaims => new Claim(warehouseClaims.ClaimType ?? "Uknow", warehouseClaims.ClaimValue ?? "Uknow"))
.ToListAsync();
return claims;
}
}
@@ -0,0 +1,250 @@
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<Item> logger,
ITopicProducer<TagTreeKafkaMessage> publishTagTreeEndpoint, ITaskManager taskManager,
IItemTagManager itemTagManager, IProducerManager producerManager) : ControllerBase
{
[HttpGet("{id}")]
public async Task<ActionResult<ItemModel>> 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<ActionResult<CreateItemRespond>> 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<ItemCategoryTag>();
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<ActionResult> 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<ActionResult> 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<ActionResult<TagTreeModel>> 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<ActionResult<int>> 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;
}
}
@@ -0,0 +1,173 @@
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using OpenWarehouse.warehouse.api.Common.Producer;
using OpenWarehouse.warehouse.api.Model.Producer;
namespace OpenWarehouse.warehouse.api.Controller;
[ApiController]
[Route("[controller]/[action]")]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public class Producer(ILogger<Producer> logger, IProducerManager producerManager) : ControllerBase
{
[HttpPost]
public async Task<IActionResult> CreateProducer([FromBody] Model.Producer.Producer? producer)
{
if (producer == null)
{
logger.LogWarning("Received null producer in CreateProducer");
return BadRequest();
}
var newProducer = new Data.Producers()
{
FiestLineAdress = producer.FirstLineAddress,
SecondLineAdress = producer.SecondLineAddress,
Name = producer.Name,
Email = producer.Email,
Phone = producer.PhoneNumber
};
var res = await producerManager.CreateAsync(newProducer);
if (!res.IsSuccess)
{
logger.LogError("Failed to create producer: {Errors}", res.Errors);
return BadRequest(res.Errors);
}
logger.LogInformation("Producer created successfully: {ProducerName}", producer.Name);
return Ok();
}
[HttpGet("{name}")]
public async Task<ActionResult<Model.Producer.Producer>> GetProducer([FromRoute] string name)
{
logger.LogInformation("Fetching producer with name: {Name}", name);
var res = await producerManager.FindProducerByName(name);
if (res == null)
{
logger.LogWarning("Producer not found with name: {Name}", name);
return NotFound();
}
var processedRes = new Model.Producer.Producer
{
Name = res.Name,
Email = res.Email,
PhoneNumber = res.Phone,
FirstLineAddress = res.FiestLineAdress,
SecondLineAddress = res.SecondLineAdress,
Claims = res.Claims?.Select(q => q as ClaimBase).ToList(),
};
logger.LogInformation("Producer fetched successfully: {Name}", name);
return Ok(processedRes);
}
[HttpGet]
public async Task<ActionResult<IEnumerable<Model.Producer.Producer>>> GetProducers()
{
logger.LogInformation("Fetching list of producers");
var res = await producerManager.GetProducerList();
var processedRes = res.Select(producer => new Model.Producer.Producer
{
Name = producer.Name,
Email = producer.Email,
PhoneNumber = producer.Phone,
FirstLineAddress = producer.FiestLineAdress,
SecondLineAddress = producer.SecondLineAdress,
Claims = producer.Claims?.Select(q => q as ClaimBase).ToList(),
}).ToList();
logger.LogInformation("Fetched {Count} producers", processedRes.Count);
return Ok(processedRes);
}
[HttpDelete]
public async Task<IActionResult> DeleteProducerByName([FromBody] string name)
{
logger.LogInformation("Deleting producer with name: {Name}", name);
var res = await producerManager.DeleteProducerByName(name);
if (!res)
{
logger.LogWarning("Failed to delete producer: {Name}", name);
return NotFound();
}
logger.LogInformation("Deleted producer successfully: {Name}", name);
return Ok();
}
[HttpPost]
public async Task<ActionResult<int>> UpdateProducerClaim([FromBody] UpdateClaimRequest request)
{
logger.LogInformation("Updating claims for producer");
int result = 0;
Producers? producers;
if (request.Id != null)
{
producers = await producerManager.FindProducerById(request.Id);
}
else if (request.Name != null)
{
producers = await producerManager.FindProducerByName(request.Name);
}
else
{
logger.LogWarning("Invalid request: both Id and Name are null");
return BadRequest();
}
if (producers == null)
{
logger.LogWarning("Producer not found during claim update");
return NotFound();
}
foreach (var claim in request.ListOfUpdateClaim)
{
if (claim.ClaimType == null) continue;
if (claim.ClaimValue == null)
{
var resultOperation = await producerManager.DeleteClaim(producers, claim.ClaimType);
if (resultOperation.IsSuccess)
{
result++;
}
else
{
logger.LogWarning("Failed to delete claim: {ClaimType}", claim.ClaimType);
}
}
else
{
var resultOperation = await producerManager.UpdateClaim(producers,
new Claim(claim.ClaimType, claim.ClaimValue, claim.ClaimIssuer));
if (resultOperation.IsSuccess)
{
result++;
}
else
{
logger.LogWarning("Failed to update claim: {ClaimType}", claim.ClaimType);
}
}
}
logger.LogInformation("Claims updated successfully. Total updated: {Count}", result);
return result > 0 ? Ok(result) : BadRequest();
}
}
@@ -0,0 +1,58 @@
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using OpenWarehouse.auth.lib.Model.Service;
using OpenWarehouse.warehouse.api.Common.DefaultServiceAccountAndRole;
using OpenWarehouse.warehouse.api.Common.ServiceClaims;
using OpenWarehouse.warehouse.api.Model.Tool;
namespace OpenWarehouse.warehouse.api.Controller;
[ApiController]
[Route("[action]")]
public class Tools(
IConfiguration configuration,
DefaultIdentify defaultIdentify,
IServiceClaimsManager serviceClaimsManager) : ControllerBase
{
[HttpGet]
public Task<ActionResult<PingModel>> Ping()
{
return Task.FromResult<ActionResult<PingModel>>(Ok(new PingModel()));
}
[HttpPost]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public async Task<ActionResult<InitialziationResultModel>> InitServiceConnectionWithAuth()
{
Request.Headers.TryGetValue("Authorization", out var token);
var result = await defaultIdentify.SeedAsync(token.ToString());
var testResult = false;
if (result.IsSuccess || result.UserAllereadyExist)
{
testResult = await defaultIdentify.TestAuth(result.ServiceToken ??
throw new ArgumentNullException(
$"Service {result.ServiceName} token is null"));
if (testResult)
{
await serviceClaimsManager.UpdateClaim(ServiceClaim.ServiceAcoountToken, result.ServiceToken);
await serviceClaimsManager.UpdateClaim(ServiceClaim.ServiceAcoountUsername, result.ServiceUsername ??
throw new ArgumentNullException(
$"Service {result.ServiceName} username is null"));
}
}
return Ok(new InitialziationResultModel()
{
Success = result.IsSuccess,
IsAlreadyInitialized = result.UserAllereadyExist,
ServiceAccountName = result.ServiceName,
TestAuthSuccess = testResult
});
}
}
@@ -0,0 +1,94 @@
using System.Runtime.InteropServices.ComTypes;
using Microsoft.AspNetCore.DataProtection.EntityFrameworkCore;
namespace OpenWarehouse.warehouse.api.Data;
public class ApplicationDbContext: DbContext, IDataProtectionKeyContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
var itemInWarehouseClaims = modelBuilder.Entity<ItemInWarehouseClaims>();
itemInWarehouseClaims.HasOne(e => e.Item)
.WithMany(e => e.ItemInWarehouseClaims)
.HasForeignKey(e => e.ItemId);
itemInWarehouseClaims.HasIndex(ic => new { ic.ItemId, ic.ClaimType })
.IsUnique();
var wrehouseClaims = modelBuilder.Entity<WarehouseClaims>();
wrehouseClaims.HasOne(e => e.Warehouse)
.WithMany(e => e.Claims)
.HasForeignKey(e => e.WarehouseId);
wrehouseClaims.HasIndex(ic => new { ic.WarehouseId, ic.ClaimType })
.IsUnique();
var itemClaims = modelBuilder.Entity<ItemClaims>();
itemClaims.HasIndex(ic => new { ic.ItemId, ic.ClaimType })
.IsUnique();
itemClaims.HasOne(ic => ic.Item)
.WithMany(i => i.ItemClaims)
.HasForeignKey(ic => ic.ItemId);
var itemTag = modelBuilder.Entity<ItemCategoryTag>();
itemTag.HasOne(e => e.ParentTag)
.WithMany(e => e.ListOfChildrenTag)
.HasForeignKey(e => e.ParentTagId);
itemTag.HasIndex(ic => new { ic.Name })
.IsUnique();
var item = modelBuilder.Entity<Item>();
item.HasOne(e => e.Producer)
.WithMany(e => e.Items)
.HasForeignKey(e => e.ProducerId)
.IsRequired(true);
item.HasIndex(ic => new { ic.Name, ic.ProducerId })
.IsUnique();
item.Navigation(ic => ic.ItemClaims)
.UsePropertyAccessMode(PropertyAccessMode.Property);
var producers = modelBuilder.Entity<Producers>();
producers.HasIndex(ic => new { ic.Name })
.IsUnique();
producers.Navigation(ic => ic.Claims)
.UsePropertyAccessMode(PropertyAccessMode.Property);
var producerClaims = modelBuilder.Entity<ProducerClaims>();
producerClaims.HasOne(e => e.Producer)
.WithMany(e => e.Claims)
.HasForeignKey(e => e.ProducerId);
producerClaims.HasIndex(ic => new { ic.ProducerId, ic.ClaimType })
.IsUnique();
modelBuilder.Entity<ServiceClaims>()
.HasIndex(ic => ic.ClaimType)
.IsUnique();
modelBuilder.Entity<ItemInWarehouse>()
.Navigation(ic => ic.ItemInWarehouseClaims)
.UsePropertyAccessMode(PropertyAccessMode.Property);
modelBuilder.Entity<Warehouse>()
.Navigation(ic => ic.Claims)
.UsePropertyAccessMode(PropertyAccessMode.Property);
}
public DbSet<Image> Images { get; init; }
public DbSet<Item> Items { get; init; }
public DbSet<ItemCategoryTag> ItemCategoryTags { get; init; }
public DbSet<ItemClaims> ItemClaims { get; init; }
public DbSet<ItemInWarehouse> ItemInWarehouses { get; init; }
public DbSet<ItemInWarehouseClaims> ItemInWarehousesClaims { get; init; }
public DbSet<Producers> Producers { get; init; }
public DbSet<ProducerClaims> ProducerClaims { get; init; }
public DbSet<Warehouse> Warehouses { get; init; }
public DbSet<WarehouseClaims> WarehouseClaims { get; init; }
public DbSet<ServiceClaims> ServiceClaims { get; init; }
public DbSet<DataProtectionKey> DataProtectionKeys { get; init; }
public DbSet<TaskStatusTable> TaskStatus { get; init; }
}
@@ -0,0 +1,20 @@
using Microsoft.EntityFrameworkCore.Design;
namespace OpenWarehouse.warehouse.api.Data;
public class ApplicationDbContextFactory : IDesignTimeDbContextFactory<ApplicationDbContext>
{
public ApplicationDbContext CreateDbContext(string[] args)
{
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.Build();
var optionsBuilder = new DbContextOptionsBuilder<ApplicationDbContext>();
optionsBuilder.UseNpgsql(configuration.GetConnectionString("DefaultConnection"));
return new ApplicationDbContext(optionsBuilder.Options);
}
}
@@ -0,0 +1,30 @@
using System.ComponentModel.DataAnnotations.Schema;
using Claim = System.Security.Claims.Claim;
namespace OpenWarehouse.warehouse.api.Data;
public class ClaimBase
{
[Required]
public string? ClaimType { get; set; }
[Required]
public string? ClaimValue { get; set; }
public string? ClaimIssuer { get; set; }
[NotMapped]
public Claim Claim {
get
{
return new Claim(ClaimType ?? throw new Exception("Null exemption")
, ClaimValue ?? throw new Exception("Null exemption")
, ClaimIssuer ?? "Local");
}
set
{
ClaimType = value.Type;
ClaimValue = value.Value;
ClaimIssuer = value.Issuer;
}
}
}
@@ -0,0 +1,7 @@
namespace OpenWarehouse.warehouse.api.Data;
public class Image
{
[Key]
public Guid? Id { get; set; }
}
+18
View File
@@ -0,0 +1,18 @@
namespace OpenWarehouse.warehouse.api.Data;
public class Item
{
[Key]
public Guid? Id { get; set; }
[Required, MaxLength(30), MinLength(4)]
public string? Name { get; set; }
public Image Image { get; set; }
[Required]
public Producers? Producer { get; set; }
[Required]
public Guid? ProducerId { get; set; }
public List<ItemCategoryTag>? ItemCategoryTags { get; set; }
public List<ItemClaims>? ItemClaims { get; set; }
}
@@ -0,0 +1,19 @@
namespace OpenWarehouse.warehouse.api.Data;
public class ItemCategoryTag
{
[Key]
public Guid Id { get; set; }
[Required]
[MinLength(4),MaxLength(30)]
public string? Name { get; set; }
public ItemCategoryTag? ParentTag { get; set; }
public Guid? ParentTagId { get; set; }
[DeleteBehavior(DeleteBehavior.Cascade)]
public List<ItemCategoryTag>? ListOfChildrenTag { get; set; }
public List<Item>? Items { get; set; }
}
@@ -0,0 +1,18 @@
using Claim = System.Security.Claims.Claim;
namespace OpenWarehouse.warehouse.api.Data;
public class ItemClaims() : ClaimBase
{
public ItemClaims(Item item, Claim claim) : this()
{
Item = item;
ClaimType = claim.Type;
ClaimValue = claim.Value;
}
[Key]
public Guid Id { get; set; }
[Required, MaxLength(30)]
public Guid? ItemId { get; set; }
public Item? Item { get; set; }
}
@@ -0,0 +1,31 @@
namespace OpenWarehouse.warehouse.api.Data;
public class ItemInWarehouse()
{
public ItemInWarehouse(List<ItemInWarehouseClaims>? itemInWarehouses = null) : this()
{
if (itemInWarehouses != null)
ItemInWarehouseClaims = itemInWarehouses;
else
ItemInWarehouseClaims = new List<ItemInWarehouseClaims>();
}
public ItemInWarehouse(Item item, string positions, List<ItemInWarehouseClaims> itemInWarehouses) : this(itemInWarehouses)
{
Item = item;
Positions = positions;
ItemInWarehouseClaims = itemInWarehouses;
}
[Key]
public Guid? Id { get; set; }
[Required]
public Item? Item { get; set; }
[Required, MaxLength(30)]
public string? Positions { get; set; }
[DeleteBehavior(DeleteBehavior.Cascade)]
public List<ItemInWarehouseClaims>? ItemInWarehouseClaims { get; set; }
}
@@ -0,0 +1,10 @@
namespace OpenWarehouse.warehouse.api.Data;
public class ItemInWarehouseClaims : ClaimBase
{
[Key]
public Guid Id { get; set; }
[Required]
public Guid? ItemId { get; set; }
public ItemInWarehouse? Item { get; set; }
}
@@ -0,0 +1,23 @@
using Claim = System.Security.Claims.Claim;
namespace OpenWarehouse.warehouse.api.Data;
public class ProducerClaims() : ClaimBase
{
public ProducerClaims(Producers producer, Claim claim) : this()
{
this.Producer = producer;
ClaimType = claim.Type;
ClaimValue = claim.Value;
}
[Key]
public Guid? Id { get; set; }
[Required, MaxLength(100)]
public Guid? ProducerId { get; set; }
public Producers? Producer { get; set; }
}
@@ -0,0 +1,23 @@
namespace OpenWarehouse.warehouse.api.Data;
public class Producers
{
[Key]
public Guid? Id { get; set; }
public Image? Image { get; set; }
[Required, MaxLength(50)]
public string? Name { get; set; }
[Required, MaxLength(60)]
public string? FiestLineAdress { get; set; }
[MaxLength(60)]
public string? SecondLineAdress { get; set; }
[EmailAddress, MaxLength(40)]
public string? Email { get; set; }
[Phone, MaxLength(15)]
public string? Phone { get; set; }
[DeleteBehavior(DeleteBehavior.SetNull)]
public List<Item>? Items { get; set; }
[DeleteBehavior(DeleteBehavior.Cascade)]
public List<ProducerClaims>? Claims { get; set; }
}
@@ -0,0 +1,26 @@
using System.ComponentModel.DataAnnotations.Schema;
using Claim = System.Security.Claims.Claim;
namespace OpenWarehouse.warehouse.api.Data;
public class ServiceClaims
{
[Key, Required, MinLength(3), MaxLength(40)]
public string? ClaimType { get; set; }
[Required, MinLength(3), MaxLength(40)]
public string? ClaimValue { get; set; }
[NotMapped]
public Claim Claim
{
get =>
new(ClaimType ??
throw new InvalidOperationException(),
ClaimValue ?? throw new InvalidOperationException());
set
{
ClaimType = value.Type;
ClaimValue = value.Value;
}
}
}
@@ -0,0 +1,15 @@
namespace OpenWarehouse.warehouse.api.Data;
public class TaskStatusTable
{
[Required, Key]
public Guid TaskId { get; init; }
[MaxLength(50)]
public string? Actor { get; init; }
[MaxLength(20), Required]
public string? Status { get; set; }
[Required]
public DateTime CreatedAt { get; init; }
[Required]
public DateTime UpdatedAt { get; set; }
}
@@ -0,0 +1,19 @@
namespace OpenWarehouse.warehouse.api.Data;
[Index(nameof(Name), IsUnique = true)]
public class Warehouse
{
[Key]
public Guid? Id { get; set; }
[Required, MinLength(4), MaxLength(30)]
public string? Name { get; set; }
[Required, MinLength(4), MaxLength(40)]
public string? FiestLineAdress { get; set; }
[MinLength(0), MaxLength(40)]
public string? SecondLineAdress { get; set; }
public Image? Image { get; set; }
[DeleteBehavior(DeleteBehavior.Cascade)]
public List<WarehouseClaims>? Claims { get; set; }
}
@@ -0,0 +1,26 @@
using Claim = System.Security.Claims.Claim;
namespace OpenWarehouse.warehouse.api.Data;
public class WarehouseClaims() : ClaimBase
{
public WarehouseClaims(Warehouse warehouse, Claim claim) : this()
{
Warehouse = warehouse;
ClaimType = claim.Type;
ClaimValue = claim.Value;
ClaimIssuer = claim.Issuer;
}
public Claim GetClaim()
{
return new Claim(ClaimType ?? "Uknow", ClaimValue ?? "Uknow");
}
[Key, MaxLength(100)]
public Guid? Id { get; set; }
[Required, MaxLength(100)]
public Guid? WarehouseId { get; set; }
public Warehouse? Warehouse { get; set; }
}
+26
View File
@@ -0,0 +1,26 @@
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
USER $APP_UID
WORKDIR /app
EXPOSE 8080
EXPOSE 8081
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
ARG BUILD_CONFIGURATION=Release
WORKDIR /src
COPY ["OpenWarehouse.warehouse.api/OpenWarehouse.warehouse.api.csproj", "OpenWarehouse.warehouse.api/"]
COPY ["OpenWarehouse.auth.lib/OpenWarehouse.auth.lib.csproj", "OpenWarehouse.auth.lib/"]
COPY ["OpenWarehouse.Common.Lib/OpenWarehouse.Common.Lib.csproj", "OpenWarehouse.Common.Lib/"]
COPY ["Kafka.Lib/Kafka.Lib.csproj", "Kafka.Lib/"]
RUN dotnet restore "OpenWarehouse.warehouse.api/OpenWarehouse.warehouse.api.csproj"
COPY . .
WORKDIR "/src/OpenWarehouse.warehouse.api"
RUN dotnet build "OpenWarehouse.warehouse.api.csproj" -c $BUILD_CONFIGURATION -o /app/build
FROM build AS publish
ARG BUILD_CONFIGURATION=Release
RUN dotnet publish "OpenWarehouse.warehouse.api.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "Openwarehouse.warehouse.api.dll"]
@@ -0,0 +1,6 @@
global using System.ComponentModel.DataAnnotations;
global using System.Security.Claims;
global using Microsoft.EntityFrameworkCore;
global using OpenWarehouse.warehouse.api.Common.Item;
global using OpenWarehouse.warehouse.api.Data;
global using OpenWarehouse.warehouse.api.Model;
@@ -0,0 +1,23 @@
namespace OpenWarehouse.warehouse.api.Model;
public class ActionResult
{
public ActionResult(bool isSuccess, Dictionary<string, string>? errors)
{
IsSuccess = isSuccess;
Errors = errors;
}
public ActionResult(bool isSuccess) : this(isSuccess, null)
{
}
public ActionResult() : this(true,null)
{
}
public bool IsSuccess { get; }
public Dictionary<string, string>? Errors { get; }
}
@@ -0,0 +1,8 @@
namespace OpenWarehouse.warehouse.api.Model.Item;
public class CreateItemRequest
{
public string? ItemName { get; set; }
public string? ProducerName { get; set; }
public List<string> Tags { get; set; } = new();
}
@@ -0,0 +1,7 @@
namespace OpenWarehouse.warehouse.api.Model.Item;
public class CreateItemRespond
{
public bool Success { get; set; }
public Dictionary<string, string> Errors { get; set; } = new();
}
@@ -0,0 +1,8 @@
using Newtonsoft.Json;
namespace OpenWarehouse.warehouse.api.Model.Item;
public class CreateTagTreeRequest
{
public List<ItemCategoryTag>? ItemCategoryTagsList { get; set; }
}
@@ -0,0 +1,9 @@
using System.Reflection;
namespace OpenWarehouse.warehouse.api.Model.Item;
public class CreateTagTreeRespond
{
public string? TaskId { get; set; }
public bool? CreatedSuccess { get; set; }
}
@@ -0,0 +1,7 @@
namespace OpenWarehouse.warehouse.api.Model.Item;
public class ItemCategoryTag()
{
public string? ParentName { get; set; }
public string? Name { get; set; }
}
@@ -0,0 +1,31 @@
using NJsonSchema.Annotations;
namespace OpenWarehouse.warehouse.api.Model.Item;
public class ClaimValueModel
{
public string? Value { get; set; }
public string? Issuer { get; set; }
}
public class ItemModel
{
[NotNull, MaxLength(30), Required]
public string? Id { get; set; }
[NotNull, MaxLength(30), Required]
public string? Name { get; set; }
[NotNull, MaxLength(30), Required]
public string? ProducerId { get; set; }
[NotNull, MaxLength(30), Required]
public string? ProducerName { get; set; }
[Required]
public Dictionary<string, ClaimValueModel>? Claims { get; set; }
[Required]
public List<string>? CategoryTag { get; set; }
}
@@ -0,0 +1,7 @@
namespace OpenWarehouse.warehouse.api.Model.Item;
public class TagTreeModel
{
public string? Name { get; init; }
public List<TagTreeModel> Child { get; } = new();
}
@@ -0,0 +1,8 @@
namespace OpenWarehouse.warehouse.api.Model.Item;
public class UpdateItemClaimRequest
{
public string? ItemId { get; set; }
public List<ClaimBase> ListOfUpdatedClaims { get; set; } = new List<ClaimBase>();
}
@@ -0,0 +1,12 @@
namespace OpenWarehouse.warehouse.api.Model.Producer;
public class Producer
{
public string? Name { get; set; }
public string? FirstLineAddress { get; set; }
public string? SecondLineAddress { get; set; }
public string? Email { get; set; }
public string? PhoneNumber { get; set; }
public List<ClaimBase>? Claims { get; set; }
}
@@ -0,0 +1,9 @@
namespace OpenWarehouse.warehouse.api.Model.Producer;
public class UpdateClaimRequest
{
public Guid? Id { get; set; }
public string? Name { get; set; }
public List<ClaimBase> ListOfUpdateClaim { get; set; } = new();
}
@@ -0,0 +1,11 @@
using NJsonSchema.Annotations;
namespace OpenWarehouse.warehouse.api.Model.Tool;
public class InitializationModel
{
[NotNull]
public string? Login { get; set; }
[NotNull]
public string? Password { get; set; }
}
@@ -0,0 +1,14 @@
using System.ComponentModel;
namespace OpenWarehouse.warehouse.api.Model.Tool;
public class InitialziationResultModel
{
[DefaultValue(false)]
public bool IsAlreadyInitialized { get; set; }
[DefaultValue(false)]
public bool Success { get; set; }
[DefaultValue(false)]
public bool TestAuthSuccess { get; set; }
public string? ServiceAccountName { get; set; }
}
@@ -0,0 +1,52 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
<UserSecretsId>5749d66d-04bd-461c-99cf-edd55df080e3</UserSecretsId>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Confluent.Kafka" Version="2.6.0" />
<PackageReference Include="Confluent.SchemaRegistry" Version="2.6.0" />
<PackageReference Include="MassTransit" Version="8.3.1" />
<PackageReference Include="MassTransit.Kafka" Version="8.3.1" />
<PackageReference Include="MassTransit.SqlTransport.PostgreSQL" Version="8.3.1" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.10" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.10" />
<PackageReference Include="Microsoft.AspNetCore.DataProtection.EntityFrameworkCore" Version="8.0.10" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.10" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.10" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.10">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.10">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.IdentityModel.Abstractions" Version="8.2.0" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.10" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.9.0" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.2.0" />
</ItemGroup>
<ItemGroup>
<Content Include="..\.dockerignore">
<Link>.dockerignore</Link>
</Content>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\OpenWarehouse.auth.lib\OpenWarehouse.auth.lib.csproj" />
<ProjectReference Include="..\OpenWarehouse.Common.Lib\OpenWarehouse.Common.Lib.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Data\Migrations\" />
</ItemGroup>
</Project>
@@ -0,0 +1,4 @@
@Openwarehouse.warehouse.api_HostAddress = http://localhost:5048
###
+161
View File
@@ -0,0 +1,161 @@
using System.Text.Json;
using MassTransit;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.OpenApi.Models;
using OpenWarehouse.auth.lib;
using OpenWarehouse.Common.Lib;
using OpenWarehouse.warehouse.api.Common.DefaultServiceAccountAndRole;
using OpenWarehouse.warehouse.api.Common.Inventory;
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.ServiceClaims;
using OpenWarehouse.warehouse.api.Common.TaskManager;
using OpenWarehouse.warehouse.api.Common.Warehouse;
using OpenWarehouse.warehouse.api.Services;
var builder = WebApplication.CreateBuilder(args);
var logger = builder.GetBuildLogger();
var authUrl = builder.Configuration["Auth:Url"] ;
if (authUrl == null)
{
logger.LogCritical("Auth url not set!!");
return;
}
await builder.WaitForAut(authUrl, logger);
var kafkaConnectionString = builder.Configuration["Kafka"];
if (string.IsNullOrEmpty(kafkaConnectionString))
{
logger.LogCritical("Kafka environment variable is not set");
throw new ArgumentNullException($"Kafka environment variable is not set");
}
var kafkaConfig = JsonSerializer.Deserialize<KafkaConfig>(kafkaConnectionString);
if (kafkaConfig != null) builder.Services.AddSingleton(kafkaConfig);
await builder.AddAuthService(authUrl, logger);
builder.Host
.UseMassTransit((hostContext, configurator) =>
{
configurator.UsingInMemory();
configurator.AddRider(r =>
{
try
{
r.AddConsumer<TagTreeConsumer>();
r.AddProducer<TagTreeKafkaMessage>($"{kafkaConfig?.TopicRoot}-tag-tree");
}
catch (Exception e)
{
logger.LogError("Error configuring Kafka producers: {Message}", e.Message);
}
r.UsingKafka((context, cfg) =>
{
cfg.Host(kafkaConfig?.BootstrapServers);
cfg.TopicEndpoint<TagTreeKafkaMessage>($"{kafkaConfig?.TopicRoot}-tag-tree",
"TagTreeCreate" , e =>
{
e.ConfigureConsumer<TagTreeConsumer>(context);
});
});
});
}).ConfigureServices(services =>
{
services.AddMassTransitHostedService();
});
var connectionString = Environment.GetEnvironmentVariable("ConnectionString")
?? builder.Configuration.GetConnectionString("DefaultConnection")
?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseNpgsql(connectionString));
builder.Services.AddDataProtection()
.PersistKeysToDbContext<ApplicationDbContext>();
builder.Services.AddScoped<DefaultIdentify>();
builder.Services.AddScoped<IServiceClaimsManager, ServiceClaimsManager>();
builder.Services.AddScoped<IProducerManager, ProducerManager>();
builder.Services.AddScoped<IWarehouseManager, WarehouseManager>();
builder.Services.AddScoped<IItemManager, ItemManager>();
builder.Services.AddScoped<IInventoryManger, InventoryManger>();
builder.Services.AddScoped<ITaskManager, TaskManager>();
builder.Services.AddScoped<IItemTagManager, ItemTagManager>();
builder.Services.AddControllers()
.ConfigureApiBehaviorOptions(options =>
{
options.SuppressConsumesConstraintForFormFileParameters = true;
options.SuppressInferBindingSourcesForParameters = true;
options.SuppressModelStateInvalidFilter = true;
options.SuppressMapClientErrors = true;
});
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(options =>
{
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
In = ParameterLocation.Header,
Description = "Please insert JWT with Bearer into field",
Name = "Authorization",
Type = SecuritySchemeType.ApiKey,
Scheme = "Bearer"
});
options.AddSecurityRequirement(new OpenApiSecurityRequirement()
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
}
},
Array.Empty<string>()
}
});
options.AddServer(new OpenApiServer()
{
Url = "/warehouse/"
});
});
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
using var scope = app.Services.CreateScope();
var services = scope.ServiceProvider;
var context = services.GetRequiredService<ApplicationDbContext>();
context.Database.Migrate();
}
app.UseHttpsRedirection();
app.UseAuthentication();
app.MapControllers();
app.Run();
@@ -0,0 +1,41 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:49002",
"sslPort": 44368
}
},
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "http://localhost:5048",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "https://localhost:7095;http://localhost:5048",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
@@ -0,0 +1,110 @@
using MassTransit;
using OpenWarehouse.warehouse.api.Common.ItemTagManager;
using OpenWarehouse.warehouse.api.Common.Kafka;
using OpenWarehouse.warehouse.api.Common.Kafka.TagTree;
using OpenWarehouse.warehouse.api.Common.TaskManager;
using OpenWarehouse.warehouse.api.Model.Item;
using ItemCategoryTag = OpenWarehouse.warehouse.api.Model.Item.ItemCategoryTag;
using TaskStatus = OpenWarehouse.warehouse.api.Common.TaskManager.TaskStatus;
namespace OpenWarehouse.warehouse.api.Services;
public class TagTreeConsumer(ILogger<TagTreeConsumer> logger, ITaskManager taskManager
,IItemTagManager itemTagManager) : IConsumer<TagTreeKafkaMessage>
{
public async Task Consume(ConsumeContext<TagTreeKafkaMessage> context)
{
logger.LogInformation("Received and processing Kafka Message: {Guid}", context.Message.Guid);
var status = TaskStatus.Processing;
try
{
await taskManager.UpdateTaskStatus(context.Message.Guid, status);
switch (context.Message.Action)
{
case Actions.Create:
status = await Create(context.Message);
break;
case Actions.Delete:
status = await Delete(context.Message.ItemCategoryTagsList);
break;
default:
logger.LogWarning("Action not allowed in request: {Guid}", context.Message.Guid);
status = TaskStatus.Error;
break;
}
await taskManager.UpdateTaskStatus(context.Message.Guid, status);
}
catch (Exception ex)
{
logger.LogError(ex, "Error occurred while processing Kafka message: {Guid}", context.Message.Guid);
status = TaskStatus.Error;
await taskManager.UpdateTaskStatus(context.Message.Guid, status);
}
}
private async Task<TaskStatus> Create(TagTreeKafkaMessage context)
{
try
{
var len = context.ItemCategoryTagsList.Count;
var preProcessingData = context.ItemCategoryTagsList
.Distinct()
.ToList();
var result = await itemTagManager.CreateTagTree(preProcessingData);
if (result == 0)
{
logger.LogWarning("Failed to create tag tree for message: {Guid}", context.Guid);
return TaskStatus.Error;
}
else if (result < len)
{
logger.LogInformation("Partial success in creating tag tree for message: {Guid}", context.Guid);
return TaskStatus.NotFullSuccess;
}
else
{
logger.LogInformation("Successfully created tag tree for message: {Guid}", context.Guid);
return TaskStatus.Success;
}
}
catch (Exception ex)
{
logger.LogError(ex, "Error occurred while creating tag tree for message: {Guid}", context.Guid);
return TaskStatus.Error;
}
}
private async Task<TaskStatus> Delete(List<ItemCategoryTag> names)
{
try
{
var result = await itemTagManager.DeleteTagsByNames(names);
if (result == 0)
{
logger.LogWarning("Failed to delete tags for names: {Names}", names);
return TaskStatus.Error;
}
else if (result < names.Count)
{
logger.LogInformation("Partial success in deleting tags for names: {Names}", names);
return TaskStatus.NotFullSuccess;
}
else
{
logger.LogInformation("Successfully deleted tags for names: {Names}", names);
return TaskStatus.Success;
}
}
catch (Exception ex)
{
logger.LogError(ex, "Error occurred while deleting tags for names: {Names}", names);
return TaskStatus.Error;
}
}
}