Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
237 changes: 237 additions & 0 deletions mydocs/BasketService-documentation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
# BasketService Documentation

## Overview

`BasketService` is a gRPC service implementation that manages shopping basket operations in the eShop application. It inherits from `Basket.BasketBase` and provides three core operations: retrieving, updating, and deleting user baskets.

## Location

**File Path:** `/src/Basket.API/Grpc/BasketService.cs`

**Namespace:** `eShop.Basket.API.Grpc`

## Dependencies

The service relies on the following dependencies injected via constructor:

- **`IBasketRepository`**: Repository for basket data persistence operations
- **`ILogger<BasketService>`**: Logger for diagnostic and debugging information

## Class Structure

```csharp
public class BasketService(
IBasketRepository repository,
ILogger<BasketService> logger) : Basket.BasketBase
```

## Public Methods

### 1. GetBasket

**Signature:**
```csharp
[AllowAnonymous]
public override async Task<CustomerBasketResponse> GetBasket(
GetBasketRequest request,
ServerCallContext context)
```

**Purpose:** Retrieves a customer's basket based on their user identity.

**Authentication:** Allows anonymous access (though returns empty basket if user is not authenticated).

**Behavior:**
- Extracts user identity from the gRPC server call context
- Returns empty basket if user identity is not available
- Logs debug information when debug logging is enabled
- Fetches basket data from repository using user ID
- Maps repository data to `CustomerBasketResponse` format
- Returns empty response if no basket exists

**Returns:** `CustomerBasketResponse` containing basket items or empty response

---

### 2. UpdateBasket

**Signature:**
```csharp
public override async Task<UpdateBasketResponse> UpdateBasket(
UpdateBasketRequest request,
ServerCallContext context)
```

**Purpose:** Updates or creates a customer's basket with new items.

**Authentication:** Requires authenticated user (throws exception if not authenticated).

**Behavior:**
- Extracts and validates user identity from context
- Throws `RpcException` with `Unauthenticated` status if user is not authenticated
- Logs debug information about the update operation
- Converts request data to `CustomerBasket` model
- Updates basket in repository
- Throws `RpcException` with `NotFound` status if update fails
- Maps updated basket to response format

**Returns:** `CustomerBasketResponse` containing updated basket items

**Exceptions:**
- `RpcException(StatusCode.Unauthenticated)`: When user is not authenticated
- `RpcException(StatusCode.NotFound)`: When basket update fails

---

### 3. DeleteBasket

**Signature:**
```csharp
public override async Task<DeleteBasketResponse> DeleteBasket(
DeleteBasketRequest request,
ServerCallContext context)
```

**Purpose:** Deletes a customer's basket.

**Authentication:** Requires authenticated user (throws exception if not authenticated).

**Behavior:**
- Extracts and validates user identity from context
- Throws `RpcException` with `Unauthenticated` status if user is not authenticated
- Deletes basket from repository using user ID
- Returns empty success response

**Returns:** `DeleteBasketResponse` (empty on success)

**Exceptions:**
- `RpcException(StatusCode.Unauthenticated)`: When user is not authenticated

---

## Private Helper Methods

### ThrowNotAuthenticated

```csharp
[DoesNotReturn]
private static void ThrowNotAuthenticated()
```

Throws a standardized `RpcException` indicating the caller is not authenticated.

### ThrowBasketDoesNotExist

```csharp
[DoesNotReturn]
private static void ThrowBasketDoesNotExist(string userId)
```

Throws a standardized `RpcException` indicating the basket for the given user ID does not exist.

### MapToCustomerBasketResponse

```csharp
private static CustomerBasketResponse MapToCustomerBasketResponse(
CustomerBasket customerBasket)
```

Maps internal `CustomerBasket` model to gRPC `CustomerBasketResponse` message format.

### MapToCustomerBasket

```csharp
private static CustomerBasket MapToCustomerBasket(
string userId,
UpdateBasketRequest customerBasketRequest)
```

Maps gRPC `UpdateBasketRequest` message to internal `CustomerBasket` model, associating it with the provided user ID.

---

## Data Models

### CustomerBasket (Internal Model)

Properties:
- `BuyerId`: User identifier
- `Items`: Collection of basket items

### BasketItem

Properties:
- `ProductId`: Identifier of the product
- `Quantity`: Quantity of the product in the basket

---

## Error Handling

The service uses gRPC status codes for error responses:

| Status Code | Scenario | Description |
|-------------|----------|-------------|
| `Unauthenticated` | User not authenticated | Thrown when operations requiring authentication are called without valid user identity |
| `NotFound` | Basket not found | Thrown when attempting to update a non-existent basket |

