Add LittleShop.Client SDK library with complete API wrapper

Features:
- Complete .NET client SDK for LittleShop API
- JWT authentication with automatic token management
- Catalog service for products and categories
- Order service with payment creation
- Retry policies using Polly for resilience
- Error handling middleware
- Dependency injection support
- Comprehensive documentation and examples

SDK Components:
- Authentication service with token refresh
- Strongly-typed models for all API responses
- HTTP handlers for retry and error handling
- Extension methods for easy DI registration
- Example console application demonstrating usage

Test Updates:
- Fixed test compilation errors
- Updated test data builders for new models
- Corrected service constructor dependencies
- Fixed enum value changes (PaymentStatus, OrderStatus)

Documentation:
- Complete project README with features and usage
- Client SDK README with detailed examples
- API endpoint documentation
- Security considerations
- Deployment guidelines

Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
sysadmin
2025-08-20 18:15:35 +01:00
parent a281bb2896
commit 1f7c0af497
28 changed files with 1810 additions and 33 deletions

View File

@@ -289,7 +289,7 @@ public class OrdersControllerTests : IClassFixture<TestWebApplicationFactory>
var webhookDto = new PaymentWebhookDto
{
InvoiceId = "INV123456",
Status = PaymentStatus.Confirmed,
Status = PaymentStatus.Paid,
Amount = 100.00m,
TransactionHash = "tx123456789"
};
@@ -407,7 +407,7 @@ public class OrdersControllerTests : IClassFixture<TestWebApplicationFactory>
{
Id = Guid.NewGuid(),
IdentityReference = identityReference,
Status = OrderStatus.Pending,
Status = OrderStatus.PendingPayment,
ShippingName = "Test Customer",
ShippingAddress = "Test Address",
ShippingCity = "Test City",

View File

@@ -78,12 +78,9 @@ public static class TestDataBuilder
{
Id = Guid.NewGuid(),
Username = user,
Email = $"{user}@test.com",
PasswordHash = "hashed-password",
Role = role,
IsActive = true,
CreatedAt = DateTime.UtcNow,
UpdatedAt = DateTime.UtcNow
CreatedAt = DateTime.UtcNow
};
}
@@ -94,12 +91,11 @@ public static class TestDataBuilder
Id = Guid.NewGuid(),
OrderId = orderId,
Currency = currency,
Amount = 0.0025m,
CryptoAddress = $"bc1q{Guid.NewGuid().ToString().Replace("-", "").Substring(0, 39)}",
RequiredAmount = 0.0025m,
WalletAddress = $"bc1q{Guid.NewGuid().ToString().Replace("-", "").Substring(0, 39)}",
Status = status,
ExchangeRate = 40000.00m,
CreatedAt = DateTime.UtcNow,
UpdatedAt = DateTime.UtcNow
ExpiresAt = DateTime.UtcNow.AddHours(1)
};
}
@@ -161,15 +157,17 @@ public static class TestDataBuilder
};
}
public static ProductPhoto CreateProductPhoto(Guid productId, int displayOrder = 1)
public static ProductPhoto CreateProductPhoto(Guid productId, int sortOrder = 1)
{
var fileName = $"{Guid.NewGuid()}.jpg";
return new ProductPhoto
{
Id = Guid.NewGuid(),
ProductId = productId,
PhotoUrl = $"/uploads/products/{Guid.NewGuid()}.jpg",
FileName = fileName,
FilePath = $"/uploads/products/{fileName}",
AltText = "Test product photo",
DisplayOrder = displayOrder,
SortOrder = sortOrder,
CreatedAt = DateTime.UtcNow
};
}

View File

@@ -15,7 +15,6 @@ public class CategoryServiceTests : IDisposable
{
private readonly LittleShopContext _context;
private readonly ICategoryService _categoryService;
private readonly IMapper _mapper;
public CategoryServiceTests()
{
@@ -25,15 +24,8 @@ public class CategoryServiceTests : IDisposable
.Options;
_context = new LittleShopContext(options);
// Set up AutoMapper
var mappingConfig = new MapperConfiguration(mc =>
{
mc.AddProfile(new MappingProfile());
});
_mapper = mappingConfig.CreateMapper();
// Create service
_categoryService = new CategoryService(_context, _mapper);
_categoryService = new CategoryService(_context);
}
[Fact]

View File

@@ -5,9 +5,9 @@ using LittleShop.Models;
using LittleShop.Services;
using LittleShop.Enums;
using Microsoft.EntityFrameworkCore;
using AutoMapper;
using Microsoft.AspNetCore.Hosting;
using Moq;
using Xunit;
using LittleShop.Mapping;
namespace LittleShop.Tests.Unit;
@@ -15,7 +15,7 @@ public class ProductServiceTests : IDisposable
{
private readonly LittleShopContext _context;
private readonly IProductService _productService;
private readonly IMapper _mapper;
private readonly Mock<IWebHostEnvironment> _mockEnvironment;
public ProductServiceTests()
{
@@ -25,15 +25,12 @@ public class ProductServiceTests : IDisposable
.Options;
_context = new LittleShopContext(options);
// Set up AutoMapper
var mappingConfig = new MapperConfiguration(mc =>
{
mc.AddProfile(new MappingProfile());
});
_mapper = mappingConfig.CreateMapper();
// Set up mock environment
_mockEnvironment = new Mock<IWebHostEnvironment>();
_mockEnvironment.Setup(e => e.WebRootPath).Returns("/test/wwwroot");
// Create service
_productService = new ProductService(_context, _mapper);
_productService = new ProductService(_context, _mockEnvironment.Object);
}
[Fact]
@@ -242,7 +239,7 @@ public class ProductServiceTests : IDisposable
// Assert
result.Should().NotBeNull();
result.PhotoUrl.Should().Be("/uploads/test-photo.jpg");
result.FilePath.Should().Contain("test-photo.jpg");
result.AltText.Should().Be("Test Photo");
// Verify in database