Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Repository layout

This is a monorepo for the BrainUp auditory-training platform, containing three independently built apps:

- **Backend** (repo root) — Kotlin + Spring Boot REST API, built with Gradle. Package root `com.epam.brn`, entry point `src/main/kotlin/com/epam/brn/Application.kt`.
- **`frontend/`** — end-user UI, **Ember.js 4.12 (Octane)** + TypeScript, uses **WarpDrive** (ember-data successor) with a schema-driven data layer. Package manager: **pnpm**.
- **`frontend-angular/`** — admin panel, **Angular 11** + npm.

Each subproject has its own README with details; read them before working in a frontend.

## Backend commands (run from repo root)

Use the Gradle wrapper (`./gradlew` / `gradlew.bat` on Windows).

- Build with tests: `gradlew build`
- Run the app: `gradlew bootRun --args=--spring.profiles.active=dev` (needs a Postgres 13 DB running — see README for the `docker run postgres:13` command)
- Run unit tests: `gradlew test`
- Run a single test class: `gradlew test --tests "com.epam.brn.service.SomeServiceTest"`
- Run integration tests: `gradlew integrationTest` — these are **excluded from `test`/`build`** and run separately against a Postgres Testcontainer.
- Coverage report: `gradlew jacocoTestReport` (output in `build/jacoco/`)
- Lint (Kotlin): `gradlew ktlintCheck` — **a failing ktlint fails the build** (`compileKotlin` depends on it). Auto-fix with `gradlew ktlintFormat`.

Building the project also installs git hooks by pointing `core.hooksPath` to `.githooks` (done automatically by `build.gradle.kts`).

### Test conventions

- Unit tests use JUnit 5 + MockK + Kotest/Kluent assertions.
- Integration tests live under `src/test/kotlin/com/epam/brn/integration/`, extend `BaseIT`, and are marked `@Tag("integration-test")`. The JUnit config in `build.gradle.kts` excludes this tag from the normal `test` task and includes it only in `integrationTest`.

## Frontend commands

Ember user app (`frontend/`, pnpm):
- `pnpm local` — dev server proxying API to `http://localhost:8081` (local backend)
- `pnpm remote` — dev server proxying to production backend
- `pnpm test:ember` — run tests; `pnpm lint` / `pnpm lint:fix` — lint

Angular admin app (`frontend-angular/`, npm):
- `npm run start:dev` (local backend) / `npm run start` (prod backend)
- `npm run test:start` — tests; `npm run lint` — lint

## Full-stack / Docker

- `docker compose up --build` (or `make start`) brings up backend + frontends. Local Swagger at `http://localhost:8081/api/swagger-ui.html`.
- `make docker_unit_test` runs unit tests inside a container.
- `Makefile` wraps common docker-compose flows (`start`, `stop`, `restart`, `clean`).

## Architecture notes

- **Auth is Firebase-based.** Clients authenticate with Firebase and send a bearer token; `auth/filter/FirebaseTokenAuthenticationFilter.kt` validates it and populates the Spring Security context. Firebase admin credentials are loaded from `src/main/resources/firebase-*.json`.
- **Data seeding on startup:** `service/load/InitialDataLoader.kt` and `FirebaseUserDataLoader.kt` load exercise content and users from CSV/resource files (`src/main/resources/initFiles`). These are excluded from coverage/Sonar as non-testable bootstrap code.
- **Persistence:** Spring Data JPA over Postgres. Schema is managed by **Flyway** migrations in `src/main/resources/db/migration/`. Migration files must be named `V2yearmonthday_taskNumber.sql` (e.g. `V220210804_899.sql`).
- **Standard layering:** `controller` → `service` → `repo`, with `dto` (request/response) and `model` (JPA entities). JPA entity classes are opened for proxying via the `allOpen` plugin (`@Entity`/`@MappedSuperclass`/`@Embeddable`).
- **External integrations:** AWS S3 and Google Cloud Storage for media/pictures; Azure & Yandex TTS for speech generation; GitHub API client for contributor data.
- **Ember data layer:** requests flow through a WarpDrive `RequestManager` pipeline (Auth → API normalization → Fetch → Cache). `BrnApiHandler` normalizes the backend's plain REST responses into JSON:API for the WarpDrive cache; models are JSON schemas under `app/schemas/`, not class-based.

## Versions / toolchain

- JDK **17** toolchain, Kotlin **2.1.20**, Spring Boot **3.5.3** (uses `jakarta.*` namespace).
- Postgres **13** (local dev and Testcontainers).
- Frontend Node **22+**; Ember app uses pnpm, Angular app uses npm.

## Conventions

- Branch names: `#GitHub_TaskNumber-# issue description`, task number in range 0–1999.
- Always run `gradlew ktlintFormat` (or IDEA `Ctrl+Alt+L`) before committing Kotlin.
- Coverage/Sonar exclude `dto`, `model`, `config`, `exception`, data loaders, and TTS config — don't chase coverage on those.
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ Autotest Python team chat: ask Elena

## TOOLS WE USE
- Back-end: KOTLIN + Spring boot. Rest api as integration layer.
- Front-end : TBD - Ember, Angular.
- DB: Postgres13.
- Front-end: Ember (user app), Angular (admin panel).
- DB: Postgres 13.
- TestContainers for running integration tests.

# GETTING STARTED!
Expand Down Expand Up @@ -89,7 +89,7 @@ Go to location where the project is download for example C:\brn\brn\frontend-ang

### Database running:
1. Install docker to your machine.
The project uses postgres 11.5. [Documentation](https://www.postgresql.org/docs/11/index.html)
The project uses postgres 13. [Documentation](https://www.postgresql.org/docs/13/index.html)
Currently for local development we use [postgres docker image](https://hub.docker.com/_/postgres)
To install docker use:
* [on windows](https://docs.docker.com/docker-for-windows/install/)
Expand All @@ -112,7 +112,7 @@ docker run --name postgres_dev -d -p 5432:5432 -e POSTGRES_DB=brn -e POSTGRES_PA
```

### Back-end Kotlin Part:
1. Run command 'gradle build' from main project folder to build project with tests.
1. Run command 'gradlew build' (Gradle wrapper) from main project folder to build project with tests.
2. Application.kt is the main class to run application from Idea for example.
3. Get Postman Collection from [https://www.postman.com/koylubaevNT/workspace/brainup/collection/2686902-d91ba307-e131-4d4f-a308-0a272e063c95 and environment from https://www.postman.com/koylubaevNT/workspace/brainup/environment/2686902-95b2c495-82a1-4244-83c7-dad7f8efebd7](https://brnteam.postman.co/workspace/My-Workspace~d7a4e69a-0ba8-4c56-8fc0-4e27e1998f7c/collection/2464717-96455e8c-ba11-49ed-a299-5c1b77b31db8?action=share&creator=2464717).
You can directly login on www.brainup.site with some user, for example default2@default.ru / password, than use his token in postamn in Authorization tab use Bearer Token and put here yours.
Expand All @@ -121,7 +121,7 @@ You can directly login on www.brainup.site with some user, for example default2@
end-point specification is here: https://github.com/Brain-up/brn/blob/master/api-contract/api.raml
6. use https://brainup.site/admin/swagger

Note that if you are using IntelliJ, you may want to use version 2019.2 and later to avoid issues with new kotlin plugin.
Note that the project targets JDK 17 and Kotlin 2.1, so use a recent IntelliJ IDEA version with an up-to-date Kotlin plugin.

#### Useful Postman scripts:
- [Script](./postman_scripts/generate_month_history.js) to generate month tasks statistics (you can use it with `brnlogin` request in `Test` tab)
Expand Down Expand Up @@ -150,9 +150,9 @@ docker rm $(docker ps -a -q) # Remove all stopped containers
2. Create branch from dev with the codename of your task, i.e. #GitHub_TaskNumber#.
3. Implement your task, do not forget to write tests. Remember to follow project's coding standards: https://github.com/Brain-up/brn/wiki/Coding-Standards.
4. Create pull request with task name and description about what was done.
5. Notify the team in our skype chat and wait for reviews. At least one reviewer is necessary, but more can be added in a case by case basis.
5. Notify the team in our Telegram dev chat and wait for reviews. At least one reviewer is necessary, but more can be added in a case by case basis.
6. The task gets merged by a project maintainer.
7. check that build job on jenkins passes successfully.
7. check that the GitHub Actions CI checks pass successfully.
8. Before load config files check them with https://orfogrammka.ru/ service.
9. Integration tests does not run in build process, it can/should be run locally with run job verification/integrationTests.

Expand Down
44 changes: 23 additions & 21 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import org.gradle.jvm.toolchain.JavaLanguageVersion
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

val kotestAssertionsVersion: String by properties
Expand Down Expand Up @@ -36,9 +37,9 @@ kotlin {
}

allOpen {
annotation("javax.persistence.Entity")
annotation("javax.persistence.MappedSuperclass")
annotation("javax.persistence.Embeddable")
annotation("jakarta.persistence.Entity")
annotation("jakarta.persistence.MappedSuperclass")
annotation("jakarta.persistence.Embeddable")
}

repositories {
Expand All @@ -47,7 +48,7 @@ repositories {

dependencyManagement {
imports {
mavenBom("software.amazon.awssdk:bom:2.17.198")
mavenBom("software.amazon.awssdk:bom:2.31.78")
}
}

Expand All @@ -58,20 +59,20 @@ dependencies {
implementation("org.springframework.boot:spring-boot-starter-webflux")
implementation("org.springframework.boot:spring-boot-starter-actuator")
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("org.springframework.boot:spring-boot-starter-batch")
implementation("org.springframework.boot:spring-boot-starter-security")
implementation("org.springframework.boot:spring-boot-starter-validation")
implementation("org.springframework.boot:spring-boot-starter-cache")
implementation("com.github.ben-manes.caffeine:caffeine:3.1.8")
implementation("com.github.ben-manes.caffeine:caffeine:3.2.0")
testImplementation("org.springframework.security:spring-security-test")
developmentOnly("org.springframework.boot:spring-boot-devtools")

implementation("org.postgresql:postgresql")
implementation("org.flywaydb:flyway-core:$flywayVersion")
implementation("org.flywaydb:flyway-database-postgresql:$flywayVersion")

implementation("com.google.firebase:firebase-admin:8.1.0")
implementation("com.google.firebase:firebase-admin:9.9.0")

implementation("com.auth0:java-jwt:3.10.3")
implementation("com.auth0:java-jwt:4.5.0")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-csv")
implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-xml")
Expand All @@ -80,11 +81,10 @@ dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactive:$kotlinxCoroutinesCoreVersion")
implementation("org.apache.logging.log4j:log4j-api-kotlin:$log4jApiKotlinVersion")

implementation("org.springdoc:springdoc-openapi-ui:$springDocOpenApiVersion")
implementation("org.springdoc:springdoc-openapi-kotlin:$springDocOpenApiVersion")
implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:$springDocOpenApiVersion")

implementation("software.amazon.awssdk:s3")
implementation("com.google.cloud:google-cloud-storage:1.110.0")
implementation("com.google.cloud:google-cloud-storage:2.69.0")

implementation("org.json:json:$jsonVersion")
implementation("commons-io:commons-io:2.17.0")
Expand Down Expand Up @@ -114,9 +114,9 @@ dependencies {
}

tasks.withType<KotlinCompile> {
kotlinOptions {
freeCompilerArgs = listOf("-Xjsr305=strict")
jvmTarget = "17"
compilerOptions {
freeCompilerArgs.add("-Xjsr305=strict")
jvmTarget.set(JvmTarget.JVM_17)
}
}

Expand Down Expand Up @@ -168,9 +168,11 @@ tasks.register<JavaExec>("ktlintFormat") {
)
}

project.exec {
commandLine = "git config core.hooksPath .githooks".split(" ")
}
providers
.exec {
commandLine("git", "config", "core.hooksPath", ".githooks")
}.result
.get()

tasks.named("compileKotlin") { dependsOn("ktlintCheck") }

Expand All @@ -195,9 +197,9 @@ tasks.withType<JacocoReport> {
reports {
xml.required.set(true)
html.required.set(true)
xml.outputLocation.set(file("$buildDir/jacoco/coverage.xml"))
xml.outputLocation.set(layout.buildDirectory.file("jacoco/coverage.xml"))
csv.required.set(false)
html.outputLocation.set(file("$buildDir/jacoco/html"))
html.outputLocation.set(layout.buildDirectory.dir("jacoco/html"))
}
afterEvaluate {
classDirectories.setFrom(
Expand All @@ -219,10 +221,10 @@ tasks.withType<JacocoReport> {
),
)
}
executionData.setFrom("$buildDir/jacoco/test.exec")
executionData.setFrom(layout.buildDirectory.file("jacoco/test.exec"))
}

task<Test>("integrationTest") {
tasks.register<Test>("integrationTest") {
useJUnitPlatform { includeTags("integration-test") }
mustRunAfter(tasks["test"])
group = "Verification"
Expand Down
20 changes: 10 additions & 10 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
kotlin.code.style=official

springBootVersion=2.5.9
springDependencyVersion=1.0.11.RELEASE
springBootVersion=3.5.3
springDependencyVersion=1.1.7
kotlinVersion=2.1.20
kotlinxCoroutinesCoreVersion=1.6.0
flywayVersion=6.5.5
junitVersion=5.3.1
kotlinxCoroutinesCoreVersion=1.10.1
flywayVersion=11.8.2
junitVersion=5.12.2
mockkVersion=1.13.17
kotestAssertionsVersion=5.9.1
jsonVersion=20210307
jsonVersion=20250107
log4jApiKotlinVersion=1.1.0
springCloudContractWiremockVersion=3.1.1
testContainersVersion=1.15.1
okhttp3Version=4.10.0
springDocOpenApiVersion=1.6.12
springCloudContractWiremockVersion=4.3.0
testContainersVersion=1.21.3
okhttp3Version=4.12.0
springDocOpenApiVersion=2.8.6

org.gradle.jvmargs=-XX:MaxMetaspaceSize=512m
Binary file modified gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
3 changes: 2 additions & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Loading
Loading