---

## Logging

The service implements debug-level logging for diagnostic purposes:

- Logs basket retrieval operations with method name and user ID
- Logs basket update operations with method name and user ID
- Only logs when `LogLevel.Debug` is enabled

---

## Security Considerations

1. **Authentication:** Most operations require authenticated users except `GetBasket` which allows anonymous access
2. **User Identity:** User identity is extracted from the gRPC server call context using `context.GetUserIdentity()`
3. **Authorization:** Service ensures users can only access their own baskets by deriving user ID from authentication context

---

## Usage Example

This is a gRPC service, typically called from client applications using generated gRPC client code:

```csharp
// Example client usage (pseudo-code)
var client = new Basket.BasketClient(channel);

// Get basket
var getResponse = await client.GetBasketAsync(new GetBasketRequest());

// Update basket
var updateRequest = new UpdateBasketRequest();
updateRequest.Items.Add(new BasketItem
{
ProductId = 123,
Quantity = 2
});
var updateResponse = await client.UpdateBasketAsync(updateRequest);

// Delete basket
var deleteResponse = await client.DeleteBasketAsync(new DeleteBasketRequest());
```

---

## Related Components

- **Repository:** `IBasketRepository` - Handles data persistence
- **Extensions:** `context.GetUserIdentity()` - Extension method for extracting user identity
- **Models:** `CustomerBasket`, `BasketItem` - Domain models in `eShop.Basket.API.Model`

---

## Notes

- The service follows the gRPC service pattern with strongly-typed request/response messages
- All async operations use proper async/await patterns
- Defensive programming with null checks and empty responses
- Uses `[DoesNotReturn]` attribute for exception-throwing helper methods
29 changes: 29 additions & 0 deletions mydocs/classed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
```mermaid
classDiagram
class CustomerBasket {
+string BuyerId
+List~BasketItem~ Items
+CustomerBasket()
+CustomerBasket(string customerId)
}

class BasketItem {
+string Id
+int ProductId
+string ProductName
+decimal UnitPrice
+decimal OldUnitPrice
+int Quantity
+string PictureUrl
+int Order
+Validate(ValidationContext validationContext) IEnumerable~ValidationResult~
}

class IValidatableObject {
<<interface>>
+Validate(ValidationContext validationContext) IEnumerable~ValidationResult~
}

CustomerBasket "1" *-- "0..*" BasketItem : contains
BasketItem ..|> IValidatableObject : implements
```
7 changes: 6 additions & 1 deletion src/Basket.API/Model/BasketItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,16 @@ public IEnumerable<ValidationResult> Validate(ValidationContext validationContex
{
var results = new List<ValidationResult>();

if (Quantity < 1)
if (Quantity < 1 || Quantity > 100)
{
results.Add(new ValidationResult("Invalid number of units", new[] { "Quantity" }));
}

if (string.IsNullOrEmpty(ProductName))
{
results.Add(new ValidationResult("Wir brauchen immer einen Produktnamen!", new[] { "ProductName" }));
}

return results;
}
}
46 changes: 46 additions & 0 deletions terraform/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# eShop Azure Infrastructure (Terraform)

This folder contains Terraform code to provision the reference architecture for eShop on Azure, as depicted in the provided architecture diagram.

## Components Deployed
- Azure Resource Group
- Azure Virtual Network (VNET) with subnets:
- Private Link Subnet (10.205.238.0/24)
- APIM Subnet (10.205.239.0/24)
- AKS Subnet (10.205.240.0/20)
- Azure DNS Zone
- Azure Kubernetes Service (AKS)
- Azure Front Door with WAF
- Azure API Management (APIM)
- Azure Private Link Service

## Usage

1. Install the [Azure CLI](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli) and [Terraform](https://learn.hashicorp.com/tutorials/terraform/install-cli).
2. Authenticate with Azure:
```bash
az login
```
3. Initialize Terraform:
```bash
terraform init
```
4. Review and customize variables in `variables.tf` as needed.
5. Plan the deployment:
```bash
terraform plan
```
6. Apply the deployment:
```bash
terraform apply
```

## Notes
- This setup uses Terraform modules for VNET and AKS for best practices.
- You may need to adjust the AKS version, region, and other settings to match your requirements.
- Additional configuration may be required for production use (e.g., Front Door backend pools, APIM APIs, AKS node pools, etc.).

---

**Diagram Reference:**
![Architecture Diagram](../img/ARCHITECTURE_DIAGRAM_PLACEHOLDER.png)
Loading