Files
2026-08-14 16:42:12 +05:00

4.4 KiB

Development

Prerequisites

  • JDK 21 (Temurin recommended)
  • MongoDB reachable at MONGODB_HOST:MONGODB_PORT
  • Maven is not required — use the bundled wrapper (./mvnw)

Setup

cp .env.example .env
$EDITOR .env                          # fill in REQUIRED values
set -a && source .env && set +a
./mvnw spring-boot:run

http://localhost:8080/swagger-ui.html gives you an interactive client; use the Authorize button to supply a JWT.

Common commands

./mvnw clean verify                   # unit tests + coverage gate — run before pushing
./mvnw test                           # unit tests only
./mvnw clean package                  # build the jar
./mvnw spring-boot:run                # run locally
./mvnw test -Dtest=JwtServiceTest     # a single test class

Helper scripts in scripts/:

Script Purpose
run-parser.sh Preflight checks then start the application
smoke-mongodb.sh Verify MongoDB reachability and print a connection string
smoke-openai-api.sh Verify OPENAI_API_KEY works against the OpenAI API
smoke-research-api.sh Exercise the report endpoints against a running instance

Testing

Two tiers, separated by an environment variable:

Unit tests run by default. They mock collaborators and never touch the network. This is what CI and ./mvnw verify execute.

Integration tests boot the full Spring context and need real MongoDB plus valid credentials. They are annotated:

@SpringBootTest
@EnabledIfEnvironmentVariable(named = "RUN_INTEGRATION_TESTS", matches = "true")

so they are skipped unless you opt in:

RUN_INTEGRATION_TESTS=true ./mvnw verify

Surefire additionally excludes the integration JUnit tag (see pom.xml).

Coverage gate

verify fails if line coverage drops below 80% on any of:

  • PostingTaskService
  • JwtService
  • SocialMediaCredentialsService

Report: target/site/jacoco/index.html. To extend the gate to another class, add it to the JaCoCo qg01-coverage-check rule in pom.xml.

Test credentials

Tests must never contain real credentials. Use placeholders that resolve from the environment:

@TestPropertySource(properties = {
        "spring.data.mongodb.host=${MONGODB_HOST:localhost}",
        "spring.data.mongodb.username=${MONGODB_USERNAME:}"
})

Conventions

  • Java 21, Lombok for boilerplate (excluded from the packaged jar).
  • Constructor injection, not field injection — see ParserManagerService.
  • Controllers stay thin: validate, delegate to a service, return a DTO. No business logic and no repository access from a controller.
  • Never return a model document directly from a controller — map it to a dto.
  • Throw domain exceptions from exception/ and let GlobalExceptionHandler render them; do not build error responses by hand.
  • Long-running AI calls belong on an async executor (AsyncConfig), not the request thread.
  • Existing code mixes Russian and English comments. New comments should be in English.

Adding an RSS source

ParserManagerService discovers parsers through Spring, so no registry needs editing:

  1. Add rss.<source>.url to application.properties.
  2. Implement ParserService — return a unique getSourceName() and implement parseAndSaveRssFeed(). Model it on KursivParserService.
  3. Annotate the scheduled entry point with @Scheduled, matching the cadence of comparable sources (5 min for high-volume, 30 min otherwise).
  4. Add a unit test alongside KursivParserServiceTest.
  5. Add a log level line in application.properties.
  6. Document the source in rss-parsers.md.

Adding an endpoint

  1. Define request/response DTOs in dto/ with Bean Validation annotations.
  2. Add the handler to the relevant controller; extract the caller with jwtService.extractUserIdFromHeader(authHeader).
  3. Put the logic in a service; add a repository method if persistence is needed.
  4. Add unit tests for the service.
  5. Document it under docs/api/ and update that index.

Adding configuration

See configuration.md. In short: environment placeholder, no default for secrets, and update .env.example.

Before you push

./mvnw clean verify
git status                            # nothing unexpected staged

Pushing to main deploys to production — deployment.md.