Files
marketing-parser/docs/development.md
T
2026-08-14 16:42:12 +05:00

134 lines
4.4 KiB
Markdown

# Development
## Prerequisites
- **JDK 21** (Temurin recommended)
- **MongoDB** reachable at `MONGODB_HOST:MONGODB_PORT`
- Maven is not required — use the bundled wrapper (`./mvnw`)
## Setup
```bash
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
```bash
./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/`](../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:
```java
@SpringBootTest
@EnabledIfEnvironmentVariable(named = "RUN_INTEGRATION_TESTS", matches = "true")
```
so they are skipped unless you opt in:
```bash
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:
```java
@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](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/`](api/README.md) and update that index.
## Adding configuration
See [configuration.md](configuration.md#adding-a-new-configuration-value). In short:
environment placeholder, no default for secrets, and update `.env.example`.
## Before you push
```bash
./mvnw clean verify
git status # nothing unexpected staged
```
Pushing to `main` deploys to production — [deployment.md](deployment.md).