diff --git a/src/main/kotlin/fr/dcproject/Application.kt b/src/main/kotlin/fr/dcproject/Application.kt index 64ef648..9b62b00 100644 --- a/src/main/kotlin/fr/dcproject/Application.kt +++ b/src/main/kotlin/fr/dcproject/Application.kt @@ -10,6 +10,7 @@ import fr.dcproject.entity.Article import fr.dcproject.entity.Constitution import fr.dcproject.routes.article import fr.dcproject.routes.constitution +import fr.dcproject.routes.followArticle import fr.postgresjson.migration.Migrations import io.ktor.application.Application import io.ktor.application.install @@ -31,7 +32,7 @@ import java.util.* import fr.dcproject.repository.Article as RepositoryArticle import fr.dcproject.repository.Constitution as RepositoryConstitution -fun main(args: Array): Unit = io.ktor.server.netty.EngineMain.main(args) +fun main(args: Array): Unit = io.ktor.server.jetty.EngineMain.main(args) @KtorExperimentalAPI @KtorExperimentalLocationsAPI @@ -106,6 +107,7 @@ fun Application.module() { install(Routing) { article(get()) constitution(get()) + followArticle(get()) } // TODO move to postgresJson lib diff --git a/src/main/kotlin/fr/dcproject/Module.kt b/src/main/kotlin/fr/dcproject/Module.kt index 134f4fd..128c39b 100644 --- a/src/main/kotlin/fr/dcproject/Module.kt +++ b/src/main/kotlin/fr/dcproject/Module.kt @@ -1,5 +1,6 @@ package fr.dcproject +import fr.dcproject.repository.FollowArticleRepository import fr.postgresjson.connexion.Connection import fr.postgresjson.connexion.Requester import fr.postgresjson.migration.Migrations @@ -25,6 +26,7 @@ val Module = module { // TODO: create generic declaration single { ArticleRepository(get()) } single { ConstitutionRepository(get()) } + single { FollowArticleRepository(get()) } single { Migrations(connection = get(), directory = config.sqlFiles) } } diff --git a/src/main/kotlin/fr/dcproject/entity/Extra.kt b/src/main/kotlin/fr/dcproject/entity/Extra.kt new file mode 100644 index 0000000..3b83477 --- /dev/null +++ b/src/main/kotlin/fr/dcproject/entity/Extra.kt @@ -0,0 +1,22 @@ +package fr.dcproject.entity + +import fr.postgresjson.entity.EntityCreatedAt +import fr.postgresjson.entity.EntityCreatedAtImp +import fr.postgresjson.entity.EntityI +import fr.postgresjson.entity.UuidEntity +import java.util.* + +interface ExtraI >: + EntityI, + EntityCreatedAt { + var citizen: Citizen + var target: T +} + +abstract class Extra>( + id: UUID? = UUID.randomUUID(), + override var citizen: Citizen +): + ExtraI, + UuidEntity(id), + EntityCreatedAt by EntityCreatedAtImp() \ No newline at end of file diff --git a/src/main/kotlin/fr/dcproject/entity/Follow.kt b/src/main/kotlin/fr/dcproject/entity/Follow.kt new file mode 100644 index 0000000..8467c74 --- /dev/null +++ b/src/main/kotlin/fr/dcproject/entity/Follow.kt @@ -0,0 +1,11 @@ +package fr.dcproject.entity +import fr.postgresjson.entity.EntityI +import java.util.* + +class Follow > ( + id: UUID = UUID.randomUUID(), + citizen: Citizen, + override var target: T +): Extra(id, citizen) + +typealias FollowArticleEntity = Follow
\ No newline at end of file diff --git a/src/main/kotlin/fr/dcproject/repository/Follow.kt b/src/main/kotlin/fr/dcproject/repository/Follow.kt new file mode 100644 index 0000000..c24ebc7 --- /dev/null +++ b/src/main/kotlin/fr/dcproject/repository/Follow.kt @@ -0,0 +1,25 @@ +package fr.dcproject.repository + +import fr.postgresjson.connexion.Requester +import fr.postgresjson.entity.EntityI +import fr.postgresjson.repository.RepositoryI +import java.util.* +import kotlin.reflect.KClass +import fr.dcproject.entity.Article as ArticleEntity +import fr.dcproject.entity.Follow as FollowEntity + +open class Follow >(override var requester: Requester): RepositoryI> { + override val entityName = FollowEntity::class as KClass> + + fun follow(follow: FollowEntity) { + val reference = follow.target::class.simpleName!!.toLowerCase() + requester + .getFunction("follow") + .sendQuery( + "reference" to reference, + "target_id" to follow.target.id, + "citizen_id" to follow.citizen.id + ) + } +} +class FollowArticleRepository(override var requester: Requester): Follow(requester) diff --git a/src/main/kotlin/fr/dcproject/routes/Follow.kt b/src/main/kotlin/fr/dcproject/routes/Follow.kt new file mode 100644 index 0000000..6cdfda4 --- /dev/null +++ b/src/main/kotlin/fr/dcproject/routes/Follow.kt @@ -0,0 +1,31 @@ +package fr.dcproject.routes + +import Paths +import fr.dcproject.entity.Citizen +import fr.dcproject.entity.User +import fr.dcproject.repository.FollowArticleRepository +import io.ktor.application.call +import io.ktor.http.HttpStatusCode +import io.ktor.locations.KtorExperimentalLocationsAPI +import io.ktor.locations.post +import io.ktor.response.respond +import io.ktor.routing.Route +import org.joda.time.DateTime +import java.util.* +import fr.dcproject.entity.Follow as FollowEntity + +// TODO get current citizen +val currentCitizen = Citizen( + id = UUID.fromString("64b7b379-2298-43ec-b428-ba134930cabd"), + name = Citizen.Name("todo", "todo"), + birthday = DateTime.now(), + user = User(username = "plop", plainPassword = "plip") +) + +@KtorExperimentalLocationsAPI +fun Route.followArticle(repo: FollowArticleRepository) { + post { + repo.follow(FollowEntity(target = it.article, citizen = currentCitizen)) + call.respond(HttpStatusCode.Created) + } +} \ No newline at end of file diff --git a/src/main/kotlin/fr/dcproject/routes/Paths.kt b/src/main/kotlin/fr/dcproject/routes/Paths.kt index 744c7b8..0ff73b8 100644 --- a/src/main/kotlin/fr/dcproject/routes/Paths.kt +++ b/src/main/kotlin/fr/dcproject/routes/Paths.kt @@ -1,5 +1,6 @@ import fr.dcproject.entity.Article import fr.dcproject.entity.Constitution +import fr.dcproject.entity.Follow import fr.postgresjson.repository.RepositoryI.Direction import io.ktor.locations.KtorExperimentalLocationsAPI import io.ktor.locations.Location @@ -11,6 +12,7 @@ object Paths { val limit: Int = if (limit > 50) 50 else if (limit < 1) 1 else limit } @Location("/articles/{article}") class ArticleRequest(val article: Article) + @Location("/articles/{article}/follow") class ArticleFollowRequest(val article: Article) @Location("/articles") class PostArticleRequest diff --git a/src/test/kotlin/FollowTest.kt b/src/test/kotlin/FollowTest.kt new file mode 100644 index 0000000..8589a85 --- /dev/null +++ b/src/test/kotlin/FollowTest.kt @@ -0,0 +1,91 @@ +import fr.dcproject.entity.Article +import fr.dcproject.entity.Citizen +import fr.dcproject.entity.Follow +import fr.dcproject.entity.User +import fr.postgresjson.serializer.deserialize +import fr.postgresjson.serializer.serialize +import io.ktor.locations.KtorExperimentalLocationsAPI +import io.ktor.util.KtorExperimentalAPI +import org.amshove.kluent.`should equal` +import org.amshove.kluent.shouldBe +import org.intellij.lang.annotations.Language +import org.joda.time.DateTime +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestInstance +import org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS + +@KtorExperimentalLocationsAPI +@KtorExperimentalAPI +@TestInstance(PER_CLASS) +class FollowTest { + @Language("JSON") + private val followJson: String = """{ + "id":"bae81585-d985-4d7a-9b58-3a13e911688a", + "citizen":{ + "id":"4a87ad24-187a-46a8-97ab-00b30a24e561", + "name":{ + "first_name":"Jaque", + "last_name":"Bono", + "civility":null + }, + "birthday":"2019-08-09T11:42:47.168Z", + "user_id":null, + "vote_annonymous":null, + "follow_annonymous":null, + "user":{ + "id":"721db690-d050-46e6-92b0-056f2e8ba993", + "username":"jaque", + "blocked_at":null, + "plain_password":"azerty", + "created_at":null, + "updated_at":null + }, + "created_at":null + }, + "target":{ + "id":"34588ea7-c180-4694-801b-1b5c5a6ed73f", + "title":"Hello world!", + "annonymous":true, + "content":"bla bla bla", + "description":"this is the changement !", + "tags":[ + + ], + "created_by":{ + "id":"4a87ad24-187a-46a8-97ab-00b30a24e561" + }, + "version_id":"a4aa7dd4-d174-42d2-9ba5-ae6f1129ffce", + "version_number":null, + "created_at":null + }, + "created_at":null + }""".trimIndent() + + @Test + fun `test Follow Article serialize`() { + val user = User(username = "jaque", plainPassword = "azerty") + val citizen = Citizen( + name = Citizen.Name("Jaque", "Bono"), + birthday = DateTime.now(), + user = user + ) + val article = Article( + title = "Hello world!", + content = "bla bla bla", + description = "this is the changement !", + createdBy = citizen + ) + val follow = Follow( + citizen = citizen, + target = article + ) + follow.serialize().contains("""Hello world!""") shouldBe true + println(follow.serialize()) + } + + @Test + fun `test Follow Article Deserialize`() { + val follow: Follow
= followJson.deserialize()!! + follow.id.toString() `should equal` "bae81585-d985-4d7a-9b58-3a13e911688a" + } +} diff --git a/src/test/resources/feature/follow.feature b/src/test/resources/feature/follow.feature new file mode 100644 index 0000000..fcce7c1 --- /dev/null +++ b/src/test/resources/feature/follow.feature @@ -0,0 +1,11 @@ +Feature: follow routes + + Scenario: The route for follow article must response a 200 and return object + Given I have citizen: + | id | 64b7b379-2298-43ec-b428-ba134930cabd | + | firstName | Jaque | + | lastName | Dupuis | + When I send a "POST" request to "/articles/9226c1a3-8091-c3fa-7d0d-c2e98c9bee7b/follow" with body: + """ + """ + Then the response status code should be 201 \ No newline at end of file