From d34552a2abee57c53331ba2909c0b3d3a3a3b174 Mon Sep 17 00:00:00 2001 From: Elena_Moshnikova Date: Wed, 19 Aug 2026 17:09:08 +0300 Subject: [PATCH 1/5] Spring Boot 2.5.9 -> 3.5.3, spring-dependency-management 1.0.11 -> 1.1.7 --- build.gradle.kts | 44 +-- docker-compose-run.yml | 2 +- docker-compose-unit-test.yml | 2 +- docker-compose-with-secret.yml | 2 +- docker-compose.yml | 2 +- gradle.properties | 20 +- gradle/wrapper/gradle-wrapper.jar | Bin 59203 -> 61624 bytes gradle/wrapper/gradle-wrapper.properties | 3 +- gradlew | 269 +++++++++++------- gradlew.bat | 15 +- .../FirebaseTokenAuthenticationFilter.kt | 6 +- .../auth/filter/RememberLastVisitFilter.kt | 6 +- .../epam/brn/config/StartupSummaryLogger.kt | 46 +++ .../com/epam/brn/config/SwaggerConfig.kt | 6 +- .../config/WebSecurityBasicConfiguration.kt | 54 ++-- .../epam/brn/controller/AudioController.kt | 2 +- .../brn/controller/AudiometryController.kt | 2 +- .../controller/AudiometryHistoryController.kt | 2 +- .../epam/brn/controller/CloudController.kt | 2 +- .../brn/controller/ContributorController.kt | 4 +- .../epam/brn/controller/DoctorController.kt | 2 +- .../epam/brn/controller/ExerciseController.kt | 4 +- .../epam/brn/controller/GroupController.kt | 2 +- .../epam/brn/controller/ResourceController.kt | 2 +- .../com/epam/brn/controller/RoleController.kt | 2 +- .../epam/brn/controller/SeriesController.kt | 2 +- .../brn/controller/StudyHistoryController.kt | 2 +- .../controller/StudyHistoryControllerV2.kt | 2 +- .../epam/brn/controller/SubGroupController.kt | 4 +- .../com/epam/brn/controller/TaskController.kt | 2 +- .../brn/controller/UserDetailController.kt | 2 +- .../controller/UserStatisticControllerV2.kt | 2 +- .../UserSubGroupStatisticController.kt | 2 +- .../com/epam/brn/dto/ExerciseGroupDto.kt | 2 +- .../kotlin/com/epam/brn/dto/HeadphonesDto.kt | 2 +- src/main/kotlin/com/epam/brn/dto/SeriesDto.kt | 4 +- .../com/epam/brn/dto/StudyHistoryDto.kt | 2 +- .../dto/request/AddPatientToDoctorRequest.kt | 4 +- .../dto/request/AudiometryHistoryRequest.kt | 2 +- .../epam/brn/dto/request/SubGroupRequest.kt | 4 +- .../dto/request/UserAccountChangeRequest.kt | 2 +- .../dto/request/UserAccountCreateRequest.kt | 10 +- .../dto/request/contributor/ContactRequest.kt | 4 +- .../request/contributor/ContributorRequest.kt | 8 +- .../dto/request/exercise/ExerciseCreateDto.kt | 8 +- .../epam/brn/dto/request/exercise/Phrases.kt | 2 +- .../brn/dto/response/AudiometryResponse.kt | 2 +- .../brn/job/GitHubContributorRefreshJob.kt | 2 + .../kotlin/com/epam/brn/model/Audiometry.kt | 20 +- .../com/epam/brn/model/AudiometryHistory.kt | 26 +- .../com/epam/brn/model/AudiometryTask.kt | 24 +- src/main/kotlin/com/epam/brn/model/Contact.kt | 14 +- .../kotlin/com/epam/brn/model/Contributor.kt | 22 +- .../kotlin/com/epam/brn/model/Exercise.kt | 28 +- .../com/epam/brn/model/ExerciseGroup.kt | 18 +- .../kotlin/com/epam/brn/model/GitHubUser.kt | 6 +- .../kotlin/com/epam/brn/model/Headphones.kt | 24 +- .../kotlin/com/epam/brn/model/Notification.kt | 16 +- .../kotlin/com/epam/brn/model/Resource.kt | 20 +- src/main/kotlin/com/epam/brn/model/Role.kt | 10 +- src/main/kotlin/com/epam/brn/model/Series.kt | 20 +- src/main/kotlin/com/epam/brn/model/Signal.kt | 14 +- .../com/epam/brn/model/SinAudiometryResult.kt | 14 +- .../kotlin/com/epam/brn/model/StudyHistory.kt | 20 +- .../kotlin/com/epam/brn/model/SubGroup.kt | 24 +- src/main/kotlin/com/epam/brn/model/Task.kt | 22 +- .../kotlin/com/epam/brn/model/UserAccount.kt | 26 +- .../brn/model/azure/tts/AzureSpeechStyle.kt | 12 +- .../brn/model/azure/tts/AzureVoiceInfo.kt | 18 +- .../epam/brn/repo/StudyHistoryRepository.kt | 12 +- .../brn/service/AudiometryHistoryService.kt | 2 +- .../com/epam/brn/service/TokenHelperUtils.kt | 2 +- .../SeriesMatrixRecordProcessor.kt | 2 +- .../SubgroupGenericRecordProcessor.kt | 2 +- .../brn/validator/PunctuationValidator.kt | 4 +- .../epam/brn/validator/WithoutPunctuation.kt | 2 +- .../com/epam/brn/webclient/GitHubApiClient.kt | 4 +- .../property/GitHubApiClientProperty.kt | 2 - .../FirebaseTokenAuthenticationFilterTest.kt | 20 +- .../filter/RememberLastVisitFilterTest.kt | 6 +- .../repo/ExerciseRepositoryTest.kt | 4 +- .../epam/brn/service/TokenHelperUtilsTest.kt | 2 +- src/test/resources/application.properties | 2 +- 83 files changed, 577 insertions(+), 464 deletions(-) create mode 100644 src/main/kotlin/com/epam/brn/config/StartupSummaryLogger.kt diff --git a/build.gradle.kts b/build.gradle.kts index 0df180429..8ec4aaea9 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -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 @@ -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 { @@ -47,7 +48,7 @@ repositories { dependencyManagement { imports { - mavenBom("software.amazon.awssdk:bom:2.17.198") + mavenBom("software.amazon.awssdk:bom:2.31.78") } } @@ -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") @@ -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") @@ -114,9 +114,9 @@ dependencies { } tasks.withType { - kotlinOptions { - freeCompilerArgs = listOf("-Xjsr305=strict") - jvmTarget = "17" + compilerOptions { + freeCompilerArgs.add("-Xjsr305=strict") + jvmTarget.set(JvmTarget.JVM_17) } } @@ -168,9 +168,11 @@ tasks.register("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") } @@ -195,9 +197,9 @@ tasks.withType { 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( @@ -219,10 +221,10 @@ tasks.withType { ), ) } - executionData.setFrom("$buildDir/jacoco/test.exec") + executionData.setFrom(layout.buildDirectory.file("jacoco/test.exec")) } -task("integrationTest") { +tasks.register("integrationTest") { useJUnitPlatform { includeTags("integration-test") } mustRunAfter(tasks["test"]) group = "Verification" diff --git a/docker-compose-run.yml b/docker-compose-run.yml index 84c51cf86..4cef23774 100644 --- a/docker-compose-run.yml +++ b/docker-compose-run.yml @@ -1,6 +1,6 @@ services: db_brn: - image: postgres:13 + image: postgres:16 restart: always ports: - 5432:5432 diff --git a/docker-compose-unit-test.yml b/docker-compose-unit-test.yml index 63d012cb0..6f0504131 100644 --- a/docker-compose-unit-test.yml +++ b/docker-compose-unit-test.yml @@ -1,6 +1,6 @@ services: db: - image: postgres:13 + image: postgres:16 restart: always environment: POSTGRES_DB: brn diff --git a/docker-compose-with-secret.yml b/docker-compose-with-secret.yml index 0f8933f09..fc1c895f2 100644 --- a/docker-compose-with-secret.yml +++ b/docker-compose-with-secret.yml @@ -1,6 +1,6 @@ services: db_brn: - image: postgres:13 + image: postgres:16 restart: always ports: - 5432:5432 diff --git a/docker-compose.yml b/docker-compose.yml index 9f7a8f75f..e362991c2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ services: db_brn: container_name: db_brn - image: postgres:13 + image: postgres:16 restart: always ports: - 5432:5432 diff --git a/gradle.properties b/gradle.properties index 47fc2d564..f95d60ee3 100644 --- a/gradle.properties +++ b/gradle.properties @@ -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 \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index e708b1c023ec8b20f512888fe07c5bd3ff77bb8f..afba109285af78dbd2a1d187e33ac4f87c76e392 100644 GIT binary patch delta 39359 zcmZs?V{j&2*fpAoZQHh;iEZ1qlL_uP6Wg|}JGO04JhAO>-cw(_b)Hk_M^|-qU%h{= zz1He$?Q3ub47dkYPxk{M>}1uG3Kj&!01E_!HmSfAGwE^-3y`A)<%_pW@MS>%et17D zI-`OqlRebP1`iWB1OpR$xdusrn3}W{lBjnP#Xx$Ry-1^AWi5-9<2L`pZ9$l;K$H{s zV`Jfmy>)>PQpa;{@{^BeW3}^|1EBZt^w0O_v+pdDkoUD82xrtItU>v{%T}?-aMapmg^L*5T;@!@o-f)SzU4*(-*q?d4VnqE1zr3g!Ifm-h=KM%z9o zL|c{CVbD(bzPwkH**j)?mpY@Psx#Cd#x&2DTLqy%CHo3px}kkO=v|uq?`qZk@9ONk zV6cMah02W`V4gJG$D`Y{nYSbuL?e=itCB~F36nq~G=TqBQ6cK{#Ak72++J7YyHjr@ z)QvMnULO?(fBCK|t5Dzpn-=|KaE$tRR(;5ED{w{yvIt1FYdd5iDU+x?w6k z%ol7AZCYK$3vW-se%4EBkYm;FT|UsL&bJL-(i}-73BLDOkLRm%{tch}-ZIbLpTsUX z{yL5k=Ya9O)N=db#6dprzinCL$u(4$qQCgh+*)Vj;D9a=F@MvN%gHdAS&y~lc%yjm7$FyR2d2paup~3%_kHp)@5m*1@(K9 zqNk{5G|?Gk7)%vJ=h7LW_j6mS*XhWIl~D36xZ9?GoC(B2 zEYTm+xyj~l4FAqx@G*4`E?X{29T(-ZC?*&C*&w%g^&6$lM6FE)Qb^_uO)N$2`qpH9;5L6s5WFRStCt4`$cN7p8!8v^hhMA z(SzN>%IqOLnF2La({t9Pvp~ujv%&?(;&_3=qzeII%VU4i`-5`X7`m^bk*n7eJFT+& zbM|kG2^5)}o{`gBy=&QEeQEa%e2k<$}x;Qp3g8qP? z-&meMC@=MR5k!TTbg=AAGvT>n>owRvkcCukfMG-jR6{%zO8&G2Y<7VR-;z1DFEsw8 zd&e)KL54Oj82;1rnF=!w0vrRxHZKVNRlAwP5fR!7p zj}-kub))D?{qkK$+^<-G6x5fhQ3XKrcI*{74y(iU>?Xh=uDL`-B8bLJn({kw5)Cwl zaLi|oMwOFm!Fx(@wF7V<{ewZM!YWU;-MV94ruAF-H+4vQSp=(u&Wl((C9N1w)*^=- zs%^|qb-@UwkUY0S6_iTK%8=6EubJhT9FgVAVmZ*%g$8JFLn4%`@JC{8a#R2*Ltg7q zREaC4JHz6LVHLO?7}~lB3R3F&Dt$#tc|LZtxiRnq9Nc7aZW%zRLb?X^c@lD&+)0Qz zrARRT@6pF(6dS!Pn=Q5YmH8yFRoser3ky4RTJEL25 z(OMp8%}CZTFvZ%vfp~cF^6qJ$PkU1IT-M0ECN(mG1d3UMt40H zUvjP!g6^4MKJ=fhTMF453F9*oMmdlHfo8cgr>x4dOf6V{s8+(i)9^wy-|s|HY9{KR z@t)GHb1p1RnHO7KN6i_U!!6T)mEb7Do+c1>5%X{69BkkmO?eUA@iJJQ#)1qx@lQJ8 z-^!RPqFl}0@ZaNt#R9S+6;-2{uBQp8K6R5B48)6jMtQlvP|l#Hmeh+ z6O5v<$f-|kP0fB0hh04V&Y+8YNBfg3m4}k4HP+MYr7)N*>^`1Fq2!U9pqZIyC#Qig zvz8KcQ(44vx7Ek%`!`gK<{{>E^U-BW z?AhK4-C5OxA_11h(z9iT+;C-o3(H2_MirP|s_)6Ucvhzok`xJPb3e&KG@mbNz2YR~ z?#+HBYu4T@%ZJy)-Z4o9APHYcGJzvHaUrR18K$(Y0%>&h;I|KMK1aDN*Dw;5Y1MQ_iW|4kgHNoHQ+?KVgxi`HAnp}(z-+s z9G3O@EheCFx+(c0R>S0&DCW+PvaHaM-i^)6vuNKVF!V{AM>Q#Ul=X$`2$eY1b~TwH z*w7k~i~#76Aa$H-+|e@)hH_5D!ob~p&Ik@La>CIK=jtpmGVgFUtIQ`cB1|Xy;)B8F zZkT1QxZ$+`jJ4lU4KUll_#eRExf?;IYp`HxyYQ`h>sG_^YY|xuaBW6h4wf%h1BCpM z-LaZ^*S`=IM_UxMi1rq*um$uZ!t?KK2`L1|BmsX|l9m6EL$ZifS2>YUWE-)^_75;i zVAFuX3^@^?Pek99`b3lT;1Kv?QjA??2BrpFok%c)0!S7M^> zAQq5N={ssM>@fY;1S7a-{L{0Rn#|{(wQ=GWdu-n>R$Ld;v0*96PUq!&piew-8pjT2 zba?op(E9@OcTNpF$VFJW>xx#z~`9 zdo9wy!0n!Upl^OP4{u`Ks@k`M#N#H-ZGku0xKE;hpS)Q2Ch>$T0d%?R8I&rC|De!`FeL{; zYbpIW(xHY<-0dyVKNnf2*cA7L$Q#h?6AP9x`6#>1h<2WF2wYKV#YX+ko}9{d?BU^K z^YQ=sy2JQMluFJ{s-Y-a7eYBiJ~V$UE_CG#@3e)LNdkjEh>yfALGE>{6ci$fAICF- zZ8NQDBe*s>2S`|ov7@&g%g9ERJziQ1>1m;`%_y#{T-R#$s5!hPnD?3J5`seKG z>@|zoVa~-hT}1sEMqL_HV;Hi+D$UW35*;l6ny(G@GvH}EJDYnw9Jx*AN@0Yi(vB&- zKefg{$9ENT9Us}Bt2*%;ukokRJH>K1m`9$=*J1CQoR~suxW)?WtawH@vsOetSTi)8 zqIe#O0zj~QZ;CPwDv*-W_tT$@A)DYY6*LE#FQg9JTPsdGH(*}pG^CFP)%DPZ{?a56 z5KO5DIMx*Z*2pmO09CfeaD=FVU@69i{HhqKj2M67Zl%MMv6f3g_p@Zeo$x!R=jDL0 zBb#93$&Q z?9jqoUP^cW#h+*Zo~r3~xvD>Q+sylO?>I_-N%tn61x5?1&zHpyF2$clF^U ztp(E8-pZ%1~iDXH&Ha#TS7NtM9GYKxQLmfx6KKo51nz==%G7-3vZERDN*!P8dx|-`{<hkfakoor1_y&3B{n_nM~mrwyXTg>^ULqBngw#j2osd1%D2Kvb92~>GYaab|x z42}zRTtcRqweHNOE2RzG?W^{ASI8LgJlvgE0hYNW?u#g~fGA@x_CJ2rQ}KEamEf~9 zgLf8w)2VTf@WOYRbKjg^!zN8%#?R7(0>|cu%-*BaN%3i&+Y&OHd($d-7)D##_EzyQLParT z0Ivo1uPhffT81~ZK^rKSaIIVM=7!~0ALB0^MS7ooMUISKR;?2?UEeM%a)LCRR79uI z+qKhtq~;nL4y9ED0yDVahJE{qNWWQSzxkcNY!$Rqq`}p=wPy1OqND0z?OJbW4J2Kj zg@elo7j4BRQ)j@`VfQH(IsD2WI3q}|fG2pia7TnU-vh-Iy*FR)mS3KeX@;-T3`O%;PY1Y3)Pf?Tv#8t4^mo3dsNiGGgai?*T zE`PE`8OEiNBx|e{SQ|XC232k1u4O( zdy-!y)$2-2`28X=^fBFs?uma@r5~>?_wlD8AjZm0k>-ZDqj&y`S17t( z^3SCC7#V=drUvp?ECY8eJ?a4@>c#@^68H@Sm~`J-2sm^*I#ijvi0wiaJ-TLpDqG{$ zA;ufDUzA%ZK_YAShi%#QoTPP`4>{Du8u8+|+&_WH73kdY-)Pzp`hGpd0&(>sKcHVI zv(idt31OGi2n(UW!O=r7!CB?5jhcoXtyJnH5e1C!j!uI(IB%soLlI42_d!Z3NML8- zd&l~~e?F~B2=ERvr&ehnsjaJc(*BaE!meo1X{pTQ;~8AKuF%kLYb@?IOAqUwa{={L%h@oAU$Noi9mFA7vs07pa5rR7mqMLcW^ZXM(Ft4Q3QY`{WNxk$s?>gy z9|TlxhDuaeCkeNkW_Gk_D4)CMEG^^`7wID?7Yko1wZQS@G%&+2O$F8Hczi|JFUiHm z;XrP;oJS$rH?|05_*DBBmC{yfD`417GEc56j3;wFAZ~{A*XfuG>xT=xd2mSPNx5KM z{jk5c-&CuMMS@3aRZ4Mu<7Uj#BM`HMD-KlvKYsRO3huqA38QSnqc90# z@DEwSN9E=crgvIgR;*L7%@Vm6?}CniS2E%3OY&(oOvF?=Y@E-scV|FhWm1w&;{&8M zq?q<8jm%8C1X-q$dF57oPaA62@eOH|lu-#e!XRejIb)%%fbW6zvco1{y%^yJAnlBr zxs|DZ5Kqd3+mm`dewqC_{bjcgl!3<+P%kzhN~yNVG^mqYWP1QIKHlV(=@}wymKT>} z{~nm5%&*i~u@8%?VcwSWc5hR6`wcKsCdR&o=;wsG#j!Je-gKz$3S3y!srNZzf8a%> zJxs?G@^hw*7|lN_+o~>KKx)CKc&kL~hZeM@Jpb((r!~ zV}KgmZ0?lz5t^2v|045k@G?RAy0*R^KYbw-{ zVGToaYOsD}a(bArz7nM!L9;{h%W5Fn^$GkMl64~T7bPZ(k?J*F!N_|<-18e7=C%)J zSoQJSg_-kS-| zb{jXR93lY2-*Z@o%(wk77mlPSIot9Gm^BedIef0iTdvnUIiLQwM|1(WHaO{PGe*Kp zH2h1GWN3W6`s~V_$}Gy}Qe4G!!IYwfTD)dbtzCgeT4z$4U4(fLA-uZ+fN<8=*wy11 z5>1u712a}HD!;H8%xCe<$I$c1pSh6^`Ne#8-c)3@Ub{4qRhJziE%;aOL9hmH{2a~U zkx2N)&ji+JPa&OP+m8&IJxZId)aURPLj3dCe8V#=LPxEG+XVz9=S>+ZSvFnD!~&j% z=NUib>THYch~r|hv6sy{0Y9#L0mG6FC$igr9%99X%4aQP?9t*xZ}ckO-7mXtjpK~p z4d>va^m*L8nOFAl0GSr}Qlno-TBaIKN#(5ij@57bB})siY?mJH38C<{+H0Zq#z>r^ zSDv(RlUgjoJ%aB$fG1QR^54cswBqo(_uD2~u{+~|tAri7s2xIsfHMq7iQf2OnoMI? zLV*>e7ikW8cD8Z%9J6Dff)5?25sY`FoGvyNod^}p zU%F*B^2~Fh1f9GSKNpd8v9aV;Xpcbl_rPdjyD*f-)S`)W^=l1fXuc!oBoD}GqdiOq zv{@bYc^>*yABE3rlcCSNIsG7$R=w9SwG@9#Da9{RQoNSQKzyw@>0A|d6R5RiM0CxWf8nNEIe{B{dLu-cJj#I`nl% zdl`a1vmFMqZvBSx_LdqL^!d* zK-O}DRmlshpWHv_<0sGkJp?X7O5?0Iy-dL`yVL`_4UcR^UwsbltKqIBTe3cJ7t64b zzmn_`qox|Z2XxciuqT(Fmfsxmx=lV$`SO%uOOg-VW|(p)F~)U>>g(CS`BhV22Fqt* zF|zsy9kNjkuI&j~5)1^0p(ja{;hsi)Y=+m1c7WFCIX z!qLU&CG%71T2$r8J)f7-Il0@{`kY(fZv0L?(oC3CFv27mY4flF3v_Sd!OXn$INj7! z5jV2Fj5eQ9IJ^31tlEl!tydbe+=cj3rxzutOh?qN3~h>Ss}LoH@LqdUzJH4WiVDra z)$upkA;%P5#5ecfLdUSCO!s$BJN8NrKV zr}?`s)raQceOtF@4lVZfd}-W<-Q~L`dWFQ15)krQl}0$381$p!u(g9afotQ-WsC7p*AqOVq0MU z^y$Dj!s@BbZRUg>C^5diicmt;7#SFFpMPLFUp6X8DnmG$wuK15KJ60#Ag)c)g8aM` z`~73ZWG#7*aMet;S7LdKH{Y5wi2B}P5gQ+gb_b;mo+rioNjzR8U$h3OKy3>4m7TC9 z!5IaobE)U}r)_e@*r|}JjV0`b^Zh9~7?cFMHN@?`x+XUCw^#HgO9Kso`4Bdx^BW&C z!U$&yR8{B$z8eG zHfjoCbEZ+*spCoi9CXi3^#sle6<)?eymNG?Hf3lbmDjiW(#jE_2c)X8VS+skh9wGn z9lthPEPLGEg!s`Kv6o7!_|)EVW#y<8f=?g;8^BIY?$Kgj0=0sdZQ)Ake7%xkv_cM& zu=cB$BfX}Xq6}7d#85!#6ibreGo3~lB95C^{U-U|1J~`3LetEqHDb_N#Edu)-dM-l z7xfS}=pvs;h@u&wDLJf+E*@6^e5Mv5qmCGnK#pCB`x(g@O^lkO*tbAN7>B8YB?3C6 zOO4`~uh$jhZNgPKM3_zWWD?;8rHMKNo5xl=upHoNA|L8$q94_TW~+OVqqT#)-L{wc zvr~XPw4tN0DBf^1$dvlLMoGlbRKwBv!8N3_x9_5e`(P8WMm@o{>grN=s)vdvDvq*( zR@5FIVcK9Rj^PbvB1G;Sz%KOL=Jtoq5J_`X96ubb->EiSM?LInqC?dwzR0{c+-Vo& zWne3wz-1r>xgzdx#$ftv;^-I;_DHp9`Gq-Cp1W5gYSX&!Ee-9krPA@_h#*ZL zpa=!AfIcX|db*iSg*@Ja8ZV1^a5xPOTO3p(hzM4&dv2_aB#6Lr2eHT^sIrG_cII3P z6YFcBDY{t0{lyIKM1UjGwQF!>t|uC9W*Xew_$xm;VRc?xNWvp&&D)LJaF>t%ERrL7 zKM3sm>!?YaUxJ4t*3$#Bt|JPAbSd-SL+Q~4=Q03i(h>5GN$m?F%dLR;Bh(g+|4{r3 z4Bzl9HfHY}-6@wZMeG68EuTLs!0RhAQPD%?kB3Tj)Gda0Z4_(D{k_-jASQpDJ5X>e z2m*Rk;U?)k42?R)>0qav_s1|_wk{%Vb5@X(J!NUKU3^i)Ti9zs5wc_{xLsP=T* zc^(5CE+}!bA}VTJob%wq1m`GCcHgt%8uB7&JpP`xf__&~Y!>eFvUiMTL+t%tTKwh( zPp&4WIHg2Bso-w}=NmVvFQorUU6sI({o;Sl>fxW<@&B*0V#+1}=qoN7p^tuB;%%>B z;L!fAlM`EqYYc>jwkdo;JrF~Kr}TR4%hX;Jps=?*9KmyZ==36q5ZMnU>O(T;SJeF* zKoM*%?K!jP>VMpFnb+Iv50>7a7>f?eyFgv4O-tjhA_>X0yq=smAuUJ6B087Nu;XHe z+6EoW1ooQZB(SItcnoz!jkZ+DylQ0NpM8LCQXj2JJFhKYxA0zUotRW8A~CD6k!E|q z69U!Vr}kv9etm|ye>DIJWx@J4jl1xZI`QeFK z#`iFA$1l4gdI;7=sNOOs5EYxNC(}yO$|bj=C)72ne7F1z*bk=O6j_<9>X~$q4RsgM z1G=Z|BxHs7OA`L9_WbD<@U-$7O{R(8PDJZ!)Pux#^haQ-A4FTSHG@BuqgKFBOV%Xy zrnM}>^Em^tmGr5hka02O_5g_dpk8+Bkf})DLNO?5SE|Ma4^Ldh*oJVzZEl1}2Kl$a zXqS``2N*mLAjgDp?PfqRz9avnn9A-73~9K!B$mTBOJ_`HT6IVN=bR_7%-B zim%GO{^btKk9dmO#VQ4v{mi zsVrRZu0R#d^2LsueN;OYSqfJ6gD*S*x&vFd#l#v55aGy>JMt{NX^&qsJnuw^1&Zh> zJ>)%e%LDd+1t9K;>RC8sY`zY6$^+G7WVhRUBg;;7OEsZ49I*oL!JMT!!W#Izfqa^}X-)>u@P!lA2!yu?z_#VT8*aXo0@_&2V5m`1F%6JaOHo zs=nKp=Ro)%#f^X~D3TI%mVXsKnxucQqED)vNee<$X{QdhwqCFlWPM5Kzi{k-6^_)2(!sP~>9e_}q_+QkQtL>08xf0twqX}(;c+z4~JX2n5Q>NP@# za0ic`L+hB_USE6(;KRsZ6g?Ynwd8r4K*Z|N89yK#estgD9JUGWtd$0a_R%kPXPoFK z$$zz)BvN$w!4J-f-C_WyT@f5sT`3f`Jp8Qhctq2qI^=+KQVh-)2&00;zlo%u{Fyp- zqvJ}~6|>G(OnWXhrRaDVlsQ)Et&2MpW~IalV94FGN3vr9{@Dx8XoIwW-M0pUBE<=0 zwJg%%rp%5)Eh9N(fd&=ow}bG(-CHV`oP!Uy+|TFY2upnO$V4UOA2StV-D&+?gu%MfXKl?~dUN(=Az^ z2Ho$YFTyqe=efQO)iEx@2o2do`_ic>?m&LFBXLGDnqn?9I&R?)=4_HGso%^s(qx@* z7WH+|II5k{fVh~A{Y-?HeIz-W{_ODI2kwf>iAX5~k~{6r7Q9b$z*dDcQA$^`!#VT8aC zr=RNkn!j#Eg*0TUWb(Af2_~XDT+fyIMisO3vc#se;UJF=f()6$Y?I7y6UbWX zyKhsYOk@_Fdvcc|{iqYi|6&rpG0C9wAH`PwqgbwgL5ZV_C6lF#v6)>`w?9f!{yssH zbRic+?`U~mbW>fFP@C78cU8^mPRGj4@4d(Nx2x$oddni; z42(khA$-v*esB2CE}fP^X3gBZk_VZupv997V9(!caP{{RvQ*}+KxQ%(cs-GkpP0cS z{G9O;Vd-fykeKyj%r2PEudxR!7kzzb^f7D$QyRc1ix9*yX!NEZ(4_pK&Z+og@$ zggZLNyieG}%Du_QFYH@lSRw9t%t58JX-vU|q~qz*%jMq=Z2^dyotC(#UQDFeWqF2? z{vEI}Nfy#N8qw)9zz2jNLRxJ+v*8oDtnl9%i|Ve^aX3YOV)zsK=FI1CA$xh2uJL92>SzMY2&Aaym@DkPdaafev$8v#Waav>S_9wesGn8=QUMv3ZIhS-X`lKNOId|qFg;~}? z8VfX39btnBP>Cadbe%^5y+8%N0!7N#|Bo37`BrvLNkXnv1(Zx;(u$}z2Wb_QjH&#D zrXh*{*VL&@Uy47mr_O2wUL)UHe_1 zPx-$-eUSx-SD#23ON29GAwVEB@v2RY9~Fdox67|A)#n%d3nJR|`L>LdV224HTh1=E z;RoF}TXI{G0G2+x-RB%OZf8@Zd%ew+5{-GgL%QFHe= z(V6Eu{>v!BMko$7SFa3BM^OT!tXZ-4s832jpT>2zJ*d>Y-VT*eVOt3ljvSgLq@f08 zg1y!$Z0*x&bX&KPsLB+Icf!8mC(;Py3m<7lfiAf5wE77^`7KWP5^y1u|ulX3L2@3j_a zJ+fNQ^|-%_{{dzVCad*t2N*XojaEWeXX0r&7==GIEc=jAz7d;^N`sk>v6}fGPuIWY zVVwX}8no!c=Bt)W($BsX=O~=%+c2&qXTuye%zD|h@;%FfjpGd}U>QS8tbUUw(UWDR z3qS!R5l8<~b;hBz*%+4zNUd=)ebSv95+F>(+o;M7Qrr(r-VaWFJ^h_SN76SSb{ma+X$GpCX^jV!LWGJ>O)LU;rvxwy`2rH=1R4nmAPf(m4{E`KcubYVa1KMFti%>a@+ z`hUJqiFotol24yZ!NIhf7Kms*lA<`XejCVWy8EJpG4|TIgK@Y>$iVxp;=JT8!i<=b zgm;LlXlBtmqQRQpmR!3O7Z$}OU`Y+=bo($0!i-|M96pd9jVpIL%Z&ZnqadePlB&w? zN6`S~8MY`8rl$Pj*e-L;oU-op$_C&#ti;0|8WS@O_hfL*F8IJ6q94}a>#R{+EMeXA zJDwMD3JQ@eeL>0B`}Bw608vVv`EB}=p23e_!4_nt+KR+5@)s%2 zP(vZMKs>aaLCRy8=ei}1gY@gHiPq9&WfASUWF)FkrZwE828+l^)n5ymzZSegYJk8O?%HWUD;Ly<&K!Cdi$prnJ=tf+Ilr%=TQxwyIUIrbokY^VwHc zv&J4n`;G5k57HtrD&o`hisw1|+V@x&aLngvAo%rQ48nF)f!}~_!1Qh_~o0w zCraSpn}1OEB6WPT!~UT^DCBZWDY#N_fJVT>UAU)8FzC#VHR{BV_3jje6i8WU8gAxI zp*u?LY`wq2qQP%4ztpKI;DR1nTO{k!ZBm|Ke8!>|6MZ8?Xr7Q@FurP*9Bcg&z&B|n z59@}$4^|g#OlP`mnys^^8mEd52v)_|xjkbM9=EQ^N{@{6}@x` z)3m63lGC3_)UMR57+-JKqG{@8l}w^t&Z+XqIZk<%d4Mp`F8_mNtx{tOVDO$a!M9XQ zor9BUYGGAKDlyP5RpXN!HFv2!`&W!>o`3J&{xkJlh0fhTTiEynRl8y`)FnalZ26E@ zo)Ht9W3L`Z9~E}U#6phQLR^v`8)kL1sNd;4aqMsGPj=)uF%E5p^k#BoHroLfHhj2| z;IypI2ypMGvj=xu@B>BwVp}+w?%>nm#7}k$iSvU%V8uAa)@@MI6bG~fFMJ&a`Vyom zi-Y~FhNNx{&6&zJDp%2ym0Xg0(EQ36Prntt!Opr=J15k@v|0U{U4+=Q|3PN8B2NA< zTVflFi%#MAEEXpkF1fZ;qov4{DjSU@`|7G%{Z?o5$4d1{#!586OX|NaAYu7QG?XZY za?|pbMguEync7}^5DJR!+OWPfA=yH?Ef_)Zcm+~x7})I$HHTg*58tdm0>nTK24`^l zB-#8*b}H0hDZ|AU#32tWGOT#(3S?WxG>j`uvbm)80nXR{RCxt0 zZ{f=3f)V{3Hmfy2Y_`jY8{3e0jFH~N4VWsHDNiBfS$V#-WhJ3WBO?5DC#6c<%!`ar z`8%6m{1CTM%)7qQ_DGz zqlYJbkwc(_FTqzaKz(w;)DZ^EYZ5lt|9jl6}~L@?+1G z0~{-fYb8guhB)s5EhYhl*}!eUn%$loWt3~;U@;CLmxMjkHQWlVbv+Ag6I}IebDkx! z>`C>6+{`s+$!P#Ey4(aQ#D%|Ny-+TlL<#P*S}GO)gR71?W@`osfxNsFKH5qMA_wm6|Zg;ZsS$#_)t}V$rj-fq z0%;B8)rcSHw^2P`0Mz10JSU6`)Os=DWzYmbI1X?0OrZwRx^X?eUJU*;o*_(3oJ0n2 zjDwn{fZOr!)XJoJRleOE!6KYu3rdqA9i6V{=?4Nun45GNiRM^Jm>z{Do=c1!l^Z%^ zGzJCr-!v?|JBW4-6n)Dv#C2(}=D*9$nZug(QR;Fg)pnxZEC+%Jz{KVCMk{tyj>BI7 ziifp8YZQ2ns%Vt6NAdRlc)TM@Q3s8%mqa_RL(JDkl1{TSl7Ax+qCKRB9EY64mAyNV zLyaVDlLYkD_(m$jBd$39`WPJYZ|Y7X$diRV=OL_@IcLskB!)?yNivl6Y+DYTRG@L% zJYs)8nOMRuQU!1-)g+4o8AGSwBUE|;frk6`qE`u7;LL~TE_M0Km2;vjfUfC>d+1w9 zosXDik1l%bCn|*JYGm|0gyD!oce z@a&$iS>Bc5(|=tkhCP!-du_+aXiqCs!3XxFwur186w2-EN{m6Jb>Fh=GPU@TW8T{! z5A45A%@-#TwgT`tyqDrf4}CHL{I#f#QAXRyJR4z-ks${^elZ}QS;2)=D6p>`I@%P4 zXVOE>Q*2Gza4Il;Pc79C$|d=QU1$bHGwJo-R6Y`DYJ`tHpSf2UtdAfJi-+-(C@z!- zOE5&eiwC13{8G+iqmD}2UJp;yp5gk{KQ#+L!iC++JGAYqE+}{khyxP^h{t~AL4%j% z9h0&rn^N50i;Rz-5~wisB4Jlg#veT!<6zKCI+UGp1y6x4?qyKepsK#@CRiQ73|+Ka z<2))AcG2N#wfa;C<P~ahl?mRucb>s!z?Sioux;TwHVq_0j>UwW zlkOFR-P2DL<4z{~uGhfoD0oH+l=aR09iQ5@j*;NyTLE7dd_C2!>ya+!&Jy%ep(1T0 zvLaU$u$>v4t`sc*%>KkI4Of6r@qA0bIkg3lC_m}gjARz}A6m8|KC&3M22Y5x2bFDK zG$MO@?U>pB#2KvFkFj{x+2NqaWok3D2VCCN+e(Iib{Aep=3eaygYbN2jd+y}T}DxO z%9gXOr1EpW;lxqxB8##)3+LUpn@vY+OWqWPyI0XiuO}n}JcPzx9&&?f%H930Oq)YW z47%0RxH51!8`D-dqWvUY-{ zQwXyf1JM1&t6tfOcJ$J;Z50m+UE8f1TTa-cR*n?n6KiHKrRU{0;%I!7ex6kn{|VkQ zkSn4CD0~a1#g|VMEj@W78vnw)6p*;RBll=79>jzBqn;^VK|x)|a4t@QgK>oQoG*rw z_K>gEa!}PJ=!i7oLAG-&Un#5X)P&1e=c@S^{g66I{bJb8W|*ratygB&tb$7Gj3)KS zozJ!SeJHM@A-++n0~qn@AcrT%C|<0~n5Dc5@bi_>Mw+4a+loZSP<&UOI1a@Qk75pu zP>NCKCc?UJ$Nf=l?4lRM$Zo{9!iurNazD90Yq<@Bz=)Ojug`9xsHnzM*%0)(tL_!&N^GRcuVmU{_v4btk#z9<6>i~=acnx) zSWli3e0th7w%Fo+1?vgEX5}gybEG~76ixMxCZ0Md`^5oo1`=k||y9rs(kpw6(RG7N>!Lwg+Sjn6n8xrzIzwXo?&AVLjaVk=wy=k~tzJTCEVIEbj zj@%$>`bSo@TnQ!)g||6E?=Uie%`uHrjuG`Q_GVizjX`6<(7!L(s&SR`ev-_&Q9X(k z3`u4}vzO3=cOFa@T*>%-`1=kRw_Xlld#-{-Ax|145WG}u1#2Dw+`AYZcaQI6KL#Qn z^|2McWFwZv1g_qg{rjv|fY+BVB>qEI{zR&`{Fbi39q=xr`I=(%?c|X`KX^D6jw=@$ zgP}Ne!O6{We&cKNkC1{KvbsHtk7Vt=q~ja5tAv=0g&56O`Aul2;P z+{DA$85AaIV^`~oG3tgCJ%0>90WAiyE1*`JrH%DzT zNtV3yU*Kh^!g0o3p>;)#igQv|{<1%2qEh;%4vjGdT`wod(o5zvHHqrbjy5Fi1ng$7 zhjqfHJ9Jiq60!Eig#g~SB-M&_Ma&G!lIrbL7s3iOqBetoOci%d!^&Ula2<+W!`K^Z z;nqj6^wcp}j?S}fG$(u^S%&vZ{%J2ygtwPmS|K|oEdG+8-*lmlCDn*K{eW_|$m7>K zAgIQ*{+o*Nnn~@aqHLV_VLjbvbmIwNQbU>rvDOs3!ng)M)B-0^qUt-uWvy)H`uFqp z*6PSRv0oX0-Wgcj`fw9;A)^^+J78ePz~D4p?V?M3t&ptRW1zPZPb5{G*ZD<^i#y%5W?3w02}lAl0T-QQ2ap|z^)a%-Np|vJLydmoNkaX$BE|q zpI}$~QHJA5HJV(u2k7*Z?V~UCTAf>S?3k!JYwX$@E=_zwWk*x&>h|BYS~Z)x!!A8s z2xp$)?CQ*Ya_Hdr)~?7uAMJXrNRG0+#*;wr6`PiZ{vswI!eb);zf&r6CU-Y$J0`Gy zlRE&M|DPAu4sPZy4#xje@!vieuA$#n+`kA*^k44vKLbMln|uA=7611`C1W=$H5X$K za~D@*J5eihQ(JQvMpHXuSJxCZS)WZYbOE&W1UovvazF^BH4LdAxkkhirpj1w;(n=; z(_%nUa1UMpmd|mntKr-E zwc$626T!}5LTK73kKVHLbe}uH_0g^1-@7>{(HueMn}UsF1bXUDaWL7luY`^*_M3)4 zbN62JT>u&w1y~zG^`SFV-rpOXE868_$euZ63v7csQK9zn{AqM99M%#mP3CO+_W~_@ z4v!GMW4<5K)h=pB{gi1v9nT>d#aE;720V!zb(F6T#9bQuP`N~uB4PYDD_$ywX}*eM z=};vd<$@S43H{NprUhM;L_Nusxf}IFiv#TVG2R@m5E@hSFNxkbQockkdF+6=k*#xO2H22cE& z_WN4pxlfJz#URcpYpFluWpkVDS2-FAnlsNG7SBpdRv)n+s68+WW98A1oRmjbDO+#m zhREV8dcH{on!c-04EA;ypg0Z>q&U3>m;plhT5%I~Q2aneNX*O)-RFZ82{xX5)Df68 z7!1w|Hf*Ld#hSVAMz!Nc(jNPWdfEjE7BS$a&!XZdv4U7|ffCwyW^|?qZAh(fW~iG- zxq2Aq|KaKzcr=T)ZM$sSwr$(CZJS?Lb=kJ@(&vzMv1ZL2gMD-Cm@Yx0k+q#&I=A4I zaO@<)h-sAy=(4!F&#`7HPu+SF#Dy$1j$0Lna`>5MAyx6pn5;4~swO;#VlVHsaeyQ( z@?^rh-VX#3UV48pJxRt0ejzcF@{ZNT6iL<>mxn@0gr-p!UIKSI+2ERFqQig8Ve}s+fM+wW9&F51Ai#zWN zvcD@dCtl)M8^_D0OZQF3$>o;C48Rw7m(DkzB>Y8tHiXG&IC$*LbUM^YXhf`&KnMVx zIyi2Ir@@HSM_R&q$mW|z5`iF+nnEbR4V{L5ToHl=x57Hf9Aw*~eS|NfG51ulR$-xT zo1^zKxukY&7CE180;$nDRar8FHI3isG_U7jyHch5vRN^5@l>Gp(ncZ@TkGvR<&5*R z#kWc0GGbu})-T||l>&(0vwnvJt_6T{)%7A~InoR^HCb|fYMYp)`#c=i%h7}v=&)ab zEi?@N92?KpYPp5vNcm6Lu6`S(V?_mu90b`dUt|H(;|^Us0!#}FrPMOI6CB>7-$kcf zD?0mUQ|PzZS0<|lzAmHNgzdexE$1ht#Yhdqx=zi*QuIO--o>s}zp%3}Spk2#6Ln3a zkcHo-quSP0vygSOvz61-^qn8bp;Q+(G_Q_APU26FGQxN5JSV2m%}cLS-YOj0;QyZH zCDL)&o+qUCo9a`040`x2Z0BR`6t*@LrEY=`9n$qNwR<_4$~tW7~emPR15LR-RDN%5yRyHM$8jfgKex>r1= zPa#PFGX5#W!CdIVJ)m7qT9@1cm`l**e2RR#1#%7p^Tw|2hb_jeJMzU7?lfs05wvE>Lg4zYScoiECRP{Mf|peR@PrbUq8awnOu}gSrv2q z6b(*1$aM~GPHmA*qC(V_Szqb>`f1Z8(X#7d2|dP+=x)pYOoSN6hNo!pB*RjwlVlFK zNLI8va^)y9m7V}qD*v0||3?tw)=HxV{}6=#zX`+oe-nm!1rw00X6K?JhU))J{z%?} zgKb=_TBRDER%Dkz+&xRIsI187_t!yDJK$H)m;#81}V7rZRjGjB%{7WK!H3~S=Sp$Z8-a^4 z+cZ73nfX+ns+XBR(CnkeOWBgkK5#Mfj67h!W*ppr!-BWi@B}+@5oF^8T~=)*ACV%( zs{g4az>K-hnQdm8jh6~hlvW}rwa-X2STaqHLI<$ZL@cuNEp4jOg_7#Tsb(Ec>|(-C zO;2rVnsYwY`)3xAR$!b&G%n(zRU<}lT)=Lf7hJMC-N1J@O~rZ`hMzOYs;F0v&GE8X zKK~O#Z}?5MT;gsaJoQrsiP(!P`^0YJ$Q3>lum(p`P z9h!WZ#V5Q+oI9e!6mfH<;DmD<^dB^W>jC6hSifpSU4cZl^p{BZi)ieRK8ld0gi4%y zn!$9vP_1g*KtPonD$HSky=Dvh3KPVYKEip2G`xaTzM^>CV#t0*Lw`dhj4Ae3o(^r8 zq7gsRb&4R9WI7c|DhDlMmzSLVKsKH*x;@(de1pXP;l#COWRwCY8qzSggt7gn2@`5T zf-9BAF0rl?#1>*N6Q-}5JdmTjA*$HKT(d%@{~97e)h8%nc<%YHD?wigEejrVV= z#5^Plz?3$W@Bc2nyCB?cN1Ww$~Dv=3m}Fnm#An zPJB;zPBu~#U)OJPed5DX?q}}HgZxkOV^3cI_>;p-jNt)j(+_c(c=+BC*_v%LIc11@i-2zV!6#%T<*pT?X+B6hwN{iRf$y&{ob`Jw1{#0Xsjkwfn3nB!`I zn%h|&U8)djCe_G%ao8q&?@Wc4TV#Fe5EfMZlKXUg?db_U(r+{rv|DKXvwV&>+T;sf z-I>b4{3YOe?LF||nP){39#wt`-<7a@r6ck?y-MeXSM)F(9aMdaRXv6fbtYTH0dc0r z@O(X3JZKNY4Yh7bGrjy&)a|`J{%egjL=RX>-m=@caiL_7icXoMxbcC&k-9v5B`?vVNVS*> zT$MFdu9|y$C&$=+AGGa}qNb1m@kXkm5o7Tvt8Z-+!PwBrrg*+RMbVob)M!|Mc(i5} z9VQDLA+s0IdWVDDCN6Wod7T0W$uIefv(?qPwqlcyC#_~!RJq;Jax-KieS4T)1mNt?2 zKB2x_DZBVn!z)A88w9K-pMv+%Fr;d`#7sk}zIG7;c0{-|JNAOVhNP3Kyv$O!=ejSx zSvig_PPPQc1FJv|?~rl#Abovx_QQpSyOWn&YKd$BPOSOgJQ4jN zZ4}5`OL(~K#7-y$wa_7d%t2I4)==wnN1u)|)3fB_{Y7r^VR7*8_VC0Tb#{=LgThJt zye%8>;Ub#gTr)(3B)#ud$AM?$ehj9y!E74diTAb??F|!3FjCDZS-O-A_{V_hTb)n? zcCDMcSsIwoB^hsdNw8J`smI-T6`?4EzM$eM&d<@LXsWpv-?BO$*Q-{K=}(D7{ma2X zDmNR!F8A!g3~H~;!3-AG0!#r4tK~Im^MU|b(k#d!D0QS}zsH%;UMOp+IS@mF)B_Dy_x)z3qKK1n+nMpMZx8gD=k z;x8&j95l0nov`%2IzJDkXDv{dhi!fnQQLh-rv!7GRn`I{z95jhG$cJ1%hhCPr!EqH zM#2ig?PhUA_>s^K*gn$$ET$*LOeB2PP-c7P_e8?&-;3$AG`@1sT%)WGfkxvL)IW!`f`$gGi!bJPxEL!* z{oc{uw5uF*DB~{J{`Cn#FrM$qHk`3_+BTkqfz9Tj_$(;^1{?U7vHhkpS_!*}rF>Kb z>=HN1b^A0Cq%==?aQ#J0$N=r?CJ5iDxKSzHA#L%74+0*5$P}=W-x29y zQnLKHRph2WQ_jiAH6A;=IFF!2_viRU26-yXkvqh9tz9YndZ|1)baEEhynr*vRnWG}(8bPv(P<29<7sK=SR{(EP`=xlr)F-0AZ`$@+?Sr_mmH?~)#Z zZhW}wFqkatpOUx$Uxt{WK*e%_9AeBep;s8^c*|Slj^0)}a#rtL|C+m(*AMKTX$QhL zn0y;i$`#h}`}{j6e-!@|dT7Wrs5cG3LCdS%27qOTUNW1qK0?@Ikhw#SqJfq2GiF#| zmp~Z6w?(%^!}g8z!wJLSd-FJvF+r{P+D#FGSxc8mW)Lx$H>CCD%Lvm(9(%Rdo zX(MVvn!}A@`mP7xHT>xlcsz^h4m13@7j_mqBq2^rxNrkuOK@KU7&vx6-Le6g#bfhC zYMvFd;xftr?5+=4l^`_C*`Gi$=Jkx=MVhY{g*kQX((iNtdkJ z`Yyyl_-iuC>r{XmHoQi{PBS!8k#H z#YCSb_XE?ZA!c0`gZfCDv{`4LF65i(Pf_>6IrInnoce#7|6I%bIG-j0Uep2)p8d*q zYPcp2U797gt6oZX#byB#>Yr2NijvO*GM&thbhy2;-6(sD)vY|#2FW$`cG1RKS!ngn zm55JDr@U>`&j1)|Vp>caIS)q22VDe0Nu@W5R%&}G_5EMxGQ7*IK^uct3lyD9?6!y= z9nYdjD`C2l3)F7MAFF8rz`{s_Wt9x5;o&dMtFQ;>=P2H7Yt;bNFVIh1Ayy;+eHGV` z`_N)df^?B``WvH7p2H|&v1FgGAE~)^gB)e-vn<9kdim*QD^Y|aPzjxqpQC1Xjkx?M zo69HnT>otPctWrD#6v?wr~`AxmuzrC(9h}$-6!Y) zITYj&dG6}TAt#{0Za#fdhn#QS0^LPb)jJ5=Q8|`ejl+yeXM_Hn7p%b#ju@q9|^!O$F-eg5bssc4ZK^*|h|KKp8`k*rS-YtaEF4 zwxgFv?<`nKay_(sGGdnX;v9l56q^Ou0ccXBa2^SY~OeTJqA{XweQlVm=+G*TfPAX zFd@VBo8;(MN+BeJtCSQQ&X4@)N&T9)cKuV-)UwTQntg{Ow8F#cGG5UInhJ!uDCk_k z1L9QGZ5uq2MwF73gV_@06t`#tjd?plJi@5Ud=^82Zxmf#H+;iWTwb#IEcoDzxVEgF zs@zp0C_A!pbxT=sk#GOrH$w)9x)6V?TdZ!={Kx4m z$;$;s((|IMdLG|dle}DdmI{j7O(s}z+v=>Wfu0{BWz@nVD>?hB!wV_FK?>7-AYV^qqEw?@?bcwsRZ%frhB4YV}OQA^#w5u44H1vvRCGMZ@k znf9@Y3Bsa*7P5CIGb{#^DBA|eE%7fgu?PrN!bk0dR-bJzL0R9n#l)Yxkeyzhn>1E) z3!UKRU2}^e;ueY@rjg&QgR;Ru>2zK&HkfMO*bVUt;rNbI+VN_0(7gmlw9ax|oi5A* zknl| zz3*Vu^AN6Vev&J{6TftYR-EYcW+qO>^`$uOsN2(L#LbZLT{(Eg2A3kkFu{@zTv55y z!H!_gaKh4j9g#~MjE@080v)G1d78g!LQc&lp@~L35MgS2NBE1hz0qjIUe)GFm>T$y zStdvxT7ljUIu3s!*yg%9@%hDeBfz2EDSKyc{{}=ba>;`o5UQ0ah6~$4<7u4A1#c^# z+68aNr@Mr$hD#DiP|5}mAzw7kJ2BQC4vG-rhel~-nmYUywh;%QzV~q=t2)txeh?@! zMv08Z7RDpRR=oPvW;@V_=7_;p;ukjSFLt{R&j#emf7vn4*sA*$FmlEr_)->Gm(5lS`YXH2~@d_}Z+3V}7<#ies6 z0v`FlHs>iqFYCQv7MuBmFq$;HoNph9ejp3*+QU@S8t5)LnIxx2lO0`wr^5H++;UgEKl8 zw(%nCPKlw;!xDZ|QbI5%ky;V_ZpaI8L1NZJbb>7ZkPbRkgOty&eaH`3>wTqpx7kz_ zDV^IfJO%}rvLpBI^aYB{{srrRUOKrsBVfD`E_eeo@0YNbI(tVi*Ld8MB>s{70mTmv zp`miXJ;a-jMiT5nWkSRcec@Cg{6&f?;V;(H%=ZRW6~vTjS{*g64JFq&VMg0_S;j5? z5HF!n&j-m?+tx}iWZrZn1hMCE_lJt0KBFZyz9I(Dm#Z%%?UyWyEcGF;BAr8iwgbCq z_Bd1jmK93+BsA`!=q6AAUU|`f>=4&Nn@jcACxeHyvLmsodF&|1R_M6|G=9Qw%q&}_ zhC!-Y&)Lt)`s!Ijt;y^ zU+-VMX2L(7jQBsKx8OQNV%8cYAX~%B7gYoAr~8s=!W7twG9SkhhpJhIP)oQC86E~| z$P$tjNH|Z7yko#TVJ+RwJw=eOtl3(};UanYqo103$Q&hteH%ji!Y^{U(7Nk_;8|tp z{bTBqyd#s0?D%2K=VAL}tLNtCWjp8NMg##wGZ-)}1Vf9j%-fj;kr~PgK!}M_Hzmc5 zN9wN&Dr7b_^>m8rv4`6utR{@Zy{vuwzc}434tzObFdL*@f?n=+UP;U~5g3 zx=wK7(8uY&NPB!D>I^XJQX0;@HQ)AB03Zn<_x=$1N{XZKPeKqFSy7Psst#*!G9-o)B;GnfQIPox z=e>tn)3Q~iSzoTGaGZybGq$)W@)d}?(Trcf0Vm6%tSrcP*uzZD)Y^6l9Gs@Kjj)ra z%T9$~pIsA{Or+78r-wd?4y&ij8)}uBQ4~PwMB`|?uBxk0O~pI|yj^sNsO~~#mL6ft zDwnIS#z?`edCfws$qV>I4aviZPGL{aifO>mQy1oqO7NB#)9efF&!^1;c!*h$&>`|S zsc$aGc-Xei&3wQYIV+*h%*iYc9*PBT4BPI^vRo#^jmV!buFW z_J~hy`ikabvNvLe8&&B?oRynGSvN>(nc3)a>{~+Q29ys5TXXAD)Ddj1iZxYmX_m_I z=3x6txD2kON?l0Sr5Z_Fn>k%al;3WbQfKXH;YZB(tIwSPi>tHnY)AEyuSr2F$oPad z6mkkOoOF&}{Au>$9?uBYOv`+w5(;S*iGQWGQqDlZz!lvFnVYNA|OEqWtlUK;};m?{i$>{aWKGEr@h_qWs(4QNpZQGub3DfjE9a8$3bwJiq?(D=hafirivUfUnQ z-DSHnul0eoyOqe@CA$}#rMoz|zCZ<%Z&|yDe2e9+HUW>ue$}Y1y|D zNG+Epq^C}w`uzl`pGXRnuPEPDy9}>1NU1>NL={uxn0MX_&gU%0)!r33&}G|yEs^Db z^X=v2bj)d_zHoRxJw8SH64MaNG9&aohFu?v*r}3C#ON;%2^n(AHKly0%t33ikXV6N z^o+v+$B8*MdW+4G69F01i{hb$N(c3qzDc@~&;_abJS~rjDs$NY zc}FgrR%FQZnvuQP9fsz>9G-18vJS)^B^L2K3mxJ9 zB=4sHo*acuyvHU2v^dM|W}`Ps(SZmhRaVxR{uRXM*!~?v_$cC8MEDruRYq{Y8Bjr0 zFN`HDvVi0Qj8#=JU&3#BSD9K+eb9y+fNt{9thO77bR$I`ER0}1RK=XCP&NIA8f{`O0^(d1*NMY8c9Pa2~R)cu4 zw9^Z3zI?V9;rra*J1ybM-%hlEb8X(hd@MOlUMt~BfyaI=~%5>=;v0wM0`#$KT(ZD~-Qi@kJ8>+-zE6{ori|MKCz(#nN>4tHde->2IU z=CYh%*dJml<;TM98{-Rm+I%M%;-DRLVF!x6mQco~Sj7zmOX=BZM)zILy$g5YRjJLO zh120e(I-sz1(a3{1*2SmW*Glp{Es1ZT*M8>NX{@3VyI}=@_zB2Rstp`c&9r^zUmnh zL0))0-f*Ei2H0>5Lsd}2wpV`GbXIy|x(=u*3aBTaviVO^0N3-pMqCjdVfF{)LC~!( zp6DRTX`xDhkrTnv8qhu;{dM1zYBoPF*|{SOd_vNL=9M)3uGk=;7_rO4J**&qUY_L; zW|e}7H2=eDvbtSJ_csaYqd_k)6cf~4=^Q&{{@G6Jn}DW->TKAQLLj_`Lp~x|C$i`w zos=S<#ouq$HEDI;z$}PFV@q7eGdI}w2SnHxTByYPUyHN)Fr7X2>sJAv|COr}fF< z9!oN>z$CrT($jYYjX@_g(5SwBX2)g1N((23CTBKS@46LF!4`T4Z~~_t52fDPxmIUw zH)F+anT%g!%xJ*&#qP27#AifM)=1Uqmip0si?w`ju@r1QVWSxUb@68;HYUg-IBAnG z`rPuO8jqS;%`Ic!?Zi>X)8VbTQn$(Wdb#anl((~tz+LXbf25hpNOLOp+A-_=u2g=( zO28xyD=^BkLf(7>-~+FQTN=3c>bA$#5wmI(b_{casoccgg24W!w3^hXM$uO6oSvW5 z!>C(NOfcamCJAKZTYHjHTN<=NcFVAU=JaQ0)eNWK(b%t0;0pbv_5 z_S+oGDs&k1=n&pjm`W!=oTc|Q{1u(X|IfEpIlW5M1dlDchNV4trK1`X?AjrvaC|f& zsyq@f2hy6MeHz$hJqb=;?XU&FE|-zGOtEcQ&;wb#b1*M=JOpOGIn{?H{QrS^GU39au^F0w+Wr3APqqr4(mbG)?#2nN^7P{h(pxG%6U>U zzXJ(*!dn0QCN3b(q{$QP`Dt@7XO_~py121P{Mhc8(E9lo9_oPa_m5Wyv*hnVDbnLr z$}qMfmtxV%paKM23eV$Gk@*#hsEO2&v1-W*hMMkq8epsBbC&6 zptUl$(Zwzb*Cl_LV5@<2G>fx8p^iQQJ>#r_pU30X@ID^f@b%KkyXz+m_R@~SxZ5XO zxX1JjUe>PQiz{M0Bcpxp#l8S9jOG?Wgw{(dsi{r&mfdL z4*6A^d}4*o0d56VyU9{rm#!Dei@Z1-v@LT6st)ZCbt5?I7_VS{`q#^7g9a$K#(|#DIdY}@ z-H61T&L@=B^ulYRP{c!N;HT9#okQuw<&33o_i!$pvNXEpCzsZf*W7C$VHaKZX~MBx zx8ebVr7u$AcAVszIIa4MS#x$ZKT3%j28%Kk%MbunTdk+v-Q}Q!MV*b+VSTmE`Jp7_ zNzTHyY^yP(yzGwFBvc8H1vz_Zsu@krb(t}`Mh3bosB0*!cs$5wHqIcw&GrCwhe@OF;AWtBYSV zkt9;DIe1Jb)s`cDm|YyjT!nkvT00XKHUc8SPTV%qNbHDb;jF2n;C7Q$V>+gm$gggS z8E!)tO>RwgLYHmzoOf?F#?oS)`O=bENd<7s$(K=uo*727em{~v9+QE}Y7OB-v!0Q1 zGo>D76(z!Nw7!(d!EPw+^qq7}Z}MF*GPG7cVr8v-iX zw&sZHXji)v#um(;^8}!G+$lrAoIuB1Lj}ieWptD}TDg+$io=92w*EGRWs49+GG>yqOoBE+!Uu|U#b5p?{A6~lc4bw?1$OX zU30?8Gp=7YBB>}+$&xLoXO;sSC#@~mHLsa>U_%Y!$_g*l@#NgAy+CO*x&bbq^Htcq zL1i-vv!Mp$bqx7mAREOsK?J4J-Te^iEFyt8($sk~q@_R!!9!L46o3T|QMLI)sG5I> zFIajcSdH5&lw9HJqg^aX(b%sxXY583y#>g7&>(x|FmY%~Nlnl!%%zDy@leZQatVn~ z67ALTV>GdG{ZyjCLDe@sp76R9g zdAVMooVBJ;+~bqwYlNBfwXIcgc@+_+KXUJ6FWyHCgwyYN{X0*PbGxzY?M~SdR*PBY zH>_DbB8Upc4vBy{6ch|lxTB18my>C;bJs2EhL7_fi+Y;Vbebt%t78P|zlueUJ9Q1p ztic_iVZ{gZgSB9<9g_|?6Z2Y27 z*LPY!`O=x$H}0ne|7`kwn%>v63VkitVtI^t4A*yFy}Y{>S3q|}?eZITxB5Zt#0ZCL zcTDZdTc$6c^(=zm2QQKPpXr@_2a#Z3xOmcxl|}V@$N4w$Z+K?0Z%ci|Kfta>e4l#R zJtG`vpMKrKCz)>$lA70MQNt~#pCa9ACtbdo(=88p^Op;4v9(KWx%!6O52|DioIQ(> zGxSnZ@GTTmssPUT#(^?VJCY1YoN#^FhB`h9GKguY6^rNdx@=evLkh@DzSU)uu05|d zuPztGVp!;w?k8H;i_TnxQ(uaIFUHRBb$ldSnSv=ycQpt=40xi>dAzi85(~tJxX|O{5 zY-zHQ0ScT$o487}Sq6lmN;_ixWaSB>?D-+Z+{r|xJSP*)ve{Tl`atQ9)zs(`u&1Ah z29fWUhz2+dP(0WiE^bkvTmO8h;0+GdW>{Sbfo}dm`m=9J{<0?{M^{jsaXG1Jkj>&K z>73}m3I!3Ix?t>}TWc!8wbe8^qGN|z^_KC|00E(@fN>S>pQPO7Hw)lbsq17Sl~Jt{-8@FXapaI3;QguJccuzhN4eR7%g8myP*odDAnajaUS_pw^#N(>+HguKxPY zCx`J|Pi$E1o()}IB_ z0Kjf4>v!Qa!$Yp?H_H#$08jS2@`=W4EqBj&ZGMp(U7@RYqQS}=oYP_EXG>ME#G*X+ zkDl|3fa^C!Ptilq;MPJ2kI7e@v7@v<_cQ^rz^BgUeTw|eEWp)2(*YP8p$Cd_ihtZ2 z^2ozoGZ6df_|rsS(Za7*H|2qPVG)57AbD~?)>AcE*jPRxeeD_(ZxW);R`w(@IEEN{ zq}C#fvL&tTmJoN})tE1oV-N1qp(jMOX|*oWM4+L~{^@NtUMJ;+fsP`hS4-J1QO~o0 zHR7E8D6ln!3WuVTUFm!dr@siw#NZ+4^8KS*2iLT&M027q^&UMbRlEE&S>hM~&_g#I z1e&d>`Qr#&LxdqNzxE4ELP)4sr~nRo+NCXolBb_79)r>b!`IG0i=0E@?Am=G1sfI* z{?OO%uPHR&_p_|1r_98mw)N+%ZF!B7#M&6!pBAa1Vz?U2`vcu2D-SAL7=^#txaFlO z%h6f69ltAhJKgOUK-`&WLeb@x03#iCS^i&m))tBsWk`d~v5j?=uWjpoIg8g69T9Gi zC%g{S-t<)$0TdFNv?mrVbq49O>ySW&K6ude<{NdkcfFcaAK1~Zz(WB|%YrkoFB{o? zlw)Ts0xr$C>|dvDcbO-7^zpbF=f*N^h)&l+S(mCC8Pdb^%~W^%wo?9`fHXe&VTPoM z+NJ5_WlurJAndut3fpKHAM7i^MJb7H0BEEWV#%~5DN31=NepYEv>v^_Dr?c;Jh6Ru zA-F?&W48FB_*cM`;E=gKd2Uu}UdZ;ayi-pRqCX0FD07S1JWgDhovMcML@d-83H>su zSk1(?@bQxU>XNE4X10_lVC^PaPgZwj(0hG^MquzjJCNSU@~fe$B>QM=y^4L zX3u{72nkNz#h&y*;X9(+*|0yX{K_LS*d!&HIcpSM;*_dTM69A=lR#jj%^3l1-(^ru z+pT*jWOEgd9?_TQuT;7#@Kpgb@x4+Qzf>-9iNZnUZ+DgWO~c(T$r_f$hX>aHZ+%IF zUKikFeNl|UFXC=~02s7BuSLp>iqYFIFr5hT=+e*+sU3;J37Y44b^W&3&m*-D%SrvA zJsHbb5`lFuO)WVTD}GZ=K^9A*Y|+S5&BTA)S7zsCQ!~!h^BjfH|U{Uvjoo2yY_Y;}I49 z)YpYlFY|{wp<`{R-1Hzv*rV-PCG%sh#MuRhx(ALEV{L)-g%d>alRbeopvi=d@7Ps` zee2Hc5jDZ)D)}cXXEukrj|-5Q3QS2^-6yj$YJRLH2xmw= zok5=vG#{t0VIIHx>AqZr2T!yv8JywYiERW%y;GW*e3@HFFiRKL z7A{{*00@l0_-BKwZp3)BAIJzU!UNXo5$|BlsG7UN-s;1?BEju8uUal+xaE%?X%RQ_ zPc^qveSwe0-yioEoQFyh$vZ=No7fXFP+0=XnCcxEEKt7LQeECJx7}7a`Y7L5k5VOe z(hJS6*b~VCVJ-drOG)U#zrr6f@fbdn6y(8P4Zs}6V6H^8E|dRaHv8%f4U%&7+Z0X@ zq`J2F>Gyv6uRStp12Us&b3Evhs%`k*X0SYG@+~Qly0!AUPd&`1`7O+qWHYl%{HXA& z;e-=CXdBntRu3!kDhc8jzjRJN)Tlu|H#G!hJ1;YVsaVfSQbr72xrg6#!8(!XR?^iG z#{hH&Zgh^8*NP0C-)c%M`#5=IbVLRMbbPHGq1+M+4nhSuR;Y?hNcd>7lYs&lyKPDK zrOAt>ta?DtG66*d<)G{lDYIXmiA5@@3*9mJisAq20wrM4?Y5j;Xa{M()Wiy8wC8x}Gz}6rhoQ9J5f!{twx+Ja5$VG$QLj z+Dd}*qkINjo5c`Hnf_fH{_e2yNe9jGmbp8u^?JLCzHY4^MN&%Q(i|GESL3g-TtgvH}r zx=+3wlkzfHqZS{7*tw%&YczSjALg<@=(7*($I(IB9Vy`}O2n*6-Khf8ixud^%$e;w znEQ@_(bq%XBTi9=#cuM6(3MuuxdT$c43GxHuh||DRRdN0D{$SGd0a&w>0p2^)F~gF zy&h|}Ke!`!uN8^q53ptUtkt8;>`Op>u0I^S{_N7-a}@B$7Z>biZ6MYVC3X+@< zhVXJIv0;DfuITmmvaY`cqnCifm`oQQM@6|e5>tOID}gbsgm|6M?H&~&Z9+iE7-Ba@ zyEJiSxnevkLF}|!GJ)7<=9Evn=b;BujscCztb5CRr%(@aQ2}B>3H#R1S1K;DM+@kDo`s_EjjiIx&NMh@aPB2E%kj zH>}7*AMWHVQw%C*EI^j8Kgd9^HA;bua`Njd7CtU`R;nrJ^UL%_g}WfR^4&;6@}e{w z8Hdlu(v>uCTR7m44?I8*DyB zXZX2)j#7MQGD~0|V!b=eg%tg2oPe7(sRstrlSD^hu5IQap76@+Qb}F|Nd~G<}&Jr>V1p0GBcAF;=P(h2&A0-3!Yzigb*s z>3b?883tP&*oGbZw*a1`fLh7UyOwo?H2xd=MIZSk(E`u}*~}V>mj%DC%$W3RN_70> z())~|&r^XMZJ)i{_Kk8C2fBtQX7AkvUilRjc@Sn%OG9?zh&rkS{X0RW6x2|!R33`Z zm(R@G&mI*fsk1^e>qYEmE$a5qqmQ0t@A>S*|1)zO?9+6Ape;J zwyB4>zN%3ER{VKKy-B939|L?SCTd~)r9eQTvxq7(ct@Ur(_XEWW|4yI+>G{Z6L^F# zU05fa_dY)|y<2^PB^>NNGDwov=`17y5c&K; zdibMoq%pz378{V4o(vj+(G~z+A@i^a>(mXq9Ry3N!9&D8G`ytJBBU=mBd*j+8mBX z%kw0j{cBnhdm>Si#W-c~OdiSxhVDaI`X%MMgiJXC#`k7t@&2NV#bk`#Ie@|8+Q(&w z?->~H;TRLHUiL> zf)zNVz!Qa7bAQi8bwd!X~`j=5tL%Gr(6|I+`;p96V-?BBbbNNj)mP|o;% z$BHl$=BYmDLc~ij{L3G+KX&yE3}63HP{90xf>MYAvP02Ds>dOHwtZ*a31wsC7S=m+5!q;YY!3;cG`@>6e4|o1eDk?^VA? z7tP)(vZ%+jnb`j6llRMhKQrmE&}lWGA0<>no^lHCxY!%pc6V16T8;)M)EiO{7wMZL za70y<;;YI)2cK48zMGLbL|1}Gw>#+x-X>L)FIxdYm~3>l%!HtWy^cHrivg~(l-bPI z=}%Bi^rw?iOD5Xr$3wbboPW7$aXDVMCd!jLS zM1@pxpod-$&LD_``W*PBwJ~fvqe2K zM?5*ox7lgG)m1SrFjHym)S=23l}LDev+rl?=`1mmKz8nX4zb%D><;@Bc{g%F?;u{)OL8!JyGr~Z-#tTT-GZMld{^p zc+k@zaQSY>R;pTeOaR>)nmIvabJQlV#5H#dv6{?B5%Yl*6w@~3yk!QF7KP8fUG^E7 zZ4(;GJ0Uk>@{kO z{4I5^LsM~^yZR+x3!l)Z%2M67LM|4hRhkQ+$~ad8Im_*#sLJ@JD(Mv05QUxF?^-Iw z+T7O08!pLN*9~(;^g)tq(`heHqU2ZR+@@Z=Gj9}0aZP-aFegB;;(J{*>nio1O+L&;R9KP4&3j%%3wYo$l(H8h7jGod9;i%m(K5SADIBMw9oP8U7J;hXehr(@q`6WH<)SOr+p(0~>ym23s#$luDwchw4m`_cW>x_~qEMB=(M6pXI%b z!59!qg>zuS4n21LOB@|ceDG%AijnA>mS8Ki9AoMehIxsPQSLf&vIdQr z>$0=$GTglR=8b429rpSmVJ7GPKZj?Ju_!m4}3kkr(gFo&KQ&cnN@{VqPO?WMEe+z>yp=HTwA9Y`R={axQFrasqoTi{Y0l1%lBhUykuO7&~|?IDySdB%b7Kj3C$l0%0^ zy*W@MUUIeuLx7P1JM)=nll`s^#^@E;j7m_nd}bmiK#g)kD9ve`ZcTr%qS762@d=pyv)N6 zh?}X<&J&wVxdoHHaf?!B2%sCuu1^e zh=DQ}31qUZ$%qO~%zHK0DXO)=V;V}DsT>W}Yq)VkbfxA#*6j@5Y014*9@N~rPQ7!> z(SsAdy_b*?$^$zdR5>$V&dsvU)t{QTlqN^^-p|>!;oc-TPy@&CfE38vt6}k-F*q5x z6M1hwNjSPr(9ILPlb6`|kVFj=QHhMO91+BfzCa3H7e|2d3;kBA+1FnPu|A2*z-Q0e zx9u?|>7GW%4tZmBA!W_ z@BRdeQX%QmMqt|GJT^k$c+|12(uRWK7!j?>3p#nu(*)96n;YC*X zVo0v8k~jV)&m~QdKbQ&&o0{C+TFuNwj(!m`$&EO=ywK?Osi}dKImOP!dvA1REx@3*9^0BhQKK#5FnzBoIbEe#!hTv{pk$#Rf$~?qDk4GrXDr zZ@I-QN!fc#4MnV>sq zGg4vIskT=NJSaC`a?cnc`XOdvEAfp`JTU({ z4Tlv?sdh#zSL_qW817ub-iY>Lx-CjwfZiPhL`SBQ_VBvB$7Aj~`xU7xAcWA`92S{9 zUvZ~|glbirJ4j@k{GO!;LXEvrKdD&kAz!R@9Nvt?TXayRYkd72g4 zGySTJNiyK36epOt(LUKJknN!n7`#I4BxzAiB5^arKnieltgap$yyH^ZWA0AzSy$+w zu5^^{x(8csksigv(w420)~%DG+O2T$jX^I$_}Rp9;$iDt)7;@Ea2CAhizBe(%^>o4 z=L7ggQ4Xm{s&9D|>@siCHYsv8xG>%#dfSbm+{g!V<7EyV5$)R!T!wO!D-a^L2fIo? zn$gO!NAurLgYit}=sPol!o;_lqn*9U7d#b|+Oz9pw=c8I^)C|z2gslTcaFfwS?12r zAcfBCSoI*0M_UfF`3j*Ly(j8NI4=?L(vJizqxsB3o;vdc9dv+MobDlSr!A<9u`Kz< z^b!KXEr6pm7ulkFT zd)Ev$Fjp!*s@q)Cy6TA?oo)W}#Y^nC?+n%Imd*irF=5Dwya1(QvR(svFmkiyyYyuW z&L;U#A|eG&F6VUFnJRKck=)5j?DW|5gVseQD|5TWDSOAhnvmF$E0|z?sHCg7{Ox$G zy@!+a(&kc%)YDL}1|EQWRVqN`HX%Y<_Y`(KaaJ>LkOAY}41%q(5w8)8j=v^IfM3vQyT_CWPNr`6J=od^uK2j-L6C$c#IE zU|cmySt=yET4CGojp$lmT#JC#m9BD5D#T8IBE09eRJG=@l5+g{Wg~4{N2O1L z5LZ*y5z_Q({nAMyzE^^}t_E%HgYdVP?PS^DZG|efHBpD{X>-=ikwv2aQjaGgqm}=AF+tm{g z(#v>9GwcE5-Tu`dYGYE|`}WLwIt_A~dh?G(jk&A#KAe8&d#3xQ#8rE*#nf8hS&^P) z3&(V?X&8guEmyn3K?J(6a4~(&VmN_?wq?ykw4>3D-e^Y>8J2~L5BU<5pE7P1hBs&G zE8u0ZBa~v=J6t?T&ENVCI(N$+NKJb>v_=T0$Yr6LooArJG=;M zpE!?mmPDd#uclaArC3z!9%17`*SE*I#1kJB`!IXa*|c4$G)1AgU(iu28uF@5^WaEk zT8t;U%c6n@i7d0v4Bxp#cM3n%>nRUDV2Zz%$|SVj&OnFEc-Z8NnRL8^5tK~$YJf!@llX^@-BX^_1<;CZ;!bl|VVA}OQzZfo}FQIE6U#mx}|+9T--?l!Cq>FTOL`HL8r zW7~e41X>7VG2fH0Q43Rt*+;LaqNqcp%B!1lqUtSWGzPMPf}!WkT7#6u%Pp5;7bBWz zoQGACk?1NtsvE`0o_+6&@bh#Jv^G97g^4Pn*4=c~_!V<^2~1y)(<|Cm)r~mBiKKj> zu6o{wzC$IaPo#qwlG5aoG;O_fJh*DjJ+2Wx$Mu%-s`J%=?oPkCspT|@9n7LF*PYEU zR-S5)?e&(s^$MS#)JOY!wt7FciW$h;4t*_?1t42rjbEFB-}Vg8j7F{1rWKbtWapFq z(UA6o(}aeR>VIH^9xt0_2E8d9^|)Gx5;i^9hMBBtgQsc0N$@%5Gnj!_JaR zJRlXjeN({~v}^zRzF0yLpG2H%kP?N76tNVJ?F`%869`SQKN zj8gKJWj9o9P!d|YB4gb6pUG1C9IFkGFL5>#DG!5;fQ)Vy4VTV^L_V8+{A9}p2Sy8T z?6?@4sI6jxJHL2Ys_B|K2dxg@l5l{Tk9gTIhsi>HrogTedlgrlbH(lZ3Kezo=(J9` zZ?>WIdK8b|XkK&IH8(N(8l7B~V{*aeQx-j9l5F8;D0@HC)l84pzdYaD6A%Z-tq`ahvb!msUkv@8wpGNswJ_?X1M&zdQ5_bbTJR9)PvA# zW${=k;hN~A7t94H)(>8WU}uh5Jr92gpjC~{?3dXo$FNqq; zh;1zJ%Q)sC4eckk^F%JRG@5#asRzUFge)FJIUfdZNzIXCUC~ zD74yclmVAKW^Fu>hZG0r1_TF(ABg_Q32aXAKry|-z9Lw4)}IQk9N5qAr64l?67Xo+ z5OjJ8n4dNSX_5ibGx8t=88GiJ4uEIDK>Z9O=yk8F#uIi0}IJez;ad?2Z62~xwgTvk9BR>zo_!Rd&ZUpk>kwgP}>|o zw88&ob{=->IXCtt>CdD5y%a=-vi*Vx0E#$rKyDsPKqSz{`5iEvrv-U&o^Sk~zywUr zfuY-;+f;CDlr5}crPl8d*s>t9hxZ)ZotK1OFn+%PAq23HGGMq2hF-ADy#VEjoktSM zWCAu9!1#ZekuK7KawN~&Rf-7#iEO&_ApbRm{2iwtvQXx4xO5SQ|I-W-7{&&vmH)e4 zw~+EY$ORY3-x087L1dNUFPI7_o&iJ8jrFi6AB44wRJ;I|sQd!SfVm|Y^xWDE3pQY{ zlGp_>Q~exNT4sY@Fu1w^?P#7u$a0qRS{h99G3Y(J%n|60H}o2o@_ z|0gpHY>qOY>lXh*jr~6*@{brX92Z1`hy9rlV{@}uv|G&=u73SZX z#)N-~8UvII5I_L;-)FOhRnPy(2efFwzd`Y&bY<7r@uZUm!6MH+}`Q^vB<;Q{yV25#W6F z4Lb~YIt2!9O)!F{o?@GrfV2rtYz8YM2%U9KXil<1FC?*E=;S{79QrG#l^*&tvE=W( zk_#YL@!#OaBnMRP-}C%0fa}kHfzlx2w)1M@6!h1aSNRkzsHWq*QNe~BSexR7UKnq= z5Gbwd7f21zFMt6@3?pcM;Jm5cf%#_>^q<#I*esX}`)1w&{Eu=zAUZDFNxu2AFP+?n~g_CJKU delta 36880 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�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%0AV^qz&fiv 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 068cdb2dc..c6f003026 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -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 diff --git a/gradlew b/gradlew index 4f906e0c8..65dcd68d6 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ -#!/usr/bin/env sh +#!/bin/sh # -# Copyright 2015 the original author or authors. +# Copyright © 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,67 +17,101 @@ # ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## # Attempt to set APP_HOME + # Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar @@ -87,9 +121,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -98,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" + JAVACMD=java which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the @@ -106,80 +140,105 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. # For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=`expr $i + 1` + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg done - case $i in - 0) set -- ;; - 1) set -- "$args0" ;; - 2) set -- "$args0" "$args1" ;; - 3) set -- "$args0" "$args1" "$args2" ;; - 4) set -- "$args0" "$args1" "$args2" "$args3" ;; - 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=`save "$@"` +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index 107acd32c..93e3f59f1 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -14,7 +14,7 @@ @rem limitations under the License. @rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +25,8 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,7 +41,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +if %ERRORLEVEL% equ 0 goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -75,13 +76,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/src/main/kotlin/com/epam/brn/auth/filter/FirebaseTokenAuthenticationFilter.kt b/src/main/kotlin/com/epam/brn/auth/filter/FirebaseTokenAuthenticationFilter.kt index 9eacfedf6..d0d948be1 100644 --- a/src/main/kotlin/com/epam/brn/auth/filter/FirebaseTokenAuthenticationFilter.kt +++ b/src/main/kotlin/com/epam/brn/auth/filter/FirebaseTokenAuthenticationFilter.kt @@ -23,9 +23,9 @@ import java.security.MessageDigest import java.time.Clock import java.time.Duration import java.time.Instant -import javax.servlet.FilterChain -import javax.servlet.http.HttpServletRequest -import javax.servlet.http.HttpServletResponse +import jakarta.servlet.FilterChain +import jakarta.servlet.http.HttpServletRequest +import jakarta.servlet.http.HttpServletResponse @Component class FirebaseTokenAuthenticationFilter( diff --git a/src/main/kotlin/com/epam/brn/auth/filter/RememberLastVisitFilter.kt b/src/main/kotlin/com/epam/brn/auth/filter/RememberLastVisitFilter.kt index 3e3e78c65..c5dd0e89b 100644 --- a/src/main/kotlin/com/epam/brn/auth/filter/RememberLastVisitFilter.kt +++ b/src/main/kotlin/com/epam/brn/auth/filter/RememberLastVisitFilter.kt @@ -5,9 +5,9 @@ import org.apache.logging.log4j.kotlin.logger import org.springframework.security.core.context.SecurityContextHolder import org.springframework.stereotype.Component import org.springframework.web.filter.OncePerRequestFilter -import javax.servlet.FilterChain -import javax.servlet.http.HttpServletRequest -import javax.servlet.http.HttpServletResponse +import jakarta.servlet.FilterChain +import jakarta.servlet.http.HttpServletRequest +import jakarta.servlet.http.HttpServletResponse @Component class RememberLastVisitFilter( diff --git a/src/main/kotlin/com/epam/brn/config/StartupSummaryLogger.kt b/src/main/kotlin/com/epam/brn/config/StartupSummaryLogger.kt new file mode 100644 index 000000000..c16ff9658 --- /dev/null +++ b/src/main/kotlin/com/epam/brn/config/StartupSummaryLogger.kt @@ -0,0 +1,46 @@ +package com.epam.brn.config + +import org.apache.logging.log4j.kotlin.logger +import org.springframework.boot.context.event.ApplicationReadyEvent +import org.springframework.context.event.EventListener +import org.springframework.core.env.Environment +import org.springframework.stereotype.Component + +@Component +class StartupSummaryLogger( + private val environment: Environment, +) { + private val log = logger() + + @EventListener(ApplicationReadyEvent::class) + fun logStartupSummary() { + val isDev = environment.activeProfiles.contains("dev") + val rows = + listOf( + "Active profile" to environment.activeProfiles.joinToString().ifBlank { "default" }, + "GitHub contributors sync" to onOff(!isDev), + "Firebase user loader" to onOff(environment.getProperty("firebase.user.loader.enabled", Boolean::class.java, true)), + "Firebase SSL verification" to onOff(!environment.getProperty("firebase.ssl.disable", Boolean::class.java, false)), + "Cloud provider" to (environment.getProperty("cloud.provider") ?: "none"), + "TTS provider" to (environment.getProperty("default.tts.provider") ?: "none"), + ) + + val label = "Startup configuration" + val nameWidth = rows.maxOf { it.first.length } + val texts = rows.map { (name, value) -> "${name.padEnd(nameWidth)} : $value" } + val innerWidth = (texts.maxOf { it.length }).coerceAtLeast(label.length) + val border = "─".repeat(innerWidth + 2) + val box = + buildString { + append('\n') + appendLine("┌$border┐") + appendLine("│ ${label.padEnd(innerWidth)} │") + appendLine("├$border┤") + texts.forEach { appendLine("│ ${it.padEnd(innerWidth)} │") } + append("└$border┘") + } + log.info(box) + } + + private fun onOff(enabled: Boolean): String = if (enabled) "ON" else "OFF" +} diff --git a/src/main/kotlin/com/epam/brn/config/SwaggerConfig.kt b/src/main/kotlin/com/epam/brn/config/SwaggerConfig.kt index 538b329ae..d625ce52d 100644 --- a/src/main/kotlin/com/epam/brn/config/SwaggerConfig.kt +++ b/src/main/kotlin/com/epam/brn/config/SwaggerConfig.kt @@ -3,11 +3,11 @@ package com.epam.brn.config import io.swagger.v3.oas.models.OpenAPI import io.swagger.v3.oas.models.info.Contact import io.swagger.v3.oas.models.info.Info -import org.springdoc.core.customizers.OpenApiCustomiser +import org.springdoc.core.customizers.OpenApiCustomizer import org.springdoc.core.customizers.OperationCustomizer import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration -import javax.annotation.security.RolesAllowed +import jakarta.annotation.security.RolesAllowed @Configuration class SwaggerConfig { @@ -52,5 +52,5 @@ class SwaggerConfig { } @Bean - fun sortTagsCustomiser(): OpenApiCustomiser = OpenApiCustomiser { openApi -> openApi.tags.sortBy { it.name } } + fun sortTagsCustomiser(): OpenApiCustomizer = OpenApiCustomizer { openApi -> openApi.tags.sortBy { it.name } } } diff --git a/src/main/kotlin/com/epam/brn/config/WebSecurityBasicConfiguration.kt b/src/main/kotlin/com/epam/brn/config/WebSecurityBasicConfiguration.kt index 24bba98fa..dc438230e 100644 --- a/src/main/kotlin/com/epam/brn/config/WebSecurityBasicConfiguration.kt +++ b/src/main/kotlin/com/epam/brn/config/WebSecurityBasicConfiguration.kt @@ -3,58 +3,59 @@ package com.epam.brn.config import com.epam.brn.auth.filter.FirebaseTokenAuthenticationFilter import com.epam.brn.auth.filter.RememberLastVisitFilter import com.epam.brn.enums.BrnRole +import jakarta.servlet.http.HttpServletResponse import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.security.authentication.AuthenticationManager -import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity +import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration +import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity import org.springframework.security.config.annotation.web.builders.HttpSecurity import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter import org.springframework.security.config.http.SessionCreationPolicy import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder import org.springframework.security.crypto.password.PasswordEncoder import org.springframework.security.web.AuthenticationEntryPoint +import org.springframework.security.web.SecurityFilterChain import org.springframework.security.web.access.AccessDeniedHandler import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter -import javax.servlet.http.HttpServletResponse @Configuration @EnableWebSecurity -@EnableGlobalMethodSecurity(securedEnabled = true, jsr250Enabled = true, prePostEnabled = true) +@EnableMethodSecurity(securedEnabled = true, jsr250Enabled = true, prePostEnabled = true) class WebSecurityBasicConfiguration( private val firebaseTokenAuthenticationFilter: FirebaseTokenAuthenticationFilter, private val rememberLastVisitFilter: RememberLastVisitFilter, -) : WebSecurityConfigurerAdapter() { - @Throws(Exception::class) - override fun configure(http: HttpSecurity) { +) { + @Bean + fun filterChain(http: HttpSecurity): SecurityFilterChain { http - .csrf() - .disable() - .sessionManagement() - .sessionCreationPolicy(SessionCreationPolicy.STATELESS) - .and() + .csrf { it.disable() } + .sessionManagement { it.sessionCreationPolicy(SessionCreationPolicy.STATELESS) } .addFilterBefore(firebaseTokenAuthenticationFilter, UsernamePasswordAuthenticationFilter::class.java) .addFilterAfter(rememberLastVisitFilter, UsernamePasswordAuthenticationFilter::class.java) - .authorizeRequests() - .antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**") - .hasRole(BrnRole.ADMIN) - .and() - .formLogin() - .disable() - .httpBasic() - .disable() - .exceptionHandling() - .authenticationEntryPoint(authenticationEntryPoint()) - .accessDeniedHandler(accessDeniedHandler()) + .authorizeHttpRequests { + it + .requestMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**") + .hasRole(BrnRole.ADMIN) + .anyRequest() + .permitAll() + }.formLogin { it.disable() } + .httpBasic { it.disable() } + .exceptionHandling { + it + .authenticationEntryPoint(authenticationEntryPoint()) + .accessDeniedHandler(accessDeniedHandler()) + } + return http.build() } @Bean - fun accessDeniedHandler(): AccessDeniedHandler? = AccessDeniedHandler { _, response: HttpServletResponse, _ -> + fun accessDeniedHandler(): AccessDeniedHandler = AccessDeniedHandler { _, response: HttpServletResponse, _ -> response.sendError(HttpServletResponse.SC_FORBIDDEN, "Access Denied") } @Bean - fun authenticationEntryPoint(): AuthenticationEntryPoint? = AuthenticationEntryPoint { _, response: HttpServletResponse, _ -> + fun authenticationEntryPoint(): AuthenticationEntryPoint = AuthenticationEntryPoint { _, response: HttpServletResponse, _ -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized") } @@ -62,5 +63,6 @@ class WebSecurityBasicConfiguration( fun passwordEncoder(): PasswordEncoder = BCryptPasswordEncoder() @Bean - fun brnAuthenticationManager(): AuthenticationManager = super.authenticationManagerBean() + fun brnAuthenticationManager(authenticationConfiguration: AuthenticationConfiguration): AuthenticationManager = + authenticationConfiguration.authenticationManager } diff --git a/src/main/kotlin/com/epam/brn/controller/AudioController.kt b/src/main/kotlin/com/epam/brn/controller/AudioController.kt index 3fda2a82d..ce2c03219 100644 --- a/src/main/kotlin/com/epam/brn/controller/AudioController.kt +++ b/src/main/kotlin/com/epam/brn/controller/AudioController.kt @@ -13,7 +13,7 @@ import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController -import javax.annotation.security.RolesAllowed +import jakarta.annotation.security.RolesAllowed @RestController @RequestMapping("/audio") diff --git a/src/main/kotlin/com/epam/brn/controller/AudiometryController.kt b/src/main/kotlin/com/epam/brn/controller/AudiometryController.kt index 2c8a4ca18..de9b86eac 100644 --- a/src/main/kotlin/com/epam/brn/controller/AudiometryController.kt +++ b/src/main/kotlin/com/epam/brn/controller/AudiometryController.kt @@ -11,7 +11,7 @@ import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController -import javax.annotation.security.RolesAllowed +import jakarta.annotation.security.RolesAllowed @RestController @RequestMapping("/audiometrics") diff --git a/src/main/kotlin/com/epam/brn/controller/AudiometryHistoryController.kt b/src/main/kotlin/com/epam/brn/controller/AudiometryHistoryController.kt index 0b5bd87cd..ef4da12a5 100644 --- a/src/main/kotlin/com/epam/brn/controller/AudiometryHistoryController.kt +++ b/src/main/kotlin/com/epam/brn/controller/AudiometryHistoryController.kt @@ -12,7 +12,7 @@ import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RestController -import javax.annotation.security.RolesAllowed +import jakarta.annotation.security.RolesAllowed @RestController @RequestMapping("/audiometry-history") diff --git a/src/main/kotlin/com/epam/brn/controller/CloudController.kt b/src/main/kotlin/com/epam/brn/controller/CloudController.kt index 7fe100064..4bfd92fb6 100644 --- a/src/main/kotlin/com/epam/brn/controller/CloudController.kt +++ b/src/main/kotlin/com/epam/brn/controller/CloudController.kt @@ -17,7 +17,7 @@ import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController import org.springframework.web.multipart.MultipartFile -import javax.annotation.security.RolesAllowed +import jakarta.annotation.security.RolesAllowed /** * Provides form parameters for client direct file upload to cloud and url for client to list bucket contents diff --git a/src/main/kotlin/com/epam/brn/controller/ContributorController.kt b/src/main/kotlin/com/epam/brn/controller/ContributorController.kt index 80893972c..2c4273b10 100644 --- a/src/main/kotlin/com/epam/brn/controller/ContributorController.kt +++ b/src/main/kotlin/com/epam/brn/controller/ContributorController.kt @@ -19,8 +19,8 @@ import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController -import javax.annotation.security.RolesAllowed -import javax.validation.Valid +import jakarta.annotation.security.RolesAllowed +import jakarta.validation.Valid @RestController @RequestMapping("/contributors") diff --git a/src/main/kotlin/com/epam/brn/controller/DoctorController.kt b/src/main/kotlin/com/epam/brn/controller/DoctorController.kt index d986437aa..3cc46e354 100644 --- a/src/main/kotlin/com/epam/brn/controller/DoctorController.kt +++ b/src/main/kotlin/com/epam/brn/controller/DoctorController.kt @@ -18,7 +18,7 @@ import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.ResponseStatus import org.springframework.web.bind.annotation.RestController -import javax.annotation.security.RolesAllowed +import jakarta.annotation.security.RolesAllowed @RestController @RequestMapping("/doctors") diff --git a/src/main/kotlin/com/epam/brn/controller/ExerciseController.kt b/src/main/kotlin/com/epam/brn/controller/ExerciseController.kt index 3c232d4b6..e5abb2e9c 100644 --- a/src/main/kotlin/com/epam/brn/controller/ExerciseController.kt +++ b/src/main/kotlin/com/epam/brn/controller/ExerciseController.kt @@ -25,8 +25,8 @@ import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController import org.springframework.web.multipart.MultipartFile -import javax.annotation.security.RolesAllowed -import javax.validation.Valid +import jakarta.annotation.security.RolesAllowed +import jakarta.validation.Valid @RestController @RequestMapping("/exercises") diff --git a/src/main/kotlin/com/epam/brn/controller/GroupController.kt b/src/main/kotlin/com/epam/brn/controller/GroupController.kt index 702c60e0b..7f286ff25 100644 --- a/src/main/kotlin/com/epam/brn/controller/GroupController.kt +++ b/src/main/kotlin/com/epam/brn/controller/GroupController.kt @@ -12,7 +12,7 @@ import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController -import javax.annotation.security.RolesAllowed +import jakarta.annotation.security.RolesAllowed @RestController @RequestMapping("/groups") diff --git a/src/main/kotlin/com/epam/brn/controller/ResourceController.kt b/src/main/kotlin/com/epam/brn/controller/ResourceController.kt index 84094d439..b3696b844 100644 --- a/src/main/kotlin/com/epam/brn/controller/ResourceController.kt +++ b/src/main/kotlin/com/epam/brn/controller/ResourceController.kt @@ -18,7 +18,7 @@ import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RestController -import javax.annotation.security.RolesAllowed +import jakarta.annotation.security.RolesAllowed @RestController @RequestMapping("/resources") diff --git a/src/main/kotlin/com/epam/brn/controller/RoleController.kt b/src/main/kotlin/com/epam/brn/controller/RoleController.kt index 5b10d5a3b..f32da9492 100644 --- a/src/main/kotlin/com/epam/brn/controller/RoleController.kt +++ b/src/main/kotlin/com/epam/brn/controller/RoleController.kt @@ -10,7 +10,7 @@ import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RestController -import javax.annotation.security.RolesAllowed +import jakarta.annotation.security.RolesAllowed @RestController @RequestMapping("/roles") diff --git a/src/main/kotlin/com/epam/brn/controller/SeriesController.kt b/src/main/kotlin/com/epam/brn/controller/SeriesController.kt index 84afe0e4e..3085d2688 100644 --- a/src/main/kotlin/com/epam/brn/controller/SeriesController.kt +++ b/src/main/kotlin/com/epam/brn/controller/SeriesController.kt @@ -15,7 +15,7 @@ import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController -import javax.annotation.security.RolesAllowed +import jakarta.annotation.security.RolesAllowed @RestController @RequestMapping("/series") diff --git a/src/main/kotlin/com/epam/brn/controller/StudyHistoryController.kt b/src/main/kotlin/com/epam/brn/controller/StudyHistoryController.kt index 13aef50b2..61e78b435 100644 --- a/src/main/kotlin/com/epam/brn/controller/StudyHistoryController.kt +++ b/src/main/kotlin/com/epam/brn/controller/StudyHistoryController.kt @@ -16,7 +16,7 @@ import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController -import javax.annotation.security.RolesAllowed +import jakarta.annotation.security.RolesAllowed @RestController @RequestMapping("/study-history") diff --git a/src/main/kotlin/com/epam/brn/controller/StudyHistoryControllerV2.kt b/src/main/kotlin/com/epam/brn/controller/StudyHistoryControllerV2.kt index 324684ee4..fc7066020 100644 --- a/src/main/kotlin/com/epam/brn/controller/StudyHistoryControllerV2.kt +++ b/src/main/kotlin/com/epam/brn/controller/StudyHistoryControllerV2.kt @@ -15,7 +15,7 @@ import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController import java.time.LocalDateTime -import javax.annotation.security.RolesAllowed +import jakarta.annotation.security.RolesAllowed @RestController @RequestMapping("/v2/study-history") diff --git a/src/main/kotlin/com/epam/brn/controller/SubGroupController.kt b/src/main/kotlin/com/epam/brn/controller/SubGroupController.kt index 5287eed1d..7463f9d4c 100644 --- a/src/main/kotlin/com/epam/brn/controller/SubGroupController.kt +++ b/src/main/kotlin/com/epam/brn/controller/SubGroupController.kt @@ -20,8 +20,8 @@ import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController -import javax.annotation.security.RolesAllowed -import javax.validation.Valid +import jakarta.annotation.security.RolesAllowed +import jakarta.validation.Valid @RestController @RequestMapping("/subgroups") diff --git a/src/main/kotlin/com/epam/brn/controller/TaskController.kt b/src/main/kotlin/com/epam/brn/controller/TaskController.kt index 60e053d8a..870bb6483 100644 --- a/src/main/kotlin/com/epam/brn/controller/TaskController.kt +++ b/src/main/kotlin/com/epam/brn/controller/TaskController.kt @@ -12,7 +12,7 @@ import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController -import javax.annotation.security.RolesAllowed +import jakarta.annotation.security.RolesAllowed @RestController @RequestMapping("/tasks") diff --git a/src/main/kotlin/com/epam/brn/controller/UserDetailController.kt b/src/main/kotlin/com/epam/brn/controller/UserDetailController.kt index 3dce155b1..c9bc88edf 100644 --- a/src/main/kotlin/com/epam/brn/controller/UserDetailController.kt +++ b/src/main/kotlin/com/epam/brn/controller/UserDetailController.kt @@ -26,7 +26,7 @@ import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.ResponseStatus import org.springframework.web.bind.annotation.RestController -import javax.annotation.security.RolesAllowed +import jakarta.annotation.security.RolesAllowed @RestController @RequestMapping("/users") diff --git a/src/main/kotlin/com/epam/brn/controller/UserStatisticControllerV2.kt b/src/main/kotlin/com/epam/brn/controller/UserStatisticControllerV2.kt index 843981681..ce91efed1 100644 --- a/src/main/kotlin/com/epam/brn/controller/UserStatisticControllerV2.kt +++ b/src/main/kotlin/com/epam/brn/controller/UserStatisticControllerV2.kt @@ -16,7 +16,7 @@ import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController import java.time.LocalDateTime -import javax.annotation.security.RolesAllowed +import jakarta.annotation.security.RolesAllowed @RestController @RequestMapping("/v2/statistics") diff --git a/src/main/kotlin/com/epam/brn/controller/UserSubGroupStatisticController.kt b/src/main/kotlin/com/epam/brn/controller/UserSubGroupStatisticController.kt index 652e3ae78..3dc09b483 100644 --- a/src/main/kotlin/com/epam/brn/controller/UserSubGroupStatisticController.kt +++ b/src/main/kotlin/com/epam/brn/controller/UserSubGroupStatisticController.kt @@ -11,7 +11,7 @@ import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController -import javax.annotation.security.RolesAllowed +import jakarta.annotation.security.RolesAllowed @RestController @RequestMapping("/statistics") diff --git a/src/main/kotlin/com/epam/brn/dto/ExerciseGroupDto.kt b/src/main/kotlin/com/epam/brn/dto/ExerciseGroupDto.kt index c10f0b7ca..53ea1a035 100644 --- a/src/main/kotlin/com/epam/brn/dto/ExerciseGroupDto.kt +++ b/src/main/kotlin/com/epam/brn/dto/ExerciseGroupDto.kt @@ -1,6 +1,6 @@ package com.epam.brn.dto -import javax.validation.constraints.NotBlank +import jakarta.validation.constraints.NotBlank data class ExerciseGroupDto( val id: Long?, diff --git a/src/main/kotlin/com/epam/brn/dto/HeadphonesDto.kt b/src/main/kotlin/com/epam/brn/dto/HeadphonesDto.kt index 290803c02..faa3eddca 100644 --- a/src/main/kotlin/com/epam/brn/dto/HeadphonesDto.kt +++ b/src/main/kotlin/com/epam/brn/dto/HeadphonesDto.kt @@ -2,7 +2,7 @@ package com.epam.brn.dto import com.epam.brn.enums.HeadphonesType import com.epam.brn.model.Headphones -import javax.validation.constraints.NotBlank +import jakarta.validation.constraints.NotBlank data class HeadphonesDto( var id: Long? = null, diff --git a/src/main/kotlin/com/epam/brn/dto/SeriesDto.kt b/src/main/kotlin/com/epam/brn/dto/SeriesDto.kt index 728d0b9d0..292e484bb 100644 --- a/src/main/kotlin/com/epam/brn/dto/SeriesDto.kt +++ b/src/main/kotlin/com/epam/brn/dto/SeriesDto.kt @@ -1,8 +1,8 @@ package com.epam.brn.dto import com.epam.brn.enums.ExerciseType -import javax.validation.constraints.NotBlank -import javax.validation.constraints.NotNull +import jakarta.validation.constraints.NotBlank +import jakarta.validation.constraints.NotNull data class SeriesDto( @field:NotNull diff --git a/src/main/kotlin/com/epam/brn/dto/StudyHistoryDto.kt b/src/main/kotlin/com/epam/brn/dto/StudyHistoryDto.kt index b21306675..ce1cdf2da 100644 --- a/src/main/kotlin/com/epam/brn/dto/StudyHistoryDto.kt +++ b/src/main/kotlin/com/epam/brn/dto/StudyHistoryDto.kt @@ -4,7 +4,7 @@ import com.epam.brn.model.Exercise import com.epam.brn.model.StudyHistory import com.epam.brn.model.UserAccount import java.time.LocalDateTime -import javax.validation.constraints.NotNull +import jakarta.validation.constraints.NotNull data class StudyHistoryDto( var id: Long?, diff --git a/src/main/kotlin/com/epam/brn/dto/request/AddPatientToDoctorRequest.kt b/src/main/kotlin/com/epam/brn/dto/request/AddPatientToDoctorRequest.kt index e598b178e..9fc1277a8 100644 --- a/src/main/kotlin/com/epam/brn/dto/request/AddPatientToDoctorRequest.kt +++ b/src/main/kotlin/com/epam/brn/dto/request/AddPatientToDoctorRequest.kt @@ -1,7 +1,7 @@ package com.epam.brn.dto.request -import javax.validation.constraints.Min -import javax.validation.constraints.NotBlank +import jakarta.validation.constraints.Min +import jakarta.validation.constraints.NotBlank data class AddPatientToDoctorRequest( @field:Min(value = 1) diff --git a/src/main/kotlin/com/epam/brn/dto/request/AudiometryHistoryRequest.kt b/src/main/kotlin/com/epam/brn/dto/request/AudiometryHistoryRequest.kt index 179d6d930..ace122cc2 100644 --- a/src/main/kotlin/com/epam/brn/dto/request/AudiometryHistoryRequest.kt +++ b/src/main/kotlin/com/epam/brn/dto/request/AudiometryHistoryRequest.kt @@ -5,7 +5,7 @@ import com.epam.brn.model.AudiometryTask import com.epam.brn.model.Headphones import com.epam.brn.model.UserAccount import java.time.LocalDateTime -import javax.validation.constraints.NotNull +import jakarta.validation.constraints.NotNull data class AudiometryHistoryRequest( @field:NotNull diff --git a/src/main/kotlin/com/epam/brn/dto/request/SubGroupRequest.kt b/src/main/kotlin/com/epam/brn/dto/request/SubGroupRequest.kt index adec7a8ca..0aa64ecfc 100644 --- a/src/main/kotlin/com/epam/brn/dto/request/SubGroupRequest.kt +++ b/src/main/kotlin/com/epam/brn/dto/request/SubGroupRequest.kt @@ -3,8 +3,8 @@ package com.epam.brn.dto.request import com.epam.brn.model.Series import com.epam.brn.model.SubGroup import com.fasterxml.jackson.annotation.JsonCreator -import javax.validation.constraints.NotBlank -import javax.validation.constraints.NotNull +import jakarta.validation.constraints.NotBlank +import jakarta.validation.constraints.NotNull data class SubGroupRequest @JsonCreator diff --git a/src/main/kotlin/com/epam/brn/dto/request/UserAccountChangeRequest.kt b/src/main/kotlin/com/epam/brn/dto/request/UserAccountChangeRequest.kt index b527b0858..b6b34a1a8 100644 --- a/src/main/kotlin/com/epam/brn/dto/request/UserAccountChangeRequest.kt +++ b/src/main/kotlin/com/epam/brn/dto/request/UserAccountChangeRequest.kt @@ -2,7 +2,7 @@ package com.epam.brn.dto.request import com.epam.brn.enums.BrnGender import com.fasterxml.jackson.annotation.JsonInclude -import javax.validation.constraints.Min +import jakarta.validation.constraints.Min @JsonInclude(JsonInclude.Include.NON_NULL) data class UserAccountChangeRequest( diff --git a/src/main/kotlin/com/epam/brn/dto/request/UserAccountCreateRequest.kt b/src/main/kotlin/com/epam/brn/dto/request/UserAccountCreateRequest.kt index c76585a15..46061ac3a 100644 --- a/src/main/kotlin/com/epam/brn/dto/request/UserAccountCreateRequest.kt +++ b/src/main/kotlin/com/epam/brn/dto/request/UserAccountCreateRequest.kt @@ -3,11 +3,11 @@ package com.epam.brn.dto.request import com.epam.brn.enums.BrnGender import com.epam.brn.model.UserAccount import com.fasterxml.jackson.annotation.JsonInclude -import javax.validation.constraints.Email -import javax.validation.constraints.NotBlank -import javax.validation.constraints.NotNull -import javax.validation.constraints.Pattern -import javax.validation.constraints.Size +import jakarta.validation.constraints.Email +import jakarta.validation.constraints.NotBlank +import jakarta.validation.constraints.NotNull +import jakarta.validation.constraints.Pattern +import jakarta.validation.constraints.Size const val VALID_EMAIL_ADDRESS_REGEX_WITH_EMPTY_SPACES_ACCEPTANCE: String = "(^\\s+$)|([a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)" diff --git a/src/main/kotlin/com/epam/brn/dto/request/contributor/ContactRequest.kt b/src/main/kotlin/com/epam/brn/dto/request/contributor/ContactRequest.kt index d240d94f2..3cf6b3bdd 100644 --- a/src/main/kotlin/com/epam/brn/dto/request/contributor/ContactRequest.kt +++ b/src/main/kotlin/com/epam/brn/dto/request/contributor/ContactRequest.kt @@ -3,8 +3,8 @@ package com.epam.brn.dto.request.contributor import com.epam.brn.enums.ContactType import com.epam.brn.model.Contact import org.hibernate.validator.constraints.Length -import javax.validation.constraints.NotBlank -import javax.validation.constraints.NotNull +import jakarta.validation.constraints.NotBlank +import jakarta.validation.constraints.NotNull data class ContactRequest( @field:NotNull diff --git a/src/main/kotlin/com/epam/brn/dto/request/contributor/ContributorRequest.kt b/src/main/kotlin/com/epam/brn/dto/request/contributor/ContributorRequest.kt index bc4bbda2b..097caae7c 100644 --- a/src/main/kotlin/com/epam/brn/dto/request/contributor/ContributorRequest.kt +++ b/src/main/kotlin/com/epam/brn/dto/request/contributor/ContributorRequest.kt @@ -3,10 +3,10 @@ package com.epam.brn.dto.request.contributor import com.epam.brn.enums.ContributorType import com.epam.brn.model.Contributor import org.hibernate.validator.constraints.Length -import javax.validation.Valid -import javax.validation.constraints.NotBlank -import javax.validation.constraints.NotNull -import javax.validation.constraints.Positive +import jakarta.validation.Valid +import jakarta.validation.constraints.NotBlank +import jakarta.validation.constraints.NotNull +import jakarta.validation.constraints.Positive data class ContributorRequest( @field:NotBlank diff --git a/src/main/kotlin/com/epam/brn/dto/request/exercise/ExerciseCreateDto.kt b/src/main/kotlin/com/epam/brn/dto/request/exercise/ExerciseCreateDto.kt index 2788addfa..27569f9aa 100644 --- a/src/main/kotlin/com/epam/brn/dto/request/exercise/ExerciseCreateDto.kt +++ b/src/main/kotlin/com/epam/brn/dto/request/exercise/ExerciseCreateDto.kt @@ -8,10 +8,10 @@ import com.epam.brn.upload.csv.seriesWords.SeriesWordsRecord import com.fasterxml.jackson.annotation.JsonSubTypes import com.fasterxml.jackson.annotation.JsonTypeInfo import io.swagger.v3.oas.annotations.media.Schema -import javax.validation.Valid -import javax.validation.constraints.NotBlank -import javax.validation.constraints.NotEmpty -import javax.validation.constraints.NotNull +import jakarta.validation.Valid +import jakarta.validation.constraints.NotBlank +import jakarta.validation.constraints.NotEmpty +import jakarta.validation.constraints.NotNull @JsonTypeInfo( use = JsonTypeInfo.Id.NAME, diff --git a/src/main/kotlin/com/epam/brn/dto/request/exercise/Phrases.kt b/src/main/kotlin/com/epam/brn/dto/request/exercise/Phrases.kt index 6f4ec8242..ce2959946 100644 --- a/src/main/kotlin/com/epam/brn/dto/request/exercise/Phrases.kt +++ b/src/main/kotlin/com/epam/brn/dto/request/exercise/Phrases.kt @@ -2,7 +2,7 @@ package com.epam.brn.dto.request.exercise import com.epam.brn.validator.WithoutPunctuation import io.swagger.v3.oas.annotations.media.Schema -import javax.validation.constraints.NotEmpty +import jakarta.validation.constraints.NotEmpty @Schema(name = "Phrases", description = "Phrases for creating exercise. Phrases should not contain punctuation marks.") data class Phrases( diff --git a/src/main/kotlin/com/epam/brn/dto/response/AudiometryResponse.kt b/src/main/kotlin/com/epam/brn/dto/response/AudiometryResponse.kt index bb80fa597..ba9a098b8 100644 --- a/src/main/kotlin/com/epam/brn/dto/response/AudiometryResponse.kt +++ b/src/main/kotlin/com/epam/brn/dto/response/AudiometryResponse.kt @@ -1,7 +1,7 @@ package com.epam.brn.dto.response import com.epam.brn.enums.AudiometryType -import javax.validation.constraints.NotBlank +import jakarta.validation.constraints.NotBlank data class AudiometryResponse( val id: Long?, diff --git a/src/main/kotlin/com/epam/brn/job/GitHubContributorRefreshJob.kt b/src/main/kotlin/com/epam/brn/job/GitHubContributorRefreshJob.kt index 84dd315b5..8e660885f 100644 --- a/src/main/kotlin/com/epam/brn/job/GitHubContributorRefreshJob.kt +++ b/src/main/kotlin/com/epam/brn/job/GitHubContributorRefreshJob.kt @@ -9,6 +9,7 @@ import com.epam.brn.webclient.GitHubApiClient import org.apache.logging.log4j.kotlin.logger import org.springframework.beans.factory.annotation.Value import org.springframework.boot.context.event.ApplicationReadyEvent +import org.springframework.context.annotation.Profile import org.springframework.context.event.EventListener import org.springframework.scheduling.annotation.Scheduled import org.springframework.stereotype.Service @@ -16,6 +17,7 @@ import org.springframework.transaction.annotation.Transactional import java.util.Optional @Service +@Profile("!dev") class GitHubContributorRefreshJob( val gitHubApiClient: GitHubApiClient, val gitHubUserRepository: GitHubUserRepository, diff --git a/src/main/kotlin/com/epam/brn/model/Audiometry.kt b/src/main/kotlin/com/epam/brn/model/Audiometry.kt index 5128cee64..6b36df323 100644 --- a/src/main/kotlin/com/epam/brn/model/Audiometry.kt +++ b/src/main/kotlin/com/epam/brn/model/Audiometry.kt @@ -2,16 +2,16 @@ package com.epam.brn.model import com.epam.brn.dto.response.AudiometryResponse import com.epam.brn.enums.AudiometryType -import javax.persistence.CascadeType -import javax.persistence.Column -import javax.persistence.Entity -import javax.persistence.FetchType -import javax.persistence.GeneratedValue -import javax.persistence.GenerationType -import javax.persistence.Id -import javax.persistence.OneToMany -import javax.persistence.Table -import javax.persistence.UniqueConstraint +import jakarta.persistence.CascadeType +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.FetchType +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.OneToMany +import jakarta.persistence.Table +import jakarta.persistence.UniqueConstraint @Entity @Table(uniqueConstraints = [UniqueConstraint(columnNames = ["locale", "name", "audiometryType"])]) diff --git a/src/main/kotlin/com/epam/brn/model/AudiometryHistory.kt b/src/main/kotlin/com/epam/brn/model/AudiometryHistory.kt index 5074074d1..2fcf3f394 100644 --- a/src/main/kotlin/com/epam/brn/model/AudiometryHistory.kt +++ b/src/main/kotlin/com/epam/brn/model/AudiometryHistory.kt @@ -2,19 +2,19 @@ package com.epam.brn.model import java.time.LocalDateTime import java.util.LinkedList -import javax.persistence.CascadeType -import javax.persistence.Column -import javax.persistence.Entity -import javax.persistence.FetchType -import javax.persistence.GeneratedValue -import javax.persistence.GenerationType -import javax.persistence.Id -import javax.persistence.JoinColumn -import javax.persistence.ManyToOne -import javax.persistence.OneToMany -import javax.persistence.OneToOne -import javax.persistence.Table -import javax.persistence.UniqueConstraint +import jakarta.persistence.CascadeType +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.FetchType +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.JoinColumn +import jakarta.persistence.ManyToOne +import jakarta.persistence.OneToMany +import jakarta.persistence.OneToOne +import jakarta.persistence.Table +import jakarta.persistence.UniqueConstraint @Entity @Table( diff --git a/src/main/kotlin/com/epam/brn/model/AudiometryTask.kt b/src/main/kotlin/com/epam/brn/model/AudiometryTask.kt index b35aff2ad..95544603a 100644 --- a/src/main/kotlin/com/epam/brn/model/AudiometryTask.kt +++ b/src/main/kotlin/com/epam/brn/model/AudiometryTask.kt @@ -5,18 +5,18 @@ import com.epam.brn.dto.response.AudiometryMatrixTaskResponse import com.epam.brn.dto.response.AudiometrySignalsTaskResponse import com.epam.brn.enums.AudiometryType import com.epam.brn.enums.EAR -import javax.persistence.CascadeType -import javax.persistence.Entity -import javax.persistence.FetchType -import javax.persistence.GeneratedValue -import javax.persistence.GenerationType -import javax.persistence.Id -import javax.persistence.JoinColumn -import javax.persistence.JoinTable -import javax.persistence.ManyToMany -import javax.persistence.ManyToOne -import javax.persistence.Table -import javax.persistence.UniqueConstraint +import jakarta.persistence.CascadeType +import jakarta.persistence.Entity +import jakarta.persistence.FetchType +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.JoinColumn +import jakarta.persistence.JoinTable +import jakarta.persistence.ManyToMany +import jakarta.persistence.ManyToOne +import jakarta.persistence.Table +import jakarta.persistence.UniqueConstraint @Entity @Table(uniqueConstraints = [UniqueConstraint(columnNames = ["audiometryGroup", "frequencyZone"])]) diff --git a/src/main/kotlin/com/epam/brn/model/Contact.kt b/src/main/kotlin/com/epam/brn/model/Contact.kt index b71607d8a..8f956960a 100644 --- a/src/main/kotlin/com/epam/brn/model/Contact.kt +++ b/src/main/kotlin/com/epam/brn/model/Contact.kt @@ -2,13 +2,13 @@ package com.epam.brn.model import com.epam.brn.dto.ContactDto import com.epam.brn.enums.ContactType -import javax.persistence.Entity -import javax.persistence.EnumType -import javax.persistence.Enumerated -import javax.persistence.GeneratedValue -import javax.persistence.GenerationType -import javax.persistence.Id -import javax.persistence.Table +import jakarta.persistence.Entity +import jakarta.persistence.EnumType +import jakarta.persistence.Enumerated +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.Table @Entity @Table diff --git a/src/main/kotlin/com/epam/brn/model/Contributor.kt b/src/main/kotlin/com/epam/brn/model/Contributor.kt index 746e64dac..8f0f76cd3 100644 --- a/src/main/kotlin/com/epam/brn/model/Contributor.kt +++ b/src/main/kotlin/com/epam/brn/model/Contributor.kt @@ -4,17 +4,17 @@ import com.epam.brn.dto.response.ContributorDetailsResponse import com.epam.brn.dto.response.ContributorResponse import com.epam.brn.enums.ContributorType import org.hibernate.annotations.DynamicUpdate -import javax.persistence.CascadeType -import javax.persistence.Entity -import javax.persistence.EnumType -import javax.persistence.Enumerated -import javax.persistence.GeneratedValue -import javax.persistence.GenerationType -import javax.persistence.Id -import javax.persistence.JoinColumn -import javax.persistence.OneToMany -import javax.persistence.OneToOne -import javax.persistence.Table +import jakarta.persistence.CascadeType +import jakarta.persistence.Entity +import jakarta.persistence.EnumType +import jakarta.persistence.Enumerated +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.JoinColumn +import jakarta.persistence.OneToMany +import jakarta.persistence.OneToOne +import jakarta.persistence.Table @Entity @Table diff --git a/src/main/kotlin/com/epam/brn/model/Exercise.kt b/src/main/kotlin/com/epam/brn/model/Exercise.kt index 6a33bf51d..e80cfefbb 100644 --- a/src/main/kotlin/com/epam/brn/model/Exercise.kt +++ b/src/main/kotlin/com/epam/brn/model/Exercise.kt @@ -10,20 +10,20 @@ import org.springframework.data.jpa.domain.support.AuditingEntityListener import java.time.LocalDateTime import java.time.ZoneOffset import org.hibernate.annotations.BatchSize -import javax.persistence.CascadeType -import javax.persistence.OrderBy -import javax.persistence.Column -import javax.persistence.Entity -import javax.persistence.EntityListeners -import javax.persistence.FetchType -import javax.persistence.GeneratedValue -import javax.persistence.GenerationType -import javax.persistence.Id -import javax.persistence.JoinColumn -import javax.persistence.ManyToOne -import javax.persistence.OneToMany -import javax.persistence.Table -import javax.persistence.UniqueConstraint +import jakarta.persistence.CascadeType +import jakarta.persistence.OrderBy +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.EntityListeners +import jakarta.persistence.FetchType +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.JoinColumn +import jakarta.persistence.ManyToOne +import jakarta.persistence.OneToMany +import jakarta.persistence.Table +import jakarta.persistence.UniqueConstraint @Entity @Table(uniqueConstraints = [UniqueConstraint(columnNames = ["name", "level"])]) diff --git a/src/main/kotlin/com/epam/brn/model/ExerciseGroup.kt b/src/main/kotlin/com/epam/brn/model/ExerciseGroup.kt index af82961d9..7287bb0ea 100644 --- a/src/main/kotlin/com/epam/brn/model/ExerciseGroup.kt +++ b/src/main/kotlin/com/epam/brn/model/ExerciseGroup.kt @@ -3,15 +3,15 @@ package com.epam.brn.model import com.epam.brn.dto.ExerciseGroupDto import com.epam.brn.enums.BrnLocale import com.epam.brn.upload.csv.group.GroupRecord -import javax.persistence.CascadeType -import javax.persistence.Column -import javax.persistence.Entity -import javax.persistence.FetchType -import javax.persistence.GeneratedValue -import javax.persistence.GenerationType -import javax.persistence.Id -import javax.persistence.OneToMany -import javax.persistence.Table +import jakarta.persistence.CascadeType +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.FetchType +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.OneToMany +import jakarta.persistence.Table // The discrepancy in naming with "Groups" endpoint and "ExerciseGroup" entity is due to group being a reserved word in db. @Entity diff --git a/src/main/kotlin/com/epam/brn/model/GitHubUser.kt b/src/main/kotlin/com/epam/brn/model/GitHubUser.kt index 8d477e33f..cfc9c79a4 100644 --- a/src/main/kotlin/com/epam/brn/model/GitHubUser.kt +++ b/src/main/kotlin/com/epam/brn/model/GitHubUser.kt @@ -2,9 +2,9 @@ package com.epam.brn.model import com.epam.brn.dto.GitHubUserDto import org.hibernate.annotations.DynamicUpdate -import javax.persistence.Entity -import javax.persistence.Id -import javax.persistence.Table +import jakarta.persistence.Entity +import jakarta.persistence.Id +import jakarta.persistence.Table @Entity @Table(name = "github_user") diff --git a/src/main/kotlin/com/epam/brn/model/Headphones.kt b/src/main/kotlin/com/epam/brn/model/Headphones.kt index c49540682..f19c5d582 100644 --- a/src/main/kotlin/com/epam/brn/model/Headphones.kt +++ b/src/main/kotlin/com/epam/brn/model/Headphones.kt @@ -2,18 +2,18 @@ package com.epam.brn.model import com.epam.brn.dto.HeadphonesDto import com.epam.brn.enums.HeadphonesType -import javax.persistence.Entity -import javax.persistence.EnumType -import javax.persistence.Enumerated -import javax.persistence.FetchType -import javax.persistence.GeneratedValue -import javax.persistence.GenerationType -import javax.persistence.Id -import javax.persistence.JoinColumn -import javax.persistence.ManyToOne -import javax.persistence.Table -import javax.persistence.UniqueConstraint -import javax.validation.constraints.NotNull +import jakarta.persistence.Entity +import jakarta.persistence.EnumType +import jakarta.persistence.Enumerated +import jakarta.persistence.FetchType +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.JoinColumn +import jakarta.persistence.ManyToOne +import jakarta.persistence.Table +import jakarta.persistence.UniqueConstraint +import jakarta.validation.constraints.NotNull @Entity @Table(uniqueConstraints = [UniqueConstraint(columnNames = ["name", "userId"])]) diff --git a/src/main/kotlin/com/epam/brn/model/Notification.kt b/src/main/kotlin/com/epam/brn/model/Notification.kt index bd7a95ee0..21462978d 100644 --- a/src/main/kotlin/com/epam/brn/model/Notification.kt +++ b/src/main/kotlin/com/epam/brn/model/Notification.kt @@ -2,14 +2,14 @@ package com.epam.brn.model import com.epam.brn.dto.notification.NotificationDto import java.time.LocalDateTime -import javax.persistence.Entity -import javax.persistence.FetchType -import javax.persistence.GeneratedValue -import javax.persistence.GenerationType -import javax.persistence.Id -import javax.persistence.JoinColumn -import javax.persistence.ManyToOne -import javax.persistence.Table +import jakarta.persistence.Entity +import jakarta.persistence.FetchType +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.JoinColumn +import jakarta.persistence.ManyToOne +import jakarta.persistence.Table @Entity @Table diff --git a/src/main/kotlin/com/epam/brn/model/Resource.kt b/src/main/kotlin/com/epam/brn/model/Resource.kt index 9f5d0b14f..1925f8ab9 100644 --- a/src/main/kotlin/com/epam/brn/model/Resource.kt +++ b/src/main/kotlin/com/epam/brn/model/Resource.kt @@ -2,16 +2,16 @@ package com.epam.brn.model import com.epam.brn.dto.response.ResourceResponse import com.epam.brn.enums.WordType -import javax.persistence.CascadeType -import javax.persistence.Column -import javax.persistence.Entity -import javax.persistence.GeneratedValue -import javax.persistence.GenerationType -import javax.persistence.Id -import javax.persistence.Index -import javax.persistence.ManyToMany -import javax.persistence.Table -import javax.persistence.UniqueConstraint +import jakarta.persistence.CascadeType +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.Index +import jakarta.persistence.ManyToMany +import jakarta.persistence.Table +import jakarta.persistence.UniqueConstraint @Entity @Table( diff --git a/src/main/kotlin/com/epam/brn/model/Role.kt b/src/main/kotlin/com/epam/brn/model/Role.kt index 8b46b5463..b6782e205 100644 --- a/src/main/kotlin/com/epam/brn/model/Role.kt +++ b/src/main/kotlin/com/epam/brn/model/Role.kt @@ -1,11 +1,11 @@ package com.epam.brn.model import com.epam.brn.dto.response.RoleResponse -import javax.persistence.Column -import javax.persistence.Entity -import javax.persistence.GeneratedValue -import javax.persistence.GenerationType -import javax.persistence.Id +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id @Entity class Role( diff --git a/src/main/kotlin/com/epam/brn/model/Series.kt b/src/main/kotlin/com/epam/brn/model/Series.kt index 4e6529255..d29023a0c 100644 --- a/src/main/kotlin/com/epam/brn/model/Series.kt +++ b/src/main/kotlin/com/epam/brn/model/Series.kt @@ -3,16 +3,16 @@ package com.epam.brn.model import com.epam.brn.dto.SeriesDto import com.epam.brn.enums.ExerciseType import com.epam.brn.upload.csv.series.SeriesGenericRecord -import javax.persistence.CascadeType -import javax.persistence.Column -import javax.persistence.Entity -import javax.persistence.FetchType -import javax.persistence.GeneratedValue -import javax.persistence.GenerationType -import javax.persistence.Id -import javax.persistence.JoinColumn -import javax.persistence.ManyToOne -import javax.persistence.OneToMany +import jakarta.persistence.CascadeType +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.FetchType +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.JoinColumn +import jakarta.persistence.ManyToOne +import jakarta.persistence.OneToMany @Entity class Series( diff --git a/src/main/kotlin/com/epam/brn/model/Signal.kt b/src/main/kotlin/com/epam/brn/model/Signal.kt index 47e2815f1..d7963d472 100644 --- a/src/main/kotlin/com/epam/brn/model/Signal.kt +++ b/src/main/kotlin/com/epam/brn/model/Signal.kt @@ -1,13 +1,13 @@ package com.epam.brn.model import com.epam.brn.dto.SignalTaskDto -import javax.persistence.Entity -import javax.persistence.FetchType -import javax.persistence.GeneratedValue -import javax.persistence.GenerationType -import javax.persistence.Id -import javax.persistence.JoinColumn -import javax.persistence.ManyToOne +import jakarta.persistence.Entity +import jakarta.persistence.FetchType +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.JoinColumn +import jakarta.persistence.ManyToOne @Entity class Signal( diff --git a/src/main/kotlin/com/epam/brn/model/SinAudiometryResult.kt b/src/main/kotlin/com/epam/brn/model/SinAudiometryResult.kt index de842fb22..4c0dcf5b4 100644 --- a/src/main/kotlin/com/epam/brn/model/SinAudiometryResult.kt +++ b/src/main/kotlin/com/epam/brn/model/SinAudiometryResult.kt @@ -1,12 +1,12 @@ package com.epam.brn.model -import javax.persistence.Entity -import javax.persistence.FetchType -import javax.persistence.GeneratedValue -import javax.persistence.GenerationType -import javax.persistence.Id -import javax.persistence.JoinColumn -import javax.persistence.ManyToOne +import jakarta.persistence.Entity +import jakarta.persistence.FetchType +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.JoinColumn +import jakarta.persistence.ManyToOne @Entity class SinAudiometryResult( diff --git a/src/main/kotlin/com/epam/brn/model/StudyHistory.kt b/src/main/kotlin/com/epam/brn/model/StudyHistory.kt index 47798b1fd..2210d5f91 100644 --- a/src/main/kotlin/com/epam/brn/model/StudyHistory.kt +++ b/src/main/kotlin/com/epam/brn/model/StudyHistory.kt @@ -3,16 +3,16 @@ package com.epam.brn.model import com.epam.brn.dto.StudyHistoryDto import java.time.LocalDateTime import java.time.ZoneOffset -import javax.persistence.Entity -import javax.persistence.FetchType -import javax.persistence.GeneratedValue -import javax.persistence.GenerationType -import javax.persistence.Id -import javax.persistence.Index -import javax.persistence.JoinColumn -import javax.persistence.ManyToOne -import javax.persistence.Table -import javax.persistence.UniqueConstraint +import jakarta.persistence.Entity +import jakarta.persistence.FetchType +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.Index +import jakarta.persistence.JoinColumn +import jakarta.persistence.ManyToOne +import jakarta.persistence.Table +import jakarta.persistence.UniqueConstraint @Entity @Table( diff --git a/src/main/kotlin/com/epam/brn/model/SubGroup.kt b/src/main/kotlin/com/epam/brn/model/SubGroup.kt index 567785f01..e17efd38f 100644 --- a/src/main/kotlin/com/epam/brn/model/SubGroup.kt +++ b/src/main/kotlin/com/epam/brn/model/SubGroup.kt @@ -2,18 +2,18 @@ package com.epam.brn.model import com.epam.brn.dto.response.SubGroupResponse import com.epam.brn.upload.csv.subgroup.SubgroupGenericRecord -import javax.persistence.CascadeType -import javax.persistence.Column -import javax.persistence.Entity -import javax.persistence.FetchType -import javax.persistence.GeneratedValue -import javax.persistence.GenerationType -import javax.persistence.Id -import javax.persistence.JoinColumn -import javax.persistence.ManyToOne -import javax.persistence.OneToMany -import javax.persistence.Table -import javax.persistence.UniqueConstraint +import jakarta.persistence.CascadeType +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.FetchType +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.JoinColumn +import jakarta.persistence.ManyToOne +import jakarta.persistence.OneToMany +import jakarta.persistence.Table +import jakarta.persistence.UniqueConstraint @Entity @Table(uniqueConstraints = [UniqueConstraint(columnNames = ["name", "level"])]) diff --git a/src/main/kotlin/com/epam/brn/model/Task.kt b/src/main/kotlin/com/epam/brn/model/Task.kt index 5e4e7c64a..5aff3a5ff 100644 --- a/src/main/kotlin/com/epam/brn/model/Task.kt +++ b/src/main/kotlin/com/epam/brn/model/Task.kt @@ -5,17 +5,17 @@ import com.epam.brn.dto.response.TaskWordsGroupResponse import com.epam.brn.enums.ExerciseType import com.epam.brn.enums.shouldBeWithPictures import org.hibernate.annotations.BatchSize -import javax.persistence.CascadeType -import javax.persistence.Entity -import javax.persistence.FetchType -import javax.persistence.GeneratedValue -import javax.persistence.GenerationType -import javax.persistence.Id -import javax.persistence.JoinColumn -import javax.persistence.JoinTable -import javax.persistence.ManyToMany -import javax.persistence.ManyToOne -import javax.persistence.OneToOne +import jakarta.persistence.CascadeType +import jakarta.persistence.Entity +import jakarta.persistence.FetchType +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.JoinColumn +import jakarta.persistence.JoinTable +import jakarta.persistence.ManyToMany +import jakarta.persistence.ManyToOne +import jakarta.persistence.OneToOne @Entity class Task( diff --git a/src/main/kotlin/com/epam/brn/model/UserAccount.kt b/src/main/kotlin/com/epam/brn/model/UserAccount.kt index aa8e0de10..5c7b909fc 100644 --- a/src/main/kotlin/com/epam/brn/model/UserAccount.kt +++ b/src/main/kotlin/com/epam/brn/model/UserAccount.kt @@ -9,19 +9,19 @@ import org.springframework.data.annotation.LastModifiedDate import org.springframework.data.jpa.domain.support.AuditingEntityListener import java.time.LocalDateTime import java.time.ZoneOffset -import javax.persistence.CascadeType -import javax.persistence.Column -import javax.persistence.Entity -import javax.persistence.EntityListeners -import javax.persistence.FetchType -import javax.persistence.GeneratedValue -import javax.persistence.GenerationType -import javax.persistence.Id -import javax.persistence.JoinColumn -import javax.persistence.JoinTable -import javax.persistence.ManyToMany -import javax.persistence.ManyToOne -import javax.persistence.OneToMany +import jakarta.persistence.CascadeType +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.EntityListeners +import jakarta.persistence.FetchType +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.JoinColumn +import jakarta.persistence.JoinTable +import jakarta.persistence.ManyToMany +import jakarta.persistence.ManyToOne +import jakarta.persistence.OneToMany @Entity @EntityListeners(AuditingEntityListener::class) diff --git a/src/main/kotlin/com/epam/brn/model/azure/tts/AzureSpeechStyle.kt b/src/main/kotlin/com/epam/brn/model/azure/tts/AzureSpeechStyle.kt index f1af98a17..a8aee2eb4 100644 --- a/src/main/kotlin/com/epam/brn/model/azure/tts/AzureSpeechStyle.kt +++ b/src/main/kotlin/com/epam/brn/model/azure/tts/AzureSpeechStyle.kt @@ -1,11 +1,11 @@ package com.epam.brn.model.azure.tts -import javax.persistence.Column -import javax.persistence.Entity -import javax.persistence.GeneratedValue -import javax.persistence.GenerationType -import javax.persistence.Id -import javax.persistence.ManyToMany +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.ManyToMany @Entity class AzureSpeechStyle( diff --git a/src/main/kotlin/com/epam/brn/model/azure/tts/AzureVoiceInfo.kt b/src/main/kotlin/com/epam/brn/model/azure/tts/AzureVoiceInfo.kt index f20abc2b1..48ccc55a8 100644 --- a/src/main/kotlin/com/epam/brn/model/azure/tts/AzureVoiceInfo.kt +++ b/src/main/kotlin/com/epam/brn/model/azure/tts/AzureVoiceInfo.kt @@ -1,14 +1,14 @@ package com.epam.brn.model.azure.tts -import javax.persistence.CascadeType -import javax.persistence.Column -import javax.persistence.Entity -import javax.persistence.GeneratedValue -import javax.persistence.GenerationType -import javax.persistence.Id -import javax.persistence.JoinColumn -import javax.persistence.JoinTable -import javax.persistence.ManyToMany +import jakarta.persistence.CascadeType +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.JoinColumn +import jakarta.persistence.JoinTable +import jakarta.persistence.ManyToMany @Entity class AzureVoiceInfo( diff --git a/src/main/kotlin/com/epam/brn/repo/StudyHistoryRepository.kt b/src/main/kotlin/com/epam/brn/repo/StudyHistoryRepository.kt index 68133cef8..f6961d06d 100644 --- a/src/main/kotlin/com/epam/brn/repo/StudyHistoryRepository.kt +++ b/src/main/kotlin/com/epam/brn/repo/StudyHistoryRepository.kt @@ -105,7 +105,7 @@ interface StudyHistoryRepository : CrudRepository { @Query( "SELECT COALESCE(sum(s.executionSeconds), 0) FROM StudyHistory s " + - " WHERE date_trunc('day', s.startTime) = :day" + + " WHERE cast(s.startTime as date) = cast(:day as date)" + " AND s.userAccount.id = :userId", ) fun getDayTimer( @@ -115,7 +115,7 @@ interface StudyHistoryRepository : CrudRepository { @Query( "SELECT COALESCE(sum(COALESCE(s.executionSeconds, 0)), 0) FROM StudyHistory s " + - " WHERE date_trunc('day', now()) = date_trunc('day', s.startTime)" + + " WHERE cast(s.startTime as date) = current_date" + " AND s.userAccount.id = :userId", ) fun getTodayDayTimer(userId: Long): Int @@ -139,15 +139,15 @@ interface StudyHistoryRepository : CrudRepository { @Query( "SELECT MIN(s.startTime) AS firstStudy, MAX(s.startTime) AS lastStudy," + " COALESCE(SUM(s.spentTimeInSeconds), 0) AS spentTime, COUNT (DISTINCT s.exercise.id) as doneExercises" + - " FROM StudyHistory s WHERE user_id = :userId", + " FROM StudyHistory s WHERE s.userAccount.id = :userId", ) fun getStatisticsByUserAccountId(userId: Long?): UserStatisticView @Query( "SELECT s FROM StudyHistory s " + "JOIN FETCH s.exercise " + - "WHERE EXTRACT(MONTH FROM s.startTime) = :month " + - "AND EXTRACT(YEAR FROM s.startTime) = :year " + + "WHERE month(s.startTime) = :month " + + "AND year(s.startTime) = :year " + "AND s.userAccount.id = :userId", ) fun getMonthHistories( @@ -158,7 +158,7 @@ interface StudyHistoryRepository : CrudRepository { @Query( "SELECT s FROM StudyHistory s " + - " WHERE date_trunc('day', now()) = date_trunc('day', s.startTime) " + + " WHERE cast(s.startTime as date) = current_date " + " AND s.userAccount.id = :userId", ) fun getTodayHistories(userId: Long): List diff --git a/src/main/kotlin/com/epam/brn/service/AudiometryHistoryService.kt b/src/main/kotlin/com/epam/brn/service/AudiometryHistoryService.kt index f55e7a612..f86ce39e1 100644 --- a/src/main/kotlin/com/epam/brn/service/AudiometryHistoryService.kt +++ b/src/main/kotlin/com/epam/brn/service/AudiometryHistoryService.kt @@ -8,7 +8,7 @@ import com.epam.brn.repo.AudiometryHistoryRepository import com.epam.brn.repo.AudiometryTaskRepository import com.epam.brn.repo.SinAudiometryResultRepository import org.springframework.stereotype.Service -import javax.transaction.Transactional +import jakarta.transaction.Transactional @Service class AudiometryHistoryService( diff --git a/src/main/kotlin/com/epam/brn/service/TokenHelperUtils.kt b/src/main/kotlin/com/epam/brn/service/TokenHelperUtils.kt index c377e4278..59231e2ce 100644 --- a/src/main/kotlin/com/epam/brn/service/TokenHelperUtils.kt +++ b/src/main/kotlin/com/epam/brn/service/TokenHelperUtils.kt @@ -2,7 +2,7 @@ package com.epam.brn.service import org.springframework.stereotype.Component import org.springframework.util.StringUtils -import javax.servlet.http.HttpServletRequest +import jakarta.servlet.http.HttpServletRequest @Component class TokenHelperUtils { diff --git a/src/main/kotlin/com/epam/brn/upload/csv/seriesMatrix/SeriesMatrixRecordProcessor.kt b/src/main/kotlin/com/epam/brn/upload/csv/seriesMatrix/SeriesMatrixRecordProcessor.kt index 418d91573..e36bbfa7f 100644 --- a/src/main/kotlin/com/epam/brn/upload/csv/seriesMatrix/SeriesMatrixRecordProcessor.kt +++ b/src/main/kotlin/com/epam/brn/upload/csv/seriesMatrix/SeriesMatrixRecordProcessor.kt @@ -16,7 +16,7 @@ import com.epam.brn.upload.csv.RecordProcessor import com.epam.brn.upload.toStringWithoutBraces import org.apache.commons.lang3.StringUtils import org.springframework.stereotype.Component -import javax.transaction.Transactional +import jakarta.transaction.Transactional @Component class SeriesMatrixRecordProcessor( diff --git a/src/main/kotlin/com/epam/brn/upload/csv/subgroup/SubgroupGenericRecordProcessor.kt b/src/main/kotlin/com/epam/brn/upload/csv/subgroup/SubgroupGenericRecordProcessor.kt index d730fc531..09def5da5 100644 --- a/src/main/kotlin/com/epam/brn/upload/csv/subgroup/SubgroupGenericRecordProcessor.kt +++ b/src/main/kotlin/com/epam/brn/upload/csv/subgroup/SubgroupGenericRecordProcessor.kt @@ -7,7 +7,7 @@ import com.epam.brn.repo.SubGroupRepository import com.epam.brn.upload.csv.RecordProcessor import org.springframework.stereotype.Component import org.springframework.transaction.annotation.Transactional -import javax.persistence.EntityNotFoundException +import jakarta.persistence.EntityNotFoundException @Component class SubgroupGenericRecordProcessor( diff --git a/src/main/kotlin/com/epam/brn/validator/PunctuationValidator.kt b/src/main/kotlin/com/epam/brn/validator/PunctuationValidator.kt index 79ce1ae72..27a4feee6 100644 --- a/src/main/kotlin/com/epam/brn/validator/PunctuationValidator.kt +++ b/src/main/kotlin/com/epam/brn/validator/PunctuationValidator.kt @@ -1,7 +1,7 @@ package com.epam.brn.validator -import javax.validation.ConstraintValidator -import javax.validation.ConstraintValidatorContext +import jakarta.validation.ConstraintValidator +import jakarta.validation.ConstraintValidatorContext import kotlin.text.Regex class PunctuationValidator : ConstraintValidator { diff --git a/src/main/kotlin/com/epam/brn/validator/WithoutPunctuation.kt b/src/main/kotlin/com/epam/brn/validator/WithoutPunctuation.kt index adae3a9d4..091e4bc45 100644 --- a/src/main/kotlin/com/epam/brn/validator/WithoutPunctuation.kt +++ b/src/main/kotlin/com/epam/brn/validator/WithoutPunctuation.kt @@ -1,6 +1,6 @@ package com.epam.brn.validator -import javax.validation.Constraint +import jakarta.validation.Constraint import kotlin.annotation.AnnotationTarget.FIELD import kotlin.reflect.KClass diff --git a/src/main/kotlin/com/epam/brn/webclient/GitHubApiClient.kt b/src/main/kotlin/com/epam/brn/webclient/GitHubApiClient.kt index 6cbf55662..0777e7d57 100644 --- a/src/main/kotlin/com/epam/brn/webclient/GitHubApiClient.kt +++ b/src/main/kotlin/com/epam/brn/webclient/GitHubApiClient.kt @@ -6,7 +6,7 @@ import com.epam.brn.webclient.property.GitHubApiClientProperty import org.springframework.beans.factory.annotation.Autowired import org.springframework.core.ParameterizedTypeReference import org.springframework.http.HttpHeaders -import org.springframework.http.HttpStatus +import org.springframework.http.HttpStatusCode import org.springframework.stereotype.Component import org.springframework.web.reactive.function.client.WebClient import reactor.core.publisher.Mono @@ -72,7 +72,7 @@ class GitHubApiClient "${gitHubApiClientProperty.typeToken} ${gitHubApiClientProperty.token}", ) }.retrieve() - .onStatus(HttpStatus::isError) { Mono.empty() } + .onStatus(HttpStatusCode::isError) { Mono.empty() } .bodyToMono(GitHubUserDto::class.java) .onErrorResume { Mono.empty() } .block() diff --git a/src/main/kotlin/com/epam/brn/webclient/property/GitHubApiClientProperty.kt b/src/main/kotlin/com/epam/brn/webclient/property/GitHubApiClientProperty.kt index 4cb3fb4b4..ec4f3a466 100644 --- a/src/main/kotlin/com/epam/brn/webclient/property/GitHubApiClientProperty.kt +++ b/src/main/kotlin/com/epam/brn/webclient/property/GitHubApiClientProperty.kt @@ -1,9 +1,7 @@ package com.epam.brn.webclient.property import org.springframework.boot.context.properties.ConfigurationProperties -import org.springframework.boot.context.properties.ConstructorBinding -@ConstructorBinding @ConfigurationProperties(prefix = "github.api") data class GitHubApiClientProperty( val token: String, diff --git a/src/test/kotlin/com/epam/brn/auth/filter/FirebaseTokenAuthenticationFilterTest.kt b/src/test/kotlin/com/epam/brn/auth/filter/FirebaseTokenAuthenticationFilterTest.kt index 103dcb558..bf0e9e785 100644 --- a/src/test/kotlin/com/epam/brn/auth/filter/FirebaseTokenAuthenticationFilterTest.kt +++ b/src/test/kotlin/com/epam/brn/auth/filter/FirebaseTokenAuthenticationFilterTest.kt @@ -34,7 +34,7 @@ import org.springframework.security.authentication.UsernamePasswordAuthenticatio import org.springframework.security.core.context.SecurityContextHolder import org.springframework.security.core.userdetails.UsernameNotFoundException import java.time.Instant -import javax.servlet.FilterChain +import jakarta.servlet.FilterChain import kotlin.test.assertEquals import kotlin.test.assertNotNull import kotlin.test.assertNull @@ -82,7 +82,7 @@ internal class FirebaseTokenAuthenticationFilterTest { @Test fun `should set authentication when user exist in local DB`() { // GIVEN - val request = MockHttpServletRequest(HttpMethod.GET.name, "/test") + val request = MockHttpServletRequest(HttpMethod.GET.name(), "/test") val token = "firebaseTokenMock" request.addHeader("Authorization", "Bearer $token") val response = MockHttpServletResponse() @@ -115,7 +115,7 @@ internal class FirebaseTokenAuthenticationFilterTest { @Test fun `should skip token verification when bearer token is missing`() { // GIVEN - val request = MockHttpServletRequest(HttpMethod.GET.name, "/test") + val request = MockHttpServletRequest(HttpMethod.GET.name(), "/test") val response = MockHttpServletResponse() val filterChain = FilterChain { _, _ -> } @@ -136,7 +136,7 @@ internal class FirebaseTokenAuthenticationFilterTest { @Test fun `should skip token verification when authentication already exists`() { // GIVEN - val request = MockHttpServletRequest(HttpMethod.GET.name, "/test") + val request = MockHttpServletRequest(HttpMethod.GET.name(), "/test") val response = MockHttpServletResponse() val filterChain = FilterChain { _, _ -> } SecurityContextHolder.getContext().authentication = @@ -162,7 +162,7 @@ internal class FirebaseTokenAuthenticationFilterTest { @Test fun `should set authentication when user not exist in local DB`() { // GIVEN - val requestMock = MockHttpServletRequest(HttpMethod.GET.name, "/test") + val requestMock = MockHttpServletRequest(HttpMethod.GET.name(), "/test") val tokenMock = "firebaseTokenMock" requestMock.addHeader("Authorization", "Bearer $tokenMock") val responseMock = MockHttpServletResponse() @@ -206,7 +206,7 @@ internal class FirebaseTokenAuthenticationFilterTest { @Test fun `should set authentication by NULL when token invalid`() { // GIVEN - val requestMock = MockHttpServletRequest(HttpMethod.GET.name, "/test") + val requestMock = MockHttpServletRequest(HttpMethod.GET.name(), "/test") val tokenMock = "firebaseTokenMock" requestMock.addHeader("Authorization", "Bearer $tokenMock") val responseMock = MockHttpServletResponse() @@ -238,7 +238,7 @@ internal class FirebaseTokenAuthenticationFilterTest { @Test fun `should set authentication by NULL when error occurred`() { // GIVEN - val requestMock = MockHttpServletRequest(HttpMethod.GET.name, "/test") + val requestMock = MockHttpServletRequest(HttpMethod.GET.name(), "/test") val tokenMock = "firebaseTokenMock" requestMock.addHeader("Authorization", "Bearer $tokenMock") val responseMock = MockHttpServletResponse() @@ -265,7 +265,7 @@ internal class FirebaseTokenAuthenticationFilterTest { @Test fun `should set authentication by NULL when user not exist in local DB and not returning from firebase DB`() { // GIVEN - val requestMock = MockHttpServletRequest(HttpMethod.GET.name, "/test") + val requestMock = MockHttpServletRequest(HttpMethod.GET.name(), "/test") val tokenMock = "firebaseTokenMock" requestMock.addHeader("Authorization", "Bearer $tokenMock") val responseMock = MockHttpServletResponse() @@ -297,9 +297,9 @@ internal class FirebaseTokenAuthenticationFilterTest { fun `should reuse cached verified token for repeated bearer token`() { // GIVEN val token = "firebaseTokenMock" - val firstRequest = MockHttpServletRequest(HttpMethod.GET.name, "/test") + val firstRequest = MockHttpServletRequest(HttpMethod.GET.name(), "/test") firstRequest.addHeader("Authorization", "Bearer $token") - val secondRequest = MockHttpServletRequest(HttpMethod.GET.name, "/test") + val secondRequest = MockHttpServletRequest(HttpMethod.GET.name(), "/test") secondRequest.addHeader("Authorization", "Bearer $token") val response = MockHttpServletResponse() val filterChain = FilterChain { _, _ -> } diff --git a/src/test/kotlin/com/epam/brn/auth/filter/RememberLastVisitFilterTest.kt b/src/test/kotlin/com/epam/brn/auth/filter/RememberLastVisitFilterTest.kt index fb3c09ad2..8b1bdc8a8 100644 --- a/src/test/kotlin/com/epam/brn/auth/filter/RememberLastVisitFilterTest.kt +++ b/src/test/kotlin/com/epam/brn/auth/filter/RememberLastVisitFilterTest.kt @@ -15,7 +15,7 @@ import org.springframework.mock.web.MockHttpServletRequest import org.springframework.mock.web.MockHttpServletResponse import org.springframework.security.authentication.UsernamePasswordAuthenticationToken import org.springframework.security.core.context.SecurityContextHolder -import javax.servlet.FilterChain +import jakarta.servlet.FilterChain @ExtendWith(MockKExtension::class) @DisplayName("RememberLastVisitFilter test using MockK") @@ -37,7 +37,7 @@ internal class RememberLastVisitFilterTest { @Test fun `should mark visit for current user`() { // GIVEN - val requestMock = MockHttpServletRequest(HttpMethod.GET.name, "/test") + val requestMock = MockHttpServletRequest(HttpMethod.GET.name(), "/test") val tokenMock = "firebaseTokenMock" requestMock.addHeader("Authorization", "Bearer $tokenMock") val responseMock = MockHttpServletResponse() @@ -56,7 +56,7 @@ internal class RememberLastVisitFilterTest { @Test fun `should not mark visit when request is anonymous`() { // GIVEN - val requestMock = MockHttpServletRequest(HttpMethod.GET.name, "/test") + val requestMock = MockHttpServletRequest(HttpMethod.GET.name(), "/test") val tokenMock = "firebaseTokenMock" requestMock.addHeader("Authorization", "Bearer $tokenMock") val responseMock = MockHttpServletResponse() diff --git a/src/test/kotlin/com/epam/brn/integration/repo/ExerciseRepositoryTest.kt b/src/test/kotlin/com/epam/brn/integration/repo/ExerciseRepositoryTest.kt index 707bfc0fa..6ffa8a2f4 100644 --- a/src/test/kotlin/com/epam/brn/integration/repo/ExerciseRepositoryTest.kt +++ b/src/test/kotlin/com/epam/brn/integration/repo/ExerciseRepositoryTest.kt @@ -19,8 +19,8 @@ import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest import org.springframework.test.context.TestPropertySource -import javax.persistence.EntityManager -import javax.persistence.EntityManagerFactory +import jakarta.persistence.EntityManager +import jakarta.persistence.EntityManagerFactory @DataJpaTest @Tag("integration-test") diff --git a/src/test/kotlin/com/epam/brn/service/TokenHelperUtilsTest.kt b/src/test/kotlin/com/epam/brn/service/TokenHelperUtilsTest.kt index 42bb52ab7..880c2d9af 100644 --- a/src/test/kotlin/com/epam/brn/service/TokenHelperUtilsTest.kt +++ b/src/test/kotlin/com/epam/brn/service/TokenHelperUtilsTest.kt @@ -8,7 +8,7 @@ import io.mockk.verify import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith -import javax.servlet.http.HttpServletRequest +import jakarta.servlet.http.HttpServletRequest import kotlin.test.assertEquals import kotlin.test.assertNull diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties index c90744a63..b89c1a534 100644 --- a/src/test/resources/application.properties +++ b/src/test/resources/application.properties @@ -2,7 +2,7 @@ spring.profiles.active=test spring.database-platform=org.hibernate.dialect.PostgreSQLDialect spring.datasource.driver-class-name=org.testcontainers.jdbc.ContainerDatabaseDriver -spring.datasource.url=jdbc:tc:postgresql:13:///integration-db +spring.datasource.url=jdbc:tc:postgresql:16:///integration-db spring.mvc.format.date-time=iso spring.jpa.show-sql=true From 289bf8bde21f40781413a9ba8c7a8a5da5f4e2d8 Mon Sep 17 00:00:00 2001 From: Elena_Moshnikova Date: Wed, 19 Aug 2026 17:20:51 +0300 Subject: [PATCH 2/5] =?UTF-8?q?=D0=B0=D0=BF=D0=B4=D0=B5=D0=B9=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CLAUDE.md | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 18 +++++++------- 2 files changed, 79 insertions(+), 9 deletions(-) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 000000000..cdfd22b6f --- /dev/null +++ b/CLAUDE.md @@ -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 16 DB running — see README for the `docker run postgres:16` 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 **16** (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. diff --git a/README.md b/README.md index 5e21b3a09..5121f1603 100644 --- a/README.md +++ b/README.md @@ -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 16. - TestContainers for running integration tests. # GETTING STARTED! @@ -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 16. [Documentation](https://www.postgresql.org/docs/16/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/) @@ -101,18 +101,18 @@ To install docker use: 2.1 To run docker db image use the following command: * on linux\windows: ``` -docker run --name postgres_dev -d -p 5432:5432 -e POSTGRES_DB=brn -e POSTGRES_PASSWORD=$PG_PASSWORD -e POSTGRES_USER=$PG_USER postgres:13 +docker run --name postgres_dev -d -p 5432:5432 -e POSTGRES_DB=brn -e POSTGRES_PASSWORD=$PG_PASSWORD -e POSTGRES_USER=$PG_USER postgres:16 ``` `$PG_PASSWORD` and `$PG_USER` are environment variables and could be replaced directly or added to your operating system. [how to add in win10](https://www.architectryan.com/2018/03/17/add-to-the-path-on-windows-10). 2.2 Alternatively, you can just replace the variables by "admin", the default user and password for development: ```bash -docker run --name postgres_dev -d -p 5432:5432 -e POSTGRES_DB=brn -e POSTGRES_PASSWORD=admin -e POSTGRES_USER=admin postgres:13 +docker run --name postgres_dev -d -p 5432:5432 -e POSTGRES_DB=brn -e POSTGRES_PASSWORD=admin -e POSTGRES_USER=admin postgres:16 ``` ### 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. @@ -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) @@ -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. From 8ae3e34646d68b679f880ec8f0c1c23dd3b3b6e0 Mon Sep 17 00:00:00 2001 From: Elena_Moshnikova Date: Thu, 20 Aug 2026 17:35:07 +0300 Subject: [PATCH 3/5] review fix --- .../com/epam/brn/config/WebSecurityBasicConfiguration.kt | 9 +++++++-- .../kotlin/com/epam/brn/webclient/GitHubApiClient.kt | 8 ++++---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/main/kotlin/com/epam/brn/config/WebSecurityBasicConfiguration.kt b/src/main/kotlin/com/epam/brn/config/WebSecurityBasicConfiguration.kt index dc438230e..45da00b8f 100644 --- a/src/main/kotlin/com/epam/brn/config/WebSecurityBasicConfiguration.kt +++ b/src/main/kotlin/com/epam/brn/config/WebSecurityBasicConfiguration.kt @@ -35,8 +35,13 @@ class WebSecurityBasicConfiguration( .addFilterAfter(rememberLastVisitFilter, UsernamePasswordAuthenticationFilter::class.java) .authorizeHttpRequests { it - .requestMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**") - .hasRole(BrnRole.ADMIN) + .requestMatchers( + "/swagger-ui.html", + "/swagger-ui/**", + "/v3/api-docs/**", + "/swagger-resources/**", + "/webjars/**", + ).hasRole(BrnRole.ADMIN) .anyRequest() .permitAll() }.formLogin { it.disable() } diff --git a/src/main/kotlin/com/epam/brn/webclient/GitHubApiClient.kt b/src/main/kotlin/com/epam/brn/webclient/GitHubApiClient.kt index 0777e7d57..52ddefd2e 100644 --- a/src/main/kotlin/com/epam/brn/webclient/GitHubApiClient.kt +++ b/src/main/kotlin/com/epam/brn/webclient/GitHubApiClient.kt @@ -6,7 +6,6 @@ import com.epam.brn.webclient.property.GitHubApiClientProperty import org.springframework.beans.factory.annotation.Autowired import org.springframework.core.ParameterizedTypeReference import org.springframework.http.HttpHeaders -import org.springframework.http.HttpStatusCode import org.springframework.stereotype.Component import org.springframework.web.reactive.function.client.WebClient import reactor.core.publisher.Mono @@ -72,8 +71,9 @@ class GitHubApiClient "${gitHubApiClientProperty.typeToken} ${gitHubApiClientProperty.token}", ) }.retrieve() - .onStatus(HttpStatusCode::isError) { Mono.empty() } .bodyToMono(GitHubUserDto::class.java) - .onErrorResume { Mono.empty() } - .block() + .onErrorResume { + println("onErrorResume getGitHubUser $it") + Mono.empty() + }.block() } From f519c73533d8403665960e2fd3d70754cdfee6c9 Mon Sep 17 00:00:00 2001 From: Elena_Moshnikova Date: Fri, 21 Aug 2026 09:58:00 +0300 Subject: [PATCH 4/5] 16 -> 13 for deploy --- docker-compose-run.yml | 2 +- docker-compose-unit-test.yml | 2 +- docker-compose-with-secret.yml | 2 +- docker-compose.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docker-compose-run.yml b/docker-compose-run.yml index 4cef23774..84c51cf86 100644 --- a/docker-compose-run.yml +++ b/docker-compose-run.yml @@ -1,6 +1,6 @@ services: db_brn: - image: postgres:16 + image: postgres:13 restart: always ports: - 5432:5432 diff --git a/docker-compose-unit-test.yml b/docker-compose-unit-test.yml index 6f0504131..63d012cb0 100644 --- a/docker-compose-unit-test.yml +++ b/docker-compose-unit-test.yml @@ -1,6 +1,6 @@ services: db: - image: postgres:16 + image: postgres:13 restart: always environment: POSTGRES_DB: brn diff --git a/docker-compose-with-secret.yml b/docker-compose-with-secret.yml index fc1c895f2..0f8933f09 100644 --- a/docker-compose-with-secret.yml +++ b/docker-compose-with-secret.yml @@ -1,6 +1,6 @@ services: db_brn: - image: postgres:16 + image: postgres:13 restart: always ports: - 5432:5432 diff --git a/docker-compose.yml b/docker-compose.yml index e362991c2..9f7a8f75f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ services: db_brn: container_name: db_brn - image: postgres:16 + image: postgres:13 restart: always ports: - 5432:5432 From 05969a6535675c529c589f64b9c7b97fe7b3f1e5 Mon Sep 17 00:00:00 2001 From: Elena_Moshnikova Date: Sat, 22 Aug 2026 11:22:44 +0300 Subject: [PATCH 5/5] 16 -> 13 for deploy --- CLAUDE.md | 4 ++-- README.md | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index cdfd22b6f..390d76f3d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -17,7 +17,7 @@ Each subproject has its own README with details; read them before working in a f 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 16 DB running — see README for the `docker run postgres:16` command) +- 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. @@ -60,7 +60,7 @@ Angular admin app (`frontend-angular/`, npm): ## Versions / toolchain - JDK **17** toolchain, Kotlin **2.1.20**, Spring Boot **3.5.3** (uses `jakarta.*` namespace). -- Postgres **16** (local dev and Testcontainers). +- Postgres **13** (local dev and Testcontainers). - Frontend Node **22+**; Ember app uses pnpm, Angular app uses npm. ## Conventions diff --git a/README.md b/README.md index 5121f1603..b5dfe73c0 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ Autotest Python team chat: ask Elena ## TOOLS WE USE - Back-end: KOTLIN + Spring boot. Rest api as integration layer. - Front-end: Ember (user app), Angular (admin panel). -- DB: Postgres 16. +- DB: Postgres 13. - TestContainers for running integration tests. # GETTING STARTED! @@ -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 16. [Documentation](https://www.postgresql.org/docs/16/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/) @@ -101,14 +101,14 @@ To install docker use: 2.1 To run docker db image use the following command: * on linux\windows: ``` -docker run --name postgres_dev -d -p 5432:5432 -e POSTGRES_DB=brn -e POSTGRES_PASSWORD=$PG_PASSWORD -e POSTGRES_USER=$PG_USER postgres:16 +docker run --name postgres_dev -d -p 5432:5432 -e POSTGRES_DB=brn -e POSTGRES_PASSWORD=$PG_PASSWORD -e POSTGRES_USER=$PG_USER postgres:13 ``` `$PG_PASSWORD` and `$PG_USER` are environment variables and could be replaced directly or added to your operating system. [how to add in win10](https://www.architectryan.com/2018/03/17/add-to-the-path-on-windows-10). 2.2 Alternatively, you can just replace the variables by "admin", the default user and password for development: ```bash -docker run --name postgres_dev -d -p 5432:5432 -e POSTGRES_DB=brn -e POSTGRES_PASSWORD=admin -e POSTGRES_USER=admin postgres:16 +docker run --name postgres_dev -d -p 5432:5432 -e POSTGRES_DB=brn -e POSTGRES_PASSWORD=admin -e POSTGRES_USER=admin postgres:13 ``` ### Back-end Kotlin Part: