Nantes Université

Skip to content
Extraits de code Groupes Projets
Non vérifiée Valider 21745885 rédigé par Thibault Duperron's avatar Thibault Duperron Validation de GitHub
Parcourir les fichiers

init (#2)

* init

* tmp version

* clean
parent 056c0019
Aucune branche associée trouvée
Aucune étiquette associée trouvée
Aucune requête de fusion associée trouvée
Affichage de
avec 505 ajouts et 11 suppressions
......@@ -2,6 +2,9 @@
HELP.md
target/
**/target/
data/
tmp/
**/data/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
......
== TD2
== Exo 33
Ajouter dans le fichier src/main/resources/application.yml la configuration suivante
[source,yaml]
----
spring:
datasource:
url: jdbc:h2:mem:testdb
driverClassName: org.h2.Driver
username: sa
password: password
h2:
console:
enabled: true
----
La base de données H2 est une base de données en mémoire.
Pour y accéder, il faut se rendre sur l'url http://localhost:8080/h2-console
A chaque démarrage de l'application, la base de données est réinitialisée.
Pour le constater vous pouvez lancer la requête suivante avant de relancer le service:
[source,sql]
----
CREATE TABLE TEST(ID INT PRIMARY KEY, NAME VARCHAR(255));
----
== Exo 34
Modifier le fichier src/main/resources/application.yml avec pour datasource.url jdbc:h2:file:./data/testdb
Les données sont stockées dans un fichier data/testdb.mv.db à la racine du projet.
Entre deux démarrages de l'application, les données sont conservées.
== Exo 35
Créer une Entity (au sens JPA) HumanEntity qui correspond aux attributs de HumanDto
Créer un repository HumanRepository qui étend JpaRepository
A l'aide de se repository, dans DatabaseProxy, implémentez les fonctions suivantes:
- saveHuman
- findHumanById
- findAllHumans
Le test `exo 35` doit passer.
== Exo 36
Modifier HumanDto pour lui ajouter un attribut contact
[source,kotlin]
----
data class HumanDto(val humanId: Int?, val name: String, val contact: ContactDto)
----
Créer une Entity (au sens JPA) ContactEntity qui correspond aux attributs de ContactDto.
A l'aide d'un @OneToOne, lier HumanEntity et ContactEntity de manière uni-directionnel
(ie on peut faire human.contact.email, mais pas contact.human.name)
Le test `exo 36` doit passer.
== Exo 37
Modifier le OneToMany pour qu'il soit bi-directionnel
Le test `exo 37` doit passer.
== Exo 38
Modifier HumanDto pour lui ajouter un attribut pets
[source,kotlin]
----
data class HumanDto(val humanId: Int?, val name: String, val contact: ContactDto, val pets: List<PetDto>)
----
Créer une Entity (au sens JPA) ContactEntity qui correspond aux attributs de PetDto.
A l'aide d'un @OneToMany, lier HumanEntity et ContactEntity de manière uni-directionnel
Le test `exo 38` doit passer.
== Exo 39
Modifier le OneToMany pour qu'il soit bi-directionnel
Le test `exo 38` doit passer.
== Exo 40
Ajouter un nouveau endpoint DELETE "/api/v1/humans/{humanId}/pets/{petId}" et les services requis.
Ce endpoint permet de supprimer un pet.
Le test `exo 40` doit passer.
== Exo Bonus
Créer une configuration application-postgres.yml où la base h2 est remplacé par par une base postgresql
Pour lancer une base postgres sur le port 5432, vous pouvez utiliser la commande suivante:
`podman run --name postgres -e POSTGRES_USER=username -e POSTGRES_PASSWORD=password -p 5432:5432 -v /tmp/data -d postgres`
== Exo 41
Ajouter spring securité au projet.
Configurer le SecurityFilterChain pour que toutes les requêtes GET passent sans authentification, mais que les autres requêtes nécessitent une authentification.
Le test `exo 41` doit passer.
== Exo 42
Appeler le endpoint DELETE "/api/v1/humans/{humanId}/pets/{petId}" nécessite d'avoir le rôle admin.
Modifier la configuration pour que le test `exo 40` ne passe plus à cause d'une erreur 403, puis corriger le test pour ne plus avoir une 403.
== Exo 43
Ajouter un InMemoryUserDetailsManager avec un utilisateur user classique et un utilisateur admin.
Une fois l'application lancé, appeler les endpoints (curl, bruno... ) avec le basic auth qui correspond à ces utilisateurs.
== Exo 44
Modifier HumanDto pour lui ajouter un attribut creatorLogin
[source,kotlin]
----
data class HumanDto(val humanId: Int?, val name: String, val contact: ContactDto, val pets: List<PetDto>, val creatorLogin: String?)
----
Ce Login doit être celui de l'utilisateur authentifié lors de la création de la donnée.
Le test `exo 44` doit passer une fois la linge MockUser activé.
== Exo Bonus
Remplacer le InMemoryUserDetailsManager par un JdbcUserDetailsManager.
\ No newline at end of file
......@@ -10,7 +10,7 @@ version = "0.0.1-SNAPSHOT"
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
languageVersion = JavaLanguageVersion.of(21)
}
}
......
......@@ -10,7 +10,7 @@ version = "0.0.1-SNAPSHOT"
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
languageVersion = JavaLanguageVersion.of(21)
}
}
......
......@@ -11,7 +11,7 @@ version = "0.0.1-SNAPSHOT"
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
languageVersion = JavaLanguageVersion.of(21)
}
}
......
......@@ -11,7 +11,7 @@ version = "0.0.1-SNAPSHOT"
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
languageVersion = JavaLanguageVersion.of(21)
}
}
......@@ -26,6 +26,8 @@ dependencies {
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.flywaydb:flyway-core")
runtimeOnly("com.h2database:h2")
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
testImplementation("org.springframework.boot:spring-boot-starter-test")
......@@ -35,6 +37,8 @@ dependencies {
testImplementation("com.willowtreeapps.assertk:assertk-jvm:0.28.1")
testImplementation("io.mockk:mockk:1.13.12")
testImplementation("com.ninja-squad:springmockk:4.0.2")
implementation("io.github.oshai:kotlin-logging-jvm:7.0.3")
}
kotlin {
......
package iut.nantes.exo33
import iut.nantes.exo33.controller.HumanDto
import iut.nantes.exo33.controller.PetDto
import org.springframework.stereotype.Service
@Service
class DatabaseProxy() {
fun savePet(pet: PetDto): PetDto {
TODO()
}
fun findPetById(id: Int): PetDto? {
TODO()
}
fun findAllPets(): List<PetDto> {
TODO()
}
fun saveHuman(humanDto: HumanDto): HumanDto {
TODO()
}
fun findHumanById(id: Int): HumanDto? {
TODO()
}
fun findAllHuman(): List<HumanDto> {
TODO()
}
}
package iut.nantes.exo33.controller
import iut.nantes.exo33.DatabaseProxy
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.DeleteMapping
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.PutMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController
@RestController
class HumanController(val db: DatabaseProxy){
@GetMapping("/api/v1/human/{human}")
fun getHuman(@PathVariable humanId: Int) = db.findHumanById(humanId)?.let {
ResponseEntity.ok(it)
} ?: ResponseEntity.notFound().build()
@PostMapping("/api/v1/humans")
fun createHuman(@RequestBody human: HumanDto): ResponseEntity<HumanDto> {
val withId = db.saveHuman(human)
return ResponseEntity.status(HttpStatus.CREATED).body(withId)
}
@PutMapping("/api/v1/humans/{humanId}")
fun updateHuman(@RequestBody human: HumanDto, @PathVariable humanId: Int): ResponseEntity<HumanDto> {
if (human.humanId != humanId) {
throw IllegalArgumentException("Human ID in path and body do not match")
}
val previous = db.findHumanById(humanId)
if (previous == null) {
throw (IllegalArgumentException("Human with ID $humanId not found"))
} else {
db.saveHuman(human).let { return ResponseEntity.ok(it) }
}
}
@GetMapping("/api/v1/humans")
fun getHumans(@RequestParam minAge: Int?, @RequestParam maxAge: Int?): ResponseEntity<List<HumanDto>> {
val result = db.findAllHuman()
return ResponseEntity.ok(result)
}
}
\ No newline at end of file
package iut.nantes.exo33.controller
data class HumanDto(val humanId: Int?, val name: String)
data class ContactDto(val email: String)
package iut.nantes.exo33.controller
import iut.nantes.exo33.DatabaseProxy
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.PutMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RestController
@RestController
class PetController(val db: DatabaseProxy){
@GetMapping("/api/v1/pets/{petId}")
fun getPet(@PathVariable petId: Int) = db.findPetById(petId)?.let {
ResponseEntity.ok(it)
} ?: ResponseEntity.notFound().build()
@PostMapping("/api/v1/pets")
fun createPet(@RequestBody pet: PetDto): ResponseEntity<PetDto> {
db.savePet(pet).let { return ResponseEntity.status(HttpStatus.CREATED).body(it) }
}
@PutMapping("/api/v1/pets/{petId}")
fun updatePet(@RequestBody pet: PetDto, @PathVariable petId: Int): ResponseEntity<PetDto> {
if (pet.id != petId) {
throw IllegalArgumentException("Pet ID in path and body do not match")
}
val previous = db.findPetById(petId)
if (previous == null) {
throw (IllegalArgumentException("Pet with ID $petId not found"))
} else {
db.savePet(pet)
}
return ResponseEntity.ok(pet)
}
@GetMapping("/api/v1/pets")
fun getPets(): ResponseEntity<List<PetDto>> {
val result = db.findAllPets()
return ResponseEntity.ok(result)
}
}
\ No newline at end of file
package iut.nantes.exo33.controller
data class PetDto(val id: Int?, val name: String, val age: Int, val kind: PetKind) {
}
enum class PetKind {
CAT, DOG, FISH, OCTOPUS
}
\ No newline at end of file
spring.application.name: exo33
spring:
r2dbc:
url: jdbc:h2:mem:///test;MODE=PostgreSQL
flyway:
enabled: true
url: jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;MODE=PostgreSQL
application.name: exo33
datasource:
url: jdbc:h2:file:./data/testdb
driverClassName: org.h2.Driver
username: sa
password: ps
h2:
console:
enabled: true
jpa:
hibernate:
ddl-auto: create
management:
endpoint:
health:
show-details: always
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<include resource="org/springframework/boot/logging/logback/console-appender.xml" />
<springProfile name="dev">
<appender name="MY_APPENDER" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>
%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n
</Pattern>
</layout>
</appender>
</springProfile>
<springProfile name="default">
<appender name="MY_APPENDER" class="ch.qos.logback.core.FileAppender">
<file>/tmp/pony/tests.log</file>
<append>true</append>
<encoder>
<pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
</encoder>
</appender>
</springProfile>
<root level="INFO">
<appender-ref ref="MY_APPENDER"/>
</root>
<logger name="org.springframework.web" level="DEBUG"/>
</configuration>
package iut.nantes.exo33.controller
import iut.nantes.exo33.DatabaseProxy
import org.hamcrest.Matchers.startsWith
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Nested
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.http.MediaType.APPLICATION_JSON
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.delete
import org.springframework.test.web.servlet.get
import org.springframework.test.web.servlet.post
import org.springframework.test.web.servlet.put
import org.springframework.web.servlet.config.annotation.EnableWebMvc
import kotlin.test.Test
@SpringBootTest
@AutoConfigureMockMvc
class PetControllerTest {
@Autowired
lateinit var mockMvc: MockMvc
@Autowired
lateinit var databaseProxy: DatabaseProxy
@BeforeEach
fun setup() {
}
@Test
fun `exo 35`() {
mockMvc.post("/api/v1/humans") {
contentType = APPLICATION_JSON
content = humanJson("Joe")
}.andExpect {
status { isCreated() }
content { contentType("application/json") }
jsonPath("$.name") { value("Joe") }
}
mockMvc.get("/api/v1/humans") {
}.andExpect {
status { isOk() }
content { contentType("application/json") }
jsonPath("$[0].name") { value("Joe") }
}
}
@Test
fun `exo 36`() {
mockMvc.post("/api/v1/humans") {
contentType = APPLICATION_JSON
content = humanJson("Joe")
}.andExpect {
status { isCreated() }
content { contentType("application/json") }
jsonPath("$.name") { value("Joe") }
jsonPath("$.contact.email") { value("Joe@Doe.pom") }
}
mockMvc.get("/api/v1/humans") {
}.andExpect {
status { isOk() }
content { contentType("application/json") }
jsonPath("$[0].name") { value("Joe") }
jsonPath("$[0].contact.email") { value("Joe@Doe.pom") }
}
}
@Test
fun `exo 38`() {
mockMvc.post("/api/v1/humans") {
contentType = APPLICATION_JSON
content = humanJson("Joe")
}.andExpect {
status { isCreated() }
content { contentType("application/json") }
jsonPath("$.name") { value("Joe") }
jsonPath("$.contact.email") { value("Joe@Doe.pom") }
jsonPath("$.pets[0].name") { value("Java") }
}
mockMvc.get("/api/v1/humans") {
}.andExpect {
status { isOk() }
content { contentType("application/json") }
jsonPath("$[0].name") { value("Joe") }
jsonPath("$[0].contact.email") { value("Joe@Doe.pom") }
jsonPath("$[0].pets[0].name") { value("Java") }
}
}
@Test
fun `exo 40`() {
// Uncomment the following line to before runing the test
// val h = databaseProxy.saveHuman(HumanDto(null, "Joe", ContactDto("Joe@Doe.pom"), listOf(PetDto(null, "Java", 3, PetKind.DOG))))
// mockMvc.delete("/api/v1/humans/${h.humanId}/pets/${h.pets[0].id}") {
// }.andExpect {
// status { isNoContent() }
// }
// mockMvc.get("/api/v1/humans") {
// }.andExpect {
// status { isOk() }
// content { contentType("application/json") }
// jsonPath("$[0].name") { value("Joe") }
// jsonPath("$[0].contact.email") { value("Joe@Doe.pom") }
// jsonPath("$[0].pets") { isEmpty() }
// }
}
@Test
fun `exo 41`() {
// Uncomment the following line to before runing the test
// val h = databaseProxy.saveHuman(HumanDto(null, "Joe", ContactDto("Joe@Doe.pom"), listOf(PetDto(null, "Java", 3, PetKind.DOG))))
// mockMvc.delete("/api/v1/humans/${h.humanId}/pets/${h.pets[0].id}") {
// }.andExpect {
// status { isUnauthorized() }
// }
// mockMvc.get("/api/v1/humans") {
// }.andExpect {
// status { isOk() }
// content { contentType("application/json") }
// jsonPath("$[0].name") { value("Joe") }
// jsonPath("$[0].contact.email") { value("Joe@Doe.pom") }
// }
}
@Test
//@WithMockUser(username = "theAdmin", roles = ["ADMIN"])
fun `exo 44`() {
mockMvc.post("/api/v1/humans") {
contentType = APPLICATION_JSON
content = humanJson("Joe")
}.andExpect {
status { isCreated() }
content { contentType("application/json") }
jsonPath("$.name") { value("Joe") }
jsonPath("$.contact.email") { value("Joe@Doe.pom") }
jsonPath("$.pets[0].name") { value("Java") }
}
mockMvc.get("/api/v1/humans") {
}.andExpect {
status { isOk() }
content { contentType("application/json") }
jsonPath("$[0].name") { value("Joe") }
jsonPath("$[0].contact.email") { value("Joe@Doe.pom") }
jsonPath("$[0].pets[0].name") { value("Java") }
jsonPath("$[0].creatorLogin") { value("theAdmin") }
}
}
private fun humanJson(name: String = "Joe", email: String = "Joe@Doe.pom", petName: String = "Java") = """
{
"name": "$name",
"contact": {
"email": "$email"
},
"pets": [
{
"name": "$petName",
"age": 3,
"kind": "DOG"
}
]
}
""".trimIndent()
}
\ No newline at end of file
0% Chargement en cours ou .
You are about to add 0 people to the discussion. Proceed with caution.
Terminez d'abord l'édition de ce message.
Veuillez vous inscrire ou vous pour commenter