Compare commits
42 Commits
refactorin
...
doc
| Author | SHA1 | Date | |
|---|---|---|---|
| 4bb458e8d6 | |||
| 921a545877 | |||
| ef942b956e | |||
| ff74ad7e47 | |||
| 2bb90ced03 | |||
| a48cd52652 | |||
| dd4c2dadab | |||
| c81b63aef2 | |||
| cb762a446a | |||
| db810ab0c6 | |||
| 01c5b78325 | |||
| 1bc7293660 | |||
| 55c890aca5 | |||
| c0e364637a | |||
| 0a1ed9ba82 | |||
| 620085fda8 | |||
| 3b5c1cf68a | |||
| a0d07e88a1 | |||
| f17277c0e9 | |||
| 9f13213a35 | |||
| 5f0b8de159 | |||
| 6b66130ddc | |||
| 7f93ec5044 | |||
| 1be608e6b2 | |||
| b13cd5544c | |||
| 104f0fb3fc | |||
| b2f40ff421 | |||
| 09e81620a1 | |||
| 7e16c7bb74 | |||
| fe953fc967 | |||
| 453fd2225c | |||
| 70fd54d831 | |||
| dcf7a2bc06 | |||
| 118af0170a | |||
| 0aa8089a9a | |||
| fef5f3b396 | |||
| 1838b90ac9 | |||
| 73fa2be91f | |||
| 52183abd08 | |||
| e19266d4cc | |||
| f458d7b674 | |||
| 29d4d6ec25 |
134
.github/workflows/tests.yml
vendored
Normal file
134
.github/workflows/tests.yml
vendored
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
# This workflow will build a Java project with Gradle
|
||||||
|
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle
|
||||||
|
|
||||||
|
name: Tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- 'master'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Set up JDK 11
|
||||||
|
uses: actions/setup-java@v1
|
||||||
|
with:
|
||||||
|
java-version: 11
|
||||||
|
|
||||||
|
- name: Cache Gradle packages
|
||||||
|
uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.gradle/caches
|
||||||
|
~/.gradle/wrapper
|
||||||
|
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-gradle-
|
||||||
|
- name: Build
|
||||||
|
uses: eskatos/gradle-command-action@v1
|
||||||
|
with:
|
||||||
|
gradle-version: 6.8
|
||||||
|
arguments: build -x test -x ktlintKotlinScriptCheck -x ktlintTestSourceSetCheck -x ktlintMainSourceSetCheck -x detekt
|
||||||
|
- name: Cleanup Gradle Cache
|
||||||
|
# Remove some files from the Gradle cache, so they aren't cached by GitHub Actions.
|
||||||
|
# Restoring these files from a GitHub Actions cache might cause problems for future builds.
|
||||||
|
run: |
|
||||||
|
rm -f ~/.gradle/caches/modules-2/modules-2.lock
|
||||||
|
rm -f ~/.gradle/caches/modules-2/gc.properties
|
||||||
|
|
||||||
|
- name: processResources
|
||||||
|
uses: eskatos/gradle-command-action@v1
|
||||||
|
with:
|
||||||
|
gradle-version: 6.8
|
||||||
|
arguments: processResources
|
||||||
|
- name: processTestResources
|
||||||
|
uses: eskatos/gradle-command-action@v1
|
||||||
|
with:
|
||||||
|
gradle-version: 6.8
|
||||||
|
arguments: processResources
|
||||||
|
- uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: Build
|
||||||
|
path: build
|
||||||
|
|
||||||
|
testSql:
|
||||||
|
needs: build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Set up JDK 11
|
||||||
|
uses: actions/setup-java@v1
|
||||||
|
with:
|
||||||
|
java-version: 11
|
||||||
|
- uses: actions/download-artifact@v2
|
||||||
|
with:
|
||||||
|
name: Build
|
||||||
|
path: build
|
||||||
|
- name: TestSql
|
||||||
|
uses: eskatos/gradle-command-action@v1
|
||||||
|
with:
|
||||||
|
gradle-version: 6.8
|
||||||
|
arguments: testSql
|
||||||
|
|
||||||
|
test:
|
||||||
|
needs: build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Set up JDK 11
|
||||||
|
uses: actions/setup-java@v1
|
||||||
|
with:
|
||||||
|
java-version: 11
|
||||||
|
- uses: actions/download-artifact@v2
|
||||||
|
with:
|
||||||
|
name: Build
|
||||||
|
path: build
|
||||||
|
- name: Test
|
||||||
|
uses: eskatos/gradle-command-action@v1
|
||||||
|
with:
|
||||||
|
gradle-version: 6.8
|
||||||
|
arguments: test -x testSql
|
||||||
|
- name: Coverage
|
||||||
|
uses: eskatos/gradle-command-action@v1
|
||||||
|
with:
|
||||||
|
gradle-version: 6.8
|
||||||
|
arguments: coveralls
|
||||||
|
env:
|
||||||
|
COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }}
|
||||||
|
- name: Cache SonarCloud packages
|
||||||
|
uses: actions/cache@v1
|
||||||
|
with:
|
||||||
|
path: ~/.sonar/cache
|
||||||
|
key: ${{ runner.os }}-sonar
|
||||||
|
restore-keys: ${{ runner.os }}-sonar
|
||||||
|
- name: Build and analyze
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
|
||||||
|
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||||
|
run: ./gradlew build sonarqube --info
|
||||||
|
|
||||||
|
lint:
|
||||||
|
needs: build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Set up JDK 11
|
||||||
|
uses: actions/setup-java@v1
|
||||||
|
with:
|
||||||
|
java-version: 11
|
||||||
|
- uses: actions/download-artifact@v2
|
||||||
|
with:
|
||||||
|
name: Build
|
||||||
|
path: build
|
||||||
|
- name: Lint
|
||||||
|
uses: eskatos/gradle-command-action@v1
|
||||||
|
with:
|
||||||
|
gradle-version: 6.8
|
||||||
|
arguments: ktlintCheck
|
||||||
2
.idea/dataSources.xml
generated
2
.idea/dataSources.xml
generated
@@ -11,7 +11,7 @@
|
|||||||
<driver-ref>postgresql</driver-ref>
|
<driver-ref>postgresql</driver-ref>
|
||||||
<synchronize>true</synchronize>
|
<synchronize>true</synchronize>
|
||||||
<jdbc-driver>org.postgresql.Driver</jdbc-driver>
|
<jdbc-driver>org.postgresql.Driver</jdbc-driver>
|
||||||
<jdbc-url>jdbc:postgresql://localhost:5433/test</jdbc-url>
|
<jdbc-url>jdbc:postgresql://localhost:15432/test</jdbc-url>
|
||||||
</data-source>
|
</data-source>
|
||||||
<data-source source="LOCAL" name="sonar@localhost" uuid="ee78beab-120d-4740-ad21-d4d9e2121d25">
|
<data-source source="LOCAL" name="sonar@localhost" uuid="ee78beab-120d-4740-ad21-d4d9e2121d25">
|
||||||
<driver-ref>postgresql</driver-ref>
|
<driver-ref>postgresql</driver-ref>
|
||||||
|
|||||||
5
.idea/runConfigurations/Sonarqube.xml
generated
5
.idea/runConfigurations/Sonarqube.xml
generated
@@ -1,6 +1,11 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="Sonarqube" type="GradleRunConfiguration" factoryName="Gradle">
|
<configuration default="false" name="Sonarqube" type="GradleRunConfiguration" factoryName="Gradle">
|
||||||
<ExternalSystemSettings>
|
<ExternalSystemSettings>
|
||||||
|
<option name="env">
|
||||||
|
<map>
|
||||||
|
<entry key="SONAR_TOKEN" value="15ad34f46763706727d884ced12c48d5222fe639" />
|
||||||
|
</map>
|
||||||
|
</option>
|
||||||
<option name="executionName" />
|
<option name="executionName" />
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||||
<option name="externalSystemIdString" value="GRADLE" />
|
<option name="externalSystemIdString" value="GRADLE" />
|
||||||
|
|||||||
12
README.md
12
README.md
@@ -1,6 +1,16 @@
|
|||||||
# DC Project
|
# DC Project
|
||||||
|
|
||||||
[Installation](./doc/installation)
|
[](https://www.codefactor.io/repository/github/flecomte/dc-project)
|
||||||
|
[](https://www.codacy.com/gh/flecomte/dc-project/dashboard?utm_source=github.com&utm_medium=referral&utm_content=flecomte/dc-project&utm_campaign=Badge_Grade)
|
||||||
|
[](https://sonarcloud.io/dashboard?id=dc-project)
|
||||||
|
|
||||||
|
[](https://github.com/flecomte/dc-project/actions/workflows/tests.yml)
|
||||||
|
[](https://coveralls.io/github/flecomte/dc-project?branch=master)
|
||||||
|
[](https://sonarcloud.io/dashboard?id=dc-project)
|
||||||
|
|
||||||
|
[](https://sonarcloud.io/dashboard?id=dc-project)
|
||||||
|
|
||||||
|
[Installation](./doc/installation/Installation.md)
|
||||||
|
|
||||||
### Run dockers
|
### Run dockers
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ plugins {
|
|||||||
id("net.nemerosa.versioning") version "2.14.0"
|
id("net.nemerosa.versioning") version "2.14.0"
|
||||||
id("io.gitlab.arturbosch.detekt") version "1.16.0-RC1"
|
id("io.gitlab.arturbosch.detekt") version "1.16.0-RC1"
|
||||||
id("com.avast.gradle.docker-compose") version "0.14.0"
|
id("com.avast.gradle.docker-compose") version "0.14.0"
|
||||||
|
id("com.github.kt3k.coveralls") version "2.8.4"
|
||||||
}
|
}
|
||||||
|
|
||||||
application {
|
application {
|
||||||
@@ -56,10 +57,13 @@ buildscript {
|
|||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath("com.typesafe:config:1.4.1")
|
classpath("com.typesafe:config:1.4.1")
|
||||||
classpath("com.github.flecomte:postgres-json:2.1.1")
|
classpath("com.github.flecomte:postgres-json:2.1.2")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tasks.distZip.configure { enabled = false }
|
||||||
|
tasks.distTar.configure { enabled = false }
|
||||||
|
|
||||||
tasks.withType<KotlinCompile> {
|
tasks.withType<KotlinCompile> {
|
||||||
kotlinOptions {
|
kotlinOptions {
|
||||||
jvmTarget = "11"
|
jvmTarget = "11"
|
||||||
@@ -71,7 +75,7 @@ val migration by tasks.registering {
|
|||||||
dependsOn(tasks.named("composeUp"))
|
dependsOn(tasks.named("composeUp"))
|
||||||
|
|
||||||
doLast {
|
doLast {
|
||||||
val config = ConfigFactory.parseFile(file("$buildDir/../src/main/resources/application.conf")).resolve()
|
val config = ConfigFactory.parseFile(file("$buildDir/resources/main/application.conf")).resolve()
|
||||||
val connection = Connection(
|
val connection = Connection(
|
||||||
host = config.getString("db.host"),
|
host = config.getString("db.host"),
|
||||||
port = config.getInt("db.port"),
|
port = config.getInt("db.port"),
|
||||||
@@ -81,8 +85,8 @@ val migration by tasks.registering {
|
|||||||
)
|
)
|
||||||
Migrations(
|
Migrations(
|
||||||
connection,
|
connection,
|
||||||
file("$buildDir/../src/main/resources/sql/migrations").toURI(),
|
file("$buildDir/resources/main/sql/migrations").toURI(),
|
||||||
file("$buildDir/../src/main/resources/sql/functions").toURI()
|
file("$buildDir/resources/main/sql/functions").toURI()
|
||||||
).run {
|
).run {
|
||||||
run()
|
run()
|
||||||
}
|
}
|
||||||
@@ -94,7 +98,7 @@ val migrationTest by tasks.registering {
|
|||||||
dependsOn(tasks.named("testComposeUp"))
|
dependsOn(tasks.named("testComposeUp"))
|
||||||
finalizedBy(tasks.named("testComposeDown"))
|
finalizedBy(tasks.named("testComposeDown"))
|
||||||
doLast {
|
doLast {
|
||||||
val config = ConfigFactory.parseFile(file("$buildDir/../src/test/resources/application-test.conf")).resolve()
|
val config = ConfigFactory.parseFile(file("$buildDir/resources/test/application-test.conf")).resolve()
|
||||||
val connection = Connection(
|
val connection = Connection(
|
||||||
host = config.getString("db.host"),
|
host = config.getString("db.host"),
|
||||||
port = config.getInt("db.port"),
|
port = config.getInt("db.port"),
|
||||||
@@ -104,8 +108,8 @@ val migrationTest by tasks.registering {
|
|||||||
)
|
)
|
||||||
Migrations(
|
Migrations(
|
||||||
connection,
|
connection,
|
||||||
file("$buildDir/../src/main/resources/sql/migrations").toURI(),
|
file("$buildDir/resources/main/sql/migrations").toURI(),
|
||||||
file("$buildDir/../src/main/resources/sql/functions").toURI()
|
file("$buildDir/resources/main/sql/functions").toURI()
|
||||||
).run {
|
).run {
|
||||||
run()
|
run()
|
||||||
connection.disconnect()
|
connection.disconnect()
|
||||||
@@ -115,11 +119,13 @@ val migrationTest by tasks.registering {
|
|||||||
|
|
||||||
val testSql by tasks.registering {
|
val testSql by tasks.registering {
|
||||||
group = "verification"
|
group = "verification"
|
||||||
dependsOn(tasks.named("testComposeUp"))
|
dependsOn(tasks.named("processResources"))
|
||||||
finalizedBy(tasks.named("testComposeDown"))
|
dependsOn(tasks.named("processTestResources"))
|
||||||
|
dependsOn(tasks.named("testSqlComposeUp"))
|
||||||
|
finalizedBy(tasks.named("testSqlComposeDown"))
|
||||||
|
|
||||||
doLast {
|
doLast {
|
||||||
val config = ConfigFactory.parseFile(file("$buildDir/../src/test/resources/application-test.conf")).resolve()
|
val config = ConfigFactory.parseFile(file("$buildDir/resources/test/application-test.conf")).resolve()
|
||||||
|
|
||||||
val connection = Connection(
|
val connection = Connection(
|
||||||
host = config.getString("db.host"),
|
host = config.getString("db.host"),
|
||||||
@@ -131,16 +137,14 @@ val testSql by tasks.registering {
|
|||||||
|
|
||||||
Migrations(
|
Migrations(
|
||||||
connection,
|
connection,
|
||||||
file("$buildDir/../src/main/resources/sql/migrations").toURI(),
|
file("$buildDir/resources/main/sql/migrations").toURI(),
|
||||||
file("$buildDir/../src/main/resources/sql/functions").toURI(),
|
file("$buildDir/resources/main/sql/functions").toURI(),
|
||||||
file("$buildDir/../src/test/sql/fixtures").toURI()
|
file("$buildDir/resources/test/sql/fixtures").toURI()
|
||||||
).run {
|
).run()
|
||||||
run()
|
|
||||||
}
|
|
||||||
|
|
||||||
Requester.RequesterFactory(
|
Requester.RequesterFactory(
|
||||||
connection = connection,
|
connection = connection,
|
||||||
queriesDirectory = file("$buildDir/../src/test/sql").toURI()
|
queriesDirectory = file("$buildDir/resources/test/sql").toURI()
|
||||||
).createRequester().run {
|
).createRequester().run {
|
||||||
getQueries().map {
|
getQueries().map {
|
||||||
try {
|
try {
|
||||||
@@ -178,7 +182,11 @@ tasks.named<ShadowJar>("shadowJar") {
|
|||||||
archiveFileName.set("${archiveBaseName.get()}-latest-all.${archiveExtension.get()}")
|
archiveFileName.set("${archiveBaseName.get()}-latest-all.${archiveExtension.get()}")
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.sonarqube.configure { dependsOn(tasks.jacocoTestReport) }
|
tasks.sonarqube.configure {
|
||||||
|
dependsOn(tasks.test)
|
||||||
|
dependsOn(tasks.detekt)
|
||||||
|
dependsOn(tasks.jacocoTestReport)
|
||||||
|
}
|
||||||
|
|
||||||
val sourcesJar by tasks.registering(Jar::class) {
|
val sourcesJar by tasks.registering(Jar::class) {
|
||||||
group = "build"
|
group = "build"
|
||||||
@@ -194,6 +202,17 @@ tasks.test {
|
|||||||
finalizedBy(tasks.jacocoTestReport) // report is always generated after tests run
|
finalizedBy(tasks.jacocoTestReport) // report is always generated after tests run
|
||||||
}
|
}
|
||||||
|
|
||||||
|
coveralls {
|
||||||
|
sourceDirs.add("src/main/kotlin")
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register("testAll") {
|
||||||
|
group = "verification"
|
||||||
|
dependsOn(testSql)
|
||||||
|
dependsOn(tasks.test)
|
||||||
|
dependsOn(tasks.ktlintCheck)
|
||||||
|
}
|
||||||
|
|
||||||
apply(plugin = "docker-compose")
|
apply(plugin = "docker-compose")
|
||||||
dockerCompose {
|
dockerCompose {
|
||||||
projectName = "dc-project"
|
projectName = "dc-project"
|
||||||
@@ -203,23 +222,22 @@ dockerCompose {
|
|||||||
removeVolumes = false
|
removeVolumes = false
|
||||||
removeContainers = false
|
removeContainers = false
|
||||||
isRequiredBy(project.tasks.run)
|
isRequiredBy(project.tasks.run)
|
||||||
|
|
||||||
|
createNested("testSql").apply {
|
||||||
|
projectName = "dc-project_test"
|
||||||
|
useComposeFiles = listOf("docker-compose-test.yml")
|
||||||
|
startedServices = listOf("db", "elasticsearch")
|
||||||
|
stopContainers = false
|
||||||
|
isRequiredBy(project.tasks.named("testSql"))
|
||||||
|
}
|
||||||
|
|
||||||
createNested("test").apply {
|
createNested("test").apply {
|
||||||
projectName = "dc-project_test"
|
projectName = "dc-project_test"
|
||||||
useComposeFiles = listOf("docker-compose-test.yml")
|
useComposeFiles = listOf("docker-compose-test.yml")
|
||||||
stopContainers = false
|
stopContainers = false
|
||||||
isRequiredBy(project.tasks.test)
|
isRequiredBy(project.tasks.test)
|
||||||
isRequiredBy(project.tasks.named("testSql"))
|
|
||||||
}
|
|
||||||
createNested("sonarqube").apply {
|
|
||||||
projectName = "dc-project"
|
|
||||||
useComposeFiles = listOf("docker-compose-sonar.yml")
|
|
||||||
stopContainers = false
|
|
||||||
removeVolumes = false
|
|
||||||
removeContainers = false
|
|
||||||
// isRequiredBy(project.tasks.sonarqube)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tasks.sonarqube.configure { dependsOn(tasks.named("sonarqubeComposeUp")) }
|
|
||||||
|
|
||||||
publishing {
|
publishing {
|
||||||
if (versioning.info.dirty == false) {
|
if (versioning.info.dirty == false) {
|
||||||
@@ -267,6 +285,7 @@ tasks.jacocoTestReport {
|
|||||||
|
|
||||||
detekt {
|
detekt {
|
||||||
buildUponDefaultConfig = true // preconfigure defaults
|
buildUponDefaultConfig = true // preconfigure defaults
|
||||||
|
ignoreFailures = true
|
||||||
// config = files("$projectDir/config/detekt.yml") // point to your custom config defining rules to run, overwriting default behavior
|
// config = files("$projectDir/config/detekt.yml") // point to your custom config defining rules to run, overwriting default behavior
|
||||||
// baseline = file("$projectDir/config/baseline.xml") // a way of suppressing issues before introducing detekt
|
// baseline = file("$projectDir/config/baseline.xml") // a way of suppressing issues before introducing detekt
|
||||||
|
|
||||||
@@ -281,6 +300,7 @@ detekt {
|
|||||||
tasks.withType<Detekt> {
|
tasks.withType<Detekt> {
|
||||||
// Target version of the generated JVM bytecode. It is used for type resolution.
|
// Target version of the generated JVM bytecode. It is used for type resolution.
|
||||||
this.jvmTarget = "11"
|
this.jvmTarget = "11"
|
||||||
|
ignoreFailures = true
|
||||||
}
|
}
|
||||||
|
|
||||||
val setMaxMapCount = tasks.create<Exec>("setMaxMapCount") {
|
val setMaxMapCount = tasks.create<Exec>("setMaxMapCount") {
|
||||||
@@ -293,7 +313,12 @@ val setMaxMapCount = tasks.create<Exec>("setMaxMapCount") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tasks.named("testComposeUp").configure { dependsOn(setMaxMapCount) }
|
|
||||||
|
tasks.named("testComposeUp").configure {
|
||||||
|
if (OperatingSystem.current().isWindows) {
|
||||||
|
dependsOn(setMaxMapCount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dependencyCheck {
|
dependencyCheck {
|
||||||
formats = listOf(ReportGenerator.Format.HTML, ReportGenerator.Format.XML)
|
formats = listOf(ReportGenerator.Format.HTML, ReportGenerator.Format.XML)
|
||||||
@@ -327,7 +352,7 @@ dependencies {
|
|||||||
implementation("net.pearx.kasechange:kasechange-jvm:1.3.0")
|
implementation("net.pearx.kasechange:kasechange-jvm:1.3.0")
|
||||||
implementation("com.auth0:java-jwt:3.12.0")
|
implementation("com.auth0:java-jwt:3.12.0")
|
||||||
implementation("com.github.jasync-sql:jasync-postgresql:1.1.6")
|
implementation("com.github.jasync-sql:jasync-postgresql:1.1.6")
|
||||||
implementation("com.github.flecomte:postgres-json:2.1.1")
|
implementation("com.github.flecomte:postgres-json:2.1.2")
|
||||||
implementation("com.sendgrid:sendgrid-java:4.7.1")
|
implementation("com.sendgrid:sendgrid-java:4.7.1")
|
||||||
implementation("io.lettuce:lettuce-core:5.3.6.RELEASE") // TODO update to 6.0.2
|
implementation("io.lettuce:lettuce-core:5.3.6.RELEASE") // TODO update to 6.0.2
|
||||||
implementation("com.rabbitmq:amqp-client:5.10.0")
|
implementation("com.rabbitmq:amqp-client:5.10.0")
|
||||||
|
|||||||
30
doc/CreateAction.md
Normal file
30
doc/CreateAction.md
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
Create Action
|
||||||
|
============
|
||||||
|
|
||||||
|
* [ ] Create [OpenApi](../src/main/resources/openapi.yaml) documentation
|
||||||
|
* [ ] Create route
|
||||||
|
* [ ] Create request with [Location](https://ktor.io/docs/features-locations.html)
|
||||||
|
* [ ] Create Validation of request with [Konform](https://www.konform.io)
|
||||||
|
* [ ] Test validation
|
||||||
|
* [ ] [Check auth](../src/main/kotlin/fr/dcproject/component/auth/CitizenContext.kt) on protected route
|
||||||
|
* [ ] [Create test for auth](../src/test/kotlin/integration/steps/given/Auth.kt)
|
||||||
|
* [ ] Return must not be an Entity
|
||||||
|
* [ ] Tests request:
|
||||||
|
* [ ] Route with these params
|
||||||
|
* [ ] Body of the request
|
||||||
|
* [ ] Success
|
||||||
|
* [ ] BadRequest
|
||||||
|
* [ ] Body and request params must [match with the openapi schema](../src/test/kotlin/integration/steps/then/schema.kt)
|
||||||
|
* [ ] Create [AccessControl](../src/main/kotlin/fr/dcproject/common/security/AccessControlModule.kt)
|
||||||
|
* [ ] Test [AccessControl](../src/test/kotlin/integration/steps/given/Auth.kt)
|
||||||
|
|
||||||
|
|
||||||
|
* [ ] Create Entity
|
||||||
|
|
||||||
|
|
||||||
|
* [ ] Create Repository
|
||||||
|
* [ ] Create SQL function in file
|
||||||
|
* [ ] Create Tests SQL
|
||||||
|
|
||||||
|
* [ ] Tests
|
||||||
|
* [ ] Test BadRequest
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
version: '3.8'
|
|
||||||
services:
|
|
||||||
sonarqube:
|
|
||||||
container_name: ${APP_NAME}_sonarqube
|
|
||||||
image: sonarqube:community
|
|
||||||
depends_on:
|
|
||||||
- sonarqube_db
|
|
||||||
ports:
|
|
||||||
- ${SONARQUBE_PORT}:9000
|
|
||||||
networks:
|
|
||||||
- sonarnet
|
|
||||||
environment:
|
|
||||||
SONAR_JDBC_URL: jdbc:postgresql://sonarqube_db:5432/sonar
|
|
||||||
SONAR_JDBC_USERNAME: sonar
|
|
||||||
SONAR_JDBC_PASSWORD: sonar
|
|
||||||
volumes:
|
|
||||||
- sonarqube_data:/opt/sonarqube/data
|
|
||||||
- sonarqube_extensions:/opt/sonarqube/extensions
|
|
||||||
- sonarqube_logs:/opt/sonarqube/logs
|
|
||||||
- sonarqube_temp:/opt/sonarqube/temp
|
|
||||||
|
|
||||||
sonarqube_db:
|
|
||||||
container_name: ${APP_NAME}_sonarqube_db
|
|
||||||
image: postgres:alpine
|
|
||||||
networks:
|
|
||||||
- sonarnet
|
|
||||||
environment:
|
|
||||||
POSTGRES_USER: sonar
|
|
||||||
POSTGRES_PASSWORD: sonar
|
|
||||||
ports:
|
|
||||||
- ${SONARQUBE_DB_PORT}:5432
|
|
||||||
volumes:
|
|
||||||
- sonarqube_postgresql:/var/lib/postgresql
|
|
||||||
# This needs explicit mapping due to https://github.com/docker-library/postgres/blob/4e48e3228a30763913ece952c611e5e9b95c8759/Dockerfile.template#L52
|
|
||||||
- sonarqube_postgresql_data:/var/lib/postgresql/data
|
|
||||||
|
|
||||||
|
|
||||||
networks:
|
|
||||||
sonarnet:
|
|
||||||
driver: bridge
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
sonarqube_data:
|
|
||||||
sonarqube_extensions:
|
|
||||||
sonarqube_logs:
|
|
||||||
sonarqube_temp:
|
|
||||||
sonarqube_postgresql:
|
|
||||||
sonarqube_postgresql_data:
|
|
||||||
@@ -38,6 +38,9 @@ services:
|
|||||||
REDIS_CONNECTION: ${REDIS_CONNECTION}
|
REDIS_CONNECTION: ${REDIS_CONNECTION}
|
||||||
RABBITMQ_CONNECTION: ${RABBITMQ_CONNECTION}
|
RABBITMQ_CONNECTION: ${RABBITMQ_CONNECTION}
|
||||||
ELASTICSEARCH_CONNECTION: ${ELASTICSEARCH_CONNECTION}
|
ELASTICSEARCH_CONNECTION: ${ELASTICSEARCH_CONNECTION}
|
||||||
|
JWT_SECRET: ${JWT_SECRET}
|
||||||
|
JWT_ISSUER: ${JWT_ISSUER}
|
||||||
|
JWT_VALIDITY: ${JWT_VALIDITY}
|
||||||
depends_on:
|
depends_on:
|
||||||
- elasticsearch
|
- elasticsearch
|
||||||
- db
|
- db
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
kotlin.code.style=official
|
kotlin.code.style=official
|
||||||
systemProp.sonar.host.url=http://localhost:9002
|
systemProp.sonar.host.url=https://sonarcloud.io
|
||||||
systemProp.sonar.login=admin
|
|
||||||
systemProp.sonar.password=sonar
|
|
||||||
systemProp.sonar.projectKey=dc-project
|
systemProp.sonar.projectKey=dc-project
|
||||||
systemProp.sonar.projectName=DC Project
|
systemProp.sonar.projectName=DC Project
|
||||||
|
systemProp.sonar.organization=flecomte
|
||||||
systemProp.sonar.java.coveragePlugin=jacoco
|
systemProp.sonar.java.coveragePlugin=jacoco
|
||||||
systemProp.sonar.coverage.jacoco.xmlReportPaths=build/reports/jacoco/test/jacocoTestReport.xml
|
systemProp.sonar.coverage.jacoco.xmlReportPaths=build/reports/jacoco/test/jacocoTestReport.xml
|
||||||
systemProp.sonar.kotlin.detekt.reportPaths=build/reports/detekt/detekt.xml
|
|
||||||
|
|||||||
@@ -6,17 +6,14 @@ import com.fasterxml.jackson.databind.DeserializationFeature
|
|||||||
import com.fasterxml.jackson.databind.PropertyNamingStrategies
|
import com.fasterxml.jackson.databind.PropertyNamingStrategies
|
||||||
import com.fasterxml.jackson.databind.SerializationFeature
|
import com.fasterxml.jackson.databind.SerializationFeature
|
||||||
import com.fasterxml.jackson.datatype.joda.JodaModule
|
import com.fasterxml.jackson.datatype.joda.JodaModule
|
||||||
import com.github.jasync.sql.db.postgresql.exceptions.GenericDatabaseException
|
|
||||||
import fr.dcproject.application.Env.PROD
|
import fr.dcproject.application.Env.PROD
|
||||||
import fr.dcproject.application.Env.TEST
|
import fr.dcproject.application.Env.TEST
|
||||||
import fr.dcproject.common.security.AccessDeniedException
|
import fr.dcproject.application.http.statusPagesInstallation
|
||||||
import fr.dcproject.component.article.articleKoinModule
|
import fr.dcproject.component.article.articleKoinModule
|
||||||
import fr.dcproject.component.article.routes.installArticleRoutes
|
import fr.dcproject.component.article.routes.installArticleRoutes
|
||||||
import fr.dcproject.component.auth.ForbiddenException
|
|
||||||
import fr.dcproject.component.auth.authKoinModule
|
import fr.dcproject.component.auth.authKoinModule
|
||||||
import fr.dcproject.component.auth.jwt.jwtInstallation
|
import fr.dcproject.component.auth.jwt.jwtInstallation
|
||||||
import fr.dcproject.component.auth.routes.installAuthRoutes
|
import fr.dcproject.component.auth.routes.installAuthRoutes
|
||||||
import fr.dcproject.component.auth.user
|
|
||||||
import fr.dcproject.component.citizen.citizenKoinModule
|
import fr.dcproject.component.citizen.citizenKoinModule
|
||||||
import fr.dcproject.component.citizen.routes.installCitizenRoutes
|
import fr.dcproject.component.citizen.routes.installCitizenRoutes
|
||||||
import fr.dcproject.component.comment.article.routes.installCommentArticleRoutes
|
import fr.dcproject.component.comment.article.routes.installCommentArticleRoutes
|
||||||
@@ -41,7 +38,6 @@ import fr.dcproject.component.workgroup.workgroupKoinModule
|
|||||||
import fr.postgresjson.migration.Migrations
|
import fr.postgresjson.migration.Migrations
|
||||||
import io.ktor.application.Application
|
import io.ktor.application.Application
|
||||||
import io.ktor.application.ApplicationStopped
|
import io.ktor.application.ApplicationStopped
|
||||||
import io.ktor.application.call
|
|
||||||
import io.ktor.application.install
|
import io.ktor.application.install
|
||||||
import io.ktor.auth.Authentication
|
import io.ktor.auth.Authentication
|
||||||
import io.ktor.client.HttpClient
|
import io.ktor.client.HttpClient
|
||||||
@@ -51,17 +47,14 @@ import io.ktor.features.CORS
|
|||||||
import io.ktor.features.CallLogging
|
import io.ktor.features.CallLogging
|
||||||
import io.ktor.features.ContentNegotiation
|
import io.ktor.features.ContentNegotiation
|
||||||
import io.ktor.features.DataConversion
|
import io.ktor.features.DataConversion
|
||||||
import io.ktor.features.NotFoundException
|
|
||||||
import io.ktor.features.StatusPages
|
import io.ktor.features.StatusPages
|
||||||
import io.ktor.http.HttpHeaders
|
import io.ktor.http.HttpHeaders
|
||||||
import io.ktor.http.HttpMethod
|
import io.ktor.http.HttpMethod
|
||||||
import io.ktor.http.HttpStatusCode
|
|
||||||
import io.ktor.http.cio.websocket.pingPeriod
|
import io.ktor.http.cio.websocket.pingPeriod
|
||||||
import io.ktor.http.cio.websocket.timeout
|
import io.ktor.http.cio.websocket.timeout
|
||||||
import io.ktor.jackson.jackson
|
import io.ktor.jackson.jackson
|
||||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||||
import io.ktor.locations.Locations
|
import io.ktor.locations.Locations
|
||||||
import io.ktor.response.respond
|
|
||||||
import io.ktor.routing.Routing
|
import io.ktor.routing.Routing
|
||||||
import io.ktor.server.jetty.EngineMain
|
import io.ktor.server.jetty.EngineMain
|
||||||
import io.ktor.util.KtorExperimentalAPI
|
import io.ktor.util.KtorExperimentalAPI
|
||||||
@@ -73,7 +66,6 @@ import org.koin.ktor.ext.Koin
|
|||||||
import org.koin.ktor.ext.get
|
import org.koin.ktor.ext.get
|
||||||
import org.slf4j.event.Level
|
import org.slf4j.event.Level
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
import java.util.concurrent.CompletionException
|
|
||||||
|
|
||||||
fun main(args: Array<String>): Unit = EngineMain.main(args)
|
fun main(args: Array<String>): Unit = EngineMain.main(args)
|
||||||
|
|
||||||
@@ -132,7 +124,7 @@ fun Application.module(env: Env = PROD) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
install(Authentication, jwtInstallation(get()))
|
install(Authentication, jwtInstallation(get(), get()))
|
||||||
|
|
||||||
install(AutoHeadResponse)
|
install(AutoHeadResponse)
|
||||||
|
|
||||||
@@ -171,26 +163,7 @@ fun Application.module(env: Env = PROD) {
|
|||||||
installDocRoutes()
|
installDocRoutes()
|
||||||
}
|
}
|
||||||
|
|
||||||
install(StatusPages) {
|
install(StatusPages, statusPagesInstallation())
|
||||||
exception<CompletionException> { e ->
|
|
||||||
val parent = e.cause?.cause
|
|
||||||
if (parent is GenericDatabaseException) {
|
|
||||||
call.respond(HttpStatusCode.BadRequest, parent.errorMessage.message!!)
|
|
||||||
} else {
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
}
|
|
||||||
exception<NotFoundException> { e ->
|
|
||||||
call.respond(HttpStatusCode.NotFound, e.message!!)
|
|
||||||
}
|
|
||||||
exception<AccessDeniedException> {
|
|
||||||
if (call.user == null) call.respond(HttpStatusCode.Unauthorized)
|
|
||||||
else call.respond(HttpStatusCode.Forbidden)
|
|
||||||
}
|
|
||||||
exception<ForbiddenException> {
|
|
||||||
call.respond(HttpStatusCode.Forbidden)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
install(CORS) {
|
install(CORS) {
|
||||||
method(HttpMethod.Options)
|
method(HttpMethod.Options)
|
||||||
|
|||||||
@@ -43,4 +43,15 @@ class Configuration(val config: Config) {
|
|||||||
val rabbitmq: String = config.getString("rabbitmq.connection")
|
val rabbitmq: String = config.getString("rabbitmq.connection")
|
||||||
val exchangeNotificationName = "notification"
|
val exchangeNotificationName = "notification"
|
||||||
val sendGridKey: String = config.getString("mail.sendGrid.key")
|
val sendGridKey: String = config.getString("mail.sendGrid.key")
|
||||||
|
|
||||||
|
interface Jwt {
|
||||||
|
val secret: String
|
||||||
|
val issuer: String
|
||||||
|
val validityInMs: Int
|
||||||
|
}
|
||||||
|
val jwt = object : Jwt {
|
||||||
|
override val secret = config.getString("jwt.secret")
|
||||||
|
override val issuer = config.getString("jwt.issuer")
|
||||||
|
override val validityInMs = config.getInt("jwt.validity")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import com.fasterxml.jackson.datatype.joda.JodaModule
|
|||||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||||
import com.rabbitmq.client.ConnectionFactory
|
import com.rabbitmq.client.ConnectionFactory
|
||||||
import fr.dcproject.common.email.Mailer
|
import fr.dcproject.common.email.Mailer
|
||||||
|
import fr.dcproject.component.auth.jwt.JwtConfig
|
||||||
import fr.dcproject.component.notification.NotificationConsumer
|
import fr.dcproject.component.notification.NotificationConsumer
|
||||||
import fr.dcproject.component.notification.NotificationEmailSender
|
import fr.dcproject.component.notification.NotificationEmailSender
|
||||||
import fr.dcproject.component.notification.NotificationsPush
|
import fr.dcproject.component.notification.NotificationsPush
|
||||||
@@ -25,6 +26,19 @@ import org.koin.dsl.module
|
|||||||
|
|
||||||
@KtorExperimentalAPI
|
@KtorExperimentalAPI
|
||||||
val KoinModule = module {
|
val KoinModule = module {
|
||||||
|
// JWT
|
||||||
|
single {
|
||||||
|
val config: Configuration = get()
|
||||||
|
JwtConfig(
|
||||||
|
config.jwt.secret,
|
||||||
|
config.jwt.issuer,
|
||||||
|
config.jwt.validityInMs,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// JWT Verifier
|
||||||
|
single {
|
||||||
|
get<JwtConfig>().verifier
|
||||||
|
}
|
||||||
// SQL connection
|
// SQL connection
|
||||||
single {
|
single {
|
||||||
val config: Configuration = get()
|
val config: Configuration = get()
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
package fr.dcproject.application.http
|
||||||
|
|
||||||
|
import com.github.jasync.sql.db.postgresql.exceptions.GenericDatabaseException
|
||||||
|
import fr.dcproject.common.security.AccessDeniedException
|
||||||
|
import fr.dcproject.component.auth.ForbiddenException
|
||||||
|
import fr.dcproject.component.auth.user
|
||||||
|
import io.ktor.application.call
|
||||||
|
import io.ktor.features.NotFoundException
|
||||||
|
import io.ktor.features.StatusPages
|
||||||
|
import io.ktor.http.HttpStatusCode
|
||||||
|
import io.ktor.response.respond
|
||||||
|
import java.util.concurrent.CompletionException
|
||||||
|
|
||||||
|
class HttpError(
|
||||||
|
statusCode: HttpStatusCode,
|
||||||
|
val cause: Throwable? = null,
|
||||||
|
val type: String? = null,
|
||||||
|
val title: String = cause?.message ?: statusCode.description,
|
||||||
|
val detail: String? = null,
|
||||||
|
val invalidParams: List<InvalidParam>? = null,
|
||||||
|
val stackTrace: String? = cause?.stackTraceToString()
|
||||||
|
) {
|
||||||
|
val statusCode: Int = statusCode.value
|
||||||
|
data class InvalidParam(
|
||||||
|
val name: String,
|
||||||
|
val reason: String
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun statusPagesInstallation(): StatusPages.Configuration.() -> Unit = {
|
||||||
|
exception<CompletionException> { e ->
|
||||||
|
val parent = e.cause?.cause
|
||||||
|
if (parent is GenericDatabaseException) {
|
||||||
|
HttpError(
|
||||||
|
HttpStatusCode.BadRequest,
|
||||||
|
cause = parent
|
||||||
|
).let {
|
||||||
|
call.respond(HttpStatusCode.BadRequest, it)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
HttpError(
|
||||||
|
HttpStatusCode.BadRequest,
|
||||||
|
cause = e
|
||||||
|
).let {
|
||||||
|
call.respond(HttpStatusCode.InternalServerError, it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exception<NotFoundException> { e ->
|
||||||
|
HttpError(
|
||||||
|
HttpStatusCode.NotFound,
|
||||||
|
cause = e
|
||||||
|
).let {
|
||||||
|
call.respond(HttpStatusCode.NotFound, it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exception<AccessDeniedException> { e ->
|
||||||
|
if (call.user == null) {
|
||||||
|
HttpError(
|
||||||
|
HttpStatusCode.Unauthorized,
|
||||||
|
cause = e
|
||||||
|
).let {
|
||||||
|
call.respond(HttpStatusCode.Unauthorized, it)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
HttpError(
|
||||||
|
HttpStatusCode.Forbidden,
|
||||||
|
cause = e
|
||||||
|
).let {
|
||||||
|
call.respond(HttpStatusCode.Forbidden, it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exception<ForbiddenException> { e ->
|
||||||
|
HttpError(
|
||||||
|
HttpStatusCode.Forbidden,
|
||||||
|
cause = e
|
||||||
|
).let {
|
||||||
|
call.respond(HttpStatusCode.Forbidden, it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
4
src/main/kotlin/fr/dcproject/common/utils/Numeric.kt
Normal file
4
src/main/kotlin/fr/dcproject/common/utils/Numeric.kt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
package fr.dcproject.common.utils
|
||||||
|
|
||||||
|
fun String.isInt(): Boolean = this.toIntOrNull() != null
|
||||||
|
fun String.isBool(): Boolean = this == "true" || this == "false"
|
||||||
@@ -1,15 +1,13 @@
|
|||||||
package fr.dcproject.component.article
|
package fr.dcproject.component.article.database
|
||||||
|
|
||||||
import fr.dcproject.common.entity.VersionableId
|
import fr.dcproject.common.entity.VersionableId
|
||||||
import fr.dcproject.common.utils.contentToString
|
import fr.dcproject.common.utils.contentToString
|
||||||
import fr.dcproject.common.utils.getJsonField
|
import fr.dcproject.common.utils.getJsonField
|
||||||
import fr.dcproject.common.utils.toIso
|
import fr.dcproject.common.utils.toIso
|
||||||
import fr.dcproject.component.article.database.ArticleI
|
|
||||||
import fr.dcproject.component.citizen.database.CitizenI
|
import fr.dcproject.component.citizen.database.CitizenI
|
||||||
import fr.dcproject.component.views.ViewManager
|
import fr.dcproject.component.views.ViewRepository
|
||||||
import fr.dcproject.component.views.entity.ViewAggregation
|
import fr.dcproject.component.views.entity.ViewAggregation
|
||||||
import org.elasticsearch.client.Request
|
import org.elasticsearch.client.Request
|
||||||
import org.elasticsearch.client.Response
|
|
||||||
import org.elasticsearch.client.RestClient
|
import org.elasticsearch.client.RestClient
|
||||||
import org.joda.time.DateTime
|
import org.joda.time.DateTime
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
@@ -17,11 +15,11 @@ import java.util.UUID
|
|||||||
/**
|
/**
|
||||||
* Wrapper for manage views with elasticsearch
|
* Wrapper for manage views with elasticsearch
|
||||||
*/
|
*/
|
||||||
class ArticleViewManager <A> (private val restClient: RestClient) : ViewManager<A> where A : VersionableId, A : ArticleI {
|
class ArticleViewRepository <A> (private val restClient: RestClient) : ViewRepository<A> where A : VersionableId, A : ArticleI {
|
||||||
/**
|
/**
|
||||||
* Add view on article to elasticsearch
|
* Add view on article to elasticsearch
|
||||||
*/
|
*/
|
||||||
override fun addView(ip: String, entity: A, citizen: CitizenI?, dateTime: DateTime): Response? {
|
override fun addView(ip: String, entity: A, citizen: CitizenI?, dateTime: DateTime) {
|
||||||
val isLogged = (citizen != null).toString()
|
val isLogged = (citizen != null).toString()
|
||||||
val ref = citizen?.id ?: UUID.nameUUIDFromBytes(ip.toByteArray())!!
|
val ref = citizen?.id ?: UUID.nameUUIDFromBytes(ip.toByteArray())!!
|
||||||
val request = Request(
|
val request = Request(
|
||||||
@@ -45,7 +43,7 @@ class ArticleViewManager <A> (private val restClient: RestClient) : ViewManager<
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return restClient.performRequest(request)
|
restClient.performRequest(request)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -59,33 +57,33 @@ class ArticleViewManager <A> (private val restClient: RestClient) : ViewManager<
|
|||||||
//language=JSON
|
//language=JSON
|
||||||
setJsonEntity(
|
setJsonEntity(
|
||||||
"""
|
"""
|
||||||
{
|
{
|
||||||
"size": 0,
|
"size": 0,
|
||||||
"query": {
|
"query": {
|
||||||
"bool": {
|
"bool": {
|
||||||
"must": {
|
"must": {
|
||||||
"term": {
|
"term": {
|
||||||
"version_id": "${entity.versionId}"
|
"version_id": "${entity.versionId}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"aggs" : {
|
||||||
|
"total": {
|
||||||
|
"composite" : {
|
||||||
|
"sources" : [
|
||||||
|
{ "version_id": { "terms": {"field": "version_id" } } }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"unique" : {
|
||||||
|
"cardinality" : {
|
||||||
|
"field" : "user_ref",
|
||||||
|
"precision_threshold": 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"aggs" : {
|
|
||||||
"total": {
|
|
||||||
"composite" : {
|
|
||||||
"sources" : [
|
|
||||||
{ "version_id": { "terms": {"field": "version_id" } } }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"unique" : {
|
|
||||||
"cardinality" : {
|
|
||||||
"field" : "user_ref",
|
|
||||||
"precision_threshold": 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -2,10 +2,10 @@ package fr.dcproject.component.article.routes
|
|||||||
|
|
||||||
import fr.dcproject.common.security.assert
|
import fr.dcproject.common.security.assert
|
||||||
import fr.dcproject.component.article.ArticleAccessControl
|
import fr.dcproject.component.article.ArticleAccessControl
|
||||||
import fr.dcproject.component.article.ArticleViewManager
|
|
||||||
import fr.dcproject.component.article.database.ArticleForView
|
import fr.dcproject.component.article.database.ArticleForView
|
||||||
import fr.dcproject.component.article.database.ArticleRef
|
import fr.dcproject.component.article.database.ArticleRef
|
||||||
import fr.dcproject.component.article.database.ArticleRepository
|
import fr.dcproject.component.article.database.ArticleRepository
|
||||||
|
import fr.dcproject.component.article.database.ArticleViewRepository
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
import io.ktor.features.NotFoundException
|
import io.ktor.features.NotFoundException
|
||||||
@@ -24,7 +24,7 @@ object GetOneArticle {
|
|||||||
val article = ArticleRef(article)
|
val article = ArticleRef(article)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Route.getOneArticle(viewManager: ArticleViewManager<ArticleForView>, ac: ArticleAccessControl, repo: ArticleRepository) {
|
fun Route.getOneArticle(viewRepository: ArticleViewRepository<ArticleForView>, ac: ArticleAccessControl, repo: ArticleRepository) {
|
||||||
get<ArticleRequest> {
|
get<ArticleRequest> {
|
||||||
val article: ArticleForView = repo.findById(it.article.id) ?: throw NotFoundException("Article ${it.article.id} not found")
|
val article: ArticleForView = repo.findById(it.article.id) ?: throw NotFoundException("Article ${it.article.id} not found")
|
||||||
ac.assert { canView(article, citizenOrNull) }
|
ac.assert { canView(article, citizenOrNull) }
|
||||||
@@ -64,7 +64,7 @@ object GetOneArticle {
|
|||||||
val total: Int = a.votes.total
|
val total: Int = a.votes.total
|
||||||
val score: Int = a.votes.score
|
val score: Int = a.votes.score
|
||||||
}
|
}
|
||||||
val views: Any = viewManager.getViewsCount(article).let { v ->
|
val views: Any = viewRepository.getViewsCount(article).let { v ->
|
||||||
object {
|
object {
|
||||||
val total = v.total
|
val total = v.total
|
||||||
val unique = v.unique
|
val unique = v.unique
|
||||||
@@ -76,7 +76,7 @@ object GetOneArticle {
|
|||||||
)
|
)
|
||||||
|
|
||||||
launch {
|
launch {
|
||||||
viewManager.addView(call.request.local.remoteHost, article, citizenOrNull)
|
viewRepository.addView(call.request.local.remoteHost, article, citizenOrNull)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import fr.dcproject.component.article.database.ArticleRepository
|
|||||||
import fr.dcproject.component.article.routes.UpsertArticle.UpsertArticleRequest.Input
|
import fr.dcproject.component.article.routes.UpsertArticle.UpsertArticleRequest.Input
|
||||||
import fr.dcproject.component.auth.citizen
|
import fr.dcproject.component.auth.citizen
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
|
import fr.dcproject.component.auth.mustBeAuth
|
||||||
import fr.dcproject.component.notification.ArticleUpdateNotification
|
import fr.dcproject.component.notification.ArticleUpdateNotification
|
||||||
import fr.dcproject.component.notification.Publisher
|
import fr.dcproject.component.notification.Publisher
|
||||||
import fr.dcproject.component.workgroup.database.WorkgroupRef
|
import fr.dcproject.component.workgroup.database.WorkgroupRef
|
||||||
@@ -54,6 +55,7 @@ object UpsertArticle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
post<UpsertArticleRequest> {
|
post<UpsertArticleRequest> {
|
||||||
|
mustBeAuth()
|
||||||
val article = call.convertRequestToEntity()
|
val article = call.convertRequestToEntity()
|
||||||
ac.assert { canUpsert(article, citizenOrNull) }
|
ac.assert { canUpsert(article, citizenOrNull) }
|
||||||
repo.upsert(article)?.let { a ->
|
repo.upsert(article)?.let { a ->
|
||||||
|
|||||||
@@ -26,7 +26,21 @@ val ApplicationCall.citizenOrNull: CitizenEntity?
|
|||||||
GlobalContext.get().koin.get<CitizenRepository>().findByUser(it)
|
GlobalContext.get().koin.get<CitizenRepository>().findByUser(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val ApplicationCall.isAuth: Boolean
|
||||||
|
get() = citizenOrNull == null
|
||||||
|
|
||||||
|
fun ApplicationCall.mustBeAuth() {
|
||||||
|
citizenOrNull ?: throw ForbiddenException("No User Connected")
|
||||||
|
}
|
||||||
|
|
||||||
val PipelineContext<Unit, ApplicationCall>.citizen get() = context.citizen
|
val PipelineContext<Unit, ApplicationCall>.citizen get() = context.citizen
|
||||||
val PipelineContext<Unit, ApplicationCall>.citizenOrNull get() = context.citizenOrNull
|
val PipelineContext<Unit, ApplicationCall>.citizenOrNull get() = context.citizenOrNull
|
||||||
|
|
||||||
val ApplicationCall.user get() = authentication.principal<User>()
|
val ApplicationCall.user get() = authentication.principal<User>()
|
||||||
|
|
||||||
|
val PipelineContext<Unit, ApplicationCall>.isAuth: Boolean
|
||||||
|
get() = citizenOrNull == null
|
||||||
|
|
||||||
|
fun PipelineContext<Unit, ApplicationCall>.mustBeAuth() {
|
||||||
|
citizenOrNull ?: throw ForbiddenException("No User Connected")
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,13 +2,16 @@ package fr.dcproject.component.auth.jwt
|
|||||||
|
|
||||||
import com.auth0.jwt.JWT
|
import com.auth0.jwt.JWT
|
||||||
import fr.dcproject.component.auth.database.UserI
|
import fr.dcproject.component.auth.database.UserI
|
||||||
|
import org.koin.core.context.GlobalContext
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Produce a token for this combination of User and Account
|
* Produce a token for this combination of User and Account
|
||||||
*/
|
*/
|
||||||
fun UserI.makeToken(): String = JWT.create()
|
fun UserI.makeToken(): String = GlobalContext.get().koin.get<JwtConfig>().run {
|
||||||
.withSubject("Authentication")
|
JWT.create()
|
||||||
.withIssuer(JwtConfig.issuer)
|
.withSubject("Authentication")
|
||||||
.withClaim("id", id.toString())
|
.withIssuer(issuer)
|
||||||
.withExpiresAt(JwtConfig.getExpiration())
|
.withClaim("id", id.toString())
|
||||||
.sign(JwtConfig.algorithm)
|
.withExpiresAt(getExpiration())
|
||||||
|
.sign(algorithm)
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,11 +5,11 @@ import com.auth0.jwt.JWTVerifier
|
|||||||
import com.auth0.jwt.algorithms.Algorithm
|
import com.auth0.jwt.algorithms.Algorithm
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
|
|
||||||
object JwtConfig {
|
class JwtConfig(
|
||||||
private const val secret = "zAP5MBA4B4Ijz0MZaS48"
|
private val secret: String,
|
||||||
const val issuer = "dc-project.fr"
|
val issuer: String,
|
||||||
private const val validityInMs = 3_600_000 * 10 // 10 hours
|
private val validityInMs: Int,
|
||||||
|
) {
|
||||||
// TODO change to RSA512
|
// TODO change to RSA512
|
||||||
val algorithm: Algorithm = Algorithm.HMAC512(secret)
|
val algorithm: Algorithm = Algorithm.HMAC512(secret)
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package fr.dcproject.component.auth.jwt
|
package fr.dcproject.component.auth.jwt
|
||||||
|
|
||||||
|
import com.auth0.jwt.JWTVerifier
|
||||||
import fr.dcproject.component.auth.database.User
|
import fr.dcproject.component.auth.database.User
|
||||||
import fr.dcproject.component.auth.database.UserRepository
|
import fr.dcproject.component.auth.database.UserRepository
|
||||||
import io.ktor.application.ApplicationCall
|
import io.ktor.application.ApplicationCall
|
||||||
@@ -9,14 +10,14 @@ import io.ktor.http.auth.HttpAuthHeader
|
|||||||
import io.ktor.routing.Routing
|
import io.ktor.routing.Routing
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
fun jwtInstallation(userRepo: UserRepository): Authentication.Configuration.() -> Unit = {
|
fun jwtInstallation(userRepo: UserRepository, verifier: JWTVerifier): Authentication.Configuration.() -> Unit = {
|
||||||
/**
|
/**
|
||||||
* Setup the JWT authentication to be used in [Routing].
|
* Setup the JWT authentication to be used in [Routing].
|
||||||
* If the token is valid, the corresponding [User] is fetched from the database.
|
* If the token is valid, the corresponding [User] is fetched from the database.
|
||||||
* The [User] can then be accessed in each [ApplicationCall].
|
* The [User] can then be accessed in each [ApplicationCall].
|
||||||
*/
|
*/
|
||||||
jwt {
|
jwt {
|
||||||
verifier(JwtConfig.verifier)
|
verifier(verifier)
|
||||||
realm = "dc-project.fr"
|
realm = "dc-project.fr"
|
||||||
validate {
|
validate {
|
||||||
it.payload.getClaim("id").asString()?.let { id ->
|
it.payload.getClaim("id").asString()?.let { id ->
|
||||||
@@ -27,7 +28,7 @@ fun jwtInstallation(userRepo: UserRepository): Authentication.Configuration.() -
|
|||||||
|
|
||||||
/* Token in URL */
|
/* Token in URL */
|
||||||
jwt("url") {
|
jwt("url") {
|
||||||
verifier(JwtConfig.verifier)
|
verifier(verifier)
|
||||||
realm = "dc-project.fr"
|
realm = "dc-project.fr"
|
||||||
authHeader { call ->
|
authHeader { call ->
|
||||||
call.request.queryParameters["token"]?.let {
|
call.request.queryParameters["token"]?.let {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import fr.dcproject.component.auth.citizen
|
|||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
import fr.dcproject.component.auth.database.UserRepository
|
import fr.dcproject.component.auth.database.UserRepository
|
||||||
import fr.dcproject.component.auth.database.UserWithPassword
|
import fr.dcproject.component.auth.database.UserWithPassword
|
||||||
|
import fr.dcproject.component.auth.mustBeAuth
|
||||||
import fr.dcproject.component.citizen.CitizenAccessControl
|
import fr.dcproject.component.citizen.CitizenAccessControl
|
||||||
import fr.dcproject.component.citizen.database.CitizenRef
|
import fr.dcproject.component.citizen.database.CitizenRef
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
@@ -29,6 +30,7 @@ object ChangeMyPassword {
|
|||||||
|
|
||||||
fun Route.changeMyPassword(ac: CitizenAccessControl, userRepository: UserRepository) {
|
fun Route.changeMyPassword(ac: CitizenAccessControl, userRepository: UserRepository) {
|
||||||
put<ChangePasswordCitizenRequest> {
|
put<ChangePasswordCitizenRequest> {
|
||||||
|
mustBeAuth()
|
||||||
ac.assert { canChangePassword(it.citizen, citizenOrNull) }
|
ac.assert { canChangePassword(it.citizen, citizenOrNull) }
|
||||||
val content = call.receiveOrBadRequest<ChangePasswordCitizenRequest.Input>()
|
val content = call.receiveOrBadRequest<ChangePasswordCitizenRequest.Input>()
|
||||||
userRepository.findByCredentials(UserPasswordCredential(citizen.user.username, content.oldPassword)) ?: throw BadRequestException("Bad Password")
|
userRepository.findByCredentials(UserPasswordCredential(citizen.user.username, content.oldPassword)) ?: throw BadRequestException("Bad Password")
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package fr.dcproject.component.citizen.routes
|
|||||||
import fr.dcproject.common.response.toOutput
|
import fr.dcproject.common.response.toOutput
|
||||||
import fr.dcproject.common.security.assert
|
import fr.dcproject.common.security.assert
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
|
import fr.dcproject.component.auth.mustBeAuth
|
||||||
import fr.dcproject.component.citizen.CitizenAccessControl
|
import fr.dcproject.component.citizen.CitizenAccessControl
|
||||||
import fr.dcproject.component.citizen.database.CitizenCreator
|
import fr.dcproject.component.citizen.database.CitizenCreator
|
||||||
import fr.dcproject.component.citizen.database.CitizenRepository
|
import fr.dcproject.component.citizen.database.CitizenRepository
|
||||||
@@ -30,6 +31,7 @@ object FindCitizens {
|
|||||||
|
|
||||||
fun Route.findCitizen(ac: CitizenAccessControl, repo: CitizenRepository) {
|
fun Route.findCitizen(ac: CitizenAccessControl, repo: CitizenRepository) {
|
||||||
get<CitizensRequest> {
|
get<CitizensRequest> {
|
||||||
|
mustBeAuth()
|
||||||
val citizens = repo.find(it.page, it.limit, it.sort, it.direction, it.search)
|
val citizens = repo.find(it.page, it.limit, it.sort, it.direction, it.search)
|
||||||
ac.assert { canView(citizens.result, citizenOrNull) }
|
ac.assert { canView(citizens.result, citizenOrNull) }
|
||||||
call.respond(
|
call.respond(
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package fr.dcproject.component.citizen.routes
|
|||||||
import fr.dcproject.common.security.assert
|
import fr.dcproject.common.security.assert
|
||||||
import fr.dcproject.component.auth.citizen
|
import fr.dcproject.component.auth.citizen
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
|
import fr.dcproject.component.auth.mustBeAuth
|
||||||
import fr.dcproject.component.citizen.CitizenAccessControl
|
import fr.dcproject.component.citizen.CitizenAccessControl
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
import io.ktor.http.HttpStatusCode
|
import io.ktor.http.HttpStatusCode
|
||||||
@@ -22,6 +23,7 @@ object GetCurrentCitizen {
|
|||||||
|
|
||||||
fun Route.getCurrentCitizen(ac: CitizenAccessControl) {
|
fun Route.getCurrentCitizen(ac: CitizenAccessControl) {
|
||||||
get<CurrentCitizenRequest> {
|
get<CurrentCitizenRequest> {
|
||||||
|
mustBeAuth()
|
||||||
val currentUser = citizenOrNull
|
val currentUser = citizenOrNull
|
||||||
if (currentUser === null) {
|
if (currentUser === null) {
|
||||||
call.respond(HttpStatusCode.Unauthorized)
|
call.respond(HttpStatusCode.Unauthorized)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package fr.dcproject.component.citizen.routes
|
|||||||
import fr.dcproject.common.security.assert
|
import fr.dcproject.common.security.assert
|
||||||
import fr.dcproject.component.auth.citizen
|
import fr.dcproject.component.auth.citizen
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
|
import fr.dcproject.component.auth.mustBeAuth
|
||||||
import fr.dcproject.component.citizen.CitizenAccessControl
|
import fr.dcproject.component.citizen.CitizenAccessControl
|
||||||
import fr.dcproject.component.citizen.database.CitizenRef
|
import fr.dcproject.component.citizen.database.CitizenRef
|
||||||
import fr.dcproject.component.citizen.database.CitizenRepository
|
import fr.dcproject.component.citizen.database.CitizenRepository
|
||||||
@@ -26,6 +27,7 @@ object GetOneCitizen {
|
|||||||
|
|
||||||
fun Route.getOneCitizen(ac: CitizenAccessControl, citizenRepository: CitizenRepository) {
|
fun Route.getOneCitizen(ac: CitizenAccessControl, citizenRepository: CitizenRepository) {
|
||||||
get<CitizenRequest> {
|
get<CitizenRequest> {
|
||||||
|
mustBeAuth()
|
||||||
val citizen = citizenRepository.findById(it.citizen.id) ?: throw NotFoundException("Citizen not found ${it.citizen.id}")
|
val citizen = citizenRepository.findById(it.citizen.id) ?: throw NotFoundException("Citizen not found ${it.citizen.id}")
|
||||||
ac.assert { canView(citizen, citizenOrNull) }
|
ac.assert { canView(citizen, citizenOrNull) }
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import fr.dcproject.common.utils.receiveOrBadRequest
|
|||||||
import fr.dcproject.component.article.database.ArticleRef
|
import fr.dcproject.component.article.database.ArticleRef
|
||||||
import fr.dcproject.component.auth.citizen
|
import fr.dcproject.component.auth.citizen
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
|
import fr.dcproject.component.auth.mustBeAuth
|
||||||
import fr.dcproject.component.comment.article.database.CommentArticleRepository
|
import fr.dcproject.component.comment.article.database.CommentArticleRepository
|
||||||
import fr.dcproject.component.comment.article.routes.CreateCommentArticle.PostArticleCommentRequest.Input
|
import fr.dcproject.component.comment.article.routes.CreateCommentArticle.PostArticleCommentRequest.Input
|
||||||
import fr.dcproject.component.comment.generic.CommentAccessControl
|
import fr.dcproject.component.comment.generic.CommentAccessControl
|
||||||
@@ -30,6 +31,7 @@ object CreateCommentArticle {
|
|||||||
|
|
||||||
fun Route.createCommentArticle(repo: CommentArticleRepository, ac: CommentAccessControl) {
|
fun Route.createCommentArticle(repo: CommentArticleRepository, ac: CommentAccessControl) {
|
||||||
post<PostArticleCommentRequest> {
|
post<PostArticleCommentRequest> {
|
||||||
|
mustBeAuth()
|
||||||
call.receiveOrBadRequest<Input>().run {
|
call.receiveOrBadRequest<Input>().run {
|
||||||
CommentForUpdate(
|
CommentForUpdate(
|
||||||
target = it.article,
|
target = it.article,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package fr.dcproject.component.comment.article.routes
|
|||||||
import fr.dcproject.common.response.toOutput
|
import fr.dcproject.common.response.toOutput
|
||||||
import fr.dcproject.common.security.assert
|
import fr.dcproject.common.security.assert
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
|
import fr.dcproject.component.auth.mustBeAuth
|
||||||
import fr.dcproject.component.citizen.database.CitizenRef
|
import fr.dcproject.component.citizen.database.CitizenRef
|
||||||
import fr.dcproject.component.comment.article.database.CommentArticleRepository
|
import fr.dcproject.component.comment.article.database.CommentArticleRepository
|
||||||
import fr.dcproject.component.comment.generic.CommentAccessControl
|
import fr.dcproject.component.comment.generic.CommentAccessControl
|
||||||
@@ -25,6 +26,7 @@ object GetCitizenArticleComments {
|
|||||||
|
|
||||||
fun Route.getCitizenArticleComments(repo: CommentArticleRepository, ac: CommentAccessControl) {
|
fun Route.getCitizenArticleComments(repo: CommentArticleRepository, ac: CommentAccessControl) {
|
||||||
get<CitizenCommentArticleRequest> {
|
get<CitizenCommentArticleRequest> {
|
||||||
|
mustBeAuth()
|
||||||
repo.findByCitizen(it.citizen).let { comments ->
|
repo.findByCitizen(it.citizen).let { comments ->
|
||||||
ac.assert { canView(comments.result, citizenOrNull) }
|
ac.assert { canView(comments.result, citizenOrNull) }
|
||||||
call.respond(
|
call.respond(
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import fr.dcproject.common.security.assert
|
|||||||
import fr.dcproject.common.utils.receiveOrBadRequest
|
import fr.dcproject.common.utils.receiveOrBadRequest
|
||||||
import fr.dcproject.component.auth.citizen
|
import fr.dcproject.component.auth.citizen
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
|
import fr.dcproject.component.auth.mustBeAuth
|
||||||
import fr.dcproject.component.comment.constitution.database.CommentConstitutionRepository
|
import fr.dcproject.component.comment.constitution.database.CommentConstitutionRepository
|
||||||
import fr.dcproject.component.comment.constitution.routes.CreateConstitutionComment.CreateConstitutionCommentRequest.Input
|
import fr.dcproject.component.comment.constitution.routes.CreateConstitutionComment.CreateConstitutionCommentRequest.Input
|
||||||
import fr.dcproject.component.comment.generic.CommentAccessControl
|
import fr.dcproject.component.comment.generic.CommentAccessControl
|
||||||
@@ -30,6 +31,7 @@ object CreateConstitutionComment {
|
|||||||
|
|
||||||
fun Route.createConstitutionComment(repo: CommentConstitutionRepository, ac: CommentAccessControl) {
|
fun Route.createConstitutionComment(repo: CommentConstitutionRepository, ac: CommentAccessControl) {
|
||||||
post<CreateConstitutionCommentRequest> {
|
post<CreateConstitutionCommentRequest> {
|
||||||
|
mustBeAuth()
|
||||||
call.receiveOrBadRequest<Input>().run {
|
call.receiveOrBadRequest<Input>().run {
|
||||||
CommentForUpdate(
|
CommentForUpdate(
|
||||||
target = it.constitution,
|
target = it.constitution,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package fr.dcproject.component.comment.constitution.routes
|
|||||||
import fr.dcproject.common.response.toOutput
|
import fr.dcproject.common.response.toOutput
|
||||||
import fr.dcproject.common.security.assert
|
import fr.dcproject.common.security.assert
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
|
import fr.dcproject.component.auth.mustBeAuth
|
||||||
import fr.dcproject.component.citizen.database.CitizenRef
|
import fr.dcproject.component.citizen.database.CitizenRef
|
||||||
import fr.dcproject.component.comment.constitution.database.CommentConstitutionRepository
|
import fr.dcproject.component.comment.constitution.database.CommentConstitutionRepository
|
||||||
import fr.dcproject.component.comment.generic.CommentAccessControl
|
import fr.dcproject.component.comment.generic.CommentAccessControl
|
||||||
@@ -25,6 +26,7 @@ object GetCitizenCommentConstitution {
|
|||||||
|
|
||||||
fun Route.getCitizenCommentConstitution(repo: CommentConstitutionRepository, ac: CommentAccessControl) {
|
fun Route.getCitizenCommentConstitution(repo: CommentConstitutionRepository, ac: CommentAccessControl) {
|
||||||
get<GetCitizenCommentConstitutionRequest> {
|
get<GetCitizenCommentConstitutionRequest> {
|
||||||
|
mustBeAuth()
|
||||||
val comments = repo.findByCitizen(it.citizen)
|
val comments = repo.findByCitizen(it.citizen)
|
||||||
ac.assert { canView(comments.result, citizenOrNull) }
|
ac.assert { canView(comments.result, citizenOrNull) }
|
||||||
call.respond(
|
call.respond(
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import fr.dcproject.common.security.assert
|
|||||||
import fr.dcproject.common.utils.receiveOrBadRequest
|
import fr.dcproject.common.utils.receiveOrBadRequest
|
||||||
import fr.dcproject.component.auth.citizen
|
import fr.dcproject.component.auth.citizen
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
|
import fr.dcproject.component.auth.mustBeAuth
|
||||||
import fr.dcproject.component.comment.generic.CommentAccessControl
|
import fr.dcproject.component.comment.generic.CommentAccessControl
|
||||||
import fr.dcproject.component.comment.generic.database.CommentForUpdate
|
import fr.dcproject.component.comment.generic.database.CommentForUpdate
|
||||||
import fr.dcproject.component.comment.generic.database.CommentRef
|
import fr.dcproject.component.comment.generic.database.CommentRef
|
||||||
@@ -29,6 +30,7 @@ object CreateCommentChildren {
|
|||||||
|
|
||||||
fun Route.createCommentChildren(repo: CommentRepository, ac: CommentAccessControl) {
|
fun Route.createCommentChildren(repo: CommentRepository, ac: CommentAccessControl) {
|
||||||
post<CreateCommentChildrenRequest> {
|
post<CreateCommentChildrenRequest> {
|
||||||
|
mustBeAuth()
|
||||||
val parent = repo.findById(it.comment.id) ?: throw NotFoundException("Comment not found")
|
val parent = repo.findById(it.comment.id) ?: throw NotFoundException("Comment not found")
|
||||||
val newComment = CommentForUpdate(
|
val newComment = CommentForUpdate(
|
||||||
content = call.receiveOrBadRequest<CreateCommentChildrenRequest.Input>().content,
|
content = call.receiveOrBadRequest<CreateCommentChildrenRequest.Input>().content,
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import fr.dcproject.common.response.toOutput
|
|||||||
import fr.dcproject.common.security.assert
|
import fr.dcproject.common.security.assert
|
||||||
import fr.dcproject.common.utils.receiveOrBadRequest
|
import fr.dcproject.common.utils.receiveOrBadRequest
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
|
import fr.dcproject.component.auth.mustBeAuth
|
||||||
import fr.dcproject.component.comment.generic.CommentAccessControl
|
import fr.dcproject.component.comment.generic.CommentAccessControl
|
||||||
import fr.dcproject.component.comment.generic.database.CommentRef
|
import fr.dcproject.component.comment.generic.database.CommentRef
|
||||||
import fr.dcproject.component.comment.generic.database.CommentRepository
|
import fr.dcproject.component.comment.generic.database.CommentRepository
|
||||||
@@ -28,6 +29,7 @@ object EditComment {
|
|||||||
|
|
||||||
fun Route.editComment(repo: CommentRepository, ac: CommentAccessControl) {
|
fun Route.editComment(repo: CommentRepository, ac: CommentAccessControl) {
|
||||||
put<EditCommentRequest> {
|
put<EditCommentRequest> {
|
||||||
|
mustBeAuth()
|
||||||
val comment = repo.findById(it.comment.id) ?: throw NotFoundException("Comment not found")
|
val comment = repo.findById(it.comment.id) ?: throw NotFoundException("Comment not found")
|
||||||
ac.assert { canUpdate(comment, citizenOrNull) }
|
ac.assert { canUpdate(comment, citizenOrNull) }
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import fr.dcproject.common.utils.receiveOrBadRequest
|
|||||||
import fr.dcproject.component.article.database.ArticleRef
|
import fr.dcproject.component.article.database.ArticleRef
|
||||||
import fr.dcproject.component.auth.citizen
|
import fr.dcproject.component.auth.citizen
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
|
import fr.dcproject.component.auth.mustBeAuth
|
||||||
import fr.dcproject.component.citizen.database.Citizen
|
import fr.dcproject.component.citizen.database.Citizen
|
||||||
import fr.dcproject.component.citizen.database.CitizenWithUserI
|
import fr.dcproject.component.citizen.database.CitizenWithUserI
|
||||||
import fr.dcproject.component.constitution.ConstitutionAccessControl
|
import fr.dcproject.component.constitution.ConstitutionAccessControl
|
||||||
@@ -68,6 +69,7 @@ object CreateConstitution {
|
|||||||
|
|
||||||
fun Route.createConstitution(repo: ConstitutionRepository, ac: ConstitutionAccessControl) {
|
fun Route.createConstitution(repo: ConstitutionRepository, ac: ConstitutionAccessControl) {
|
||||||
post<PostConstitutionRequest> {
|
post<PostConstitutionRequest> {
|
||||||
|
mustBeAuth()
|
||||||
getNewConstitution(call.receiveOrBadRequest(), citizen).let {
|
getNewConstitution(call.receiveOrBadRequest(), citizen).let {
|
||||||
ac.assert { canCreate(it, citizenOrNull) }
|
ac.assert { canCreate(it, citizenOrNull) }
|
||||||
val c = repo.upsert(it) ?: error("Unable to create Constitution")
|
val c = repo.upsert(it) ?: error("Unable to create Constitution")
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import fr.dcproject.common.security.assert
|
|||||||
import fr.dcproject.component.article.database.ArticleRef
|
import fr.dcproject.component.article.database.ArticleRef
|
||||||
import fr.dcproject.component.auth.citizen
|
import fr.dcproject.component.auth.citizen
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
|
import fr.dcproject.component.auth.mustBeAuth
|
||||||
import fr.dcproject.component.follow.FollowAccessControl
|
import fr.dcproject.component.follow.FollowAccessControl
|
||||||
import fr.dcproject.component.follow.database.FollowArticleRepository
|
import fr.dcproject.component.follow.database.FollowArticleRepository
|
||||||
import fr.dcproject.component.follow.database.FollowForUpdate
|
import fr.dcproject.component.follow.database.FollowForUpdate
|
||||||
@@ -25,6 +26,7 @@ object FollowArticle {
|
|||||||
|
|
||||||
fun Route.followArticle(repo: FollowArticleRepository, ac: FollowAccessControl) {
|
fun Route.followArticle(repo: FollowArticleRepository, ac: FollowAccessControl) {
|
||||||
post<ArticleFollowRequest> {
|
post<ArticleFollowRequest> {
|
||||||
|
mustBeAuth()
|
||||||
val follow = FollowForUpdate(target = it.article, createdBy = this.citizen)
|
val follow = FollowForUpdate(target = it.article, createdBy = this.citizen)
|
||||||
ac.assert { canCreate(follow, citizenOrNull) }
|
ac.assert { canCreate(follow, citizenOrNull) }
|
||||||
repo.follow(follow)
|
repo.follow(follow)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package fr.dcproject.component.follow.routes.article
|
|||||||
import fr.dcproject.common.response.toOutput
|
import fr.dcproject.common.response.toOutput
|
||||||
import fr.dcproject.common.security.assert
|
import fr.dcproject.common.security.assert
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
|
import fr.dcproject.component.auth.mustBeAuth
|
||||||
import fr.dcproject.component.citizen.database.CitizenRef
|
import fr.dcproject.component.citizen.database.CitizenRef
|
||||||
import fr.dcproject.component.follow.FollowAccessControl
|
import fr.dcproject.component.follow.FollowAccessControl
|
||||||
import fr.dcproject.component.follow.database.FollowArticleRepository
|
import fr.dcproject.component.follow.database.FollowArticleRepository
|
||||||
@@ -25,6 +26,7 @@ object GetMyFollowsArticle {
|
|||||||
|
|
||||||
fun Route.getMyFollowsArticle(repo: FollowArticleRepository, ac: FollowAccessControl) {
|
fun Route.getMyFollowsArticle(repo: FollowArticleRepository, ac: FollowAccessControl) {
|
||||||
get<CitizenFollowArticleRequest> {
|
get<CitizenFollowArticleRequest> {
|
||||||
|
mustBeAuth()
|
||||||
val follows = repo.findByCitizen(it.citizen)
|
val follows = repo.findByCitizen(it.citizen)
|
||||||
ac.assert { canView(follows.result, citizenOrNull) }
|
ac.assert { canView(follows.result, citizenOrNull) }
|
||||||
call.respond(
|
call.respond(
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import fr.dcproject.common.security.assert
|
|||||||
import fr.dcproject.component.article.database.ArticleRef
|
import fr.dcproject.component.article.database.ArticleRef
|
||||||
import fr.dcproject.component.auth.citizen
|
import fr.dcproject.component.auth.citizen
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
|
import fr.dcproject.component.auth.mustBeAuth
|
||||||
import fr.dcproject.component.follow.FollowAccessControl
|
import fr.dcproject.component.follow.FollowAccessControl
|
||||||
import fr.dcproject.component.follow.database.FollowArticleRepository
|
import fr.dcproject.component.follow.database.FollowArticleRepository
|
||||||
import fr.dcproject.component.follow.database.FollowForUpdate
|
import fr.dcproject.component.follow.database.FollowForUpdate
|
||||||
@@ -25,6 +26,7 @@ object UnfollowArticle {
|
|||||||
|
|
||||||
fun Route.unfollowArticle(repo: FollowArticleRepository, ac: FollowAccessControl) {
|
fun Route.unfollowArticle(repo: FollowArticleRepository, ac: FollowAccessControl) {
|
||||||
delete<ArticleFollowRequest> {
|
delete<ArticleFollowRequest> {
|
||||||
|
mustBeAuth()
|
||||||
val follow = FollowForUpdate(target = it.article, createdBy = this.citizen)
|
val follow = FollowForUpdate(target = it.article, createdBy = this.citizen)
|
||||||
ac.assert { canDelete(follow, citizenOrNull) }
|
ac.assert { canDelete(follow, citizenOrNull) }
|
||||||
repo.unfollow(follow)
|
repo.unfollow(follow)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package fr.dcproject.component.follow.routes.constitution
|
|||||||
import fr.dcproject.common.security.assert
|
import fr.dcproject.common.security.assert
|
||||||
import fr.dcproject.component.auth.citizen
|
import fr.dcproject.component.auth.citizen
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
|
import fr.dcproject.component.auth.mustBeAuth
|
||||||
import fr.dcproject.component.constitution.database.ConstitutionRef
|
import fr.dcproject.component.constitution.database.ConstitutionRef
|
||||||
import fr.dcproject.component.follow.FollowAccessControl
|
import fr.dcproject.component.follow.FollowAccessControl
|
||||||
import fr.dcproject.component.follow.database.FollowConstitutionRepository
|
import fr.dcproject.component.follow.database.FollowConstitutionRepository
|
||||||
@@ -25,6 +26,7 @@ object FollowConstitution {
|
|||||||
|
|
||||||
fun Route.followConstitution(repo: FollowConstitutionRepository, ac: FollowAccessControl) {
|
fun Route.followConstitution(repo: FollowConstitutionRepository, ac: FollowAccessControl) {
|
||||||
post<ConstitutionFollowRequest> {
|
post<ConstitutionFollowRequest> {
|
||||||
|
mustBeAuth()
|
||||||
val follow = FollowForUpdate(target = it.constitution, createdBy = this.citizen)
|
val follow = FollowForUpdate(target = it.constitution, createdBy = this.citizen)
|
||||||
ac.assert { canCreate(follow, citizenOrNull) }
|
ac.assert { canCreate(follow, citizenOrNull) }
|
||||||
repo.follow(follow)
|
repo.follow(follow)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package fr.dcproject.component.follow.routes.constitution
|
|||||||
import fr.dcproject.common.response.toOutput
|
import fr.dcproject.common.response.toOutput
|
||||||
import fr.dcproject.common.security.assert
|
import fr.dcproject.common.security.assert
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
|
import fr.dcproject.component.auth.mustBeAuth
|
||||||
import fr.dcproject.component.citizen.database.CitizenRef
|
import fr.dcproject.component.citizen.database.CitizenRef
|
||||||
import fr.dcproject.component.follow.FollowAccessControl
|
import fr.dcproject.component.follow.FollowAccessControl
|
||||||
import fr.dcproject.component.follow.database.FollowConstitutionRepository
|
import fr.dcproject.component.follow.database.FollowConstitutionRepository
|
||||||
@@ -25,6 +26,7 @@ object GetMyFollowsConstitution {
|
|||||||
|
|
||||||
fun Route.getMyFollowsConstitution(repo: FollowConstitutionRepository, ac: FollowAccessControl) {
|
fun Route.getMyFollowsConstitution(repo: FollowConstitutionRepository, ac: FollowAccessControl) {
|
||||||
get<CitizenFollowConstitutionRequest> {
|
get<CitizenFollowConstitutionRequest> {
|
||||||
|
mustBeAuth()
|
||||||
val follows = repo.findByCitizen(it.citizen)
|
val follows = repo.findByCitizen(it.citizen)
|
||||||
ac.assert { canView(follows.result, citizenOrNull) }
|
ac.assert { canView(follows.result, citizenOrNull) }
|
||||||
call.respond(
|
call.respond(
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package fr.dcproject.component.follow.routes.constitution
|
|||||||
import fr.dcproject.common.security.assert
|
import fr.dcproject.common.security.assert
|
||||||
import fr.dcproject.component.auth.citizen
|
import fr.dcproject.component.auth.citizen
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
|
import fr.dcproject.component.auth.mustBeAuth
|
||||||
import fr.dcproject.component.constitution.database.ConstitutionRef
|
import fr.dcproject.component.constitution.database.ConstitutionRef
|
||||||
import fr.dcproject.component.follow.FollowAccessControl
|
import fr.dcproject.component.follow.FollowAccessControl
|
||||||
import fr.dcproject.component.follow.database.FollowConstitutionRepository
|
import fr.dcproject.component.follow.database.FollowConstitutionRepository
|
||||||
@@ -25,6 +26,7 @@ object UnfollowConstitution {
|
|||||||
|
|
||||||
fun Route.unfollowConstitution(repo: FollowConstitutionRepository, ac: FollowAccessControl) {
|
fun Route.unfollowConstitution(repo: FollowConstitutionRepository, ac: FollowAccessControl) {
|
||||||
delete<ConstitutionUnfollowRequest> {
|
delete<ConstitutionUnfollowRequest> {
|
||||||
|
mustBeAuth()
|
||||||
val follow = FollowForUpdate(target = it.constitution, createdBy = this.citizen)
|
val follow = FollowForUpdate(target = it.constitution, createdBy = this.citizen)
|
||||||
ac.assert { canDelete(follow, citizenOrNull) }
|
ac.assert { canDelete(follow, citizenOrNull) }
|
||||||
repo.unfollow(follow)
|
repo.unfollow(follow)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import fr.dcproject.common.security.assert
|
|||||||
import fr.dcproject.common.utils.toUUID
|
import fr.dcproject.common.utils.toUUID
|
||||||
import fr.dcproject.component.article.database.ArticleRef
|
import fr.dcproject.component.article.database.ArticleRef
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
|
import fr.dcproject.component.auth.mustBeAuth
|
||||||
import fr.dcproject.component.citizen.database.CitizenRef
|
import fr.dcproject.component.citizen.database.CitizenRef
|
||||||
import fr.dcproject.component.opinion.OpinionAccessControl
|
import fr.dcproject.component.opinion.OpinionAccessControl
|
||||||
import fr.dcproject.component.opinion.database.Opinion
|
import fr.dcproject.component.opinion.database.Opinion
|
||||||
@@ -31,6 +32,7 @@ object GetCitizenOpinions {
|
|||||||
|
|
||||||
fun Route.getCitizenOpinions(repo: OpinionArticleRepository, ac: OpinionAccessControl) {
|
fun Route.getCitizenOpinions(repo: OpinionArticleRepository, ac: OpinionAccessControl) {
|
||||||
get<CitizenOpinions> {
|
get<CitizenOpinions> {
|
||||||
|
mustBeAuth()
|
||||||
val opinionsEntities: List<Opinion<ArticleRef>> = repo.findCitizenOpinionsByTargets(it.citizen, it.id)
|
val opinionsEntities: List<Opinion<ArticleRef>> = repo.findCitizenOpinionsByTargets(it.citizen, it.id)
|
||||||
ac.assert { canView(opinionsEntities, citizenOrNull) }
|
ac.assert { canView(opinionsEntities, citizenOrNull) }
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import fr.dcproject.common.response.toOutput
|
|||||||
import fr.dcproject.common.security.assert
|
import fr.dcproject.common.security.assert
|
||||||
import fr.dcproject.component.auth.citizen
|
import fr.dcproject.component.auth.citizen
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
|
import fr.dcproject.component.auth.mustBeAuth
|
||||||
import fr.dcproject.component.citizen.database.CitizenRef
|
import fr.dcproject.component.citizen.database.CitizenRef
|
||||||
import fr.dcproject.component.opinion.OpinionAccessControl
|
import fr.dcproject.component.opinion.OpinionAccessControl
|
||||||
import fr.dcproject.component.opinion.database.Opinion
|
import fr.dcproject.component.opinion.database.Opinion
|
||||||
@@ -37,6 +38,7 @@ object GetMyOpinionsArticle {
|
|||||||
|
|
||||||
fun Route.getMyOpinionsArticle(repo: OpinionArticleRepository, ac: OpinionAccessControl) {
|
fun Route.getMyOpinionsArticle(repo: OpinionArticleRepository, ac: OpinionAccessControl) {
|
||||||
get<CitizenOpinionsArticleRequest> {
|
get<CitizenOpinionsArticleRequest> {
|
||||||
|
mustBeAuth()
|
||||||
val opinions: Paginated<Opinion<TargetRef>> = repo.findCitizenOpinions(citizen, it.page, it.limit)
|
val opinions: Paginated<Opinion<TargetRef>> = repo.findCitizenOpinions(citizen, it.page, it.limit)
|
||||||
ac.assert { canView(opinions.result, citizenOrNull) }
|
ac.assert { canView(opinions.result, citizenOrNull) }
|
||||||
call.respond(
|
call.respond(
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import fr.dcproject.common.utils.toUUID
|
|||||||
import fr.dcproject.component.article.database.ArticleRef
|
import fr.dcproject.component.article.database.ArticleRef
|
||||||
import fr.dcproject.component.auth.citizen
|
import fr.dcproject.component.auth.citizen
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
|
import fr.dcproject.component.auth.mustBeAuth
|
||||||
import fr.dcproject.component.opinion.OpinionAccessControl
|
import fr.dcproject.component.opinion.OpinionAccessControl
|
||||||
import fr.dcproject.component.opinion.database.OpinionChoiceRef
|
import fr.dcproject.component.opinion.database.OpinionChoiceRef
|
||||||
import fr.dcproject.component.opinion.database.OpinionForUpdate
|
import fr.dcproject.component.opinion.database.OpinionForUpdate
|
||||||
@@ -34,6 +35,7 @@ object OpinionArticle {
|
|||||||
|
|
||||||
fun Route.setOpinionOnArticle(repo: OpinionArticleRepository, ac: OpinionAccessControl) {
|
fun Route.setOpinionOnArticle(repo: OpinionArticleRepository, ac: OpinionAccessControl) {
|
||||||
put<ArticleOpinion> {
|
put<ArticleOpinion> {
|
||||||
|
mustBeAuth()
|
||||||
call.receiveOrBadRequest<ArticleOpinion.Body>().ids.map { id ->
|
call.receiveOrBadRequest<ArticleOpinion.Body>().ids.map { id ->
|
||||||
OpinionForUpdate(
|
OpinionForUpdate(
|
||||||
choice = OpinionChoiceRef(id),
|
choice = OpinionChoiceRef(id),
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package fr.dcproject.component.views
|
package fr.dcproject.component.views
|
||||||
|
|
||||||
import fr.dcproject.application.Configuration
|
import fr.dcproject.application.Configuration
|
||||||
import fr.dcproject.component.article.ArticleViewManager
|
|
||||||
import fr.dcproject.component.article.database.ArticleForView
|
import fr.dcproject.component.article.database.ArticleForView
|
||||||
|
import fr.dcproject.component.article.database.ArticleViewRepository
|
||||||
import org.apache.http.HttpHost
|
import org.apache.http.HttpHost
|
||||||
import org.elasticsearch.client.RestClient
|
import org.elasticsearch.client.RestClient
|
||||||
import org.koin.dsl.module
|
import org.koin.dsl.module
|
||||||
@@ -17,6 +17,6 @@ val viewKoinModule = module {
|
|||||||
).build().apply {
|
).build().apply {
|
||||||
createEsIndexForViews()
|
createEsIndexForViews()
|
||||||
}
|
}
|
||||||
ArticleViewManager<ArticleForView>(esClient)
|
ArticleViewRepository<ArticleForView>(esClient)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,14 +2,13 @@ package fr.dcproject.component.views
|
|||||||
|
|
||||||
import fr.dcproject.component.citizen.database.CitizenI
|
import fr.dcproject.component.citizen.database.CitizenI
|
||||||
import fr.dcproject.component.views.entity.ViewAggregation
|
import fr.dcproject.component.views.entity.ViewAggregation
|
||||||
import org.elasticsearch.client.Response
|
|
||||||
import org.joda.time.DateTime
|
import org.joda.time.DateTime
|
||||||
|
|
||||||
interface ViewManager <T> {
|
interface ViewRepository <T> {
|
||||||
/**
|
/**
|
||||||
* Add view to one entity
|
* Add view to one entity
|
||||||
*/
|
*/
|
||||||
fun addView(ip: String, entity: T, citizen: CitizenI? = null, dateTime: DateTime = DateTime.now()): Response?
|
fun addView(ip: String, entity: T, citizen: CitizenI? = null, dateTime: DateTime = DateTime.now())
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get Views aggregations
|
* Get Views aggregations
|
||||||
@@ -4,6 +4,7 @@ import fr.dcproject.common.response.toOutput
|
|||||||
import fr.dcproject.common.security.assert
|
import fr.dcproject.common.security.assert
|
||||||
import fr.dcproject.common.utils.toUUID
|
import fr.dcproject.common.utils.toUUID
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
|
import fr.dcproject.component.auth.mustBeAuth
|
||||||
import fr.dcproject.component.citizen.database.CitizenRef
|
import fr.dcproject.component.citizen.database.CitizenRef
|
||||||
import fr.dcproject.component.vote.VoteAccessControl
|
import fr.dcproject.component.vote.VoteAccessControl
|
||||||
import fr.dcproject.component.vote.database.VoteRepository
|
import fr.dcproject.component.vote.database.VoteRepository
|
||||||
@@ -26,6 +27,7 @@ object GetCitizenVotes {
|
|||||||
|
|
||||||
fun Route.getCitizenVote(repo: VoteRepository, ac: VoteAccessControl) {
|
fun Route.getCitizenVote(repo: VoteRepository, ac: VoteAccessControl) {
|
||||||
get<CitizenVotesRequest> {
|
get<CitizenVotesRequest> {
|
||||||
|
mustBeAuth()
|
||||||
val votes = repo.findCitizenVotesByTargets(it.citizen, it.id)
|
val votes = repo.findCitizenVotesByTargets(it.citizen, it.id)
|
||||||
if (votes.isNotEmpty()) {
|
if (votes.isNotEmpty()) {
|
||||||
ac.assert { canView(votes, citizenOrNull) }
|
ac.assert { canView(votes, citizenOrNull) }
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package fr.dcproject.component.vote.routes
|
|||||||
import fr.dcproject.common.response.toOutput
|
import fr.dcproject.common.response.toOutput
|
||||||
import fr.dcproject.common.security.assert
|
import fr.dcproject.common.security.assert
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
|
import fr.dcproject.component.auth.mustBeAuth
|
||||||
import fr.dcproject.component.citizen.database.CitizenRef
|
import fr.dcproject.component.citizen.database.CitizenRef
|
||||||
import fr.dcproject.component.vote.VoteAccessControl
|
import fr.dcproject.component.vote.VoteAccessControl
|
||||||
import fr.dcproject.component.vote.database.VoteArticleRepository
|
import fr.dcproject.component.vote.database.VoteArticleRepository
|
||||||
@@ -31,6 +32,7 @@ object GetCitizenVotesOnArticle {
|
|||||||
|
|
||||||
fun Route.getCitizenVotesOnArticle(repo: VoteArticleRepository, ac: VoteAccessControl) {
|
fun Route.getCitizenVotesOnArticle(repo: VoteArticleRepository, ac: VoteAccessControl) {
|
||||||
get<CitizenVoteArticleRequest> {
|
get<CitizenVoteArticleRequest> {
|
||||||
|
mustBeAuth()
|
||||||
val votes = repo.findByCitizen(it.citizen, it.page, it.limit)
|
val votes = repo.findByCitizen(it.citizen, it.page, it.limit)
|
||||||
ac.assert { canView(votes.result, citizenOrNull) }
|
ac.assert { canView(votes.result, citizenOrNull) }
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import fr.dcproject.component.article.database.ArticleRef
|
|||||||
import fr.dcproject.component.article.database.ArticleRepository
|
import fr.dcproject.component.article.database.ArticleRepository
|
||||||
import fr.dcproject.component.auth.citizen
|
import fr.dcproject.component.auth.citizen
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
|
import fr.dcproject.component.auth.mustBeAuth
|
||||||
import fr.dcproject.component.vote.VoteAccessControl
|
import fr.dcproject.component.vote.VoteAccessControl
|
||||||
import fr.dcproject.component.vote.database.VoteArticleRepository
|
import fr.dcproject.component.vote.database.VoteArticleRepository
|
||||||
import fr.dcproject.component.vote.database.VoteForUpdate
|
import fr.dcproject.component.vote.database.VoteForUpdate
|
||||||
@@ -29,6 +30,7 @@ object PutVoteOnArticle {
|
|||||||
|
|
||||||
fun Route.putVoteOnArticle(repo: VoteArticleRepository, ac: VoteAccessControl, articleRepo: ArticleRepository) {
|
fun Route.putVoteOnArticle(repo: VoteArticleRepository, ac: VoteAccessControl, articleRepo: ArticleRepository) {
|
||||||
put<ArticleVoteRequest> {
|
put<ArticleVoteRequest> {
|
||||||
|
mustBeAuth()
|
||||||
val input = call.receiveOrBadRequest<ArticleVoteRequest.Input>()
|
val input = call.receiveOrBadRequest<ArticleVoteRequest.Input>()
|
||||||
val article = articleRepo.findById(it.article.id) ?: throw NotFoundException("Article ${it.article.id} not found")
|
val article = articleRepo.findById(it.article.id) ?: throw NotFoundException("Article ${it.article.id} not found")
|
||||||
val vote = VoteForUpdate(
|
val vote = VoteForUpdate(
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import fr.dcproject.common.security.assert
|
|||||||
import fr.dcproject.common.utils.receiveOrBadRequest
|
import fr.dcproject.common.utils.receiveOrBadRequest
|
||||||
import fr.dcproject.component.auth.citizen
|
import fr.dcproject.component.auth.citizen
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
|
import fr.dcproject.component.auth.mustBeAuth
|
||||||
import fr.dcproject.component.comment.generic.database.CommentRepository
|
import fr.dcproject.component.comment.generic.database.CommentRepository
|
||||||
import fr.dcproject.component.vote.VoteAccessControl
|
import fr.dcproject.component.vote.VoteAccessControl
|
||||||
import fr.dcproject.component.vote.database.VoteCommentRepository
|
import fr.dcproject.component.vote.database.VoteCommentRepository
|
||||||
@@ -26,6 +27,7 @@ object PutVoteOnComment {
|
|||||||
|
|
||||||
fun Route.putVoteOnComment(voteCommentRepo: VoteCommentRepository, commentRepo: CommentRepository, ac: VoteAccessControl) {
|
fun Route.putVoteOnComment(voteCommentRepo: VoteCommentRepository, commentRepo: CommentRepository, ac: VoteAccessControl) {
|
||||||
put<CommentVoteRequest> {
|
put<CommentVoteRequest> {
|
||||||
|
mustBeAuth()
|
||||||
val comment = commentRepo.findById(it.comment)!!
|
val comment = commentRepo.findById(it.comment)!!
|
||||||
val content = call.receiveOrBadRequest<CommentVoteRequest.Content>()
|
val content = call.receiveOrBadRequest<CommentVoteRequest.Content>()
|
||||||
val vote = VoteForUpdate(
|
val vote = VoteForUpdate(
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import fr.dcproject.common.security.assert
|
|||||||
import fr.dcproject.common.utils.receiveOrBadRequest
|
import fr.dcproject.common.utils.receiveOrBadRequest
|
||||||
import fr.dcproject.component.auth.citizen
|
import fr.dcproject.component.auth.citizen
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
|
import fr.dcproject.component.auth.mustBeAuth
|
||||||
import fr.dcproject.component.constitution.database.ConstitutionRef
|
import fr.dcproject.component.constitution.database.ConstitutionRef
|
||||||
import fr.dcproject.component.constitution.database.ConstitutionRepository
|
import fr.dcproject.component.constitution.database.ConstitutionRepository
|
||||||
import fr.dcproject.component.vote.VoteAccessControl
|
import fr.dcproject.component.vote.VoteAccessControl
|
||||||
@@ -30,6 +31,7 @@ object PutVoteOnConstitution {
|
|||||||
|
|
||||||
fun Route.voteConstitution(repo: VoteConstitutionRepository, ac: VoteAccessControl, constitutionRepo: ConstitutionRepository) {
|
fun Route.voteConstitution(repo: VoteConstitutionRepository, ac: VoteAccessControl, constitutionRepo: ConstitutionRepository) {
|
||||||
put<ConstitutionVoteRequest> {
|
put<ConstitutionVoteRequest> {
|
||||||
|
mustBeAuth()
|
||||||
val constitution = constitutionRepo.findById(it.constitution.id) ?: throw NotFoundException("Unable to find constitution ${it.constitution.id}")
|
val constitution = constitutionRepo.findById(it.constitution.id) ?: throw NotFoundException("Unable to find constitution ${it.constitution.id}")
|
||||||
val content = call.receiveOrBadRequest<Input>()
|
val content = call.receiveOrBadRequest<Input>()
|
||||||
val vote = VoteForUpdate(
|
val vote = VoteForUpdate(
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import fr.dcproject.common.security.assert
|
|||||||
import fr.dcproject.common.utils.receiveOrBadRequest
|
import fr.dcproject.common.utils.receiveOrBadRequest
|
||||||
import fr.dcproject.component.auth.citizen
|
import fr.dcproject.component.auth.citizen
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
|
import fr.dcproject.component.auth.mustBeAuth
|
||||||
import fr.dcproject.component.workgroup.WorkgroupAccessControl
|
import fr.dcproject.component.workgroup.WorkgroupAccessControl
|
||||||
import fr.dcproject.component.workgroup.database.WorkgroupForUpdate
|
import fr.dcproject.component.workgroup.database.WorkgroupForUpdate
|
||||||
import fr.dcproject.component.workgroup.database.WorkgroupRepository
|
import fr.dcproject.component.workgroup.database.WorkgroupRepository
|
||||||
@@ -33,6 +34,7 @@ object CreateWorkgroup {
|
|||||||
|
|
||||||
fun Route.createWorkgroup(repo: WorkgroupRepository, ac: WorkgroupAccessControl) {
|
fun Route.createWorkgroup(repo: WorkgroupRepository, ac: WorkgroupAccessControl) {
|
||||||
post<PostWorkgroupRequest> {
|
post<PostWorkgroupRequest> {
|
||||||
|
mustBeAuth()
|
||||||
call.receiveOrBadRequest<Input>().run {
|
call.receiveOrBadRequest<Input>().run {
|
||||||
WorkgroupForUpdate(
|
WorkgroupForUpdate(
|
||||||
id ?: UUID.randomUUID(),
|
id ?: UUID.randomUUID(),
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package fr.dcproject.component.workgroup.routes
|
|||||||
|
|
||||||
import fr.dcproject.common.security.assert
|
import fr.dcproject.common.security.assert
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
|
import fr.dcproject.component.auth.mustBeAuth
|
||||||
import fr.dcproject.component.workgroup.WorkgroupAccessControl
|
import fr.dcproject.component.workgroup.WorkgroupAccessControl
|
||||||
import fr.dcproject.component.workgroup.database.WorkgroupRepository
|
import fr.dcproject.component.workgroup.database.WorkgroupRepository
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
@@ -20,6 +21,7 @@ object DeleteWorkgroup {
|
|||||||
|
|
||||||
fun Route.deleteWorkgroup(repo: WorkgroupRepository, ac: WorkgroupAccessControl) {
|
fun Route.deleteWorkgroup(repo: WorkgroupRepository, ac: WorkgroupAccessControl) {
|
||||||
delete<DeleteWorkgroupRequest> {
|
delete<DeleteWorkgroupRequest> {
|
||||||
|
mustBeAuth()
|
||||||
repo.findById(it.workgroupId)?.let { workgroup ->
|
repo.findById(it.workgroupId)?.let { workgroup ->
|
||||||
ac.assert { canDelete(workgroup, citizenOrNull) }
|
ac.assert { canDelete(workgroup, citizenOrNull) }
|
||||||
repo.delete(workgroup)
|
repo.delete(workgroup)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package fr.dcproject.component.workgroup.routes
|
|||||||
import fr.dcproject.common.security.assert
|
import fr.dcproject.common.security.assert
|
||||||
import fr.dcproject.common.utils.receiveOrBadRequest
|
import fr.dcproject.common.utils.receiveOrBadRequest
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
|
import fr.dcproject.component.auth.mustBeAuth
|
||||||
import fr.dcproject.component.workgroup.WorkgroupAccessControl
|
import fr.dcproject.component.workgroup.WorkgroupAccessControl
|
||||||
import fr.dcproject.component.workgroup.database.WorkgroupForUpdate
|
import fr.dcproject.component.workgroup.database.WorkgroupForUpdate
|
||||||
import fr.dcproject.component.workgroup.database.WorkgroupRepository
|
import fr.dcproject.component.workgroup.database.WorkgroupRepository
|
||||||
@@ -31,6 +32,7 @@ object EditWorkgroup {
|
|||||||
|
|
||||||
fun Route.editWorkgroup(repo: WorkgroupRepository, ac: WorkgroupAccessControl) {
|
fun Route.editWorkgroup(repo: WorkgroupRepository, ac: WorkgroupAccessControl) {
|
||||||
put<PutWorkgroupRequest> {
|
put<PutWorkgroupRequest> {
|
||||||
|
mustBeAuth()
|
||||||
repo.findById(it.workgroupId)?.let { old ->
|
repo.findById(it.workgroupId)?.let { old ->
|
||||||
call.receiveOrBadRequest<Input>().run {
|
call.receiveOrBadRequest<Input>().run {
|
||||||
WorkgroupForUpdate(
|
WorkgroupForUpdate(
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package fr.dcproject.component.workgroup.routes.members
|
|||||||
import fr.dcproject.common.security.assert
|
import fr.dcproject.common.security.assert
|
||||||
import fr.dcproject.common.utils.receiveOrBadRequest
|
import fr.dcproject.common.utils.receiveOrBadRequest
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
|
import fr.dcproject.component.auth.mustBeAuth
|
||||||
import fr.dcproject.component.citizen.database.CitizenRef
|
import fr.dcproject.component.citizen.database.CitizenRef
|
||||||
import fr.dcproject.component.workgroup.WorkgroupAccessControl
|
import fr.dcproject.component.workgroup.WorkgroupAccessControl
|
||||||
import fr.dcproject.component.workgroup.database.WorkgroupRepository
|
import fr.dcproject.component.workgroup.database.WorkgroupRepository
|
||||||
@@ -44,6 +45,7 @@ object AddMemberToWorkgroup {
|
|||||||
fun Route.addMemberToWorkgroup(repo: WorkgroupRepository, ac: WorkgroupAccessControl) {
|
fun Route.addMemberToWorkgroup(repo: WorkgroupRepository, ac: WorkgroupAccessControl) {
|
||||||
/* Add members to workgroup */
|
/* Add members to workgroup */
|
||||||
post<WorkgroupsMembersRequest> {
|
post<WorkgroupsMembersRequest> {
|
||||||
|
mustBeAuth()
|
||||||
repo.findById(it.workgroupId)?.let { workgroup ->
|
repo.findById(it.workgroupId)?.let { workgroup ->
|
||||||
call.getMembersFromRequest().let { members ->
|
call.getMembersFromRequest().let { members ->
|
||||||
ac.assert { canAddMembers(workgroup, citizenOrNull) }
|
ac.assert { canAddMembers(workgroup, citizenOrNull) }
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package fr.dcproject.component.workgroup.routes.members
|
|||||||
import fr.dcproject.common.security.assert
|
import fr.dcproject.common.security.assert
|
||||||
import fr.dcproject.common.utils.receiveOrBadRequest
|
import fr.dcproject.common.utils.receiveOrBadRequest
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
|
import fr.dcproject.component.auth.mustBeAuth
|
||||||
import fr.dcproject.component.citizen.database.CitizenRef
|
import fr.dcproject.component.citizen.database.CitizenRef
|
||||||
import fr.dcproject.component.workgroup.WorkgroupAccessControl
|
import fr.dcproject.component.workgroup.WorkgroupAccessControl
|
||||||
import fr.dcproject.component.workgroup.database.WorkgroupRepository
|
import fr.dcproject.component.workgroup.database.WorkgroupRepository
|
||||||
@@ -35,6 +36,7 @@ object DeleteMembersOfWorkgroup {
|
|||||||
fun Route.deleteMemberOfWorkgroup(repo: WorkgroupRepository, ac: WorkgroupAccessControl) {
|
fun Route.deleteMemberOfWorkgroup(repo: WorkgroupRepository, ac: WorkgroupAccessControl) {
|
||||||
/* Delete members of workgroup */
|
/* Delete members of workgroup */
|
||||||
delete<WorkgroupsMembersRequest> {
|
delete<WorkgroupsMembersRequest> {
|
||||||
|
mustBeAuth()
|
||||||
repo.findById(it.workgroupId)?.let { workgroup ->
|
repo.findById(it.workgroupId)?.let { workgroup ->
|
||||||
call.getMembersFromRequest()
|
call.getMembersFromRequest()
|
||||||
.let { members ->
|
.let { members ->
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package fr.dcproject.component.workgroup.routes.members
|
|||||||
import fr.dcproject.common.security.assert
|
import fr.dcproject.common.security.assert
|
||||||
import fr.dcproject.common.utils.receiveOrBadRequest
|
import fr.dcproject.common.utils.receiveOrBadRequest
|
||||||
import fr.dcproject.component.auth.citizenOrNull
|
import fr.dcproject.component.auth.citizenOrNull
|
||||||
|
import fr.dcproject.component.auth.mustBeAuth
|
||||||
import fr.dcproject.component.citizen.database.CitizenRef
|
import fr.dcproject.component.citizen.database.CitizenRef
|
||||||
import fr.dcproject.component.workgroup.WorkgroupAccessControl
|
import fr.dcproject.component.workgroup.WorkgroupAccessControl
|
||||||
import fr.dcproject.component.workgroup.database.WorkgroupRepository
|
import fr.dcproject.component.workgroup.database.WorkgroupRepository
|
||||||
@@ -42,6 +43,7 @@ object UpdateMemberOfWorkgroup {
|
|||||||
fun Route.updateMemberOfWorkgroup(repo: WorkgroupRepository, ac: WorkgroupAccessControl) {
|
fun Route.updateMemberOfWorkgroup(repo: WorkgroupRepository, ac: WorkgroupAccessControl) {
|
||||||
/* Update members of workgroup */
|
/* Update members of workgroup */
|
||||||
put<WorkgroupsMembersRequest> {
|
put<WorkgroupsMembersRequest> {
|
||||||
|
mustBeAuth()
|
||||||
repo.findById(it.workgroupId)?.let { workgroup ->
|
repo.findById(it.workgroupId)?.let { workgroup ->
|
||||||
call.getMembersFromRequest().let { members ->
|
call.getMembersFromRequest().let { members ->
|
||||||
ac.assert { canUpdateMembers(workgroup, citizenOrNull) }
|
ac.assert { canUpdateMembers(workgroup, citizenOrNull) }
|
||||||
|
|||||||
@@ -42,3 +42,11 @@ mail {
|
|||||||
key = ${?SEND_GRID_KEY}
|
key = ${?SEND_GRID_KEY}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
jwt {
|
||||||
|
secret = ${?JWT_SECRET}
|
||||||
|
issuer = "dc-project.fr"
|
||||||
|
issuer = ${?JWT_ISSUER}
|
||||||
|
validity = 36000000
|
||||||
|
validity = ${?JWT_VALIDITY}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
7
src/test/kotlin/assert/Range.kt
Normal file
7
src/test/kotlin/assert/Range.kt
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package assert
|
||||||
|
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
|
infix fun IntProgression.assertContain(expected: Int) {
|
||||||
|
assertTrue(this.contains(expected), "Expected $this less than $expected")
|
||||||
|
}
|
||||||
@@ -22,7 +22,7 @@ import org.koin.test.get
|
|||||||
@KtorExperimentalLocationsAPI
|
@KtorExperimentalLocationsAPI
|
||||||
@KtorExperimentalAPI
|
@KtorExperimentalAPI
|
||||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
@Tags(Tag("functional"))
|
@Tags(Tag("functional"), Tag("mail"))
|
||||||
class MailerTest : KoinTest, AutoCloseKoinTest() {
|
class MailerTest : KoinTest, AutoCloseKoinTest() {
|
||||||
@InternalCoroutinesApi
|
@InternalCoroutinesApi
|
||||||
@ExperimentalCoroutinesApi
|
@ExperimentalCoroutinesApi
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ import org.junit.jupiter.api.TestInstance
|
|||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
|
|
||||||
@TestInstance(TestInstance.Lifecycle.PER_METHOD)
|
@TestInstance(TestInstance.Lifecycle.PER_METHOD)
|
||||||
@Tags(Tag("functional"))
|
@Tags(Tag("functional"), Tag("notification"))
|
||||||
class NotificationConsumerTest {
|
class NotificationConsumerTest {
|
||||||
companion object {
|
companion object {
|
||||||
@BeforeAll
|
@BeforeAll
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import org.junit.jupiter.api.Tags
|
|||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
@Tags(Tag("functional"))
|
@Tags(Tag("functional"), Tag("notification"))
|
||||||
internal class NotificationsPushTest {
|
internal class NotificationsPushTest {
|
||||||
companion object {
|
companion object {
|
||||||
@BeforeAll
|
@BeforeAll
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import org.junit.jupiter.api.TestInstance
|
|||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
@Tags(Tag("functional"))
|
@Tags(Tag("functional"), Tag("utils"))
|
||||||
class ResourcesKtTest {
|
class ResourcesKtTest {
|
||||||
@Test
|
@Test
|
||||||
fun readResource() {
|
fun readResource() {
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ package functional
|
|||||||
|
|
||||||
import fr.dcproject.application.Env.TEST
|
import fr.dcproject.application.Env.TEST
|
||||||
import fr.dcproject.application.module
|
import fr.dcproject.application.module
|
||||||
import fr.dcproject.component.article.ArticleViewManager
|
|
||||||
import fr.dcproject.component.article.database.ArticleForView
|
import fr.dcproject.component.article.database.ArticleForView
|
||||||
|
import fr.dcproject.component.article.database.ArticleViewRepository
|
||||||
import fr.dcproject.component.auth.database.UserCreator
|
import fr.dcproject.component.auth.database.UserCreator
|
||||||
import fr.dcproject.component.citizen.database.CitizenCreator
|
import fr.dcproject.component.citizen.database.CitizenCreator
|
||||||
import fr.dcproject.component.citizen.database.CitizenI
|
import fr.dcproject.component.citizen.database.CitizenI
|
||||||
@@ -25,7 +25,7 @@ import java.util.UUID
|
|||||||
@KtorExperimentalAPI
|
@KtorExperimentalAPI
|
||||||
@ExperimentalCoroutinesApi
|
@ExperimentalCoroutinesApi
|
||||||
@TestInstance(PER_CLASS)
|
@TestInstance(PER_CLASS)
|
||||||
@Tags(Tag("functional"))
|
@Tags(Tag("functional"), Tag("view"))
|
||||||
class ViewTest {
|
class ViewTest {
|
||||||
@Test
|
@Test
|
||||||
fun `test View Article`() {
|
fun `test View Article`() {
|
||||||
@@ -44,33 +44,33 @@ class ViewTest {
|
|||||||
val citizenRef = CitizenRef()
|
val citizenRef = CitizenRef()
|
||||||
|
|
||||||
withTestApplication({ module(TEST) }) {
|
withTestApplication({ module(TEST) }) {
|
||||||
val viewManager: ArticleViewManager<ArticleForView> = application.get()
|
val viewRepository: ArticleViewRepository<ArticleForView> = application.get()
|
||||||
|
|
||||||
/* Get view before */
|
/* Get view before */
|
||||||
val startView = viewManager.getViewsCount(article)
|
val startView = viewRepository.getViewsCount(article)
|
||||||
|
|
||||||
/* Add View */
|
/* Add View */
|
||||||
viewManager.addView(
|
viewRepository.addView(
|
||||||
"1.2.3.4",
|
"1.2.3.4",
|
||||||
article,
|
article,
|
||||||
citizenRef
|
citizenRef
|
||||||
)
|
)
|
||||||
|
|
||||||
/* Add View */
|
/* Add View */
|
||||||
viewManager.addView(
|
viewRepository.addView(
|
||||||
"10.10.10.10",
|
"10.10.10.10",
|
||||||
article,
|
article,
|
||||||
citizenRef
|
citizenRef
|
||||||
)
|
)
|
||||||
|
|
||||||
/* Add View */
|
/* Add View */
|
||||||
viewManager.addView(
|
viewRepository.addView(
|
||||||
"8.8.8.8",
|
"8.8.8.8",
|
||||||
article
|
article
|
||||||
)
|
)
|
||||||
|
|
||||||
/* Add View */
|
/* Add View */
|
||||||
viewManager.addView(
|
viewRepository.addView(
|
||||||
"1.1.1.1",
|
"1.1.1.1",
|
||||||
article
|
article
|
||||||
)
|
)
|
||||||
@@ -79,7 +79,7 @@ class ViewTest {
|
|||||||
Thread.sleep(1000)
|
Thread.sleep(1000)
|
||||||
|
|
||||||
/* Get view */
|
/* Get view */
|
||||||
val afterView = viewManager.getViewsCount(article)
|
val afterView = viewRepository.getViewsCount(article)
|
||||||
|
|
||||||
/* Check if view has increment */
|
/* Check if view has increment */
|
||||||
afterView.total `should be equal to` startView.total + 4
|
afterView.total `should be equal to` startView.total + 4
|
||||||
|
|||||||
@@ -1,22 +1,24 @@
|
|||||||
package integration
|
package integration
|
||||||
|
|
||||||
import integration.steps.then.`And have property`
|
|
||||||
import integration.steps.then.`And the response should contain pattern`
|
|
||||||
import integration.steps.then.`And the response should not be null`
|
|
||||||
import integration.steps.then.`Then the response should be`
|
|
||||||
import integration.steps.`when`.`When I send a GET request`
|
import integration.steps.`when`.`When I send a GET request`
|
||||||
import integration.steps.`when`.`When I send a POST request`
|
import integration.steps.`when`.`When I send a POST request`
|
||||||
import integration.steps.`when`.`with body`
|
import integration.steps.`when`.`with body`
|
||||||
import integration.steps.then.`whish contains`
|
|
||||||
import integration.steps.then.and
|
|
||||||
import integration.steps.given.`Given I have article created by workgroup`
|
import integration.steps.given.`Given I have article created by workgroup`
|
||||||
import integration.steps.given.`Given I have article`
|
import integration.steps.given.`Given I have article`
|
||||||
import integration.steps.given.`Given I have articles`
|
import integration.steps.given.`Given I have articles`
|
||||||
import integration.steps.given.`Given I have citizen`
|
import integration.steps.given.`Given I have citizen`
|
||||||
import integration.steps.given.`Given I have workgroup`
|
import integration.steps.given.`Given I have workgroup`
|
||||||
import integration.steps.given.`authenticated as`
|
import integration.steps.given.`authenticated as`
|
||||||
|
import integration.steps.then.`And have property`
|
||||||
import integration.steps.then.`And the response should contain list`
|
import integration.steps.then.`And the response should contain list`
|
||||||
|
import integration.steps.then.`And the response should contain pattern`
|
||||||
|
import integration.steps.then.`And the response should contain`
|
||||||
|
import integration.steps.then.`And the response should not be null`
|
||||||
import integration.steps.then.`And the response should not contain`
|
import integration.steps.then.`And the response should not contain`
|
||||||
|
import integration.steps.then.`Then the response should be`
|
||||||
|
import integration.steps.then.`whish contains`
|
||||||
|
import integration.steps.then.and
|
||||||
|
import io.ktor.http.HttpStatusCode.Companion.Forbidden
|
||||||
import io.ktor.http.HttpStatusCode.Companion.OK
|
import io.ktor.http.HttpStatusCode.Companion.OK
|
||||||
import org.junit.jupiter.api.Tag
|
import org.junit.jupiter.api.Tag
|
||||||
import org.junit.jupiter.api.Tags
|
import org.junit.jupiter.api.Tags
|
||||||
@@ -36,7 +38,7 @@ class `Article routes` : BaseTest() {
|
|||||||
`And the response should contain pattern`("$.result[1].createdBy.name.firstName", "firstName.+")
|
`And the response should contain pattern`("$.result[1].createdBy.name.firstName", "firstName.+")
|
||||||
`And the response should contain pattern`("$.result[2].createdBy.name.firstName", "firstName.+")
|
`And the response should contain pattern`("$.result[2].createdBy.name.firstName", "firstName.+")
|
||||||
`And the response should not contain`("$.result[3]")
|
`And the response should not contain`("$.result[3]")
|
||||||
`And the response should contain list`("$.result", 3, 3)
|
`And the response should contain list`("$.result", 3)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -84,22 +86,50 @@ class `Article routes` : BaseTest() {
|
|||||||
`Given I have citizen`("John", "Doe")
|
`Given I have citizen`("John", "Doe")
|
||||||
`When I send a POST request`("/articles") {
|
`When I send a POST request`("/articles") {
|
||||||
`authenticated as`("John", "Doe")
|
`authenticated as`("John", "Doe")
|
||||||
`with body`("""
|
`with body`(
|
||||||
{
|
"""
|
||||||
"versionId": "09c418b6-63ba-448b-b38b-502b41cd500e",
|
{
|
||||||
"title": "title2",
|
"versionId": "09c418b6-63ba-448b-b38b-502b41cd500e",
|
||||||
"anonymous": false,
|
"title": "title2",
|
||||||
"content": "content2",
|
"anonymous": false,
|
||||||
"description": "description2",
|
"content": "content2",
|
||||||
"tags": [
|
"description": "description2",
|
||||||
"green"
|
"tags": [
|
||||||
]
|
"green"
|
||||||
}
|
]
|
||||||
""")
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
} `Then the response should be` OK and {
|
} `Then the response should be` OK and {
|
||||||
`And the response should not be null`()
|
`And the response should not be null`()
|
||||||
`And have property`("$.versionId") `whish contains` "09c418b6-63ba-448b-b38b-502b41cd500e"
|
`And have property`("$.versionId") `whish contains` "09c418b6-63ba-448b-b38b-502b41cd500e"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `I cannot create an article if I'm not connected`() {
|
||||||
|
withIntegrationApplication {
|
||||||
|
`When I send a POST request`("/articles") {
|
||||||
|
`with body`(
|
||||||
|
"""
|
||||||
|
{
|
||||||
|
"versionId": "e3c7ce42-241c-4caf-9a59-aba4e466440e",
|
||||||
|
"title": "title2",
|
||||||
|
"anonymous": false,
|
||||||
|
"content": "content2",
|
||||||
|
"description": "description2",
|
||||||
|
"tags": [
|
||||||
|
"green"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
} `Then the response should be` Forbidden and {
|
||||||
|
`And the response should not be null`()
|
||||||
|
`And the response should contain`("$.statusCode", 403)
|
||||||
|
`And the response should contain`("$.title", "No User Connected")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
147
src/test/kotlin/integration/Check auth on all routes.kt
Normal file
147
src/test/kotlin/integration/Check auth on all routes.kt
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
package integration
|
||||||
|
|
||||||
|
import fr.dcproject.common.utils.getResource
|
||||||
|
import io.ktor.http.ContentType
|
||||||
|
import io.ktor.http.HttpHeaders
|
||||||
|
import io.ktor.http.HttpMethod
|
||||||
|
import io.ktor.http.HttpStatusCode
|
||||||
|
import org.junit.jupiter.api.Tag
|
||||||
|
import org.junit.jupiter.api.Tags
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.junit.jupiter.api.TestInstance
|
||||||
|
import org.openapi4j.core.model.OAIContext
|
||||||
|
import org.openapi4j.parser.OpenApi3Parser
|
||||||
|
import org.openapi4j.parser.model.v3.OpenApi3
|
||||||
|
import org.openapi4j.parser.model.v3.Operation
|
||||||
|
import org.openapi4j.parser.model.v3.Parameter
|
||||||
|
import org.openapi4j.parser.model.v3.Path
|
||||||
|
import java.io.File
|
||||||
|
import java.util.UUID
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
|
@Tags(Tag("integration"), Tag("auth"))
|
||||||
|
class `Check auth on all routes` : BaseTest() {
|
||||||
|
@Test
|
||||||
|
fun `Check all routes`() {
|
||||||
|
val filePath = "/openapi.yaml"
|
||||||
|
OpenApi3Parser().parse(File(filePath.getResource().toURI()), true).let { api: OpenApi3 ->
|
||||||
|
/* Loop on paths and http methods */
|
||||||
|
api.paths.flatMap { (pathName: String, path: Path) ->
|
||||||
|
path.operations
|
||||||
|
/* Take only the secure route */
|
||||||
|
.filter { (_, operation: Operation) -> operation.hasSecurityRequirements() }
|
||||||
|
.map { (methodName, _) ->
|
||||||
|
/* Send request to check security */
|
||||||
|
sendRequest(
|
||||||
|
path.buildUrl(pathName, methodName, api.context), /* Replace route to real URL */
|
||||||
|
HttpMethod.parse(methodName.toUpperCase()) /* Convert http method name to enum */
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}.let { requests ->
|
||||||
|
/* Check security of routes */
|
||||||
|
assertTrue(
|
||||||
|
requests.all { it.statusCode == HttpStatusCode.Forbidden },
|
||||||
|
requests
|
||||||
|
.filter { it.statusCode != HttpStatusCode.Forbidden }
|
||||||
|
.joinToString("\n") { it.toString() }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun sendRequest(uri: String, method: HttpMethod): RequestResponse {
|
||||||
|
return try {
|
||||||
|
withIntegrationApplication {
|
||||||
|
handleRequest(true) {
|
||||||
|
this.method = method
|
||||||
|
this.uri = uri
|
||||||
|
addHeader(HttpHeaders.ContentType, ContentType.Application.Json.toString())
|
||||||
|
addHeader(HttpHeaders.Accept, ContentType.Application.Json.toString())
|
||||||
|
}.run {
|
||||||
|
RequestResponse(
|
||||||
|
response.status() ?: error("Request error"),
|
||||||
|
method,
|
||||||
|
uri
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
RequestResponse(
|
||||||
|
HttpStatusCode.InternalServerError,
|
||||||
|
method,
|
||||||
|
uri
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private data class RequestResponse(
|
||||||
|
val statusCode: HttpStatusCode,
|
||||||
|
val method: HttpMethod,
|
||||||
|
val uri: String
|
||||||
|
) {
|
||||||
|
override fun toString(): String {
|
||||||
|
return """HttpStatus ${statusCode.value} for: ${method.value.padStart(6, ' ')} $uri"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Path.buildUrl(path: String, methodName: String, context: OAIContext): String {
|
||||||
|
val urlReplaced = this.getParametersIn(context, "path")
|
||||||
|
.fold(path) { pathToReplace: String, parameter: Parameter ->
|
||||||
|
"""\{${parameter.name}}""".toRegex().replace(
|
||||||
|
pathToReplace,
|
||||||
|
parameter.generateFakeValue()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val rootQueryParameters = this.getParametersIn(context, "query")
|
||||||
|
.filter { it.isRequired }
|
||||||
|
.map { parameter ->
|
||||||
|
parameter
|
||||||
|
.generateFakeArray()
|
||||||
|
.joinToString("&") { "${parameter.name}=$it" }
|
||||||
|
}
|
||||||
|
|
||||||
|
val queryParameters = this.getOperation(methodName).getParametersIn(context, "query")
|
||||||
|
.filter { it.isRequired }
|
||||||
|
.map { parameter ->
|
||||||
|
parameter
|
||||||
|
.generateFakeArray()
|
||||||
|
.joinToString("&") { "${parameter.name}=$it" }
|
||||||
|
}
|
||||||
|
val allParameters: String = (rootQueryParameters + queryParameters)
|
||||||
|
.joinToString("&")
|
||||||
|
.let {
|
||||||
|
if (it.isNotEmpty()) {
|
||||||
|
"?$it"
|
||||||
|
} else {
|
||||||
|
it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "$urlReplaced$allParameters"
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Parameter.generateFakeValue(): String {
|
||||||
|
return if (example != null) {
|
||||||
|
example.toString()
|
||||||
|
} else if (schema.type == "string" && schema.format == "uuid") {
|
||||||
|
UUID.randomUUID().toString()
|
||||||
|
} else {
|
||||||
|
"example123"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Parameter.generateFakeArray(): List<String> {
|
||||||
|
if (schema.type != "array") {
|
||||||
|
error("Parameter is not an array")
|
||||||
|
}
|
||||||
|
return if (example != null && example is Iterable<*>) {
|
||||||
|
(example as Iterable<*>).map { it.toString() }
|
||||||
|
} else if (schema.itemsSchema.type == "string" && schema.itemsSchema.format == "uuid") {
|
||||||
|
listOf(UUID.randomUUID().toString())
|
||||||
|
} else {
|
||||||
|
listOf("example123")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,16 +1,16 @@
|
|||||||
package integration
|
package integration
|
||||||
|
|
||||||
import integration.steps.`when`.Validate
|
import integration.steps.`when`.Validate
|
||||||
import integration.steps.then.`And have property`
|
|
||||||
import integration.steps.then.`And the response should not be null`
|
|
||||||
import integration.steps.then.`Then the response should be`
|
|
||||||
import integration.steps.`when`.`When I send a GET request`
|
import integration.steps.`when`.`When I send a GET request`
|
||||||
import integration.steps.`when`.`When I send a PUT request`
|
import integration.steps.`when`.`When I send a PUT request`
|
||||||
import integration.steps.`when`.`with body`
|
import integration.steps.`when`.`with body`
|
||||||
import integration.steps.then.`whish contains`
|
|
||||||
import integration.steps.then.and
|
|
||||||
import integration.steps.given.`Given I have citizen`
|
import integration.steps.given.`Given I have citizen`
|
||||||
import integration.steps.given.`authenticated as`
|
import integration.steps.given.`authenticated as`
|
||||||
|
import integration.steps.then.`And have property`
|
||||||
|
import integration.steps.then.`And the response should not be null`
|
||||||
|
import integration.steps.then.`Then the response should be`
|
||||||
|
import integration.steps.then.`whish contains`
|
||||||
|
import integration.steps.then.and
|
||||||
import io.ktor.http.HttpStatusCode.Companion.BadRequest
|
import io.ktor.http.HttpStatusCode.Companion.BadRequest
|
||||||
import io.ktor.http.HttpStatusCode.Companion.Created
|
import io.ktor.http.HttpStatusCode.Companion.Created
|
||||||
import io.ktor.http.HttpStatusCode.Companion.OK
|
import io.ktor.http.HttpStatusCode.Companion.OK
|
||||||
@@ -66,12 +66,14 @@ class `Citizen routes` : BaseTest() {
|
|||||||
`Given I have citizen`("Georges", "Charpak", id = "0c966522-4071-43e5-a3ca-cfff2557f2cf")
|
`Given I have citizen`("Georges", "Charpak", id = "0c966522-4071-43e5-a3ca-cfff2557f2cf")
|
||||||
`When I send a PUT request`("/citizens/0c966522-4071-43e5-a3ca-cfff2557f2cf/password/change") {
|
`When I send a PUT request`("/citizens/0c966522-4071-43e5-a3ca-cfff2557f2cf/password/change") {
|
||||||
`authenticated as`("Georges", "Charpak")
|
`authenticated as`("Georges", "Charpak")
|
||||||
`with body`("""
|
`with body`(
|
||||||
{
|
"""
|
||||||
"oldPassword": "azerty",
|
{
|
||||||
"newPassword": "qwerty"
|
"oldPassword": "azerty",
|
||||||
}
|
"newPassword": "qwerty"
|
||||||
""")
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
} `Then the response should be` Created
|
} `Then the response should be` Created
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -82,12 +84,14 @@ class `Citizen routes` : BaseTest() {
|
|||||||
`Given I have citizen`("Louis", "Breguet", id = "6cf2a19d-d15d-4ee5-b2a9-907afd26b525")
|
`Given I have citizen`("Louis", "Breguet", id = "6cf2a19d-d15d-4ee5-b2a9-907afd26b525")
|
||||||
`When I send a PUT request`("/citizens/6cf2a19d-d15d-4ee5-b2a9-907afd26b525/password/change", Validate.ALL - Validate.REQUEST_BODY) {
|
`When I send a PUT request`("/citizens/6cf2a19d-d15d-4ee5-b2a9-907afd26b525/password/change", Validate.ALL - Validate.REQUEST_BODY) {
|
||||||
`authenticated as`("Louis", "Breguet")
|
`authenticated as`("Louis", "Breguet")
|
||||||
`with body`("""
|
`with body`(
|
||||||
{
|
"""
|
||||||
"plup": "azerty",
|
{
|
||||||
"gloup": "qwerty"
|
"plup": "azerty",
|
||||||
}
|
"gloup": "qwerty"
|
||||||
""")
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
} `Then the response should be` BadRequest
|
} `Then the response should be` BadRequest
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
package integration
|
package integration
|
||||||
|
|
||||||
import fr.dcproject.component.citizen.database.CitizenI.Name
|
import fr.dcproject.component.citizen.database.CitizenI.Name
|
||||||
import integration.steps.then.`And the response should contain`
|
|
||||||
import integration.steps.then.`And the response should not be null`
|
|
||||||
import integration.steps.then.`Then the response should be`
|
|
||||||
import integration.steps.`when`.`When I send a GET request`
|
import integration.steps.`when`.`When I send a GET request`
|
||||||
import integration.steps.`when`.`When I send a POST request`
|
import integration.steps.`when`.`When I send a POST request`
|
||||||
import integration.steps.`when`.`When I send a PUT request`
|
import integration.steps.`when`.`When I send a PUT request`
|
||||||
import integration.steps.`when`.`with body`
|
import integration.steps.`when`.`with body`
|
||||||
import integration.steps.then.and
|
|
||||||
import integration.steps.given.`Given I have article`
|
import integration.steps.given.`Given I have article`
|
||||||
import integration.steps.given.`Given I have citizen`
|
import integration.steps.given.`Given I have citizen`
|
||||||
import integration.steps.given.`Given I have comment on article`
|
import integration.steps.given.`Given I have comment on article`
|
||||||
import integration.steps.given.`authenticated as`
|
import integration.steps.given.`authenticated as`
|
||||||
|
import integration.steps.then.`And the response should contain`
|
||||||
|
import integration.steps.then.`And the response should not be null`
|
||||||
|
import integration.steps.then.`Then the response should be`
|
||||||
|
import integration.steps.then.and
|
||||||
import io.ktor.http.HttpStatusCode.Companion.Created
|
import io.ktor.http.HttpStatusCode.Companion.Created
|
||||||
import io.ktor.http.HttpStatusCode.Companion.OK
|
import io.ktor.http.HttpStatusCode.Companion.OK
|
||||||
import org.junit.jupiter.api.Tag
|
import org.junit.jupiter.api.Tag
|
||||||
@@ -30,11 +30,13 @@ class `Comment articles routes` : BaseTest() {
|
|||||||
`Given I have article`(id = "aa16c635-28da-46f0-9a89-934eef88c7ca")
|
`Given I have article`(id = "aa16c635-28da-46f0-9a89-934eef88c7ca")
|
||||||
`When I send a POST request`("/articles/aa16c635-28da-46f0-9a89-934eef88c7ca/comments") {
|
`When I send a POST request`("/articles/aa16c635-28da-46f0-9a89-934eef88c7ca/comments") {
|
||||||
`authenticated as`("Michael", "Faraday")
|
`authenticated as`("Michael", "Faraday")
|
||||||
`with body`("""
|
`with body`(
|
||||||
{
|
"""
|
||||||
"content": "Hello mister"
|
{
|
||||||
}
|
"content": "Hello mister"
|
||||||
""")
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
} `Then the response should be` Created and {
|
} `Then the response should be` Created and {
|
||||||
`And the response should not be null`()
|
`And the response should not be null`()
|
||||||
`And the response should contain`("$.target.id", "aa16c635-28da-46f0-9a89-934eef88c7ca")
|
`And the response should contain`("$.target.id", "aa16c635-28da-46f0-9a89-934eef88c7ca")
|
||||||
@@ -82,6 +84,7 @@ class `Comment articles routes` : BaseTest() {
|
|||||||
`Given I have article`(id = "17df7fb9-b388-4e20-ab19-29c29972da01", createdBy = Name("Erwin", "Schrodinger"))
|
`Given I have article`(id = "17df7fb9-b388-4e20-ab19-29c29972da01", createdBy = Name("Erwin", "Schrodinger"))
|
||||||
`Given I have comment on article`(article = "17df7fb9-b388-4e20-ab19-29c29972da01", createdBy = Name("Erwin", "Schrodinger"))
|
`Given I have comment on article`(article = "17df7fb9-b388-4e20-ab19-29c29972da01", createdBy = Name("Erwin", "Schrodinger"))
|
||||||
`When I send a GET request`("/citizens/292a20cc-4a60-489e-9866-a95d38ffaf47/comments/articles") {
|
`When I send a GET request`("/citizens/292a20cc-4a60-489e-9866-a95d38ffaf47/comments/articles") {
|
||||||
|
`authenticated as`("Erwin", "Schrodinger")
|
||||||
} `Then the response should be` OK and {
|
} `Then the response should be` OK and {
|
||||||
`And the response should not be null`()
|
`And the response should not be null`()
|
||||||
`And the response should contain`("$.currentPage", 1)
|
`And the response should contain`("$.currentPage", 1)
|
||||||
@@ -99,11 +102,13 @@ class `Comment articles routes` : BaseTest() {
|
|||||||
`Given I have comment on article`(article = "bb05e4a3-55a1-4088-85e7-8d8c23be29b1", createdBy = Name("Hubert", "Reeves"), id = "fd30d20f-656c-42c6-8955-f61c04537464")
|
`Given I have comment on article`(article = "bb05e4a3-55a1-4088-85e7-8d8c23be29b1", createdBy = Name("Hubert", "Reeves"), id = "fd30d20f-656c-42c6-8955-f61c04537464")
|
||||||
`When I send a PUT request`("/comments/fd30d20f-656c-42c6-8955-f61c04537464") {
|
`When I send a PUT request`("/comments/fd30d20f-656c-42c6-8955-f61c04537464") {
|
||||||
`authenticated as`("Hubert", "Reeves")
|
`authenticated as`("Hubert", "Reeves")
|
||||||
`with body`("""
|
`with body`(
|
||||||
{
|
"""
|
||||||
"content": "Hello boy"
|
{
|
||||||
}
|
"content": "Hello boy"
|
||||||
""")
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
} `Then the response should be` OK and {
|
} `Then the response should be` OK and {
|
||||||
`And the response should not be null`()
|
`And the response should not be null`()
|
||||||
`And the response should contain`("$.content", "Hello boy")
|
`And the response should contain`("$.content", "Hello boy")
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
package integration
|
package integration
|
||||||
|
|
||||||
import fr.dcproject.component.citizen.database.CitizenI.Name
|
import fr.dcproject.component.citizen.database.CitizenI.Name
|
||||||
import integration.steps.then.`And the response should contain list`
|
|
||||||
import integration.steps.then.`And the response should contain`
|
|
||||||
import integration.steps.then.`And the response should not be null`
|
|
||||||
import integration.steps.then.`Then the response should be`
|
|
||||||
import integration.steps.`when`.`When I send a GET request`
|
import integration.steps.`when`.`When I send a GET request`
|
||||||
import integration.steps.`when`.`When I send a POST request`
|
import integration.steps.`when`.`When I send a POST request`
|
||||||
import integration.steps.`when`.`with body`
|
import integration.steps.`when`.`with body`
|
||||||
import integration.steps.then.and
|
|
||||||
import integration.steps.given.`Given I have citizen`
|
import integration.steps.given.`Given I have citizen`
|
||||||
import integration.steps.given.`Given I have comment on constitution`
|
import integration.steps.given.`Given I have comment on constitution`
|
||||||
import integration.steps.given.`Given I have constitution`
|
import integration.steps.given.`Given I have constitution`
|
||||||
import integration.steps.given.`authenticated as`
|
import integration.steps.given.`authenticated as`
|
||||||
|
import integration.steps.then.`And the response should contain list`
|
||||||
|
import integration.steps.then.`And the response should contain`
|
||||||
|
import integration.steps.then.`And the response should not be null`
|
||||||
|
import integration.steps.then.`Then the response should be`
|
||||||
|
import integration.steps.then.and
|
||||||
import io.ktor.http.HttpStatusCode.Companion.Created
|
import io.ktor.http.HttpStatusCode.Companion.Created
|
||||||
import io.ktor.http.HttpStatusCode.Companion.OK
|
import io.ktor.http.HttpStatusCode.Companion.OK
|
||||||
import org.junit.jupiter.api.Tag
|
import org.junit.jupiter.api.Tag
|
||||||
@@ -30,11 +30,13 @@ class `Comment constitutions routes` : BaseTest() {
|
|||||||
`Given I have constitution`(id = "1707c287-a472-4a62-89f2-9e85030e915c")
|
`Given I have constitution`(id = "1707c287-a472-4a62-89f2-9e85030e915c")
|
||||||
`When I send a POST request`("/constitutions/1707c287-a472-4a62-89f2-9e85030e915c/comments") {
|
`When I send a POST request`("/constitutions/1707c287-a472-4a62-89f2-9e85030e915c/comments") {
|
||||||
`authenticated as`("Nicolas", "Copernic")
|
`authenticated as`("Nicolas", "Copernic")
|
||||||
`with body`("""
|
`with body`(
|
||||||
{
|
"""
|
||||||
"content": "Hello mister"
|
{
|
||||||
}
|
"content": "Hello mister"
|
||||||
""")
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
} `Then the response should be` Created and {
|
} `Then the response should be` Created and {
|
||||||
`And the response should not be null`()
|
`And the response should not be null`()
|
||||||
}
|
}
|
||||||
@@ -48,13 +50,14 @@ class `Comment constitutions routes` : BaseTest() {
|
|||||||
`Given I have constitution`(id = "34ddd50a-da00-4a90-a869-08baa2a121be", createdBy = Name("Charles", "Darwin"))
|
`Given I have constitution`(id = "34ddd50a-da00-4a90-a869-08baa2a121be", createdBy = Name("Charles", "Darwin"))
|
||||||
`Given I have comment on constitution`(constitution = "34ddd50a-da00-4a90-a869-08baa2a121be", createdBy = Name("Charles", "Darwin"))
|
`Given I have comment on constitution`(constitution = "34ddd50a-da00-4a90-a869-08baa2a121be", createdBy = Name("Charles", "Darwin"))
|
||||||
`When I send a GET request`("/citizens/46e0bda9-ca6a-4c65-a58b-7e7267a0bbc5/comments/constitutions") {
|
`When I send a GET request`("/citizens/46e0bda9-ca6a-4c65-a58b-7e7267a0bbc5/comments/constitutions") {
|
||||||
|
`authenticated as`("Charles", "Darwin")
|
||||||
} `Then the response should be` OK and {
|
} `Then the response should be` OK and {
|
||||||
`And the response should not be null`()
|
`And the response should not be null`()
|
||||||
`And the response should contain`("$.currentPage", 1)
|
`And the response should contain`("$.currentPage", 1)
|
||||||
`And the response should contain`("$.limit", 50)
|
`And the response should contain`("$.limit", 50)
|
||||||
`And the response should contain`("$.result[0].createdBy.id", "46e0bda9-ca6a-4c65-a58b-7e7267a0bbc5")
|
`And the response should contain`("$.result[0].createdBy.id", "46e0bda9-ca6a-4c65-a58b-7e7267a0bbc5")
|
||||||
`And the response should contain`("$.result[0].target.id", "34ddd50a-da00-4a90-a869-08baa2a121be")
|
`And the response should contain`("$.result[0].target.id", "34ddd50a-da00-4a90-a869-08baa2a121be")
|
||||||
`And the response should contain list`("$.result[*]", 1, 1)
|
`And the response should contain list`("$.result[*]", 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
package integration
|
package integration
|
||||||
|
|
||||||
import integration.steps.then.`And the response should not be null`
|
|
||||||
import integration.steps.then.`Then the response should be`
|
|
||||||
import integration.steps.`when`.`When I send a GET request`
|
import integration.steps.`when`.`When I send a GET request`
|
||||||
import integration.steps.then.and
|
|
||||||
import integration.steps.given.`Given I have article`
|
import integration.steps.given.`Given I have article`
|
||||||
import integration.steps.given.`Given I have citizen`
|
import integration.steps.given.`Given I have citizen`
|
||||||
import integration.steps.given.`Given I have comment on article`
|
import integration.steps.given.`Given I have comment on article`
|
||||||
import integration.steps.given.`authenticated as`
|
import integration.steps.given.`authenticated as`
|
||||||
|
import integration.steps.then.`And the response should not be null`
|
||||||
|
import integration.steps.then.`Then the response should be`
|
||||||
|
import integration.steps.then.and
|
||||||
import io.ktor.http.HttpStatusCode.Companion.OK
|
import io.ktor.http.HttpStatusCode.Companion.OK
|
||||||
import org.junit.jupiter.api.Tag
|
import org.junit.jupiter.api.Tag
|
||||||
import org.junit.jupiter.api.Tags
|
import org.junit.jupiter.api.Tags
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
package integration
|
package integration
|
||||||
|
|
||||||
import integration.steps.`when`.Validate
|
import integration.steps.`when`.Validate
|
||||||
import integration.steps.then.`And have property`
|
|
||||||
import integration.steps.then.`And the response should not be null`
|
|
||||||
import integration.steps.then.`Then the response should be`
|
|
||||||
import integration.steps.`when`.`When I send a GET request`
|
import integration.steps.`when`.`When I send a GET request`
|
||||||
import integration.steps.`when`.`When I send a POST request`
|
import integration.steps.`when`.`When I send a POST request`
|
||||||
import integration.steps.`when`.`with body`
|
import integration.steps.`when`.`with body`
|
||||||
import integration.steps.then.`whish contains`
|
|
||||||
import integration.steps.then.and
|
|
||||||
import integration.steps.given.`Given I have citizen`
|
import integration.steps.given.`Given I have citizen`
|
||||||
import integration.steps.given.`Given I have constitution`
|
import integration.steps.given.`Given I have constitution`
|
||||||
import integration.steps.given.`Given I have constitutions`
|
import integration.steps.given.`Given I have constitutions`
|
||||||
import integration.steps.given.`authenticated as`
|
import integration.steps.given.`authenticated as`
|
||||||
|
import integration.steps.then.`And have property`
|
||||||
|
import integration.steps.then.`And the response should not be null`
|
||||||
|
import integration.steps.then.`Then the response should be`
|
||||||
|
import integration.steps.then.`whish contains`
|
||||||
|
import integration.steps.then.and
|
||||||
import io.ktor.http.HttpStatusCode.Companion.BadRequest
|
import io.ktor.http.HttpStatusCode.Companion.BadRequest
|
||||||
import io.ktor.http.HttpStatusCode.Companion.Created
|
import io.ktor.http.HttpStatusCode.Companion.Created
|
||||||
import io.ktor.http.HttpStatusCode.Companion.OK
|
import io.ktor.http.HttpStatusCode.Companion.OK
|
||||||
@@ -66,18 +66,20 @@ class `Constitution routes` : BaseTest() {
|
|||||||
`Given I have citizen`("Henri", "Poincaré")
|
`Given I have citizen`("Henri", "Poincaré")
|
||||||
`When I send a POST request`("/constitutions") {
|
`When I send a POST request`("/constitutions") {
|
||||||
`authenticated as`("Henri", "Poincaré")
|
`authenticated as`("Henri", "Poincaré")
|
||||||
`with body`("""
|
`with body`(
|
||||||
{
|
"""
|
||||||
"versionId":"15814bb6-8d90-4c6a-a456-c3939a8ec75e",
|
{
|
||||||
"title":"Hello world!",
|
"versionId":"15814bb6-8d90-4c6a-a456-c3939a8ec75e",
|
||||||
"anonymous":true,
|
"title":"Hello world!",
|
||||||
"titles":[
|
"anonymous":true,
|
||||||
{
|
"titles":[
|
||||||
"name":"plop"
|
{
|
||||||
}
|
"name":"plop"
|
||||||
]
|
}
|
||||||
}
|
]
|
||||||
""")
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
} `Then the response should be` Created and {
|
} `Then the response should be` Created and {
|
||||||
`And the response should not be null`()
|
`And the response should not be null`()
|
||||||
`And have property`("$.versionId") `whish contains` "15814bb6-8d90-4c6a-a456-c3939a8ec75e"
|
`And have property`("$.versionId") `whish contains` "15814bb6-8d90-4c6a-a456-c3939a8ec75e"
|
||||||
@@ -92,19 +94,21 @@ class `Constitution routes` : BaseTest() {
|
|||||||
`Given I have citizen`("Henri", "Poincaré")
|
`Given I have citizen`("Henri", "Poincaré")
|
||||||
`When I send a POST request`("/constitutions", Validate.ALL - Validate.REQUEST_BODY) {
|
`When I send a POST request`("/constitutions", Validate.ALL - Validate.REQUEST_BODY) {
|
||||||
`authenticated as`("Henri", "Poincaré")
|
`authenticated as`("Henri", "Poincaré")
|
||||||
`with body`("""
|
`with body`(
|
||||||
{
|
"""
|
||||||
"versionId":"15814bb6-8d90-4c6a-a456-c3939a8ec75e",
|
{
|
||||||
"title":"Hello world!",
|
"versionId":"15814bb6-8d90-4c6a-a456-c3939a8ec75e",
|
||||||
"anonymous":true,
|
"title":"Hello world!",
|
||||||
"titles":[
|
"anonymous":true,
|
||||||
{
|
"titles":[
|
||||||
"name":"plop",
|
{
|
||||||
"wrongField":0
|
"name":"plop",
|
||||||
}
|
"wrongField":0
|
||||||
]
|
}
|
||||||
}
|
]
|
||||||
""")
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
} `Then the response should be` BadRequest
|
} `Then the response should be` BadRequest
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
package integration
|
package integration
|
||||||
|
|
||||||
import integration.steps.then.`And the response should be null`
|
|
||||||
import integration.steps.then.`And the response should contain`
|
|
||||||
import integration.steps.then.`And the response should not be null`
|
|
||||||
import integration.steps.then.`Then the response should be`
|
|
||||||
import integration.steps.`when`.`When I send a DELETE request`
|
import integration.steps.`when`.`When I send a DELETE request`
|
||||||
import integration.steps.`when`.`When I send a GET request`
|
import integration.steps.`when`.`When I send a GET request`
|
||||||
import integration.steps.`when`.`When I send a POST request`
|
import integration.steps.`when`.`When I send a POST request`
|
||||||
import integration.steps.then.and
|
|
||||||
import integration.steps.given.`And follow article`
|
import integration.steps.given.`And follow article`
|
||||||
import integration.steps.given.`Given I have article`
|
import integration.steps.given.`Given I have article`
|
||||||
import integration.steps.given.`Given I have citizen`
|
import integration.steps.given.`Given I have citizen`
|
||||||
import integration.steps.given.`authenticated as`
|
import integration.steps.given.`authenticated as`
|
||||||
import integration.steps.given.`with no content`
|
import integration.steps.given.`with no content`
|
||||||
|
import integration.steps.then.`And the response should be null`
|
||||||
|
import integration.steps.then.`And the response should contain`
|
||||||
|
import integration.steps.then.`And the response should not be null`
|
||||||
|
import integration.steps.then.`Then the response should be`
|
||||||
|
import integration.steps.then.and
|
||||||
import io.ktor.http.HttpStatusCode.Companion.Created
|
import io.ktor.http.HttpStatusCode.Companion.Created
|
||||||
import io.ktor.http.HttpStatusCode.Companion.NoContent
|
import io.ktor.http.HttpStatusCode.Companion.NoContent
|
||||||
import io.ktor.http.HttpStatusCode.Companion.OK
|
import io.ktor.http.HttpStatusCode.Companion.OK
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
package integration
|
package integration
|
||||||
|
|
||||||
import integration.steps.then.`And the response should be null`
|
|
||||||
import integration.steps.then.`And the response should contain`
|
|
||||||
import integration.steps.then.`And the response should not be null`
|
|
||||||
import integration.steps.then.`Then the response should be`
|
|
||||||
import integration.steps.`when`.`When I send a DELETE request`
|
import integration.steps.`when`.`When I send a DELETE request`
|
||||||
import integration.steps.`when`.`When I send a GET request`
|
import integration.steps.`when`.`When I send a GET request`
|
||||||
import integration.steps.`when`.`When I send a POST request`
|
import integration.steps.`when`.`When I send a POST request`
|
||||||
import integration.steps.then.and
|
|
||||||
import integration.steps.given.`And follow constitution`
|
import integration.steps.given.`And follow constitution`
|
||||||
import integration.steps.given.`Given I have citizen`
|
import integration.steps.given.`Given I have citizen`
|
||||||
import integration.steps.given.`Given I have constitution`
|
import integration.steps.given.`Given I have constitution`
|
||||||
import integration.steps.given.`authenticated as`
|
import integration.steps.given.`authenticated as`
|
||||||
import integration.steps.given.`with no content`
|
import integration.steps.given.`with no content`
|
||||||
|
import integration.steps.then.`And the response should be null`
|
||||||
|
import integration.steps.then.`And the response should contain`
|
||||||
|
import integration.steps.then.`And the response should not be null`
|
||||||
|
import integration.steps.then.`Then the response should be`
|
||||||
|
import integration.steps.then.and
|
||||||
import io.ktor.http.HttpStatusCode.Companion.Created
|
import io.ktor.http.HttpStatusCode.Companion.Created
|
||||||
import io.ktor.http.HttpStatusCode.Companion.NoContent
|
import io.ktor.http.HttpStatusCode.Companion.NoContent
|
||||||
import io.ktor.http.HttpStatusCode.Companion.OK
|
import io.ktor.http.HttpStatusCode.Companion.OK
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
package integration
|
package integration
|
||||||
|
|
||||||
import integration.steps.then.`And the response should not be null`
|
|
||||||
import integration.steps.then.`Then the response should be`
|
|
||||||
import integration.steps.then.`and should contains`
|
|
||||||
import integration.steps.`when`.`When I send a POST request`
|
import integration.steps.`when`.`When I send a POST request`
|
||||||
import integration.steps.`when`.`with body`
|
import integration.steps.`when`.`with body`
|
||||||
import integration.steps.given.`Given I have citizen`
|
import integration.steps.given.`Given I have citizen`
|
||||||
import integration.steps.given.`authenticated as`
|
import integration.steps.given.`authenticated as`
|
||||||
|
import integration.steps.then.`And the response should not be null`
|
||||||
|
import integration.steps.then.`Then the response should be`
|
||||||
|
import integration.steps.then.`and should contains`
|
||||||
import integration.steps.then.and
|
import integration.steps.then.and
|
||||||
import io.ktor.http.HttpStatusCode.Companion.NoContent
|
import io.ktor.http.HttpStatusCode.Companion.NoContent
|
||||||
import io.ktor.http.HttpStatusCode.Companion.OK
|
import io.ktor.http.HttpStatusCode.Companion.OK
|
||||||
@@ -23,15 +23,17 @@ class `Login routes` : BaseTest() {
|
|||||||
withIntegrationApplication {
|
withIntegrationApplication {
|
||||||
`Given I have citizen`("Niels", "Bohr")
|
`Given I have citizen`("Niels", "Bohr")
|
||||||
`When I send a POST request`("/login") {
|
`When I send a POST request`("/login") {
|
||||||
`with body`("""
|
`with body`(
|
||||||
{
|
"""
|
||||||
"username": "niels-bohr",
|
{
|
||||||
"password": "azerty"
|
"username": "niels-bohr",
|
||||||
}
|
"password": "azerty"
|
||||||
""")
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
} `Then the response should be` OK and {
|
} `Then the response should be` OK and {
|
||||||
`And the response should not be null`() `and should contains` "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9."
|
`And the response should not be null`() `and should contains` "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9."
|
||||||
//TODO valid requestBody
|
// TODO valid requestBody
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -42,12 +44,14 @@ class `Login routes` : BaseTest() {
|
|||||||
`Given I have citizen`("Leonhard", "Euler", "fabrice.lecomte.be@gmail.com", id = "c606110c-ff0e-4d09-a79e-74632d7bf7bd")
|
`Given I have citizen`("Leonhard", "Euler", "fabrice.lecomte.be@gmail.com", id = "c606110c-ff0e-4d09-a79e-74632d7bf7bd")
|
||||||
`When I send a POST request`("/auth/passwordless") {
|
`When I send a POST request`("/auth/passwordless") {
|
||||||
`authenticated as`("Leonhard", "Euler")
|
`authenticated as`("Leonhard", "Euler")
|
||||||
`with body`("""
|
`with body`(
|
||||||
{
|
"""
|
||||||
"url": "https://dc-project.fr/password/reset",
|
{
|
||||||
"email": "fabrice.lecomte.be@gmail.com"
|
"url": "https://dc-project.fr/password/reset",
|
||||||
}
|
"email": "fabrice.lecomte.be@gmail.com"
|
||||||
""")
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
} `Then the response should be` NoContent
|
} `Then the response should be` NoContent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
package integration
|
package integration
|
||||||
|
|
||||||
import fr.dcproject.component.citizen.database.CitizenI.Name
|
import fr.dcproject.component.citizen.database.CitizenI.Name
|
||||||
import integration.steps.then.`And the response should contain list`
|
|
||||||
import integration.steps.then.`And the response should contain`
|
|
||||||
import integration.steps.then.`Then the response should be`
|
|
||||||
import integration.steps.`when`.`When I send a GET request`
|
import integration.steps.`when`.`When I send a GET request`
|
||||||
import integration.steps.`when`.`When I send a PUT request`
|
import integration.steps.`when`.`When I send a PUT request`
|
||||||
import integration.steps.`when`.`with body`
|
import integration.steps.`when`.`with body`
|
||||||
import integration.steps.then.and
|
|
||||||
import integration.steps.given.`Given I have an opinion choice`
|
import integration.steps.given.`Given I have an opinion choice`
|
||||||
import integration.steps.given.`Given I have article`
|
import integration.steps.given.`Given I have article`
|
||||||
import integration.steps.given.`Given I have citizen`
|
import integration.steps.given.`Given I have citizen`
|
||||||
import integration.steps.given.`Given I have opinion on article`
|
import integration.steps.given.`Given I have opinion on article`
|
||||||
import integration.steps.given.`authenticated as`
|
import integration.steps.given.`authenticated as`
|
||||||
|
import integration.steps.then.`And the response should contain list`
|
||||||
|
import integration.steps.then.`And the response should contain`
|
||||||
|
import integration.steps.then.`Then the response should be`
|
||||||
|
import integration.steps.then.and
|
||||||
import io.ktor.http.HttpStatusCode.Companion.Created
|
import io.ktor.http.HttpStatusCode.Companion.Created
|
||||||
import io.ktor.http.HttpStatusCode.Companion.OK
|
import io.ktor.http.HttpStatusCode.Companion.OK
|
||||||
import org.junit.jupiter.api.Tag
|
import org.junit.jupiter.api.Tag
|
||||||
@@ -21,7 +21,7 @@ import org.junit.jupiter.api.Test
|
|||||||
import org.junit.jupiter.api.TestInstance
|
import org.junit.jupiter.api.TestInstance
|
||||||
|
|
||||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
@Tags(Tag("integration"), Tag("article"), Tag("opinion"))
|
@Tags(Tag("integration"), Tag("opinion"))
|
||||||
class `Opinion routes` : BaseTest() {
|
class `Opinion routes` : BaseTest() {
|
||||||
@Test
|
@Test
|
||||||
fun `I can get all opinion choices`() {
|
fun `I can get all opinion choices`() {
|
||||||
@@ -48,6 +48,7 @@ class `Opinion routes` : BaseTest() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@Tag("article")
|
||||||
fun `I can create opinion on article`() {
|
fun `I can create opinion on article`() {
|
||||||
withIntegrationApplication {
|
withIntegrationApplication {
|
||||||
`Given I have citizen`("Isaac", "Newton", id = "2f414045-95d9-42ca-a3a9-8cdde52ad253")
|
`Given I have citizen`("Isaac", "Newton", id = "2f414045-95d9-42ca-a3a9-8cdde52ad253")
|
||||||
@@ -55,13 +56,15 @@ class `Opinion routes` : BaseTest() {
|
|||||||
`Given I have article`(id = "9226c1a3-8091-c3fa-7d0d-c2e98c9bee7b", createdBy = Name("Isaac", "Newton"))
|
`Given I have article`(id = "9226c1a3-8091-c3fa-7d0d-c2e98c9bee7b", createdBy = Name("Isaac", "Newton"))
|
||||||
`When I send a PUT request`("/articles/9226c1a3-8091-c3fa-7d0d-c2e98c9bee7b/opinions") {
|
`When I send a PUT request`("/articles/9226c1a3-8091-c3fa-7d0d-c2e98c9bee7b/opinions") {
|
||||||
`authenticated as`("Isaac", "Newton")
|
`authenticated as`("Isaac", "Newton")
|
||||||
`with body`("""
|
`with body`(
|
||||||
{
|
"""
|
||||||
"ids": [
|
{
|
||||||
"0f4f1721-3136-44f1-9f31-1459f3317b15"
|
"ids": [
|
||||||
]
|
"0f4f1721-3136-44f1-9f31-1459f3317b15"
|
||||||
}
|
]
|
||||||
""")
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
} `Then the response should be` Created
|
} `Then the response should be` Created
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -87,6 +90,7 @@ class `Opinion routes` : BaseTest() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@Tag("article")
|
||||||
fun `I can receive opinion aggregation with article`() {
|
fun `I can receive opinion aggregation with article`() {
|
||||||
withIntegrationApplication {
|
withIntegrationApplication {
|
||||||
`Given I have an opinion choice`("Opinion6")
|
`Given I have an opinion choice`("Opinion6")
|
||||||
@@ -118,6 +122,7 @@ class `Opinion routes` : BaseTest() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@Tag("article")
|
||||||
fun `I can get all my opinion of one article`() {
|
fun `I can get all my opinion of one article`() {
|
||||||
withIntegrationApplication {
|
withIntegrationApplication {
|
||||||
`Given I have citizen`("Albert", "Einstein", id = "c1542096-3431-432d-8e35-9dc071d4c818")
|
`Given I have citizen`("Albert", "Einstein", id = "c1542096-3431-432d-8e35-9dc071d4c818")
|
||||||
@@ -132,7 +137,7 @@ class `Opinion routes` : BaseTest() {
|
|||||||
`authenticated as`("Albert", "Einstein")
|
`authenticated as`("Albert", "Einstein")
|
||||||
} `Then the response should be` OK and {
|
} `Then the response should be` OK and {
|
||||||
`And the response should contain`("$.result[0].name", "Opinion9")
|
`And the response should contain`("$.result[0].name", "Opinion9")
|
||||||
`And the response should contain list`("$.result[*]", 1, 1)
|
`And the response should contain list`("$.result[*]", 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,17 +22,19 @@ class `Register routes` : BaseTest() {
|
|||||||
fun `I can register`() {
|
fun `I can register`() {
|
||||||
withIntegrationApplication {
|
withIntegrationApplication {
|
||||||
`When I send a POST request`("/register") {
|
`When I send a POST request`("/register") {
|
||||||
`with body`("""
|
`with body`(
|
||||||
{
|
"""
|
||||||
"name": {"firstName":"George", "lastName":"MICHEL"},
|
{
|
||||||
"birthday": "2001-01-01",
|
"name": {"firstName":"George", "lastName":"MICHEL"},
|
||||||
"user":{
|
"birthday": "2001-01-01",
|
||||||
"username": "george-junior",
|
"user":{
|
||||||
"password": "azerty"
|
"username": "george-junior",
|
||||||
},
|
"password": "azerty"
|
||||||
"email": "george-junior@gmail.com"
|
},
|
||||||
}
|
"email": "george-junior@gmail.com"
|
||||||
""")
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
} `Then the response should be` OK and {
|
} `Then the response should be` OK and {
|
||||||
`And the response should not be null`()
|
`And the response should not be null`()
|
||||||
`And the response should contain pattern`("$.token", "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.")
|
`And the response should contain pattern`("$.token", "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.")
|
||||||
@@ -44,16 +46,18 @@ class `Register routes` : BaseTest() {
|
|||||||
fun `I cannot register if no username was sent`() {
|
fun `I cannot register if no username was sent`() {
|
||||||
withIntegrationApplication {
|
withIntegrationApplication {
|
||||||
`When I send a POST request`("/register", Validate.ALL - Validate.REQUEST_BODY) {
|
`When I send a POST request`("/register", Validate.ALL - Validate.REQUEST_BODY) {
|
||||||
`with body`("""
|
`with body`(
|
||||||
{
|
"""
|
||||||
"name": {"firstName":"George2", "lastName":"MICHEL2"},
|
{
|
||||||
"birthday": "2001-01-01",
|
"name": {"firstName":"George2", "lastName":"MICHEL2"},
|
||||||
"user":{
|
"birthday": "2001-01-01",
|
||||||
"password": ""
|
"user":{
|
||||||
},
|
"password": ""
|
||||||
"email": "george-junior@gmail.com"
|
},
|
||||||
}
|
"email": "george-junior@gmail.com"
|
||||||
""")
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
} `Then the response should be` BadRequest and {
|
} `Then the response should be` BadRequest and {
|
||||||
`And the response should be null`()
|
`And the response should be null`()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
package integration
|
package integration
|
||||||
|
|
||||||
import fr.dcproject.component.citizen.database.CitizenI.Name
|
import fr.dcproject.component.citizen.database.CitizenI.Name
|
||||||
import integration.steps.then.`And the response should contain`
|
|
||||||
import integration.steps.then.`Then the response should be`
|
|
||||||
import integration.steps.`when`.`When I send a GET request`
|
import integration.steps.`when`.`When I send a GET request`
|
||||||
import integration.steps.`when`.`When I send a PUT request`
|
import integration.steps.`when`.`When I send a PUT request`
|
||||||
import integration.steps.`when`.`with body`
|
import integration.steps.`when`.`with body`
|
||||||
import integration.steps.then.and
|
|
||||||
import integration.steps.given.`Given I have article`
|
import integration.steps.given.`Given I have article`
|
||||||
import integration.steps.given.`Given I have citizen`
|
import integration.steps.given.`Given I have citizen`
|
||||||
import integration.steps.given.`Given I have comment on article`
|
import integration.steps.given.`Given I have comment on article`
|
||||||
@@ -14,6 +11,9 @@ import integration.steps.given.`Given I have constitution`
|
|||||||
import integration.steps.given.`Given I have vote +1 on article`
|
import integration.steps.given.`Given I have vote +1 on article`
|
||||||
import integration.steps.given.`Given I have vote -1 on article`
|
import integration.steps.given.`Given I have vote -1 on article`
|
||||||
import integration.steps.given.`authenticated as`
|
import integration.steps.given.`authenticated as`
|
||||||
|
import integration.steps.then.`And the response should contain`
|
||||||
|
import integration.steps.then.`Then the response should be`
|
||||||
|
import integration.steps.then.and
|
||||||
import io.ktor.http.HttpStatusCode.Companion.Created
|
import io.ktor.http.HttpStatusCode.Companion.Created
|
||||||
import io.ktor.http.HttpStatusCode.Companion.OK
|
import io.ktor.http.HttpStatusCode.Companion.OK
|
||||||
import org.junit.jupiter.api.Tag
|
import org.junit.jupiter.api.Tag
|
||||||
@@ -31,11 +31,13 @@ class `Vote routes` : BaseTest() {
|
|||||||
`Given I have article`(id = "835c5101-ca39-4038-a4e6-da6ee62ca6d5")
|
`Given I have article`(id = "835c5101-ca39-4038-a4e6-da6ee62ca6d5")
|
||||||
`When I send a PUT request`("/articles/835c5101-ca39-4038-a4e6-da6ee62ca6d5/vote") {
|
`When I send a PUT request`("/articles/835c5101-ca39-4038-a4e6-da6ee62ca6d5/vote") {
|
||||||
`authenticated as`("Thalès", "Milet")
|
`authenticated as`("Thalès", "Milet")
|
||||||
`with body`("""
|
`with body`(
|
||||||
{
|
"""
|
||||||
"note": 1
|
{
|
||||||
}
|
"note": 1
|
||||||
""")
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
} `Then the response should be` Created
|
} `Then the response should be` Created
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -47,11 +49,13 @@ class `Vote routes` : BaseTest() {
|
|||||||
`Given I have constitution`(id = "76e79c89-efc1-492d-9e8f-dc9717363a11")
|
`Given I have constitution`(id = "76e79c89-efc1-492d-9e8f-dc9717363a11")
|
||||||
`When I send a PUT request`("/constitutions/76e79c89-efc1-492d-9e8f-dc9717363a11/vote") {
|
`When I send a PUT request`("/constitutions/76e79c89-efc1-492d-9e8f-dc9717363a11/vote") {
|
||||||
`authenticated as`("Gregor", "Mendel")
|
`authenticated as`("Gregor", "Mendel")
|
||||||
`with body`("""
|
`with body`(
|
||||||
{
|
"""
|
||||||
"note": 1
|
{
|
||||||
}
|
"note": 1
|
||||||
""")
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
} `Then the response should be` Created
|
} `Then the response should be` Created
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -102,11 +106,13 @@ class `Vote routes` : BaseTest() {
|
|||||||
)
|
)
|
||||||
`When I send a PUT request`("/comments/e793eccc-456b-4450-a292-46d592229b74/vote") {
|
`When I send a PUT request`("/comments/e793eccc-456b-4450-a292-46d592229b74/vote") {
|
||||||
`authenticated as`("Antoine", "Lavoisier")
|
`authenticated as`("Antoine", "Lavoisier")
|
||||||
`with body`("""
|
`with body`(
|
||||||
{
|
"""
|
||||||
"note": -1
|
{
|
||||||
}
|
"note": -1
|
||||||
""")
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
} `Then the response should be` Created and {
|
} `Then the response should be` Created and {
|
||||||
`And the response should contain`("$.down", 1)
|
`And the response should contain`("$.down", 1)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,22 @@
|
|||||||
package integration
|
package integration
|
||||||
|
|
||||||
import fr.dcproject.component.citizen.database.CitizenI.Name
|
import fr.dcproject.component.citizen.database.CitizenI.Name
|
||||||
import integration.steps.then.`And the response should be null`
|
|
||||||
import integration.steps.then.`And the response should contain list`
|
|
||||||
import integration.steps.then.`And the response should contain`
|
|
||||||
import integration.steps.then.`Then the response should be`
|
|
||||||
import integration.steps.`when`.`When I send a DELETE request`
|
import integration.steps.`when`.`When I send a DELETE request`
|
||||||
import integration.steps.`when`.`When I send a GET request`
|
import integration.steps.`when`.`When I send a GET request`
|
||||||
import integration.steps.`when`.`When I send a POST request`
|
import integration.steps.`when`.`When I send a POST request`
|
||||||
import integration.steps.`when`.`When I send a PUT request`
|
import integration.steps.`when`.`When I send a PUT request`
|
||||||
import integration.steps.`when`.`with body`
|
import integration.steps.`when`.`with body`
|
||||||
import integration.steps.then.and
|
|
||||||
import integration.steps.given.`Given I have citizen`
|
import integration.steps.given.`Given I have citizen`
|
||||||
import integration.steps.given.`Given I have workgroup`
|
import integration.steps.given.`Given I have workgroup`
|
||||||
import integration.steps.given.`With members`
|
import integration.steps.given.`With members`
|
||||||
import integration.steps.given.`authenticated as`
|
import integration.steps.given.`authenticated as`
|
||||||
import integration.steps.given.`with no content`
|
import integration.steps.given.`with no content`
|
||||||
import integration.steps.then.`And have property`
|
import integration.steps.then.`And have property`
|
||||||
|
import integration.steps.then.`And the response should be null`
|
||||||
|
import integration.steps.then.`And the response should contain list`
|
||||||
|
import integration.steps.then.`And the response should contain`
|
||||||
|
import integration.steps.then.`Then the response should be`
|
||||||
|
import integration.steps.then.and
|
||||||
import io.ktor.http.HttpStatusCode.Companion.Created
|
import io.ktor.http.HttpStatusCode.Companion.Created
|
||||||
import io.ktor.http.HttpStatusCode.Companion.NoContent
|
import io.ktor.http.HttpStatusCode.Companion.NoContent
|
||||||
import io.ktor.http.HttpStatusCode.Companion.NotFound
|
import io.ktor.http.HttpStatusCode.Companion.NotFound
|
||||||
@@ -68,14 +68,16 @@ class `Workgroup routes` : BaseTest() {
|
|||||||
`Given I have citizen`("Werner", "Heisenberg")
|
`Given I have citizen`("Werner", "Heisenberg")
|
||||||
`When I send a POST request`("/workgroups") {
|
`When I send a POST request`("/workgroups") {
|
||||||
`authenticated as`("Werner", "Heisenberg")
|
`authenticated as`("Werner", "Heisenberg")
|
||||||
`with body`("""
|
`with body`(
|
||||||
{
|
"""
|
||||||
"id":"f496d86d-6654-4068-91ff-90e1dbcc5f38",
|
{
|
||||||
"name":"Les Bouffons",
|
"id":"f496d86d-6654-4068-91ff-90e1dbcc5f38",
|
||||||
"description":"La vie est belle",
|
"name":"Les Bouffons",
|
||||||
"anonymous":false
|
"description":"La vie est belle",
|
||||||
}
|
"anonymous":false
|
||||||
""")
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
} `Then the response should be` Created and {
|
} `Then the response should be` Created and {
|
||||||
`And the response should contain`("$.id", "f496d86d-6654-4068-91ff-90e1dbcc5f38")
|
`And the response should contain`("$.id", "f496d86d-6654-4068-91ff-90e1dbcc5f38")
|
||||||
`And the response should contain`("$.name", "Les Bouffons")
|
`And the response should contain`("$.name", "Les Bouffons")
|
||||||
@@ -103,19 +105,21 @@ class `Workgroup routes` : BaseTest() {
|
|||||||
}
|
}
|
||||||
`When I send a PUT request`("/workgroups/aa875a24-0050-4252-9130-d37391714e26") {
|
`When I send a PUT request`("/workgroups/aa875a24-0050-4252-9130-d37391714e26") {
|
||||||
`authenticated as`("John", "Wheeler")
|
`authenticated as`("John", "Wheeler")
|
||||||
`with body`("""
|
`with body`(
|
||||||
{
|
"""
|
||||||
"name":"La ratatouille",
|
{
|
||||||
"description":"Une petite souris"
|
"name":"La ratatouille",
|
||||||
}
|
"description":"Une petite souris"
|
||||||
""")
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
} `Then the response should be` OK and {
|
} `Then the response should be` OK and {
|
||||||
`And the response should contain`("$.id", "aa875a24-0050-4252-9130-d37391714e26")
|
`And the response should contain`("$.id", "aa875a24-0050-4252-9130-d37391714e26")
|
||||||
`And the response should contain`("$.name", "La ratatouille")
|
`And the response should contain`("$.name", "La ratatouille")
|
||||||
`And the response should contain`("$.description", "Une petite souris")
|
`And the response should contain`("$.description", "Une petite souris")
|
||||||
|
|
||||||
`And have property`("$.members")
|
`And have property`("$.members")
|
||||||
`And the response should contain list`("$.members", 3, 3)
|
`And the response should contain list`("$.members", 3)
|
||||||
`And the response should contain`("$.members.[1]citizen.id", "94f92424-c257-4582-907c-98564a8c4ac9")
|
`And the response should contain`("$.members.[1]citizen.id", "94f92424-c257-4582-907c-98564a8c4ac9")
|
||||||
`And the response should contain`("$.members.[2]citizen.id", "87909ba3-2069-431c-9924-219fd8411cf2")
|
`And the response should contain`("$.members.[2]citizen.id", "87909ba3-2069-431c-9924-219fd8411cf2")
|
||||||
}
|
}
|
||||||
@@ -171,18 +175,20 @@ class `Workgroup routes` : BaseTest() {
|
|||||||
`Given I have workgroup`("b0ea1922-3bc6-44e2-aa7c-40158998cfbb", createdBy = Name("Blaise", "Pascal"))
|
`Given I have workgroup`("b0ea1922-3bc6-44e2-aa7c-40158998cfbb", createdBy = Name("Blaise", "Pascal"))
|
||||||
`When I send a POST request`("/workgroups/b0ea1922-3bc6-44e2-aa7c-40158998cfbb/members") {
|
`When I send a POST request`("/workgroups/b0ea1922-3bc6-44e2-aa7c-40158998cfbb/members") {
|
||||||
`authenticated as`("Blaise", "Pascal")
|
`authenticated as`("Blaise", "Pascal")
|
||||||
`with body`("""
|
`with body`(
|
||||||
[
|
"""
|
||||||
{
|
[
|
||||||
"citizen": {"id":"6d883fe7-5fc0-4a50-8858-72230673eba4"},
|
{
|
||||||
"roles": ["MASTER"]
|
"citizen": {"id":"6d883fe7-5fc0-4a50-8858-72230673eba4"},
|
||||||
},
|
"roles": ["MASTER"]
|
||||||
{
|
},
|
||||||
"citizen": {"id":"b5bac515-45d4-4aeb-9b6d-2627a0bbc419"},
|
{
|
||||||
"roles": ["MASTER"]
|
"citizen": {"id":"b5bac515-45d4-4aeb-9b6d-2627a0bbc419"},
|
||||||
}
|
"roles": ["MASTER"]
|
||||||
]
|
}
|
||||||
""")
|
]
|
||||||
|
"""
|
||||||
|
)
|
||||||
} `Then the response should be` Created
|
} `Then the response should be` Created
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -209,7 +215,7 @@ class `Workgroup routes` : BaseTest() {
|
|||||||
]
|
]
|
||||||
"""
|
"""
|
||||||
} `Then the response should be` OK and {
|
} `Then the response should be` OK and {
|
||||||
`And the response should contain list`("$", 2, 2)
|
`And the response should contain list`("$", 2)
|
||||||
`And the response should contain`("$.[0]citizen.id", "94f92424-c257-4582-907c-98564a8c4ac9")
|
`And the response should contain`("$.[0]citizen.id", "94f92424-c257-4582-907c-98564a8c4ac9")
|
||||||
`And the response should contain`("$.[1]citizen.id", "1baf48bb-02bc-4d8f-ac86-33335354f5e7")
|
`And the response should contain`("$.[1]citizen.id", "1baf48bb-02bc-4d8f-ac86-33335354f5e7")
|
||||||
}
|
}
|
||||||
@@ -231,20 +237,22 @@ class `Workgroup routes` : BaseTest() {
|
|||||||
}
|
}
|
||||||
`When I send a PUT request`("/workgroups/784fe6bc-7635-4ae2-b080-3a4743b998bf/members") {
|
`When I send a PUT request`("/workgroups/784fe6bc-7635-4ae2-b080-3a4743b998bf/members") {
|
||||||
`authenticated as`("Leon", "Foucault")
|
`authenticated as`("Leon", "Foucault")
|
||||||
`with body`("""
|
`with body`(
|
||||||
[
|
"""
|
||||||
{
|
[
|
||||||
"citizen": {"id":"be3b0926-8628-4426-804a-75188a6eb315"},
|
{
|
||||||
"roles": ["MASTER"]
|
"citizen": {"id":"be3b0926-8628-4426-804a-75188a6eb315"},
|
||||||
},
|
"roles": ["MASTER"]
|
||||||
{
|
},
|
||||||
"citizen": {"id":"b49e20c1-8393-45d6-a6a0-3fa5c71cbdc1"},
|
{
|
||||||
"roles": ["MASTER"]
|
"citizen": {"id":"b49e20c1-8393-45d6-a6a0-3fa5c71cbdc1"},
|
||||||
}
|
"roles": ["MASTER"]
|
||||||
]
|
}
|
||||||
""")
|
]
|
||||||
|
"""
|
||||||
|
)
|
||||||
} `Then the response should be` OK and {
|
} `Then the response should be` OK and {
|
||||||
`And the response should contain list`("$", 2, 2)
|
`And the response should contain list`("$", 2)
|
||||||
`And the response should contain`("$.[0]citizen.id", "be3b0926-8628-4426-804a-75188a6eb315")
|
`And the response should contain`("$.[0]citizen.id", "be3b0926-8628-4426-804a-75188a6eb315")
|
||||||
`And the response should contain`("$.[1]citizen.id", "b49e20c1-8393-45d6-a6a0-3fa5c71cbdc1")
|
`And the response should contain`("$.[1]citizen.id", "b49e20c1-8393-45d6-a6a0-3fa5c71cbdc1")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,10 +15,11 @@ fun TestApplicationRequest.`authenticated as`(
|
|||||||
val username = "$firstName-$lastName".toLowerCase()
|
val username = "$firstName-$lastName".toLowerCase()
|
||||||
val repo: CitizenRepository by lazy<CitizenRepository> { GlobalContext.get().koin.get() }
|
val repo: CitizenRepository by lazy<CitizenRepository> { GlobalContext.get().koin.get() }
|
||||||
val citizen = repo.findByUsername(username) ?: error("Citizen not exist with username $username")
|
val citizen = repo.findByUsername(username) ?: error("Citizen not exist with username $username")
|
||||||
|
val algorithm = GlobalContext.get().koin.get<JwtConfig>().algorithm
|
||||||
val jwtAsString: String = JWT.create()
|
val jwtAsString: String = JWT.create()
|
||||||
.withIssuer("dc-project.fr")
|
.withIssuer("dc-project.fr")
|
||||||
.withClaim("id", citizen.user.id.toString())
|
.withClaim("id", citizen.user.id.toString())
|
||||||
.sign(JwtConfig.algorithm)
|
.sign(algorithm)
|
||||||
|
|
||||||
addHeader(HttpHeaders.Authorization, "Bearer $jwtAsString")
|
addHeader(HttpHeaders.Authorization, "Bearer $jwtAsString")
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package integration.steps.then
|
package integration.steps.then
|
||||||
|
|
||||||
import assert.assertGreaterThan
|
import assert.assertContain
|
||||||
import assert.assertLessThan
|
|
||||||
import com.jayway.jsonpath.JsonPath
|
import com.jayway.jsonpath.JsonPath
|
||||||
import com.jayway.jsonpath.PathNotFoundException
|
import com.jayway.jsonpath.PathNotFoundException
|
||||||
import io.ktor.http.HttpStatusCode
|
import io.ktor.http.HttpStatusCode
|
||||||
@@ -85,15 +84,13 @@ fun TestApplicationResponse.`And the response should contain pattern`(path: Stri
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun TestApplicationResponse.`And the response should contain list`(path: String, min: Int? = null, max: Int? = null) {
|
fun TestApplicationResponse.`And the response should contain list`(path: String, exactCount: Int) =
|
||||||
|
`And the response should contain list`(path, IntRange(exactCount, exactCount))
|
||||||
|
|
||||||
|
fun TestApplicationResponse.`And the response should contain list`(path: String, range: IntRange) {
|
||||||
JsonPath.read<JSONArray?>(content, path).also {
|
JsonPath.read<JSONArray?>(content, path).also {
|
||||||
assertNotNull(it)
|
assertNotNull(it)
|
||||||
if (min != null) {
|
range assertContain it.size
|
||||||
it.size assertGreaterThan min
|
|
||||||
}
|
|
||||||
if (max != null) {
|
|
||||||
it.size assertLessThan max
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,12 @@ package integration.steps.then
|
|||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode
|
import com.fasterxml.jackson.databind.JsonNode
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
|
import com.fasterxml.jackson.databind.node.BooleanNode
|
||||||
|
import com.fasterxml.jackson.databind.node.IntNode
|
||||||
import com.fasterxml.jackson.databind.node.TextNode
|
import com.fasterxml.jackson.databind.node.TextNode
|
||||||
import fr.dcproject.common.utils.getResource
|
import fr.dcproject.common.utils.getResource
|
||||||
|
import fr.dcproject.common.utils.isBool
|
||||||
|
import fr.dcproject.common.utils.isInt
|
||||||
import io.ktor.http.ContentType
|
import io.ktor.http.ContentType
|
||||||
import io.ktor.http.Url
|
import io.ktor.http.Url
|
||||||
import io.ktor.request.contentType
|
import io.ktor.request.contentType
|
||||||
@@ -33,7 +37,7 @@ fun Schema.validate(api: OpenApi3, toValidate: JsonNode) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun TestApplicationResponse.operation(route: String? = null, callback: Operation.(OpenApi3, String) -> Unit): Operation {
|
fun TestApplicationResponse.operation(route: String? = null, callback: Operation.(OpenApi3, String) -> Unit): Operation {
|
||||||
val filePath = "/openapi2.yaml"
|
val filePath = "/openapi.yaml"
|
||||||
return OpenApi3Parser().parse(File(filePath.getResource().toURI()), true).let { api: OpenApi3 ->
|
return OpenApi3Parser().parse(File(filePath.getResource().toURI()), true).let { api: OpenApi3 ->
|
||||||
val httpMethod = call.request.httpMethod
|
val httpMethod = call.request.httpMethod
|
||||||
val uri = route ?: "/" + Url(call.request.uri).encodedPath
|
val uri = route ?: "/" + Url(call.request.uri).encodedPath
|
||||||
@@ -55,16 +59,17 @@ fun TestApplicationResponse.`And the schema response body must be valid`(content
|
|||||||
/* Validate Response */
|
/* Validate Response */
|
||||||
this.apply {
|
this.apply {
|
||||||
val status = call.response.status()
|
val status = call.response.status()
|
||||||
|
val httpMethod = call.request.httpMethod.value.toUpperCase()
|
||||||
val responseContent: JsonNode = if (content != null)
|
val responseContent: JsonNode = if (content != null)
|
||||||
ObjectMapper().readTree(content)
|
ObjectMapper().readTree(content)
|
||||||
else TextNode("")
|
else TextNode("")
|
||||||
|
|
||||||
val response = getResponse(status?.value?.toString() ?: error("HttpStatus not found")) ?: fail("""No Status "${status.value}" found for "$this $uri".""")
|
val response = getResponse(status?.value?.toString() ?: error("HttpStatus not found")) ?: fail("""No Status "${status.value}" found for "$httpMethod $uri".""")
|
||||||
val schema = response.getContentMediaType(contentType.toString())?.schema
|
val schema = response.getContentMediaType(contentType.toString())?.schema
|
||||||
|
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
schema?.validate(api, responseContent)
|
schema?.validate(api, responseContent)
|
||||||
?: fail("""No Status "${status.value}" found with media type "$contentType" for "$this $uri".""")
|
?: fail("""No Status "${status.value}" found with media type "$contentType" for "$httpMethod $uri".""")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -74,13 +79,18 @@ fun TestApplicationResponse.`And the schema parameters must be valid`() {
|
|||||||
operation { api, uri ->
|
operation { api, uri ->
|
||||||
/* Validate Request URL */
|
/* Validate Request URL */
|
||||||
this.apply {
|
this.apply {
|
||||||
|
val methodName = call.request.httpMethod.value.toUpperCase()
|
||||||
Url(call.request.uri).parameters.forEach { parameter: String, values: List<String> ->
|
Url(call.request.uri).parameters.forEach { parameter: String, values: List<String> ->
|
||||||
val schema = getParametersIn(api.context, "query")
|
val schema = getParametersIn(api.context, "query")
|
||||||
?.firstOrNull { it.name == parameter }?.schema
|
?.firstOrNull { it.name == parameter }?.schema
|
||||||
?: error("""No parameter found ($parameter) for "$this $uri".""")
|
?: error("""No parameter found ($parameter) for "$methodName $uri".""")
|
||||||
|
|
||||||
if (schema.type == "array") {
|
if (schema.type == "array") {
|
||||||
schema.validate(api, ObjectMapper().valueToTree(values))
|
schema.validate(api, ObjectMapper().valueToTree(values))
|
||||||
|
} else if (schema.type == "integer" && values.first().isInt()) {
|
||||||
|
schema.validate(api, IntNode(values.first().toInt()))
|
||||||
|
} else if (schema.type == "boolean" && values.first().isBool()) {
|
||||||
|
schema.validate(api, BooleanNode.valueOf(values.first().toBoolean()))
|
||||||
} else {
|
} else {
|
||||||
schema.validate(api, TextNode(values.first()))
|
schema.validate(api, TextNode(values.first()))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ import fr.dcproject.component.article.database.ArticleRepository as ArticleRepo
|
|||||||
|
|
||||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
@Execution(CONCURRENT)
|
@Execution(CONCURRENT)
|
||||||
@Tags(Tag("security"), Tag("unit"))
|
@Tags(Tag("security"), Tag("unit"), Tag("article"))
|
||||||
internal class `Article Access Control` {
|
internal class `Article Access Control` {
|
||||||
private val tesla = CitizenCreator(
|
private val tesla = CitizenCreator(
|
||||||
id = UUID.fromString("e6efc288-4283-4729-a268-6debb18de1a0"),
|
id = UUID.fromString("e6efc288-4283-4729-a268-6debb18de1a0"),
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT
|
|||||||
|
|
||||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
@Execution(CONCURRENT)
|
@Execution(CONCURRENT)
|
||||||
@Tags(Tag("security"), Tag("unit"))
|
@Tags(Tag("security"), Tag("unit"), Tag("citizen"))
|
||||||
internal class `Citizen Access Control` {
|
internal class `Citizen Access Control` {
|
||||||
private val tesla = CitizenCart(
|
private val tesla = CitizenCart(
|
||||||
user = User(
|
user = User(
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import java.util.UUID
|
|||||||
|
|
||||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
@Execution(CONCURRENT)
|
@Execution(CONCURRENT)
|
||||||
@Tags(Tag("security"), Tag("unit"))
|
@Tags(Tag("security"), Tag("unit"), Tag("comment"))
|
||||||
internal class `Comment Access Control` {
|
internal class `Comment Access Control` {
|
||||||
private val tesla = Citizen(
|
private val tesla = Citizen(
|
||||||
user = User(
|
user = User(
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import java.util.UUID
|
|||||||
|
|
||||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
@Execution(CONCURRENT)
|
@Execution(CONCURRENT)
|
||||||
@Tags(Tag("security"), Tag("unit"))
|
@Tags(Tag("security"), Tag("unit"), Tag("follow"))
|
||||||
internal class `Follow Access Control` {
|
internal class `Follow Access Control` {
|
||||||
private val tesla = CitizenCreator(
|
private val tesla = CitizenCreator(
|
||||||
user = UserCreator(
|
user = UserCreator(
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import java.util.UUID
|
|||||||
|
|
||||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
@Execution(CONCURRENT)
|
@Execution(CONCURRENT)
|
||||||
@Tags(Tag("security"), Tag("unit"))
|
@Tags(Tag("security"), Tag("unit"), Tag("opinion"))
|
||||||
internal class `Opinion Access Control` {
|
internal class `Opinion Access Control` {
|
||||||
private val tesla = CitizenCreator(
|
private val tesla = CitizenCreator(
|
||||||
user = UserCreator(
|
user = UserCreator(
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import java.util.UUID
|
|||||||
|
|
||||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
@Execution(CONCURRENT)
|
@Execution(CONCURRENT)
|
||||||
@Tags(Tag("security"), Tag("unit"))
|
@Tags(Tag("security"), Tag("unit"), Tag("opinion"))
|
||||||
internal class `OpinionChoice Access Control` {
|
internal class `OpinionChoice Access Control` {
|
||||||
private val tesla = CitizenRef(
|
private val tesla = CitizenRef(
|
||||||
id = UUID.fromString("e6efc288-4283-4729-a268-6debb18de1a0"),
|
id = UUID.fromString("e6efc288-4283-4729-a268-6debb18de1a0"),
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import fr.dcproject.component.citizen.database.CitizenCreator
|
|||||||
import fr.dcproject.component.citizen.database.CitizenI
|
import fr.dcproject.component.citizen.database.CitizenI
|
||||||
import fr.dcproject.component.vote.VoteAccessControl
|
import fr.dcproject.component.vote.VoteAccessControl
|
||||||
import fr.dcproject.component.vote.database.VoteForUpdate
|
import fr.dcproject.component.vote.database.VoteForUpdate
|
||||||
|
import fr.dcproject.component.vote.database.VoteForView
|
||||||
import org.amshove.kluent.`should be`
|
import org.amshove.kluent.`should be`
|
||||||
import org.joda.time.DateTime
|
import org.joda.time.DateTime
|
||||||
import org.junit.jupiter.api.Tag
|
import org.junit.jupiter.api.Tag
|
||||||
@@ -20,11 +21,10 @@ import org.junit.jupiter.api.TestInstance
|
|||||||
import org.junit.jupiter.api.parallel.Execution
|
import org.junit.jupiter.api.parallel.Execution
|
||||||
import org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT
|
import org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import fr.dcproject.component.vote.database.VoteForView
|
|
||||||
|
|
||||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
@Execution(CONCURRENT)
|
@Execution(CONCURRENT)
|
||||||
@Tags(Tag("security"), Tag("unit"))
|
@Tags(Tag("security"), Tag("unit"), Tag("vote"))
|
||||||
internal class `Vote Access Control` {
|
internal class `Vote Access Control` {
|
||||||
private val tesla = Citizen(
|
private val tesla = Citizen(
|
||||||
id = UUID.fromString("a1e35c99-9d33-4fb4-9201-58d7071243bb"),
|
id = UUID.fromString("a1e35c99-9d33-4fb4-9201-58d7071243bb"),
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import fr.dcproject.component.workgroup.database.WorkgroupForView as WorkgroupEn
|
|||||||
|
|
||||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
@Execution(CONCURRENT)
|
@Execution(CONCURRENT)
|
||||||
@Tags(Tag("security"), Tag("unit"))
|
@Tags(Tag("security"), Tag("unit"), Tag("workgroup"))
|
||||||
internal class `Workgroup Access Control` {
|
internal class `Workgroup Access Control` {
|
||||||
private val tesla = CitizenCreator(
|
private val tesla = CitizenCreator(
|
||||||
user = UserCreator(
|
user = UserCreator(
|
||||||
|
|||||||
@@ -37,3 +37,9 @@ mail {
|
|||||||
key = "abcd"
|
key = "abcd"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
jwt {
|
||||||
|
secret = "zAP5MBA4B4Ijz0MZaS48"
|
||||||
|
issuer = "dc-project.fr"
|
||||||
|
validity = 36000000
|
||||||
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user