Skip to content

Feature/documentation central - #30

Merged
jomariabejo merged 5 commits into
mainfrom
feature/documentation-central
Aug 4, 2026
Merged

jomariabejo merged 5 commits into
mainfrom
feature/documentation-central

Conversation

@jomariabejo

Copy link
Copy Markdown
Owner

Summary

Adds a documentation site, an interactive API console, a test suite, and environment-based configuration to a repository that previously had none of the four. The endpoint reference is derived from the controllers rather than the old README, which advertised an /api prefix the code has never had.

Two fixes are bundled here because the rest of the PR could not be verified without them: the application could not start at all on main, and every malformed request body returned 500.

Stacked PR 1 of 3. Merge before fix/known-issues.

Changes

Features

  • documentation-central/ — Docusaurus 3.10 site (TypeScript, Mermaid), 18 pages: getting started, API reference, architecture, data model, testing, known issues. onBrokenLinks: 'throw', so a dead cross-reference fails the build.
  • Swagger UI and OpenAPI 3 via springdoc 2.8.6 at /swagger-ui.html and /v3/api-docs, with a bearer-JWT scheme so Authorize works. @Tag/@Operation/@ApiResponse on every controller, @Schema on request DTOs, @Hidden on the three smoke-test controllers.
  • 139 tests — 80 Mockito service unit tests, 59 @WebMvcTest controller slices. New ./gradlew unitTest task excludes the Postgres-dependent ConnectlyApiApplicationTests; JaCoCo wired up.
  • .env.example documenting every setting. application.properties now reads ${VAR:default} throughout, loaded via spring.config.import=optional:file:.env[.properties]. A fresh clone still runs with no .env at all.

Fixes

  • PostLikeController mapped /{postId}/{postId}/likes/toggle. Spring Boot 3's PathPatternParser rejects capturing the same variable twice, so the context aborted and no endpoint served any request. Now /{postId}/likes/toggle, with a regression test that fails at context load if a duplicate segment returns.
  • GlobalExceptionHandler's catch-all claimed MethodArgumentNotValidException before Spring could map it, so every malformed body returned 500. An explicit handler now returns 400 listing the offending fields.

Chores

  • Corrected the .http templates and the README endpoint list, both of which pointed at the non-existent /api prefix.
  • Removed the unused dotenv-java dependency — Spring reads .env natively.

Testing

  • ./gradlew unitTest — 139 tests, 0 failures, no database required
  • cd documentation-central && npm run build && npm run typecheck — both clean
  • Manual verification:
    • Confirmed the app fails to start before the PostLikeController fix (Invalid mapping pattern detected) and starts cleanly after
    • ./gradlew bootRun → Swagger UI serves at /swagger-ui.html (302 → /swagger-ui/index.html, 200), /v3/api-docs returns 24 paths, the three @Hidden demo endpoints are excluded
    • Full happy path against the running API: register → verify → login → GET /users/me → POST /posts (201) → POST /posts/{id}/comments (201) → like toggle returning true then false
    • Validation now returns 400 live, not 500
    • MAIL_PORT=1026 ./gradlew bootRun — confirmed the env-override path works end to end

Not applicable: no frontend, no Playwright, no Maven (this repo is Gradle).

Screenshots

N/A — no UI changes. The docs site renders locally via npm start.

Database Changes

None. Schema handling is unchanged in this PR (still Hibernate create-drop); Flyway arrives in PR 2.

Breaking Changes

One, and it is a repair rather than a regression: the like-toggle route moves from /{postId}/{postId}/likes/toggle to /{postId}/likes/toggle. No client can be depending on the old path — the application could never start with it, so the endpoint has never served a request.

Validation failures change from 500 to 400. Any client branching on 500 for bad input should be updated, though that behaviour was clearly unintended.

Deployment Notes

  • Vercel: import the repo and set Root Directory = documentation-central. vercel.json already pins the build command and output directory. Update url in docusaurus.config.ts once the real domain is known — only the sitemap and social-card metadata depend on it.
  • No new environment variables are required; every setting has a working default.
  • doc/api-documentation.yml (which .github/workflows/bump.yml deploys) is still missing at this commit — it lands in PR 2.

