#60 Can follow citizen #96
@@ -25,6 +25,7 @@ import fr.dcproject.component.constitution.routes.installConstitutionRoutes
|
||||
import fr.dcproject.component.doc.routes.installDocRoutes
|
||||
import fr.dcproject.component.follow.followKoinModule
|
||||
import fr.dcproject.component.follow.routes.article.installFollowArticleRoutes
|
||||
import fr.dcproject.component.follow.routes.citizen.installFollowCitizenRoutes
|
||||
import fr.dcproject.component.follow.routes.constitution.installFollowConstitutionRoutes
|
||||
import fr.dcproject.component.notification.NotificationConsumer
|
||||
import fr.dcproject.component.notification.routes.installNotificationsRoutes
|
||||
@@ -154,6 +155,7 @@ fun Application.module(env: Env = PROD) {
|
||||
installCommentRoutes()
|
||||
installFollowArticleRoutes()
|
||||
installFollowConstitutionRoutes()
|
||||
installFollowCitizenRoutes()
|
||||
installWorkgroupRoutes()
|
||||
installOpinionRoutes()
|
||||
installVoteRoutes()
|
||||
|
||||
@@ -69,7 +69,7 @@ val KoinModule = module {
|
||||
|
||||
single {
|
||||
val config: Configuration = get()
|
||||
NotificationConsumer(get(), get(), get(), get(), get(), config.exchangeNotificationName)
|
||||
NotificationConsumer(get(), get(), get(), get(), get(), get(), config.exchangeNotificationName)
|
||||
}
|
||||
|
||||
// RabbitMQ
|
||||
|
||||
@@ -2,6 +2,7 @@ package fr.dcproject.common.entity
|
||||
|
||||
import fr.dcproject.component.article.database.ArticleRef
|
||||
import fr.dcproject.component.citizen.database.CitizenI
|
||||
import fr.dcproject.component.citizen.database.CitizenRef
|
||||
import fr.dcproject.component.comment.generic.database.CommentRef
|
||||
import fr.dcproject.component.constitution.database.ConstitutionRef
|
||||
import fr.dcproject.component.opinion.database.OpinionRef
|
||||
@@ -34,7 +35,8 @@ interface TargetI : EntityI {
|
||||
Article("article"),
|
||||
Constitution("constitution"),
|
||||
Comment("comment"),
|
||||
Opinion("opinion")
|
||||
Opinion("opinion"),
|
||||
Citizen("citizen"),
|
||||
}
|
||||
|
||||
companion object {
|
||||
@@ -44,6 +46,7 @@ interface TargetI : EntityI {
|
||||
t.isSubclassOf(ConstitutionRef::class) -> TargetName.Constitution.targetReference
|
||||
t.isSubclassOf(CommentRef::class) -> TargetName.Comment.targetReference
|
||||
t.isSubclassOf(OpinionRef::class) -> TargetName.Opinion.targetReference
|
||||
t.isSubclassOf(CitizenRef::class) -> TargetName.Citizen.targetReference
|
||||
else -> throw error("target not implemented: ${t.qualifiedName} \nImplement it or return 'reference' from SQL")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,9 @@ package fr.dcproject.component.citizen.database
|
||||
|
||||
import fr.dcproject.common.entity.CreatedAt
|
||||
import fr.dcproject.common.entity.DeletedAt
|
||||
import fr.dcproject.common.entity.Entity
|
||||
import fr.dcproject.common.entity.EntityI
|
||||
import fr.dcproject.common.entity.TargetI
|
||||
import fr.dcproject.common.entity.TargetRef
|
||||
import fr.dcproject.component.auth.database.User
|
||||
import fr.dcproject.component.auth.database.UserCreator
|
||||
import fr.dcproject.component.auth.database.UserForCreate
|
||||
@@ -95,10 +96,10 @@ open class CitizenRefWithUser(
|
||||
|
||||
open class CitizenRef(
|
||||
id: UUID = UUID.randomUUID()
|
||||
) : Entity(id),
|
||||
) : TargetRef(id),
|
||||
CitizenI
|
||||
|
||||
interface CitizenI : EntityI {
|
||||
interface CitizenI : EntityI, TargetI {
|
||||
data class Name(
|
||||
override val firstName: String,
|
||||
override val lastName: String,
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
package fr.dcproject.component.follow
|
||||
|
||||
import fr.dcproject.component.follow.database.FollowArticleRepository
|
||||
import fr.dcproject.component.follow.database.FollowCitizenRepository
|
||||
import fr.dcproject.component.follow.database.FollowConstitutionRepository
|
||||
import org.koin.dsl.module
|
||||
|
||||
val followKoinModule = module {
|
||||
single { FollowArticleRepository(get()) }
|
||||
single { FollowConstitutionRepository(get()) }
|
||||
single { FollowCitizenRepository(get()) }
|
||||
single { FollowAccessControl() }
|
||||
}
|
||||
|
||||
@@ -4,7 +4,9 @@ import fr.dcproject.common.entity.Entity
|
||||
import fr.dcproject.common.entity.TargetRef
|
||||
import fr.dcproject.component.article.database.ArticleForView
|
||||
import fr.dcproject.component.article.database.ArticleRef
|
||||
import fr.dcproject.component.citizen.database.Citizen
|
||||
import fr.dcproject.component.citizen.database.CitizenI
|
||||
import fr.dcproject.component.citizen.database.CitizenRef
|
||||
import fr.dcproject.component.constitution.database.ConstitutionForView
|
||||
import fr.dcproject.component.constitution.database.ConstitutionRef
|
||||
import fr.postgresjson.connexion.Paginated
|
||||
@@ -144,3 +146,28 @@ class FollowConstitutionRepository(requester: Requester) : FollowRepository<Cons
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
}
|
||||
|
||||
class FollowCitizenRepository(requester: Requester) : FollowRepository<CitizenRef, Citizen>(requester) {
|
||||
override fun findByCitizen(
|
||||
citizenId: UUID,
|
||||
page: Int,
|
||||
limit: Int
|
||||
): Paginated<FollowForView<Citizen>> {
|
||||
return requester.run {
|
||||
getFunction("find_follows_citizen_by_citizen")
|
||||
.select(
|
||||
page,
|
||||
limit,
|
||||
"created_by_id" to citizenId
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun findFollowsByTarget(
|
||||
target: Entity,
|
||||
page: Int,
|
||||
limit: Int
|
||||
): Paginated<FollowForView<CitizenRef>> {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package fr.dcproject.component.follow.routes.citizen
|
||||
|
||||
import fr.dcproject.common.security.assert
|
||||
import fr.dcproject.component.auth.citizen
|
||||
import fr.dcproject.component.auth.citizenOrNull
|
||||
import fr.dcproject.component.auth.mustBeAuth
|
||||
import fr.dcproject.component.citizen.database.CitizenRef
|
||||
import fr.dcproject.component.follow.FollowAccessControl
|
||||
import fr.dcproject.component.follow.database.FollowCitizenRepository
|
||||
import fr.dcproject.component.follow.database.FollowForUpdate
|
||||
import io.ktor.application.call
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||
import io.ktor.locations.Location
|
||||
import io.ktor.locations.post
|
||||
import io.ktor.response.respond
|
||||
import io.ktor.routing.Route
|
||||
import java.util.UUID
|
||||
|
||||
@KtorExperimentalLocationsAPI
|
||||
object FollowCitizen {
|
||||
@Location("/citizens/{citizen}/follows")
|
||||
class CitizenFollowRequest(citizen: UUID) {
|
||||
val citizen = CitizenRef(citizen)
|
||||
}
|
||||
|
||||
fun Route.followCitizen(repo: FollowCitizenRepository, ac: FollowAccessControl) {
|
||||
post<CitizenFollowRequest> {
|
||||
mustBeAuth()
|
||||
val follow = FollowForUpdate(target = it.citizen, createdBy = this.citizen)
|
||||
ac.assert { canCreate(follow, citizenOrNull) }
|
||||
repo.follow(follow)
|
||||
call.respond(HttpStatusCode.Created)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package fr.dcproject.component.follow.routes.citizen
|
||||
|
||||
import fr.dcproject.common.response.toOutput
|
||||
import fr.dcproject.common.security.assert
|
||||
import fr.dcproject.component.auth.citizen
|
||||
import fr.dcproject.component.auth.citizenOrNull
|
||||
import fr.dcproject.component.citizen.database.CitizenRef
|
||||
import fr.dcproject.component.follow.FollowAccessControl
|
||||
import fr.dcproject.component.follow.database.FollowCitizenRepository
|
||||
import io.ktor.application.call
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||
import io.ktor.locations.Location
|
||||
import io.ktor.locations.get
|
||||
import io.ktor.response.respond
|
||||
import io.ktor.routing.Route
|
||||
import org.joda.time.DateTime
|
||||
import java.util.UUID
|
||||
|
||||
@KtorExperimentalLocationsAPI
|
||||
object GetFollowCitizen {
|
||||
@Location("/citizens/{citizen}/follows")
|
||||
class CitizenFollowRequest(citizen: UUID) {
|
||||
val citizen = CitizenRef(citizen)
|
||||
}
|
||||
|
||||
fun Route.getFollowCitizen(repo: FollowCitizenRepository, ac: FollowAccessControl) {
|
||||
get<CitizenFollowRequest> {
|
||||
repo.findFollow(citizen, it.citizen)?.let { follow ->
|
||||
ac.assert { canView(follow, citizenOrNull) }
|
||||
call.respond(
|
||||
HttpStatusCode.OK,
|
||||
follow.let { f ->
|
||||
object {
|
||||
val id: UUID = f.id
|
||||
val createdBy: Any = f.createdBy.toOutput()
|
||||
val target: Any = f.target.let { t ->
|
||||
object {
|
||||
val id: UUID = t.id
|
||||
val reference: String = f.target.reference
|
||||
}
|
||||
}
|
||||
val createdAt: DateTime = f.createdAt
|
||||
}
|
||||
}
|
||||
)
|
||||
} ?: call.respond(HttpStatusCode.NoContent)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package fr.dcproject.component.follow.routes.citizen
|
||||
|
||||
import fr.dcproject.common.response.toOutput
|
||||
import fr.dcproject.common.security.assert
|
||||
import fr.dcproject.component.auth.citizenOrNull
|
||||
import fr.dcproject.component.auth.mustBeAuth
|
||||
import fr.dcproject.component.citizen.database.CitizenRef
|
||||
import fr.dcproject.component.follow.FollowAccessControl
|
||||
import fr.dcproject.component.follow.database.FollowCitizenRepository
|
||||
import io.ktor.application.call
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||
import io.ktor.locations.Location
|
||||
import io.ktor.locations.get
|
||||
import io.ktor.response.respond
|
||||
import io.ktor.routing.Route
|
||||
import org.joda.time.DateTime
|
||||
import java.util.UUID
|
||||
|
||||
@KtorExperimentalLocationsAPI
|
||||
object GetMyFollowsCitizen {
|
||||
@Location("/citizens/{citizen}/follows/citizens")
|
||||
class CitizenFollowCitizenRequest(citizen: UUID) {
|
||||
val citizen = CitizenRef(citizen)
|
||||
}
|
||||
|
||||
fun Route.getMyFollowsCitizen(repo: FollowCitizenRepository, ac: FollowAccessControl) {
|
||||
get<CitizenFollowCitizenRequest> {
|
||||
mustBeAuth()
|
||||
val follows = repo.findByCitizen(it.citizen)
|
||||
ac.assert { canView(follows.result, citizenOrNull) }
|
||||
call.respond(
|
||||
HttpStatusCode.OK,
|
||||
follows.toOutput { f ->
|
||||
object {
|
||||
val id: UUID = f.id
|
||||
val createdBy: Any = f.createdBy.toOutput()
|
||||
val target: Any = f.target.let { t ->
|
||||
object {
|
||||
val id: UUID = t.id
|
||||
val reference: String = f.target.reference
|
||||
}
|
||||
}
|
||||
val createdAt: DateTime = f.createdAt
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package fr.dcproject.component.follow.routes.citizen
|
||||
|
||||
import fr.dcproject.common.security.assert
|
||||
import fr.dcproject.component.auth.citizen
|
||||
import fr.dcproject.component.auth.citizenOrNull
|
||||
import fr.dcproject.component.auth.mustBeAuth
|
||||
import fr.dcproject.component.citizen.database.CitizenRef
|
||||
import fr.dcproject.component.follow.FollowAccessControl
|
||||
import fr.dcproject.component.follow.database.FollowCitizenRepository
|
||||
import fr.dcproject.component.follow.database.FollowForUpdate
|
||||
import io.ktor.application.call
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||
import io.ktor.locations.Location
|
||||
import io.ktor.locations.delete
|
||||
import io.ktor.response.respond
|
||||
import io.ktor.routing.Route
|
||||
import java.util.UUID
|
||||
|
||||
@KtorExperimentalLocationsAPI
|
||||
object UnfollowCitizen {
|
||||
@Location("/citizens/{citizen}/follows")
|
||||
class CitizenFollowRequest(citizen: UUID) {
|
||||
val citizen = CitizenRef(citizen)
|
||||
}
|
||||
|
||||
fun Route.unfollowCitizen(repo: FollowCitizenRepository, ac: FollowAccessControl) {
|
||||
delete<CitizenFollowRequest> {
|
||||
mustBeAuth()
|
||||
val follow = FollowForUpdate(target = it.citizen, createdBy = this.citizen)
|
||||
ac.assert { canDelete(follow, citizenOrNull) }
|
||||
repo.unfollow(follow)
|
||||
call.respond(HttpStatusCode.NoContent)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package fr.dcproject.component.follow.routes.citizen
|
||||
|
||||
import fr.dcproject.component.follow.routes.citizen.FollowCitizen.followCitizen
|
||||
import fr.dcproject.component.follow.routes.citizen.GetFollowCitizen.getFollowCitizen
|
||||
import fr.dcproject.component.follow.routes.citizen.GetMyFollowsCitizen.getMyFollowsCitizen
|
||||
import fr.dcproject.component.follow.routes.citizen.UnfollowCitizen.unfollowCitizen
|
||||
import io.ktor.auth.authenticate
|
||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||
import io.ktor.routing.Routing
|
||||
import org.koin.ktor.ext.get
|
||||
|
||||
@KtorExperimentalLocationsAPI
|
||||
fun Routing.installFollowCitizenRoutes() {
|
||||
authenticate(optional = true) {
|
||||
followCitizen(get(), get())
|
||||
unfollowCitizen(get(), get())
|
||||
getFollowCitizen(get(), get())
|
||||
getMyFollowsCitizen(get(), get())
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import com.rabbitmq.client.DefaultConsumer
|
||||
import com.rabbitmq.client.Envelope
|
||||
import fr.dcproject.common.entity.TargetRef
|
||||
import fr.dcproject.component.follow.database.FollowArticleRepository
|
||||
import fr.dcproject.component.follow.database.FollowCitizenRepository
|
||||
import fr.dcproject.component.follow.database.FollowConstitutionRepository
|
||||
import fr.dcproject.component.follow.database.FollowForView
|
||||
import io.ktor.utils.io.errors.IOException
|
||||
@@ -23,6 +24,7 @@ class NotificationConsumer(
|
||||
private val redisClient: RedisClient,
|
||||
private val followConstitutionRepo: FollowConstitutionRepository,
|
||||
private val followArticleRepo: FollowArticleRepository,
|
||||
private val followCitizenRepo: FollowCitizenRepository,
|
||||
private val notificationEmailSender: NotificationEmailSender,
|
||||
private val exchangeName: String,
|
||||
) {
|
||||
@@ -98,6 +100,7 @@ class NotificationConsumer(
|
||||
val follows = when (notification.type) {
|
||||
"article" -> followArticleRepo.findFollowsByTarget(notification.target)
|
||||
"constitution" -> followConstitutionRepo.findFollowsByTarget(notification.target)
|
||||
"citizen" -> followCitizenRepo.findFollowsByTarget(notification.target)
|
||||
else -> error("event '${notification.type}' not implemented")
|
||||
}
|
||||
|
||||
|
||||
@@ -955,13 +955,105 @@ paths:
|
||||
description: Return only http status 204 on success
|
||||
401:
|
||||
$ref: '#/components/responses/401'
|
||||
|
||||
/citizens/{citizen}/follows:
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/citizen'
|
||||
get:
|
||||
security:
|
||||
- JWTAuth: [ ]
|
||||
summary: Return Follows of citizen
|
||||
tags:
|
||||
- follow
|
||||
- citizen
|
||||
responses:
|
||||
200:
|
||||
description: Return follows
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/FollowResponse'
|
||||
404:
|
||||
description: Citizen not exist
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/404'
|
||||
post:
|
||||
security:
|
||||
- JWTAuth: []
|
||||
summary: Follow citizen
|
||||
description: Follow a citizen to receive notifications of his activity
|
||||
tags:
|
||||
- follow
|
||||
- citizen
|
||||
responses:
|
||||
201:
|
||||
description: Return only http status 201 on success
|
||||
401:
|
||||
$ref: '#/components/responses/401'
|
||||
404:
|
||||
description: Citizen not exist
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/404'
|
||||
delete:
|
||||
security:
|
||||
- JWTAuth: [ ]
|
||||
summary: Unfollow one citizen
|
||||
tags:
|
||||
- follow
|
||||
- citizen
|
||||
responses:
|
||||
204:
|
||||
description: Return only http status 204 on success
|
||||
401:
|
||||
$ref: '#/components/responses/401'
|
||||
404:
|
||||
description: Citizen not exist
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/404'
|
||||
/citizens/{citizen}/follows/citizens:
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/citizen'
|
||||
get:
|
||||
security:
|
||||
- JWTAuth: [ ]
|
||||
summary: Return citizen Follow of citizen
|
||||
tags:
|
||||
- follow
|
||||
- citizen
|
||||
responses:
|
||||
200:
|
||||
description: Return citizen Follow of citizen
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/Paginated'
|
||||
- type: object
|
||||
properties:
|
||||
result:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/FollowResponse'
|
||||
404:
|
||||
description: Citizen not exist
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/404'
|
||||
|
||||
/citizens/{citizen}/follows/articles:
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/citizen'
|
||||
get:
|
||||
security:
|
||||
- JWTAuth: [ ]
|
||||
summary: Return Follow or nothing if you not follow
|
||||
summary: Return article Follow of citizen
|
||||
tags:
|
||||
- follow
|
||||
- article
|
||||
@@ -1036,7 +1128,7 @@ paths:
|
||||
- citizen
|
||||
responses:
|
||||
200:
|
||||
description: Return your follows
|
||||
description: Return constitution Follow of citizen
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
create or replace function find_follows_citizen_by_citizen(
|
||||
_created_by_id uuid,
|
||||
"limit" int default 50,
|
||||
"offset" int default 0,
|
||||
out resource json,
|
||||
out total int
|
||||
) language plpgsql as
|
||||
$$
|
||||
begin
|
||||
select json_agg(t), (select count(id) from follow)
|
||||
into resource, total
|
||||
from (
|
||||
select
|
||||
f.*,
|
||||
find_citizen_by_id_with_user(f.target_id) as target,
|
||||
find_citizen_by_id_with_user(f.created_by_id) as created_by
|
||||
from follow as f
|
||||
where created_by_id = _created_by_id
|
||||
order by created_at desc,
|
||||
f.created_at desc
|
||||
limit "limit" offset "offset"
|
||||
) as t;
|
||||
end;
|
||||
$$;
|
||||
@@ -92,7 +92,8 @@ class NotificationConsumerTest {
|
||||
rabbitFactory = rabbitFactory,
|
||||
redisClient = redisClient,
|
||||
followArticleRepo = followArticleRepo,
|
||||
followConstitutionRepo = mockk(),
|
||||
followConstitutionRepo = mockk(), // TODO test followConstitution
|
||||
followCitizenRepo = mockk(), // TODO test followCitizen
|
||||
notificationEmailSender = emailSender,
|
||||
exchangeName = "notification",
|
||||
).apply { start() }
|
||||
|
||||
96
src/test/kotlin/integration/Follow citizen routes.kt
Normal file
96
src/test/kotlin/integration/Follow citizen routes.kt
Normal file
@@ -0,0 +1,96 @@
|
||||
package integration
|
||||
|
||||
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 POST request`
|
||||
import integration.steps.given.`And follow citizen`
|
||||
import integration.steps.given.`Given I have citizen`
|
||||
import integration.steps.given.`authenticated as`
|
||||
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.NoContent
|
||||
import io.ktor.http.HttpStatusCode.Companion.OK
|
||||
import org.junit.jupiter.api.Tag
|
||||
import org.junit.jupiter.api.Tags
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
@Tags(Tag("integration"), Tag("article"), Tag("follow"))
|
||||
class `Follow citizen routes` : BaseTest() {
|
||||
@Test
|
||||
fun `I can follow citizen`() {
|
||||
withIntegrationApplication {
|
||||
/* Followed user */
|
||||
`Given I have citizen`("John", "Glenn", id = "7e1580c5-05b7-4557-84f4-faac9f0a9441")
|
||||
/* Current user */
|
||||
`Given I have citizen`("Valentina", "Terechkova")
|
||||
`When I send a POST request`("/citizens/7e1580c5-05b7-4557-84f4-faac9f0a9441/follows") {
|
||||
`authenticated as`("Valentina", "Terechkova")
|
||||
`with no content`()
|
||||
} `Then the response should be` Created
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `I can get my follow citizen`() {
|
||||
withIntegrationApplication {
|
||||
/* Followed user */
|
||||
`Given I have citizen`("Jean-Loup", "Chrétien", id = "c2432b94-a509-4116-a8b6-9774bc963372")
|
||||
/* Current user */
|
||||
`Given I have citizen`("John", "Young", id = "6d41ce65-9df7-47e0-af46-8da4a909490b") {
|
||||
`And follow citizen`("c2432b94-a509-4116-a8b6-9774bc963372")
|
||||
}
|
||||
/* Get my all follows */
|
||||
`When I send a GET request`("/citizens/6d41ce65-9df7-47e0-af46-8da4a909490b/follows/citizens") {
|
||||
`authenticated as`("John", "Young")
|
||||
} `Then the response should be` OK and {
|
||||
`And the response should not be null`()
|
||||
`And the response should contain`("$.currentPage", 1)
|
||||
`And the response should contain`("$.limit", 50)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `I can unfollow citizen`() {
|
||||
withIntegrationApplication {
|
||||
/* Followed user */
|
||||
`Given I have citizen`("Bruce", "McCandless", id = "680c7af7-d2de-4249-bfcb-47007ef546fe")
|
||||
/* Current user */
|
||||
`Given I have citizen`("Jean-François", "Clervoy", id = "a12455ae-1047-43ff-826d-0d826dbe90f7") {
|
||||
`And follow citizen`("680c7af7-d2de-4249-bfcb-47007ef546fe")
|
||||
}
|
||||
`When I send a DELETE request`("/citizens/680c7af7-d2de-4249-bfcb-47007ef546fe/follows") {
|
||||
`authenticated as`("Jean-François", "Clervoy")
|
||||
`with no content`()
|
||||
} `Then the response should be` NoContent and {
|
||||
`And the response should be null`()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `I can know if I follow an citizen`() {
|
||||
withIntegrationApplication {
|
||||
/* Followed user */
|
||||
`Given I have citizen`("Eugene", "Cernan", id = "c755788f-7f48-4cde-8ff0-e75bcffdafc2")
|
||||
/* Current user */
|
||||
`Given I have citizen`("Buzz", "Aldrin", id = "39e2915a-e96f-43ea-babd-bd339d8bf197") {
|
||||
`And follow citizen`("c755788f-7f48-4cde-8ff0-e75bcffdafc2")
|
||||
}
|
||||
`When I send a GET request`("/citizens/c755788f-7f48-4cde-8ff0-e75bcffdafc2/follows") {
|
||||
`authenticated as`("Buzz", "Aldrin")
|
||||
`with no content`()
|
||||
} `Then the response should be` OK and {
|
||||
`And the response should not be null`()
|
||||
`And the response should contain`("$.target.id", "c755788f-7f48-4cde-8ff0-e75bcffdafc2")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import fr.dcproject.component.citizen.database.CitizenRef
|
||||
import fr.dcproject.component.citizen.database.CitizenRepository
|
||||
import fr.dcproject.component.constitution.database.ConstitutionRef
|
||||
import fr.dcproject.component.follow.database.FollowArticleRepository
|
||||
import fr.dcproject.component.follow.database.FollowCitizenRepository
|
||||
import fr.dcproject.component.follow.database.FollowConstitutionRepository
|
||||
import fr.dcproject.component.follow.database.FollowForUpdate
|
||||
import io.ktor.server.testing.TestApplicationEngine
|
||||
@@ -24,6 +25,11 @@ fun Citizen.`And follow constitution`(
|
||||
) {
|
||||
createFollow(this, ConstitutionRef(constitution.toUUID()))
|
||||
}
|
||||
fun Citizen.`And follow citizen`(
|
||||
citizen: String,
|
||||
) {
|
||||
createFollow(this, CitizenRef(citizen.toUUID()))
|
||||
}
|
||||
|
||||
fun TestApplicationEngine.`Given I have follow on article`(
|
||||
firstName: String,
|
||||
@@ -56,3 +62,9 @@ fun createFollow(citizen: CitizenRef, constitution: ConstitutionRef) {
|
||||
val follow = FollowForUpdate(createdBy = citizen, target = constitution)
|
||||
followConstitutionRepository.follow(follow)
|
||||
}
|
||||
|
||||
fun createFollow(createdBy: CitizenRef, target: CitizenRef) {
|
||||
val followCitizenRepository: FollowCitizenRepository by lazy { GlobalContext.get().get() }
|
||||
val follow = FollowForUpdate(createdBy = createdBy, target = target)
|
||||
followCitizenRepository.follow(follow)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user