Initial commit: Custom Start Page application with authentication and DynamoDB storage
This commit is contained in:
165
docs/task-2.1-implementation.md
Normal file
165
docs/task-2.1-implementation.md
Normal file
@@ -0,0 +1,165 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user