…config

Four additions, plus two fixes that were required to make them work.

Documentation site (documentation-central/)
- Docusaurus 3.10 + TypeScript + Mermaid, 18 pages: getting started,
  API reference, architecture, data model, testing, known issues
- Endpoint reference derived from the controllers, not the old README,
  which advertised a /api prefix the code has never had
- onBrokenLinks: throw, so a dead cross-reference fails the build
- vercel.json for deployment with Root Directory = documentation-central

Swagger UI / OpenAPI (springdoc 2.8.6)
- OpenApiConfig with a bearer-JWT scheme so Authorize works in the UI
- @tag / @operation / @apiresponse on every controller, @Schema on
  request DTOs, @hidden on the three smoke-test controllers
- Swagger routes allow-listed in SecurityConfiguration

Mockito test suite (139 tests, no database required)
- 80 service unit tests, 59 @WebMvcTest controller slices
- New ./gradlew unitTest task excluding the Postgres-dependent
  ConnectlyApiApplicationTests; JaCoCo wired up
- Shared @ControllerSliceTest annotation; @MockitoBean throughout,
  since Boot 3.4 deprecated @MockBean

Environment configuration
- .env.example documenting every setting
- application.properties now reads ${VAR:default} throughout, loaded via
  spring.config.import=optional:file:.env[.properties] -- no dotenv library
- A fresh clone still runs with no .env at all
- .env gitignored; removed the unused dotenv-java dependency

Fixes required along the way
- PostLikeController mapped /{postId}/{postId}/likes/toggle, which Spring
  Boot 3's PathPatternParser rejects -- the application could not start at
  all. Now /{postId}/likes/toggle, with a regression test.
- GlobalExceptionHandler's catch-all claimed MethodArgumentNotValidException
  before Spring could map it, so every malformed request body returned 500.
  Added an explicit handler returning 400 with the offending fields.

Also corrected the .http request templates and the README endpoint list,
both of which pointed at the non-existent /api prefix.
…tion

Every fix here was verified against a running instance, not just unit tests.

Security
- Responses no longer leak the BCrypt hash or the verification token.
  Registration, GET /users/me, the user listings and every embedded comment
  author now return UserResponseDto. The verification token was the worse
  half: anyone seeing a registration response could activate the account
  without the mailbox.
