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>
373 lines
9.8 KiB
Markdown
373 lines
9.8 KiB
Markdown
# LittleShop Client SDK
|
|
|
|
A .NET client library for interacting with the LittleShop e-commerce API. This SDK provides a strongly-typed, easy-to-use interface for all LittleShop API endpoints with built-in authentication, retry policies, and error handling.
|
|
|
|
## Installation
|
|
|
|
Add the LittleShop.Client library to your project:
|
|
|
|
```bash
|
|
dotnet add reference ../LittleShop.Client/LittleShop.Client.csproj
|
|
```
|
|
|
|
Or via NuGet (when published):
|
|
```bash
|
|
dotnet add package LittleShop.Client
|
|
```
|
|
|
|
## Quick Start
|
|
|
|
### Basic Setup with Dependency Injection
|
|
|
|
```csharp
|
|
using LittleShop.Client.Extensions;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Microsoft.Extensions.Hosting;
|
|
|
|
var host = Host.CreateDefaultBuilder(args)
|
|
.ConfigureServices((context, services) =>
|
|
{
|
|
// Add LittleShop client with configuration
|
|
services.AddLittleShopClient(options =>
|
|
{
|
|
options.BaseUrl = "https://localhost:5001";
|
|
options.TimeoutSeconds = 30;
|
|
options.MaxRetryAttempts = 3;
|
|
options.EnableLogging = true;
|
|
});
|
|
})
|
|
.Build();
|
|
|
|
// Get the client from DI
|
|
var client = host.Services.GetRequiredService<ILittleShopClient>();
|
|
```
|
|
|
|
### Simple Console Application Example
|
|
|
|
```csharp
|
|
using LittleShop.Client;
|
|
using LittleShop.Client.Extensions;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
// Setup DI container
|
|
var services = new ServiceCollection();
|
|
services.AddLogging(builder => builder.AddConsole());
|
|
services.AddLittleShopClient("https://localhost:5001");
|
|
|
|
var serviceProvider = services.BuildServiceProvider();
|
|
var client = serviceProvider.GetRequiredService<ILittleShopClient>();
|
|
|
|
// Authenticate
|
|
var loginResult = await client.Authentication.LoginAsync("admin", "admin");
|
|
if (loginResult.IsSuccess)
|
|
{
|
|
Console.WriteLine($"Logged in as: {loginResult.Data.Username}");
|
|
Console.WriteLine($"Token expires: {loginResult.Data.ExpiresAt}");
|
|
}
|
|
|
|
// Get categories
|
|
var categoriesResult = await client.Catalog.GetCategoriesAsync();
|
|
if (categoriesResult.IsSuccess)
|
|
{
|
|
foreach (var category in categoriesResult.Data)
|
|
{
|
|
Console.WriteLine($"Category: {category.Name} - {category.Description}");
|
|
}
|
|
}
|
|
|
|
// Get products with filtering
|
|
var productsResult = await client.Catalog.GetProductsAsync(
|
|
pageNumber: 1,
|
|
pageSize: 10,
|
|
searchTerm: "wireless",
|
|
minPrice: 10,
|
|
maxPrice: 100
|
|
);
|
|
|
|
if (productsResult.IsSuccess)
|
|
{
|
|
Console.WriteLine($"Found {productsResult.Data.TotalCount} products");
|
|
foreach (var product in productsResult.Data.Items)
|
|
{
|
|
Console.WriteLine($"Product: {product.Name} - £{product.Price}");
|
|
}
|
|
}
|
|
```
|
|
|
|
## Authentication
|
|
|
|
The SDK handles JWT authentication automatically. Once you log in, the token is stored and automatically included in subsequent requests.
|
|
|
|
```csharp
|
|
// Login
|
|
var loginResult = await client.Authentication.LoginAsync("username", "password");
|
|
if (!loginResult.IsSuccess)
|
|
{
|
|
Console.WriteLine($"Login failed: {loginResult.ErrorMessage}");
|
|
return;
|
|
}
|
|
|
|
// Check if authenticated
|
|
if (client.Authentication.IsAuthenticated())
|
|
{
|
|
Console.WriteLine("User is authenticated");
|
|
}
|
|
|
|
// Get current token (if needed for external use)
|
|
var token = client.Authentication.GetToken();
|
|
|
|
// Logout
|
|
client.Authentication.Logout();
|
|
```
|
|
|
|
## Catalog Operations
|
|
|
|
### Get All Categories
|
|
```csharp
|
|
var result = await client.Catalog.GetCategoriesAsync();
|
|
if (result.IsSuccess)
|
|
{
|
|
foreach (var category in result.Data)
|
|
{
|
|
Console.WriteLine($"{category.Name}: {category.Description}");
|
|
}
|
|
}
|
|
```
|
|
|
|
### Get Products with Pagination and Filtering
|
|
```csharp
|
|
var result = await client.Catalog.GetProductsAsync(
|
|
pageNumber: 1,
|
|
pageSize: 20,
|
|
categoryId: categoryGuid, // Optional: Filter by category
|
|
searchTerm: "headphones", // Optional: Search term
|
|
minPrice: 50, // Optional: Minimum price
|
|
maxPrice: 200 // Optional: Maximum price
|
|
);
|
|
|
|
if (result.IsSuccess)
|
|
{
|
|
Console.WriteLine($"Page {result.Data.PageNumber} of {result.Data.TotalPages}");
|
|
Console.WriteLine($"Total products: {result.Data.TotalCount}");
|
|
|
|
foreach (var product in result.Data.Items)
|
|
{
|
|
Console.WriteLine($"{product.Name} - £{product.Price}");
|
|
if (product.Photos.Any())
|
|
{
|
|
Console.WriteLine($" Photo: {product.Photos.First().Url}");
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Get Single Product
|
|
```csharp
|
|
var result = await client.Catalog.GetProductByIdAsync(productId);
|
|
if (result.IsSuccess)
|
|
{
|
|
var product = result.Data;
|
|
Console.WriteLine($"Name: {product.Name}");
|
|
Console.WriteLine($"Price: £{product.Price}");
|
|
Console.WriteLine($"Weight: {product.Weight} {product.WeightUnit}");
|
|
}
|
|
```
|
|
|
|
## Order Operations
|
|
|
|
### Create Order
|
|
```csharp
|
|
var orderRequest = new CreateOrderRequest
|
|
{
|
|
IdentityReference = "CUST123",
|
|
ShippingName = "John Smith",
|
|
ShippingAddress = "123 Main Street",
|
|
ShippingCity = "London",
|
|
ShippingPostCode = "SW1A 1AA",
|
|
ShippingCountry = "United Kingdom",
|
|
Notes = "Please deliver to reception",
|
|
Items = new List<CreateOrderItem>
|
|
{
|
|
new CreateOrderItem
|
|
{
|
|
ProductId = productId1,
|
|
Quantity = 2
|
|
},
|
|
new CreateOrderItem
|
|
{
|
|
ProductId = productId2,
|
|
Quantity = 1
|
|
}
|
|
}
|
|
};
|
|
|
|
var result = await client.Orders.CreateOrderAsync(orderRequest);
|
|
if (result.IsSuccess)
|
|
{
|
|
Console.WriteLine($"Order created: {result.Data.Id}");
|
|
Console.WriteLine($"Total: £{result.Data.TotalAmount}");
|
|
}
|
|
```
|
|
|
|
### Get Orders by Customer Identity
|
|
```csharp
|
|
var result = await client.Orders.GetOrdersByIdentityAsync("CUST123");
|
|
if (result.IsSuccess)
|
|
{
|
|
foreach (var order in result.Data)
|
|
{
|
|
Console.WriteLine($"Order {order.Id}: {order.Status} - £{order.TotalAmount}");
|
|
Console.WriteLine($" Created: {order.CreatedAt}");
|
|
if (!string.IsNullOrEmpty(order.TrackingNumber))
|
|
{
|
|
Console.WriteLine($" Tracking: {order.TrackingNumber}");
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Create Cryptocurrency Payment
|
|
```csharp
|
|
var result = await client.Orders.CreatePaymentAsync(orderId, "BTC");
|
|
if (result.IsSuccess)
|
|
{
|
|
var payment = result.Data;
|
|
Console.WriteLine($"Payment ID: {payment.Id}");
|
|
Console.WriteLine($"Send {payment.RequiredAmount} {payment.Currency} to:");
|
|
Console.WriteLine($"Address: {payment.WalletAddress}");
|
|
Console.WriteLine($"Expires: {payment.ExpiresAt}");
|
|
|
|
if (!string.IsNullOrEmpty(payment.BTCPayCheckoutUrl))
|
|
{
|
|
Console.WriteLine($"Or pay via: {payment.BTCPayCheckoutUrl}");
|
|
}
|
|
}
|
|
```
|
|
|
|
## Error Handling
|
|
|
|
All API methods return an `ApiResponse<T>` wrapper that includes success status, data, and error information:
|
|
|
|
```csharp
|
|
var result = await client.Catalog.GetProductByIdAsync(productId);
|
|
|
|
if (result.IsSuccess)
|
|
{
|
|
// Success - access data
|
|
var product = result.Data;
|
|
Console.WriteLine($"Product: {product.Name}");
|
|
}
|
|
else
|
|
{
|
|
// Error - handle appropriately
|
|
Console.WriteLine($"Error: {result.ErrorMessage}");
|
|
Console.WriteLine($"Status Code: {result.StatusCode}");
|
|
|
|
switch (result.StatusCode)
|
|
{
|
|
case HttpStatusCode.NotFound:
|
|
Console.WriteLine("Product not found");
|
|
break;
|
|
case HttpStatusCode.Unauthorized:
|
|
Console.WriteLine("Please login first");
|
|
break;
|
|
case HttpStatusCode.BadRequest:
|
|
Console.WriteLine("Invalid request");
|
|
break;
|
|
default:
|
|
Console.WriteLine("An error occurred");
|
|
break;
|
|
}
|
|
}
|
|
```
|
|
|
|
## Advanced Configuration
|
|
|
|
### Custom HTTP Client Configuration
|
|
|
|
The SDK uses Polly for retry policies and includes automatic retry for transient failures:
|
|
|
|
```csharp
|
|
services.AddLittleShopClient(options =>
|
|
{
|
|
options.BaseUrl = "https://api.littleshop.com";
|
|
options.TimeoutSeconds = 60; // Request timeout
|
|
options.MaxRetryAttempts = 5; // Number of retries for transient failures
|
|
options.EnableLogging = true; // Enable detailed logging
|
|
});
|
|
```
|
|
|
|
### Using in ASP.NET Core Web Application
|
|
|
|
```csharp
|
|
// In Program.cs or Startup.cs
|
|
builder.Services.AddLittleShopClient(options =>
|
|
{
|
|
options.BaseUrl = builder.Configuration["LittleShop:ApiUrl"];
|
|
options.TimeoutSeconds = 30;
|
|
});
|
|
|
|
// In a controller or service
|
|
public class ProductController : Controller
|
|
{
|
|
private readonly ILittleShopClient _client;
|
|
|
|
public ProductController(ILittleShopClient client)
|
|
{
|
|
_client = client;
|
|
}
|
|
|
|
public async Task<IActionResult> Index()
|
|
{
|
|
var result = await _client.Catalog.GetProductsAsync();
|
|
if (result.IsSuccess)
|
|
{
|
|
return View(result.Data.Items);
|
|
}
|
|
|
|
return View("Error", result.ErrorMessage);
|
|
}
|
|
}
|
|
```
|
|
|
|
## Features
|
|
|
|
- ✅ **Strongly Typed Models** - All API responses are mapped to C# classes
|
|
- ✅ **Automatic Authentication** - JWT tokens are managed automatically
|
|
- ✅ **Retry Policies** - Automatic retry for transient failures
|
|
- ✅ **Error Handling** - Consistent error responses with detailed information
|
|
- ✅ **Logging Support** - Built-in logging for debugging
|
|
- ✅ **Dependency Injection** - Full DI support for ASP.NET Core applications
|
|
- ✅ **Async/Await** - All methods are async for better performance
|
|
- ✅ **Pagination Support** - Built-in pagination for product listings
|
|
- ✅ **Filtering & Search** - Advanced filtering options for products
|
|
|
|
## API Coverage
|
|
|
|
- **Authentication**
|
|
- Login
|
|
- Token refresh
|
|
- Logout
|
|
|
|
- **Catalog**
|
|
- Get all categories
|
|
- Get category by ID
|
|
- Get products (with pagination and filtering)
|
|
- Get product by ID
|
|
|
|
- **Orders**
|
|
- Create order
|
|
- Get orders by customer identity
|
|
- Get order by ID
|
|
- Create cryptocurrency payment
|
|
- Get order payments
|
|
|
|
## Requirements
|
|
|
|
- .NET 9.0 or later
|
|
- LittleShop API server running and accessible
|
|
|
|
## License
|
|
|
|
This SDK is part of the LittleShop e-commerce platform. |