From 578fe6fa193bc140f64e34c2821a6c58bc65e981 Mon Sep 17 00:00:00 2001 From: Sean Starkey Date: Sun, 31 May 2026 16:48:04 -0600 Subject: [PATCH] Add start of README file --- README.md | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..10f8763 --- /dev/null +++ b/README.md @@ -0,0 +1,49 @@ +# 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. |