diff --git a/README.md b/README.md index 5e50a44..9ec5cb2 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,12 @@ With a focus on **Clean Architecture** and **extreme code simplicity**, CleanAspire provides developers with the tools to create responsive and maintainable web applications with minimal effort. The template also supports **Microsoft.Kiota** to simplify API client generation, ensuring consistency and productivity in every project. +### 🎉 Auto-generate Code Using Chat GPTs + +![chatgpt](./gpts.png) + [Link to CleanAspire Code Generator](https://chatgpt.com/g/g-677e40252ff88191933bb84567b62e7b-cleanaspire-code-generator) + + ### 🌐 Offline Support CleanAspire fully supports **offline mode** through its integrated PWA capabilities, enabling your application to function seamlessly without an internet connection. By leveraging **Service Workers** and **browser caching**, the application can store essential resources and data locally, ensuring quick load times and uninterrupted access. Additionally, CleanAspire offers streamlined configuration options to help developers manage caching strategies and data synchronization effortlessly, guaranteeing that users receive the latest updates once the network is restored. @@ -66,10 +72,7 @@ By incorporating robust offline capabilities, CleanAspire empowers developers to - Uses **IndexedDB** to cache data locally, allowing the application to retrieve data and function offline. - The system detects the online/offline status and fetches data from **IndexedDB** when offline, ensuring uninterrupted access to key features. -### How to generate code use Chat GPTs -![chatgpt](./gpts.png) - [Link to CleanAspire Code Generator](https://chatgpt.com/g/g-677e40252ff88191933bb84567b62e7b-cleanaspire-code-generator) - + ### How to Create a New Object in a CRUD Application: A Step-by-Step Guide https://github.com/neozhu/cleanaspire/issues/34 @@ -95,7 +98,7 @@ https://github.com/neozhu/cleanaspire/issues/34 version: '3.8' services: apiservice: - image: blazordevlab/cleanaspire-api:0.0.64 + image: blazordevlab/cleanaspire-api:0.0.66 environment: - ASPNETCORE_ENVIRONMENT=Development - AllowedHosts=* @@ -117,7 +120,7 @@ services: - "8018:443" blazorweb: - image: blazordevlab/cleanaspire-webapp:0.0.64 + image: blazordevlab/cleanaspire-webapp:0.0.66 environment: - ASPNETCORE_ENVIRONMENT=Production - AllowedHosts=* @@ -129,7 +132,7 @@ services: - "8014:443" standalone: - image: blazordevlab/cleanaspire-standalone:0.0.64 + image: blazordevlab/cleanaspire-standalone:0.0.66 ports: - "8020:80" - "8021:443" diff --git a/src/CleanAspire.Api/CleanAspire.Api.csproj b/src/CleanAspire.Api/CleanAspire.Api.csproj index 4d1d92f..337fca3 100644 --- a/src/CleanAspire.Api/CleanAspire.Api.csproj +++ b/src/CleanAspire.Api/CleanAspire.Api.csproj @@ -1,4 +1,4 @@ - + net9.0 @@ -18,8 +18,8 @@ - - + + diff --git a/src/CleanAspire.Application/CleanAspire.Application.csproj b/src/CleanAspire.Application/CleanAspire.Application.csproj index a387907..a68d22d 100644 --- a/src/CleanAspire.Application/CleanAspire.Application.csproj +++ b/src/CleanAspire.Application/CleanAspire.Application.csproj @@ -8,14 +8,14 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/src/CleanAspire.Application/Features/Products/Commands/CreateProductCommand.cs b/src/CleanAspire.Application/Features/Products/Commands/CreateProductCommand.cs index c2a3261..8f5b6c9 100644 --- a/src/CleanAspire.Application/Features/Products/Commands/CreateProductCommand.cs +++ b/src/CleanAspire.Application/Features/Products/Commands/CreateProductCommand.cs @@ -1,68 +1,58 @@ -// This code defines a command and its handler for creating a new product in the database. -// The CreateProductCommand encapsulates the required data to create a product. -// The CreateProductCommandHandler processes the command, creates a product entity, adds a domain event, saves it to the database, and returns a ProductDto. +// Summary: +// This file defines a command and its handler for creating a new product in the database. +// The CreateProductCommand encapsulates the necessary data for a product, while the +// CreateProductCommandHandler processes the command, creates a product entity, +// triggers domain events such as ProductCreatedEvent, and commits the changes. This ensures +// a structured and efficient approach to handling product creation. +using CleanAspire.Application.Features.Products.DTOs; +using CleanAspire.Application.Features.Products.EventHandlers; +using CleanAspire.Application.Pipeline; -// Using directives for necessary namespaces, bringing in external types used in the code -using CleanAspire.Application.Features.Products.DTOs; // Contains data transfer objects (DTOs) for the Product feature -using CleanAspire.Application.Features.Products.EventHandlers; // Contains event handlers related to Product events -using CleanAspire.Application.Pipeline; // Contains pipeline behaviors and related interfaces - -// Namespace for organizing related classes and features namespace CleanAspire.Application.Features.Products.Commands; -// A record that defines the CreateProductCommand, which encapsulates the data needed to create a new product +// Command object that encapsulates the data required for creating a new product. +// Its fields directly map to the properties of ProductDto. public record CreateProductCommand( - string SKU, // The stock-keeping unit, a unique identifier for the product, corresponding to the SKU field in ProductDto - string Name, // The name of the product, corresponding to the SKU field in ProductDto - ProductCategoryDto? Category, // The category of the product, nullable, referencing ProductCategoryDto definition - string? Description, // A description of the product, nullable, corresponding to the Description field in ProductDto - decimal Price, // The price of the product, matching the Price field in ProductDto - string? Currency, // The currency of the price, nullable, referencing the Currency field in ProductDto - string? UOM // The unit of measure for the product, nullable, consistent with the UOM field in ProductDto -) : IFusionCacheRefreshRequest, // Implements interface for cache refresh requests - IRequiresValidation // Implements interface for validation requirements + string SKU, + string Name, + ProductCategoryDto? Category, + string? Description, + decimal Price, + string? Currency, + string? UOM +) : IFusionCacheRefreshRequest, + IRequiresValidation { - // Optional tags for categorizing the command, useful for logging or debugging public IEnumerable? Tags => new[] { "products" }; } -// Handler class responsible for processing the CreateProductCommand and returning the result public class CreateProductCommandHandler : IRequestHandler { - private readonly IApplicationDbContext _context; // Database context for interacting with the data layer + private readonly IApplicationDbContext _context; - // Constructor to inject dependencies public CreateProductCommandHandler(IApplicationDbContext context) { _context = context; } - // Asynchronously handles the CreateProductCommand public async ValueTask Handle(CreateProductCommand request, CancellationToken cancellationToken) { - // Creates a new Product entity using the data from the command var product = new Product { - SKU = request.SKU, // Assigns SKU from command - Name = request.Name, // Assigns Name from command - Category = (ProductCategory)request.Category, // Maps Category DTO to domain entity - Description = request.Description, // Assigns Description from command - Price = request.Price, // Assigns Price from command - Currency = request.Currency, // Assigns Currency from command - UOM = request.UOM // Assigns Unit of Measure from command + SKU = request.SKU, + Name = request.Name, + Category = (ProductCategory)request.Category, + Description = request.Description, + Price = request.Price, + Currency = request.Currency, + UOM = request.UOM }; - // Adds a domain event to signal that a new product has been created product.AddDomainEvent(new ProductCreatedEvent(product)); - - // Adds the new product to the database context _context.Products.Add(product); - - // Saves changes asynchronously to the database await _context.SaveChangesAsync(cancellationToken); - // Returns a ProductDto containing essential information about the created product return new ProductDto() { Id = product.Id, Name = product.Name, SKU = product.SKU }; } } diff --git a/src/CleanAspire.Application/Features/Products/Commands/DeleteProductCommand.cs b/src/CleanAspire.Application/Features/Products/Commands/DeleteProductCommand.cs index 4fb5720..96d5211 100644 --- a/src/CleanAspire.Application/Features/Products/Commands/DeleteProductCommand.cs +++ b/src/CleanAspire.Application/Features/Products/Commands/DeleteProductCommand.cs @@ -1,51 +1,45 @@ -// This code defines a command and its handler for deleting products from a database. -// The DeleteProductCommand encapsulates product IDs, supporting cache refresh and validation. -// The DeleteProductCommandHandler processes the command, removes products, triggers domain events, and saves changes. +// Summary: +// This file defines a command and its handler for deleting products from the database. +// The DeleteProductCommand encapsulates the product IDs to be deleted, while the +// DeleteProductCommandHandler processes the command, removes the corresponding products, +// triggers domain events such as ProductDeletedEvent, and commits the changes. This ensures +// a structured and efficient approach to handling product deletions. +using CleanAspire.Application.Features.Products.EventHandlers; +using CleanAspire.Application.Pipeline; -// A record that defines the DeleteProductCommand, which encapsulates the data needed to delete products by their IDs -using CleanAspire.Application.Features.Products.EventHandlers; // Contains event handlers related to Product events -using CleanAspire.Application.Pipeline; // Contains pipeline behaviors and related interfaces - -// Namespace for organizing related classes and features namespace CleanAspire.Application.Features.Products.Commands; -public record DeleteProductCommand(params IEnumerable Ids) // Takes a list of product IDs as parameters - : IFusionCacheRefreshRequest, // Implements interface for cache refresh requests - IRequiresValidation // Implements interface for validation requirements +// Command object that encapsulates the IDs of products to be deleted. +public record DeleteProductCommand(params IEnumerable Ids) + : IFusionCacheRefreshRequest, + IRequiresValidation { - // Optional tags for categorizing the command, useful for logging or debugging public IEnumerable? Tags => new[] { "products" }; } -// Handler class responsible for processing the DeleteProductCommand public class DeleteProductCommandHandler : IRequestHandler { - private readonly IApplicationDbContext _dbContext; // Database context for interacting with the data layer + private readonly IApplicationDbContext _dbContext; - // Constructor to inject dependencies, including logger and database context public DeleteProductCommandHandler(ILogger logger, IApplicationDbContext dbContext) { _dbContext = dbContext; } - // Asynchronously handles the DeleteProductCommand public async ValueTask Handle(DeleteProductCommand request, CancellationToken cancellationToken) { - // Retrieves products from the database that match the provided IDs var products = _dbContext.Products.Where(p => request.Ids.Contains(p.Id)); - // Iterates through each product to add a deletion domain event and remove it from the database context foreach (var product in products) { - product.AddDomainEvent(new ProductDeletedEvent(product)); // Adds domain event for product deletion - _dbContext.Products.Remove(product); // Removes product from the database + product.AddDomainEvent(new ProductDeletedEvent(product)); + _dbContext.Products.Remove(product); } - // Saves changes asynchronously to the database await _dbContext.SaveChangesAsync(cancellationToken); - // Returns a Unit value to signal successful completion return Unit.Value; } } + diff --git a/src/CleanAspire.Application/Features/Products/Commands/ImportProductsCommand.cs b/src/CleanAspire.Application/Features/Products/Commands/ImportProductsCommand.cs index 6a7d8d3..4b6bb89 100644 --- a/src/CleanAspire.Application/Features/Products/Commands/ImportProductsCommand.cs +++ b/src/CleanAspire.Application/Features/Products/Commands/ImportProductsCommand.cs @@ -1,7 +1,8 @@ -// This code defines a command and its handler for importing products from a CSV file. -// The ImportProductsCommand encapsulates the input stream, supporting cache refresh and validation. -// The ImportProductsCommandHandler processes the command, reads CSV data, maps to entities, adds domain events, and saves changes. - +// Summary: +// This file defines a command and its handler for importing products from a CSV file. +// The ImportProductsCommand encapsulates the input stream, while the ImportProductsCommandHandler +// reads the CSV data, maps it to product entities, triggers domain events like ProductCreatedEvent, +// and commits the changes to the database. using System.Globalization; using CleanAspire.Application.Features.Products.DTOs; @@ -10,66 +11,51 @@ using CsvHelper; namespace CleanAspire.Application.Features.Products.Commands; -// A record that defines the ImportProductsCommand, which encapsulates the data needed to import products from a CSV file -public record ImportProductsCommand(Stream Stream // Stream containing CSV data -) : IFusionCacheRefreshRequest, // Implements interface for cache refresh requests - IRequiresValidation // Implements interface for validation requirements + +// Command object that encapsulates the input stream containing CSV data. +public record ImportProductsCommand(Stream Stream) + : IFusionCacheRefreshRequest, + IRequiresValidation { - // Optional tags for categorizing the command, useful for logging or debugging public IEnumerable? Tags => new[] { "products" }; } -// Handler class responsible for processing the ImportProductsCommand public class ImportProductsCommandHandler : IRequestHandler { - private readonly IApplicationDbContext _context; // Database context for interacting with the data layer + private readonly IApplicationDbContext _context; - // Constructor to inject dependencies public ImportProductsCommandHandler(IApplicationDbContext context) { _context = context; } - // Asynchronously handles the ImportProductsCommand public async ValueTask Handle(ImportProductsCommand request, CancellationToken cancellationToken) { - // Resets the stream position to the beginning to ensure correct reading request.Stream.Position = 0; - // Uses CsvHelper to read and parse the CSV data using (var reader = new StreamReader(request.Stream)) using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture)) { - // Maps CSV records to ProductDto objects var records = csv.GetRecords(); - // CsvHelper automatically handles parsing and mapping of CSV fields to object properties - // based on their headers and property names, ensuring accuracy and reducing boilerplate code. - - // Iterates through each ProductDto and maps it to a Product entity - foreach (var product in records.Select(x => new Product() + foreach (var product in records.Select(x => new Product { - SKU = x.SKU, // Maps SKU from ProductDto - Name = x.Name, // Maps Name from ProductDto - Category = (ProductCategory)x.Category, // Maps Category from ProductDto - Description = x.Description, // Maps Description from ProductDto - Price = x.Price, // Maps Price from ProductDto - Currency = x.Currency, // Maps Currency from ProductDto - UOM = x.UOM // Maps Unit of Measure from ProductDto + SKU = x.SKU, + Name = x.Name, + Category = (ProductCategory)x.Category, + Description = x.Description, + Price = x.Price, + Currency = x.Currency, + UOM = x.UOM })) { - // Adds a domain event for product creation product.AddDomainEvent(new ProductCreatedEvent(product)); - - // Adds the new product to the database context _context.Products.Add(product); } - // Saves changes asynchronously to the database await _context.SaveChangesAsync(cancellationToken); } - // Returns a Unit value to signal successful completion return Unit.Value; } } diff --git a/src/CleanAspire.Application/Features/Products/Commands/UpdateProductCommand.cs b/src/CleanAspire.Application/Features/Products/Commands/UpdateProductCommand.cs index ea1f9bd..e9dd499 100644 --- a/src/CleanAspire.Application/Features/Products/Commands/UpdateProductCommand.cs +++ b/src/CleanAspire.Application/Features/Products/Commands/UpdateProductCommand.cs @@ -1,73 +1,64 @@ -// This code defines a command and its handler for updating product details in the database. -// The UpdateProductCommand encapsulates the necessary data to update a product. -// The UpdateProductCommandHandler processes the command, validates existence, updates product details, triggers a domain event, and saves changes. +// Summary: +// This file defines a command and its handler for updating product details in the database. +// The UpdateProductCommand encapsulates the necessary data to update a product, while the +// UpdateProductCommandHandler validates the product's existence, updates its details, +// triggers a domain event such as ProductUpdatedEvent, and commits the changes. +using CleanAspire.Application.Features.Products.DTOs; +using CleanAspire.Application.Features.Products.EventHandlers; +using CleanAspire.Application.Pipeline; -using CleanAspire.Application.Features.Products.DTOs; // Import DTOs related to products. -using CleanAspire.Application.Features.Products.EventHandlers; // Import event handlers for product-related events. -using CleanAspire.Application.Pipeline; // Import application pipeline interfaces and behaviors. +namespace CleanAspire.Application.Features.Products.Commands; -namespace CleanAspire.Application.Features.Products.Commands; // Define the namespace for product commands. - -// A record representing the command to update a product. +// Command object that encapsulates the data required to update a product. +// Each field corresponds to a property in ProductDto. public record UpdateProductCommand( - string Id, // Product ID. corresponding to the Id field in ProductDto - string SKU, // Stock Keeping Unit, a unique identifier for the product. corresponding to the SKU field in ProductDto - string Name, // Product name. corresponding to the Name field in ProductDto - ProductCategoryDto? Category, // Optional category of the product. corresponding to the ProductCategoryDto field in ProductDto - string? Description, // Optional product description. corresponding to the Description field in ProductDto - decimal Price, // Product price. corresponding to the Price field in ProductDto - string? Currency, // Optional currency code (e.g., USD, EUR). corresponding to the Currency field in ProductDto - string? UOM // Optional Unit of Measurement (e.g., kg, pcs). corresponding to the UOM field in ProductDto -) : IFusionCacheRefreshRequest, // Interface for cache refresh requests. - IRequiresValidation // Interface indicating that the command requires validation. + string Id, + string SKU, + string Name, + ProductCategoryDto? Category, + string? Description, + decimal Price, + string? Currency, + string? UOM +) : IFusionCacheRefreshRequest, + IRequiresValidation { - // Tags associated with this command, used for caching or categorization. public IEnumerable? Tags => new[] { "products" }; } -// Handler for processing the UpdateProductCommand. public class UpdateProductCommandHandler : IRequestHandler { - private readonly IApplicationDbContext _context; // Database context for accessing product data. + private readonly IApplicationDbContext _context; - // Constructor to inject the database context. public UpdateProductCommandHandler(IApplicationDbContext context) { _context = context; } - // Asynchronously handles the update product command. public async ValueTask Handle(UpdateProductCommand request, CancellationToken cancellationToken) { - // Retrieve the product from the database using the provided ID. var product = await _context.Products.FindAsync(new object[] { request.Id }, cancellationToken); if (product == null) { - // Throw an exception if the product does not exist. throw new KeyNotFoundException($"Product with Id '{request.Id}' was not found."); } - // Update the product properties with the values from the command. product.SKU = request.SKU; product.Name = request.Name; product.Category = request.Category.HasValue - ? (ProductCategory)request.Category // Map to domain category if provided. - : ProductCategory.Electronics; // Default to "Electronics" if no category is provided. + ? (ProductCategory)request.Category + : product.Category; // Retain existing category if not provided. product.Description = request.Description; product.Price = request.Price; product.Currency = request.Currency; product.UOM = request.UOM; - // Add a domain event indicating the product has been updated. product.AddDomainEvent(new ProductUpdatedEvent(product)); - - // Mark the product entity as modified. _context.Products.Update(product); - // Save changes to the database. await _context.SaveChangesAsync(cancellationToken); - return Unit.Value; // Indicate successful completion. + return Unit.Value; } } diff --git a/src/CleanAspire.Application/Features/Products/DTOs/ProductDto.cs b/src/CleanAspire.Application/Features/Products/DTOs/ProductDto.cs index d433106..fbcf46d 100644 --- a/src/CleanAspire.Application/Features/Products/DTOs/ProductDto.cs +++ b/src/CleanAspire.Application/Features/Products/DTOs/ProductDto.cs @@ -1,31 +1,32 @@ -// This code defines a data transfer object (DTO) for products and an enumeration for product categories. +// Summary: +// This file defines a data transfer object (DTO) for products and an enumeration for product categories. // The ProductDto class encapsulates product details for data transfer between application layers. // The ProductCategoryDto enum provides predefined categories for products. -namespace CleanAspire.Application.Features.Products.DTOs; // Define the namespace for product-related DTOs. +namespace CleanAspire.Application.Features.Products.DTOs; // A DTO representing a product, used to transfer data between application layers. +// By default, field names match the corresponding entity fields. For enums or referenced entities, a Dto suffix is used. public class ProductDto { - public string Id { get; set; } = string.Empty; // Unique identifier for the product. - public string SKU { get; set; } = string.Empty; // Stock Keeping Unit, a unique code for tracking the product. - public string Name { get; set; } = string.Empty; // The name of the product. - public ProductCategoryDto? Category { get; set; } // Optional product category, using the ProductCategoryDto enum. - public string? Description { get; set; } // Optional description of the product. - public decimal Price { get; set; } // The price of the product. - public string? Currency { get; set; } // Optional currency in which the price is listed (e.g., USD, EUR). - public string? UOM { get; set; } // Optional Unit of Measurement for the product (e.g., kg, pcs). + public string Id { get; set; } = string.Empty; + public string SKU { get; set; } = string.Empty; + public string Name { get; set; } = string.Empty; + public ProductCategoryDto? Category { get; set; } + public string? Description { get; set; } + public decimal Price { get; set; } + public string? Currency { get; set; } + public string? UOM { get; set; } } // An enumeration representing possible product categories. public enum ProductCategoryDto { - Electronics, // Products like mobile phones, laptops, etc. - Furniture, // Products like tables, chairs, and sofas. - Clothing, // Apparel like shirts, pants, and jackets. - Food, // Edible products like fruits, vegetables, and snacks. - Beverages, // Drinks like coffee, tea, and juice. - HealthCare, // Products related to health and wellness. - Sports // Sports-related items like equipment and apparel. + Electronics, + Furniture, + Clothing, + Food, + Beverages, + HealthCare, + Sports } - diff --git a/src/CleanAspire.Application/Features/Products/EventHandlers/ProductCreatedEvent.cs b/src/CleanAspire.Application/Features/Products/EventHandlers/ProductCreatedEvent.cs index 1c3dc82..25fb1f0 100644 --- a/src/CleanAspire.Application/Features/Products/EventHandlers/ProductCreatedEvent.cs +++ b/src/CleanAspire.Application/Features/Products/EventHandlers/ProductCreatedEvent.cs @@ -1,8 +1,4 @@ -// Namespace: CleanAspire.Application.Features.Products.EventHandlers -// Class: ProductCreatedEvent -// Inherits from: DomainEvent, representing a base class for domain events - -namespace CleanAspire.Application.Features.Products.EventHandlers; +namespace CleanAspire.Application.Features.Products.EventHandlers; /// /// Represents an event triggered when a product is created. /// Purpose: diff --git a/src/CleanAspire.Application/Features/Products/EventHandlers/ProductDeletedEvent.cs b/src/CleanAspire.Application/Features/Products/EventHandlers/ProductDeletedEvent.cs index 43196d6..7dda1ad 100644 --- a/src/CleanAspire.Application/Features/Products/EventHandlers/ProductDeletedEvent.cs +++ b/src/CleanAspire.Application/Features/Products/EventHandlers/ProductDeletedEvent.cs @@ -1,8 +1,4 @@ -// Namespace: CleanAspire.Application.Features.Products.EventHandlers -// Class: ProductDeletedEvent -// Inherits from: DomainEvent, representing a base class for domain events - -namespace CleanAspire.Application.Features.Products.EventHandlers; +namespace CleanAspire.Application.Features.Products.EventHandlers; /// /// Represents an event triggered when a product is deleted. /// Purpose: diff --git a/src/CleanAspire.Application/Features/Products/EventHandlers/ProductUpdatedEvent.cs b/src/CleanAspire.Application/Features/Products/EventHandlers/ProductUpdatedEvent.cs index 6c5e21e..bb00d26 100644 --- a/src/CleanAspire.Application/Features/Products/EventHandlers/ProductUpdatedEvent.cs +++ b/src/CleanAspire.Application/Features/Products/EventHandlers/ProductUpdatedEvent.cs @@ -1,8 +1,4 @@ -// Namespace: CleanAspire.Application.Features.Products.EventHandlers -// Class: ProductUpdatedEvent -// Inherits from: DomainEvent, representing a base class for domain events - -namespace CleanAspire.Application.Features.Products.EventHandlers; +namespace CleanAspire.Application.Features.Products.EventHandlers; /// /// Represents an event triggered when a product is updated. /// Purpose: diff --git a/src/CleanAspire.Application/Features/Products/Queries/GetAllProductsQuery.cs b/src/CleanAspire.Application/Features/Products/Queries/GetAllProductsQuery.cs index 8d42f63..bb9cae5 100644 --- a/src/CleanAspire.Application/Features/Products/Queries/GetAllProductsQuery.cs +++ b/src/CleanAspire.Application/Features/Products/Queries/GetAllProductsQuery.cs @@ -1,8 +1,4 @@ -// Using CleanAspire.Application.Features.Products.DTOs for data transfer objects -// Namespace: CleanAspire.Application.Features.Products.Queries -// Defines a query and its handler to retrieve all products from the database - -using CleanAspire.Application.Features.Products.DTOs; +using CleanAspire.Application.Features.Products.DTOs; namespace CleanAspire.Application.Features.Products.Queries; /// diff --git a/src/CleanAspire.Application/Features/Products/Queries/GetProductByIdQuery.cs b/src/CleanAspire.Application/Features/Products/Queries/GetProductByIdQuery.cs index a70dedc..5cd3532 100644 --- a/src/CleanAspire.Application/Features/Products/Queries/GetProductByIdQuery.cs +++ b/src/CleanAspire.Application/Features/Products/Queries/GetProductByIdQuery.cs @@ -1,6 +1,4 @@ -// Defines a query to retrieve a product by its ID and the corresponding handler to process the query. - -using CleanAspire.Application.Features.Products.DTOs; +using CleanAspire.Application.Features.Products.DTOs; namespace CleanAspire.Application.Features.Products.Queries; /// diff --git a/src/CleanAspire.Application/Features/Products/Queries/ProductsWithPaginationQuery.cs b/src/CleanAspire.Application/Features/Products/Queries/ProductsWithPaginationQuery.cs index a8c753f..8fd414b 100644 --- a/src/CleanAspire.Application/Features/Products/Queries/ProductsWithPaginationQuery.cs +++ b/src/CleanAspire.Application/Features/Products/Queries/ProductsWithPaginationQuery.cs @@ -1,6 +1,4 @@ -// Defines a query to retrieve products with pagination and the corresponding handler to process the query. - -using CleanAspire.Application.Features.Products.DTOs; +using CleanAspire.Application.Features.Products.DTOs; namespace CleanAspire.Application.Features.Products.Queries; /// diff --git a/src/CleanAspire.Application/Features/Products/Validators/CreateProductCommandValidator.cs b/src/CleanAspire.Application/Features/Products/Validators/CreateProductCommandValidator.cs index c17c044..f5a4038 100644 --- a/src/CleanAspire.Application/Features/Products/Validators/CreateProductCommandValidator.cs +++ b/src/CleanAspire.Application/Features/Products/Validators/CreateProductCommandValidator.cs @@ -1,6 +1,4 @@ -// Defines a validator for the CreateProductCommand, ensuring data integrity and correctness of input values. - -using CleanAspire.Application.Features.Products.Commands; +using CleanAspire.Application.Features.Products.Commands; namespace CleanAspire.Application.Features.Products.Validators; /// /// Validator for CreateProductCommand. diff --git a/src/CleanAspire.Application/Features/Products/Validators/DeleteProductCommandValidator.cs b/src/CleanAspire.Application/Features/Products/Validators/DeleteProductCommandValidator.cs index 6857a6d..a73aa18 100644 --- a/src/CleanAspire.Application/Features/Products/Validators/DeleteProductCommandValidator.cs +++ b/src/CleanAspire.Application/Features/Products/Validators/DeleteProductCommandValidator.cs @@ -1,6 +1,4 @@ -// Defines a validator for the DeleteProductCommand, ensuring the validity of product IDs for deletion. - -using CleanAspire.Application.Features.Products.Commands; +using CleanAspire.Application.Features.Products.Commands; namespace CleanAspire.Application.Features.Products.Validators; /// diff --git a/src/CleanAspire.Application/Features/Products/Validators/UpdateProductCommandValidator.cs b/src/CleanAspire.Application/Features/Products/Validators/UpdateProductCommandValidator.cs index 580a494..a836359 100644 --- a/src/CleanAspire.Application/Features/Products/Validators/UpdateProductCommandValidator.cs +++ b/src/CleanAspire.Application/Features/Products/Validators/UpdateProductCommandValidator.cs @@ -1,7 +1,4 @@ -// Defines a validator for the UpdateProductCommand, ensuring data integrity and correctness of input values. - - -using CleanAspire.Application.Features.Products.Commands; +using CleanAspire.Application.Features.Products.Commands; namespace CleanAspire.Application.Features.Products.Validators; /// diff --git a/src/CleanAspire.ClientApp/CleanAspire.ClientApp.csproj b/src/CleanAspire.ClientApp/CleanAspire.ClientApp.csproj index ea5182f..5a4202f 100644 --- a/src/CleanAspire.ClientApp/CleanAspire.ClientApp.csproj +++ b/src/CleanAspire.ClientApp/CleanAspire.ClientApp.csproj @@ -15,16 +15,16 @@ - - + + - - - - - + + + + + diff --git a/src/CleanAspire.ClientApp/Services/ApiClientServiceProxy.cs b/src/CleanAspire.ClientApp/Services/ApiClientServiceProxy.cs index eeff30d..1fc6770 100644 --- a/src/CleanAspire.ClientApp/Services/ApiClientServiceProxy.cs +++ b/src/CleanAspire.ClientApp/Services/ApiClientServiceProxy.cs @@ -1,19 +1,4 @@ -// This class provides a service proxy for interacting with an API, incorporating caching and error handling mechanisms. -// It uses IndexedDb for local caching and integrates with a logging system to handle API-related errors effectively. - -// Purpose: -// 1. **Caching API Responses**: -// - `QueryAsync` method retrieves data from the cache or fetches it via the provided factory function and stores it with optional tags and expiration. -// - `ClearCache` method removes cached data associated with specific tags. - -// 2. **Error-Handled API Calls**: -// - `ExecuteAsync` method wraps API calls with robust error handling, returning either the response, validation issues, or generic problem details. -// - Handles specific exceptions (e.g., `HttpValidationProblemDetails`, `ProblemDetails`, and `ApiException`) and logs errors for easier debugging. - -// 3. **Seamless API Integration**: -// - Simplifies calling APIs and managing cached data, improving performance and resilience of the application. - -using CleanAspire.Api.Client.Models; +using CleanAspire.Api.Client.Models; using CleanAspire.ClientApp.Services.JsInterop; using Microsoft.Kiota.Abstractions; using OneOf; diff --git a/src/CleanAspire.ClientApp/Services/DialogServiceHelper.cs b/src/CleanAspire.ClientApp/Services/DialogServiceHelper.cs index 8c03462..c0f61ff 100644 --- a/src/CleanAspire.ClientApp/Services/DialogServiceHelper.cs +++ b/src/CleanAspire.ClientApp/Services/DialogServiceHelper.cs @@ -10,9 +10,6 @@ // - Enhances user experience by offering configurable dialogs for confirmations or custom content components. // - Supports optional callback actions for both confirmation and cancellation scenarios. -// Key Features: -// - `ShowConfirmationDialog`: Displays a pre-defined confirmation dialog with title, content, and confirm/cancel actions. -// - `ShowDialogAsync`: Shows a customizable dialog based on the generic component `T`, allowing flexible input/output through parameters and actions. using CleanAspire.ClientApp.Components; using Microsoft.AspNetCore.Components; diff --git a/src/CleanAspire.ClientApp/Services/JsInterop/IndexedDbCache.cs b/src/CleanAspire.ClientApp/Services/JsInterop/IndexedDbCache.cs index 1927230..9d0fa40 100644 --- a/src/CleanAspire.ClientApp/Services/JsInterop/IndexedDbCache.cs +++ b/src/CleanAspire.ClientApp/Services/JsInterop/IndexedDbCache.cs @@ -1,29 +1,53 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Text.Json; +using System.Text.Json; using Microsoft.JSInterop; namespace CleanAspire.ClientApp.Services.JsInterop; +/// +/// Provides methods to interact with IndexedDB for caching purposes. +/// public sealed class IndexedDbCache { + /// + /// The name of the IndexedDB database. + /// public const string DATABASENAME = "CleanAspire.IndexedDB"; private readonly IJSRuntime _jsRuntime; + /// + /// Initializes a new instance of the class. + /// + /// The JavaScript runtime. public IndexedDbCache(IJSRuntime jsRuntime) { _jsRuntime = jsRuntime; } - // Save data to IndexedDB with optional tags + /// + /// Saves data to IndexedDB with optional tags and expiration. + /// + /// The type of the data to save. + /// The name of the database. + /// The key to identify the data. + /// The data to save. + /// Optional tags to associate with the data. + /// Optional expiration time for the data. public async Task SaveDataAsync(string dbName, string key, T value, string[]? tags = null, TimeSpan? expiration = null) { var expirationMs = expiration.HasValue ? (int)expiration.Value.TotalMilliseconds : (int?)null; await _jsRuntime.InvokeVoidAsync("indexedDbStorage.saveData", dbName, key, value, tags ?? Array.Empty(), expirationMs); } - // Get or set data in IndexedDB + + /// + /// Gets data from IndexedDB or sets it if it does not exist. + /// + /// The type of the data. + /// The name of the database. + /// The key to identify the data. + /// The factory function to create the data if it does not exist. + /// Optional tags to associate with the data. + /// Optional expiration time for the data. + /// The data from the cache or the newly created data. public async Task GetOrSetAsync(string dbName, string key, Func> factory, string[]? tags = null, TimeSpan? expiration = null) { var existingData = await GetDataAsync(dbName, key); @@ -37,43 +61,63 @@ public async Task GetOrSetAsync(string dbName, string key, Func> f return newData; } - // Get data from IndexedDB by key + /// + /// Gets data from IndexedDB by key. + /// + /// The type of the data. + /// The name of the database. + /// The key to identify the data. + /// The data from the cache. public async Task GetDataAsync(string dbName, string key) { return await _jsRuntime.InvokeAsync("indexedDbStorage.getData", dbName, key); } - // Get all data by tags (supports array of tags) + /// + /// Gets all data from IndexedDB by tags. + /// + /// The type of the data. + /// The name of the database. + /// The tags to filter the data. + /// A dictionary of key-value pairs of the data. public async Task> GetDataByTagsAsync(string dbName, string[] tags) { - // Call the JavaScript function and retrieve a list of { key, value } - var results = await _jsRuntime.InvokeAsync>>( - "indexedDbStorage.getDataByTags", dbName, tags); + var results = await _jsRuntime.InvokeAsync>>("indexedDbStorage.getDataByTags", dbName, tags); - // Convert the results to a dictionary return results.ToDictionary( - result => result["key"].ToString(), // Extract the key as a string + result => result["key"].ToString(), result => { - // Handle deserialization of 'value' var jsonElement = result["value"]; return JsonSerializer.Deserialize(jsonElement.ToString(), new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); } ); } - // Delete specific data by key + + /// + /// Deletes specific data from IndexedDB by key. + /// + /// The name of the database. + /// The key to identify the data. public async Task DeleteDataAsync(string dbName, string key) { await _jsRuntime.InvokeVoidAsync("indexedDbStorage.deleteData", dbName, key); } - // Delete all data by tags (supports array of tags) + /// + /// Deletes all data from IndexedDB by tags. + /// + /// The name of the database. + /// The tags to filter the data. public async Task DeleteDataByTagsAsync(string dbName, string[] tags) { await _jsRuntime.InvokeVoidAsync("indexedDbStorage.deleteDataByTags", dbName, tags); } - // Clear all data from IndexedDB store + /// + /// Clears all data from the IndexedDB store. + /// + /// The name of the database. public async Task ClearDataAsync(string dbName) { await _jsRuntime.InvokeVoidAsync("indexedDbStorage.clearData", dbName); diff --git a/src/CleanAspire.ClientApp/Services/Products/ProductCacheService.cs b/src/CleanAspire.ClientApp/Services/Products/ProductCacheService.cs index 32c47bd..800a1e8 100644 --- a/src/CleanAspire.ClientApp/Services/Products/ProductCacheService.cs +++ b/src/CleanAspire.ClientApp/Services/Products/ProductCacheService.cs @@ -12,17 +12,6 @@ // 3. **Data Integrity**: // - Updates cached data to reflect deletions and changes, maintaining consistency between cache and server state. -// 4. **Key Features**: -// - `SaveOrUpdateProductAsync`: Saves or updates a product in the cache. -// - `GetProductAsync`: Retrieves a product from the cache. -// - `SaveOrUpdatePaginatedProductsAsync`: Caches paginated product data. -// - `UpdateDeletedProductsAsync`: Updates cached data after deleting products. -// - `StoreOfflineCreateCommandAsync`, `StoreOfflineUpdateCommandAsync`, `StoreOfflineDeleteCommandAsync`: Caches offline commands for later synchronization. -// - `GetAllPendingCommandsAsync`: Retrieves all pending offline commands. -// - `ClearCommands`: Clears all cached offline commands. -// - `GenerateProductCacheKey`, `GeneratePaginationCacheKey`: Generates unique keys for caching products and paginated data. - - using CleanAspire.Api.Client.Models; using CleanAspire.ClientApp.Services.JsInterop; diff --git a/src/CleanAspire.ClientApp/Services/Products/ProductServiceProxy.cs b/src/CleanAspire.ClientApp/Services/Products/ProductServiceProxy.cs index 2573aff..9d85527 100644 --- a/src/CleanAspire.ClientApp/Services/Products/ProductServiceProxy.cs +++ b/src/CleanAspire.ClientApp/Services/Products/ProductServiceProxy.cs @@ -14,20 +14,6 @@ // - Wraps API calls with robust error handling to manage exceptions like `HttpValidationProblemDetails`, `ProblemDetails`, and general exceptions. // - Logs errors to facilitate debugging and provides detailed error responses when necessary. -// 4. **Key Features**: -// - `GetPaginatedProductsAsync`: Retrieves paginated product data, using cache if offline. -// - `GetProductByIdAsync`: Fetches a product by ID, falling back to cached data in offline mode. -// - `CreateProductAsync`: Creates a new product, supporting both online and offline scenarios. -// - `UpdateProductAsync`: Updates an existing product, with offline mode support. -// - `DeleteProductsAsync`: Deletes products, queuing commands for later synchronization if offline. -// - `SyncOfflineCachedDataAsync`: Synchronizes offline cached commands (create, update, delete) with the server upon reconnecting to the internet. - -// 5. **Integration**: -// - `NavigationManager`: Generates product-related URLs. -// - `IWebpushrService`: Sends notifications for important events (e.g., new product launch). -// - `OfflineSyncService`: Tracks and manages synchronization status. - - using CleanAspire.Api.Client; using CleanAspire.Api.Client.Models; using CleanAspire.ClientApp.Services.JsInterop; diff --git a/src/CleanAspire.ClientApp/wwwroot/appsettings.json b/src/CleanAspire.ClientApp/wwwroot/appsettings.json index c3f998d..070f137 100644 --- a/src/CleanAspire.ClientApp/wwwroot/appsettings.json +++ b/src/CleanAspire.ClientApp/wwwroot/appsettings.json @@ -7,7 +7,7 @@ }, "ClientAppSettings": { "AppName": "Blazor Aspire", - "Version": "v0.0.64", + "Version": "v0.0.66", "ServiceBaseUrl": "https://apiservice.blazorserver.com" } } diff --git a/src/CleanAspire.ServiceDefaults/CleanAspire.ServiceDefaults.csproj b/src/CleanAspire.ServiceDefaults/CleanAspire.ServiceDefaults.csproj index bcd3e63..843b553 100644 --- a/src/CleanAspire.ServiceDefaults/CleanAspire.ServiceDefaults.csproj +++ b/src/CleanAspire.ServiceDefaults/CleanAspire.ServiceDefaults.csproj @@ -14,8 +14,8 @@ - - + + diff --git a/src/CleanAspire.WebApp/appsettings.json b/src/CleanAspire.WebApp/appsettings.json index e6e9c4c..a9275b5 100644 --- a/src/CleanAspire.WebApp/appsettings.json +++ b/src/CleanAspire.WebApp/appsettings.json @@ -8,7 +8,7 @@ "AllowedHosts": "*", "ClientAppSettings": { "AppName": "Blazor Aspire", - "Version": "v0.0.64", + "Version": "v0.0.66", "ServiceBaseUrl": "https://apiservice.blazorserver.com" } } diff --git a/tests/CleanAspire.Tests/CleanAspire.Tests.csproj b/tests/CleanAspire.Tests/CleanAspire.Tests.csproj index d4deea9..e917fde 100644 --- a/tests/CleanAspire.Tests/CleanAspire.Tests.csproj +++ b/tests/CleanAspire.Tests/CleanAspire.Tests.csproj @@ -13,11 +13,11 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - +