4.2 KiB
4.2 KiB
Secure Notes Vault API
Design decisions
- Java programming language since Bluestaq is a Java shop
- Maven for building
- Spring Boot as a framework
- H2 for persistance - plan for Postgress
- JPA/Hibernate for SQL
- JWT for auth
HTTP return codes
Application-defined HTTP status codes are generated in AuthController for successful registration and in GlobalExceptionHandler for API error responses. Spring Security and Spring MVC may still produce framework-level responses for requests that do not reach a controller, such as unauthenticated protected requests or unsupported methods.
| HTTP code | Status | Generated in | Trigger | Response body |
|---|---|---|---|---|
201 |
Created | AuthController.register |
POST /auth/register creates a new user. |
UserResponse with id, username, and createdAt. |
400 |
Bad Request | GlobalExceptionHandler.handleMalformedJson |
Request body cannot be parsed or converted from JSON. | ErrorResponse with Malformed request body. |
400 |
Bad Request | GlobalExceptionHandler.handleTypeMismatch |
Path variable or query parameter cannot be converted to the expected type. | ErrorResponse with Invalid request parameter. |
401 |
Unauthorized | GlobalExceptionHandler.handleAuthentication; Spring Security filter chain |
Authentication fails or an unauthenticated request targets a protected route. | ErrorResponse with Invalid username or password when handled by GlobalExceptionHandler; framework-generated body when rejected before controller handling. |
403 |
Forbidden | GlobalExceptionHandler.handleForbidden |
Authenticated caller is not allowed to perform the requested operation. | ErrorResponse with the exception message. |
404 |
Not Found | GlobalExceptionHandler.handleNotFound |
Requested domain resource does not exist or should be hidden from the caller. | ErrorResponse with the exception message. |
404 |
Not Found | GlobalExceptionHandler.handleNoResourceFound |
No controller route or static resource matches the request. | ErrorResponse with Not found. |
409 |
Conflict | GlobalExceptionHandler.handleConflict; AuthController.register throws ConflictException |
Duplicate username or other persistence/domain uniqueness conflict. | ErrorResponse with the exception message. |
422 |
Unprocessable Entity | GlobalExceptionHandler.handleValidation |
Bean Validation fails for a request body annotated with @Valid. |
ValidationErrorResponse with error and per-field fields. |
DB schema
The schema is the basic requirements for a User, Note and NoteShare. Nothing additional was added in order to save time.
| Table | Column | Type | Required | Key / Constraint | Description |
|---|---|---|---|---|---|
users |
id |
UUID |
Yes | Primary key | Unique user identifier. |
users |
username |
VARCHAR(100) |
Yes | Unique: uq_users_username |
User login handle. |
users |
password_hash |
VARCHAR(255) |
Yes | BCrypt password hash. | |
users |
created_at |
TIMESTAMP WITH TIME ZONE |
Yes | Account creation timestamp. | |
notes |
id |
UUID |
Yes | Primary key | Unique note identifier. |
notes |
owner_id |
UUID |
Yes | FK to users(id); indexed by idx_notes_owner_id |
User who owns the note. |
notes |
title |
VARCHAR(255) |
Yes | Note title. | |
notes |
content |
TEXT |
Yes | Note body content. | |
notes |
created_at |
TIMESTAMP WITH TIME ZONE |
Yes | Note creation timestamp. | |
notes |
updated_at |
TIMESTAMP WITH TIME ZONE |
Yes | Last note update timestamp. | |
note_shares |
id |
UUID |
Yes | Primary key | Unique share identifier. |
note_shares |
note_id |
UUID |
Yes | FK to notes(id) with ON DELETE CASCADE; indexed by idx_note_shares_note_id |
Note being shared. |
note_shares |
shared_with_user_id |
UUID |
Yes | FK to users(id); indexed by idx_note_shares_shared_with_user_id |
User receiving read-only access. |
note_shares |
created_at |
TIMESTAMP WITH TIME ZONE |
Yes | Share creation timestamp. | |
note_shares |
note_id, shared_with_user_id |
UUID, UUID |
Yes | Unique: uq_note_shares_note_user |
Prevents duplicate shares for the same note and user. |