# Task 2.1 Implementation: User Data Model and DynamoDB Users Table ## Overview This task implements the User data model and DynamoDB Users table with full CRUD operations. ## Files Created ### 1. User Model (`internal/models/user.go`) - Defines the `User` struct with all required fields: - `ID` (user_id): Unique identifier (UUID) - `Email`: User's email address - `OAuthProvider`: OAuth provider name (e.g., "google", "github") - `OAuthID`: OAuth provider's user ID - `CreatedAt`: Timestamp when user was created - `UpdatedAt`: Timestamp when user was last updated ### 2. DynamoDB Client (`internal/storage/dynamodb.go`) - `NewDynamoDBClient`: Creates a new DynamoDB client with optional endpoint override - `CreateUsersTable`: Creates the Users table with proper schema - Partition Key: `user_id` (String) - Billing Mode: Pay-per-request (on-demand) - Waits for table to be active before returning ### 3. User Storage Layer (`internal/storage/user_storage.go`) Implements all CRUD operations: - `CreateUser`: Creates a new user with auto-generated UUID - `GetUserByID`: Retrieves a user by their ID - `GetUserByOAuth`: Retrieves a user by OAuth provider and ID - `UpdateUser`: Updates an existing user (auto-updates UpdatedAt) - `DeleteUser`: Deletes a user by their ID ### 4. Unit Tests (`internal/storage/user_storage_test.go`) Comprehensive test coverage: - `TestCreateUser`: Verifies user creation with all fields - `TestGetUserByID`: Tests user retrieval by ID - `TestGetUserByID_NotFound`: Tests error handling for non-existent users - `TestGetUserByOAuth`: Tests user retrieval by OAuth credentials - `TestGetUserByOAuth_NotFound`: Tests error handling for OAuth lookup - `TestUpdateUser`: Verifies user updates and timestamp changes - `TestDeleteUser`: Tests user deletion - `TestCreateUser_MultipleUsers`: Verifies multiple users can be created with unique IDs ### 5. Database Initialization (`cmd/init-db/main.go`) Standalone CLI tool to initialize DynamoDB tables: ```bash go run cmd/init-db/main.go -endpoint http://localhost:8000 ``` ### 6. Table Initialization Helper (`cmd/server/init_tables.go`) Helper function for the main server to initialize tables on startup. ## DynamoDB Schema ### Users Table ``` Table Name: Users Partition Key: user_id (String) Billing Mode: Pay-per-request Attributes: - user_id: String (UUID) - email: String - oauth_provider: String - oauth_id: String - created_at: Number (Unix timestamp in nanoseconds) - updated_at: Number (Unix timestamp in nanoseconds) ``` ## Dependencies Added - `github.com/aws/aws-sdk-go-v2`: AWS SDK for Go v2 - `github.com/aws/aws-sdk-go-v2/config`: AWS configuration - `github.com/aws/aws-sdk-go-v2/service/dynamodb`: DynamoDB service client - `github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue`: DynamoDB attribute marshaling - `github.com/google/uuid`: UUID generation ## Running Tests ### Prerequisites 1. Start DynamoDB Local: ```bash make db-start ``` Or manually: ```bash docker-compose up -d dynamodb-local ``` 2. Set environment variable (optional): ```bash export DYNAMODB_ENDPOINT=http://localhost:8000 ``` ### Run Tests ```bash make test # or go test -v ./internal/storage/... ``` ## Usage Example ```go package main import ( "context" "log" "custom-start-page/internal/storage" ) func main() { ctx := context.Background() // Create DynamoDB client db, err := storage.NewDynamoDBClient(ctx, "http://localhost:8000") if err != nil { log.Fatal(err) } // Create Users table if err := db.CreateUsersTable(ctx); err != nil { log.Fatal(err) } // Create user storage userStorage := storage.NewUserStorage(db) // Create a new user user, err := userStorage.CreateUser(ctx, "user@example.com", "google", "google123") if err != nil { log.Fatal(err) } log.Printf("Created user: %+v", user) // Retrieve user by ID retrievedUser, err := userStorage.GetUserByID(ctx, user.ID) if err != nil { log.Fatal(err) } log.Printf("Retrieved user: %+v", retrievedUser) } ``` ## Notes ### OAuth Lookup Performance The `GetUserByOAuth` method currently uses a Scan operation since there's no GSI for `oauth_provider` + `oauth_id`. For production use with many users, consider adding a GSI: - Partition Key: `oauth_provider` - Sort Key: `oauth_id` This would change the query from O(n) scan to O(1) lookup. ### Timestamp Storage Timestamps are stored as Go `time.Time` which DynamoDB marshals as Unix timestamps in nanoseconds. This provides high precision for audit trails. ## Requirements Validated - ✓ Requirement 1.1: User authentication foundation - ✓ Requirement 1.3: OAuth user creation and retrieval - ✓ Requirement 8.7: User data association ## Next Steps Task 2.2 will implement the OAuth service using this User model to handle Google OAuth authentication.