- Registration grants ROLE_USER. It used to leave roles empty, so every
  account had zero authorities and /user/**, /admin/** and all three
  @PreAuthorize endpoints were unreachable by anyone.
- Registration enforces the same password strength rules as reset, so an
  account can no longer be created with a password its owner could never
  reset back to.
- CORS is actually applied. Two configurations existed and neither took
  effect, because the filter chain never called .cors(...) -- no browser
  frontend on another origin could reach the API. One config now, driven by
  CORS_ALLOWED_ORIGINS.
- bump.yml reads ${{ secrets.BUMP_TOKEN }} instead of a literal token.
  The old value is still in git history and needs rotating.

Correctness
- /auth/registrationConfirm accepts the token users are actually sent.
  Registration produced two unrelated UUIDs -- one emailed, one written to
  the verification_token table by RegistrationListener -- and the confirm
  endpoint only accepted the second. The listener now reuses the emailed
  token, and no longer sends a duplicate registration email carrying a dead
  link. Both endpoints check expiry.
- Deleted accounts with content are purged. Every FK to app_user was
  NO ACTION, so deleting a user with a single post, comment or like threw a
  constraint violation that ScheduledDeletionTask swallowed -- the account
  stayed soft-deleted forever after its owner was told the data would be
  gone in 30 days. permanentlyDeleteUser now clears dependants in order,
  and the migration declares ON DELETE CASCADE.
- The admin endpoints can see the accounts they manage. Both resolved users
  through an active-only lookup, so force-delete answered 404 for exactly
  the soft-deleted accounts it existed to purge, and extend-deletion could
  never reach its "not scheduled for deletion" branch.
- getComment validates its {postId}. /posts/999/comments/1 used to return a
  comment belonging to post 10.
- PostService.deletePost compared a Set<Role> against the string "ADMIN",
  which is never true, so the admin arm of the check was dead code.

Status codes
- 401 for missing/invalid tokens, 403 for authenticated-but-forbidden, via
  JwtAuthenticationEntryPoint and JwtAccessDeniedHandler. This was inverted.
- Unknown URLs 404, wrong verbs 405, unparseable bodies 400. The catch-all
  handler was swallowing Spring's own statuses and reporting 500.
- PUT /posts/{id} on a missing post is 404; duplicate email or username at
  registration is 409. Both were 500.

Schema
- Flyway owns the schema (V1__baseline_schema.sql), replacing schema.sql,
  which spring.sql.init.mode=never meant never ran and which had drifted.
- ddl-auto defaults to validate rather than create-drop, so a restart no
  longer drops every table. Validation immediately caught VerificationToken
  using GenerationType.AUTO where every other entity uses IDENTITY.

Scheduling
- ShedLock on both jobs, so replicas no longer each run the 02:00 deletion
  sweep.
- VerificationTokenService.cleanupExpiredTokens() is finally called; it had
  no @scheduled annotation, so expired tokens accumulated forever.

Also removed the stale "http-template copy" directory, updated the docs site
to match every change, and regenerated doc/api-documentation.yml.

Tests: 143, all green, no database required.
README
- Centered shields.io badge row: Java, Spring Boot, PostgreSQL, Gradle, JWT,
  Flyway, Swagger, Docusaurus, test count, endpoint count, licence
- Blockquote tagline, horizontal rules, tech-stack table
- Endpoints as per-area tables rather than bullet lists
- Annotated project tree
- Collapsible <details> blocks for sample requests and responses
- Constraints, acknowledgments and licence sections

New docs page: api/postman.md
- Import the live spec by link (http://localhost:8080/v3/api-docs) so the
  collection stays in sync with the code; the checked-in
  doc/api-documentation.yml as the offline fallback
- Collection-level bearer auth with a {{jwt}} variable, and a login
  post-response script that captures the token automatically
- A first-run request order, environments for multiple targets, Newman and
  REST Client alternatives, and troubleshooting
- Linked from the sidebar, the Swagger page, the intro and the README

Also corrected a stale line in api/swagger.md: an expired token now answers
401, not 403.
The workflow deployed doc/api-documentation.yml to Bump.sh on every push to
main and commented API diffs on pull requests. Removing it drops the external
dependency and the credential it needed.

- Deleted .github/workflows/bump.yml (the only workflow in the repo)
- doc/api-documentation.yml stays: it is still the offline fallback for a
  Postman import, and the docs describe it as a snapshot rather than a
  CI-published artifact
- Reworded the Known Issues entry -- the file is gone, but the token it once
  carried in plaintext is still in earlier commits and needs revoking in the
  Bump.sh dashboard rather than scrubbing from history
- Dropped Bump.sh from the tooling list and the README project tree
One canonical template at .github/pull_request_template.md, so PR descriptions
stop being written from scratch each time.

Structure follows the usual Summary / Changes / Testing / Screenshots / Database
Changes / Breaking Changes / Deployment Notes, with the prompts pointed at this
repo's actual hazards rather than generic ones:

- Testing lists ./gradlew unitTest, ./gradlew test and the Docusaurus build. It
  states plainly that there is NO CI here -- the last workflow went with the
  Bump.sh removal -- so an unchecked box is a real gap, not a formality.
- Database Changes covers the Flyway rules that actually bite: V-number
  collisions between parallel branches, never editing an applied migration,
  ddl-auto=validate meaning entities and migrations must agree exactly, FKs to
  app_user needing ON DELETE CASCADE, and listings honouring soft delete.
- Breaking Changes calls out that status codes count -- the deliberate 401/403
  split and the 403 on a missing post look like bugs and are not.
- Deployment Notes covers the Vercel root directory, adding new config to both
  .env.example and application.properties, overriding the public JWT_SECRET
  default, and hand-refreshing doc/api-documentation.yml now that nothing
  regenerates it.
@jomariabejo
jomariabejo merged commit 74fb636 into main Aug 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant