14 Commits
0.0.1 ... 0.0.3

Author SHA1 Message Date
70092efaf5 Improve README 2020-06-02 16:31:38 +02:00
9c10a88a36 Improve Makefile 2020-06-02 15:20:11 +02:00
2ee8b80596 Add descriptions on OpenAPI 2020-06-02 15:08:12 +02:00
ee60f5b4e7 Fix tests 2020-06-02 15:08:05 +02:00
10928251e6 Can update roles of one citizen on workgroup 2020-06-02 14:20:35 +02:00
9b79301662 update workgroup in OpenApi 2020-06-02 01:01:29 +02:00
32510652d1 Fix upsert_workgroup 2020-06-02 01:01:08 +02:00
a6c36c542e Fixture not delete table content before apply 2020-06-01 13:52:48 +02:00
7874f5cec4 #55 Can be assign a role to members of my Workgroup
Remove Owner on Workgroup (use role MASTER instead)
"find_citizen_by_id" not return user anymore, use "find_citizen_by_id_with_user" instead
2020-06-01 13:46:15 +02:00
8ff6fcc970 Add volume to docker for redis 2020-05-27 09:56:54 +02:00
bd123d03e9 Fix fixtures script 2020-05-14 13:46:55 +02:00
ac852baaf6 Improve README 2020-05-14 13:46:44 +02:00
159b9de19a Create a auto build docker image
Version of application is calculated by the git tags
2020-05-14 00:54:21 +02:00
e4a85722f1 Move Elasticsearch configuration into external function "configElasticIndexes" 2020-05-13 13:58:17 +02:00
68 changed files with 666 additions and 362 deletions

13
.dockerignore Normal file
View File

@@ -0,0 +1,13 @@
build
out
GH_TOKEN.txt
Makefile
var
gradle
.idea
.gradle
docker
gradlew
gradlew.bat
docker-compose.yml
src/test

2
.gitignore vendored
View File

@@ -7,3 +7,5 @@
*.iws *.iws
dcproject.iml dcproject.iml
/var /var
GH_TOKEN.txt
allSQL.sql

View File

@@ -14,8 +14,6 @@
<option name="sourceFilePath" value="docker-compose.yml" /> <option name="sourceFilePath" value="docker-compose.yml" />
</settings> </settings>
</deployment> </deployment>
<method v="2"> <method v="2" />
<option name="Gradle.BeforeRunTask" enabled="true" tasks="build" externalProjectPath="$PROJECT_DIR$" vmOptions="" scriptParameters="" />
</method>
</configuration> </configuration>
</component> </component>

48
Makefile Normal file
View File

@@ -0,0 +1,48 @@
.EXPORT_ALL_VARIABLES:
VERSION=$(shell ./hook/version.sh)
GITHUB_USERNAME=$(shell git config user.email)
GITHUB_TOKEN=$(shell cat ./GH_TOKEN.txt)
# HELP
# This will output the help for each task
# thanks to https://marmelab.com/blog/2016/02/29/auto-documented-makefile.html
.PHONY: help
help: ## This help.
@awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST)
.DEFAULT_GOAL := help
bd: build-docker
build-docker: ## Build the docker image of application
docker build -t dc-project -f docker/app/Dockerfile .
pd: publish-docker
publish-docker: build-docker ## Publish docker image of application to Github
git diff --quiet --exit-code || (echo "The git is DIRTY !!! You cannot publish this crap!" && exit 1)
cat ./GH_TOKEN.txt | docker login docker.pkg.github.com -u ${GITHUB_USERNAME} --password-stdin
docker tag dc-project docker.pkg.github.com/flecomte/dc-project/dc-project:${VERSION}
docker push docker.pkg.github.com/flecomte/dc-project/dc-project:${VERSION}
rd: run-docker
run-docker: ## Build and Run all docker services
docker-compose up -d --build
pm: publish-maven
publish-maven: ## Publish JAR file to Github
@git diff --quiet --exit-code || (echo "The git is DIRTY !!! You cannot publish this crap!" && exit 1)
gradlew publish
f: fixtures
fixtures: ## Import fixtures
bash src/main/resources/sql/fixtures/fixtures.sh
v: vertion
vertion: ## Show current version
@echo ${VERSION}

42
README.md Normal file
View File

@@ -0,0 +1,42 @@
# DC Project
## Installation of Development environment
### On windows
1. Install git
- Download and install: https://git-scm.com/download/win
2. Install Make
- Go to [ezwinports](https://sourceforge.net/projects/ezwinports/files/).
- Download `make-4.1-2-without-guile-w32-bin.zip` (get the version without guile).
- Extract zip.
- Copy the contents to your `Git\mingw64\` merging the folders, but **do NOT overwrite/replace** any existing files.
### Add JAVA_HOME path
In CMD (Not Powershell)
```cmd
$ setx -m JAVA_HOME "C:\Users\%USERNAME%\.jdks\corretto-11.0.7"
$ setx -m PATH "%PATH%;%JAVA_HOME%\bin";
```
### Run dockers
```bash
$ make run-docker
```
### Add fixtures
```bash
$ make fixtures
```
## Publish package
1. Got to [https://github.com](https://github.com/settings/tokens/new) and create a new token with packages scopes
2. Create a file `GH_TOKEN.txt` and put it the github token
### Maven
```bash
$ make publish-maven
```
### Docker
```bash
$ make publish-docker
```

View File

@@ -1,6 +1,7 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.owasp.dependencycheck.reporting.ReportGenerator import org.owasp.dependencycheck.reporting.ReportGenerator
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
import org.slf4j.LoggerFactory
val ktor_version: String by project val ktor_version: String by project
val kotlin_version: String by project val kotlin_version: String by project
@@ -11,7 +12,13 @@ val jackson_version: String by project
val cucumber_version: String by project val cucumber_version: String by project
group = "com.github.flecomte" group = "com.github.flecomte"
version = "0.0.1" version = versioning.info.run {
if (dirty) {
versioning.info.full
} else {
versioning.info.lastTag
}
}
plugins { plugins {
jacoco jacoco
@@ -24,6 +31,7 @@ plugins {
id("org.jlleitschuh.gradle.ktlint") version "8.2.0" id("org.jlleitschuh.gradle.ktlint") version "8.2.0"
id("org.owasp.dependencycheck") version "5.1.0" id("org.owasp.dependencycheck") version "5.1.0"
id("org.sonarqube") version "2.7" id("org.sonarqube") version "2.7"
id("net.nemerosa.versioning") version "2.13.1"
} }
application { application {
@@ -48,6 +56,38 @@ tasks.withType<Jar> {
tasks { tasks {
named<ShadowJar>("shadowJar") { named<ShadowJar>("shadowJar") {
mergeServiceFiles("META-INF/services") mergeServiceFiles("META-INF/services")
archiveFileName.set("${archiveBaseName.get()}-latest-all.${archiveExtension.get()}")
}
}
val sourcesJar by tasks.creating(Jar::class) {
archiveClassifier.set("sources")
from(sourceSets.getByName("main").allSource)
}
publishing {
if (versioning.info.dirty == false) {
repositories {
maven {
name = "dc-project"
group = "com.github.flecomte"
url = uri("https://maven.pkg.github.com/flecomte/dc-project")
credentials {
username = System.getenv("GITHUB_USERNAME")
password = System.getenv("GITHUB_TOKEN")
}
}
}
publications {
create<MavenPublication>("dc-project") {
from(components["java"])
artifact(sourcesJar)
}
}
} else {
LoggerFactory.getLogger("gradle")
.warn("The git is DIRTY !!! You cannot publish this crap! (${versioning.info.full})")
} }
} }

View File

@@ -32,12 +32,14 @@ services:
restart: always restart: always
ports: ports:
- ${REDIS_PORT}:6379 - ${REDIS_PORT}:6379
volumes:
- redis-data:/var/lib/redis:rw
app: app:
container_name: app_${NAME} container_name: app_${NAME}
build: build:
context: ./build context: .
dockerfile: ../docker/app/Dockerfile dockerfile: docker/app/Dockerfile
restart: always restart: always
ports: ports:
- ${APP_PORT}:8080 - ${APP_PORT}:8080
@@ -60,8 +62,12 @@ services:
ports: ports:
- ${ELASTIC_REST}:9200 - ${ELASTIC_REST}:9200
- ${ELASTIC_NODES}:9300 - ${ELASTIC_NODES}:9300
healthcheck:
test: ["CMD", "curl", "-f", "http://elasticsearch:9200"]
interval: 3s
timeout: 2s
retries: 20
# Database
db: db:
container_name: postgresql_${NAME} container_name: postgresql_${NAME}
build: build:
@@ -75,11 +81,15 @@ services:
POSTGRES_DB: ${DB_PWD} POSTGRES_DB: ${DB_PWD}
volumes: volumes:
- ./var/log/postgresql:/var/log/postgresql:rw - ./var/log/postgresql:/var/log/postgresql:rw
- ./var/postgresql/data:/var/lib/postgresql/data:rw - db-data:/var/lib/postgresql/data:rw
depends_on: depends_on:
- elasticsearch - elasticsearch
healthcheck: healthcheck:
test: ["CMD", "curl", "-f", "http://elasticsearch:9200/"] test: [ "CMD", "pg_isready", "-q", "-d", "${DB_NAME}", "-U", "${DB_USER}" ]
interval: 3s interval: 3s
timeout: 2s timeout: 2s
retries: 20 retries: 20
volumes:
db-data:
redis-data:

View File

@@ -1,5 +1,12 @@
FROM adoptopenjdk/openjdk11:jre-11.0.4_11-alpine #### BUILD ####
FROM gradle:5.6.4-jdk11 AS build
COPY --chown=gradle:gradle . /home/gradle/src
WORKDIR /home/gradle/src
RUN gradle build -x test -x ktlintKotlinScriptCheck -x ktlintTestSourceSetCheck -x ktlintMainSourceSetCheck --no-daemon
#### RUN ####
FROM adoptopenjdk/openjdk11:jre-11.0.4_11-alpine
ENV APPLICATION_USER ktor ENV APPLICATION_USER ktor
RUN adduser -D -g '' $APPLICATION_USER RUN adduser -D -g '' $APPLICATION_USER
@@ -8,7 +15,7 @@ RUN chown -R $APPLICATION_USER /app
USER $APPLICATION_USER USER $APPLICATION_USER
COPY ./libs/dcproject-0.0.1-all.jar /app/dcproject.jar COPY --from=build /home/gradle/src/build/libs/dcproject-latest-all.jar /app/dcproject.jar
WORKDIR /app WORKDIR /app
CMD ["java", "-server", "-XX:+UnlockExperimentalVMOptions", "-XX:InitialRAMFraction=2", "-XX:MinRAMFraction=2", "-XX:MaxRAMFraction=2", "-XX:+UseG1GC", "-XX:MaxGCPauseMillis=100", "-XX:+UseStringDeduplication", "-jar", "dcproject.jar"] CMD ["java", "-server", "-XX:+UnlockExperimentalVMOptions", "-XX:InitialRAMFraction=2", "-XX:MinRAMFraction=2", "-XX:MaxRAMFraction=2", "-XX:+UseG1GC", "-XX:MaxGCPauseMillis=100", "-XX:+UseStringDeduplication", "-jar", "dcproject.jar"]

12
hook/version.sh Normal file
View File

@@ -0,0 +1,12 @@
#!/usr/bin/env bash
set -e
if [[ $(git describe --tags --dirty) =~ ^V?([0-9][0-9.]*(-dirty)?)$ ]]; then
VERSION="${BASH_REMATCH[1]}"
elif [[ $(git describe --tags --dirty) =~ ^V?([0-9][0-9.]*)-([0-9]+)-g(.+(-dirty)?)$ ]]; then
VERSION="${BASH_REMATCH[1]}-${BASH_REMATCH[3]}"
else
exit 1
fi
echo $VERSION

View File

@@ -8,6 +8,7 @@ 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 com.github.jasync.sql.db.postgresql.exceptions.GenericDatabaseException
import fr.dcproject.Env.PROD import fr.dcproject.Env.PROD
import fr.dcproject.elasticsearch.configElasticIndexes
import fr.dcproject.entity.* import fr.dcproject.entity.*
import fr.dcproject.event.EventNotification import fr.dcproject.event.EventNotification
import fr.dcproject.event.EventSubscriber import fr.dcproject.event.EventSubscriber
@@ -38,8 +39,6 @@ import io.ktor.routing.Routing
import io.ktor.util.KtorExperimentalAPI import io.ktor.util.KtorExperimentalAPI
import io.ktor.websocket.WebSockets import io.ktor.websocket.WebSockets
import org.eclipse.jetty.util.log.Slf4jLog import org.eclipse.jetty.util.log.Slf4jLog
import org.elasticsearch.client.Request
import org.elasticsearch.client.RestClient
import org.koin.core.qualifier.named import org.koin.core.qualifier.named
import org.koin.ktor.ext.Koin import org.koin.ktor.ext.Koin
import org.koin.ktor.ext.get import org.koin.ktor.ext.get
@@ -131,7 +130,7 @@ fun Application.module(env: Env = PROD) {
decode { values, _ -> decode { values, _ ->
val id = values.singleOrNull()?.let { UUID.fromString(it) } val id = values.singleOrNull()?.let { UUID.fromString(it) }
?: throw InternalError("Cannot convert $values to UUID") ?: throw InternalError("Cannot convert $values to UUID")
get<RepositoryCitizen>().findById(id, true) ?: throw NotFoundException("Citizen $values not found") get<RepositoryCitizen>().findById(id) ?: throw NotFoundException("Citizen $values not found")
} }
} }
@@ -192,56 +191,7 @@ fun Application.module(env: Env = PROD) {
} }
} }
/* Create index if not exist */ configElasticIndexes(get())
get<RestClient>().run {
if (performRequest(Request("HEAD", "/views?include_type_name=false")).statusLine.statusCode == 404) {
Request(
"PUT",
"/views?include_type_name=false"
).apply {
//language=JSON
setJsonEntity(
"""
{
"settings": {
"number_of_shards": 5
},
"mappings": {
"properties": {
"logged": {
"type": "boolean"
},
"type": {
"type": "keyword"
},
"user_ref": {
"type": "keyword"
},
"id": {
"type": "keyword"
},
"version_id": {
"type": "keyword"
},
"ip": {
"type": "keyword"
},
"citizen_id": {
"type": "keyword"
},
"view_at": {
"type": "date"
}
}
}
}
""".trimIndent()
)
}.let {
performRequest(it)
}
}
}
install(WebSockets) { install(WebSockets) {
pingPeriod = Duration.ofSeconds(60) // Disabled (null) by default pingPeriod = Duration.ofSeconds(60) // Disabled (null) by default

View File

@@ -0,0 +1,83 @@
package fr.dcproject.elasticsearch
import org.elasticsearch.client.Request
import org.elasticsearch.client.RestClient
import org.slf4j.Logger
import org.slf4j.LoggerFactory
fun waitElasticsearchIsUp(client: RestClient) {
val logger: Logger = LoggerFactory.getLogger("fr.dcproject.elasticsearch")
val request = Request("GET", "/_cluster/health")
repeat(40) {
runCatching {
client.performRequest(request).statusLine.statusCode
}.onSuccess {
if (it == 200) {
logger.debug("Elasticsearch is Ready! Continue...")
return
} else {
logger.debug("sleep 2s and retry...")
Thread.sleep(2000)
}
}.onFailure {
logger.debug("${it.message}, sleep 2s and retry...")
Thread.sleep(2000)
}
}
error("Elasticsearch is not ready")
}
fun configElasticIndexes(client: RestClient) {
waitElasticsearchIsUp(client)
/* Create index if not exist */
client.run {
if (performRequest(Request("HEAD", "/views?include_type_name=false")).statusLine.statusCode == 404) {
Request(
"PUT",
"/views?include_type_name=false"
).apply {
//language=JSON
setJsonEntity(
"""
{
"settings": {
"number_of_shards": 5
},
"mappings": {
"properties": {
"logged": {
"type": "boolean"
},
"type": {
"type": "keyword"
},
"user_ref": {
"type": "keyword"
},
"id": {
"type": "keyword"
},
"version_id": {
"type": "keyword"
},
"ip": {
"type": "keyword"
},
"citizen_id": {
"type": "keyword"
},
"view_at": {
"type": "date"
}
}
}
}
""".trimIndent()
)
}.let {
performRequest(it)
}
}
}
}

View File

@@ -21,7 +21,12 @@ class Citizen(
) : CitizenFull, ) : CitizenFull,
CitizenBasic(id, name, email, birthday, voteAnonymous, followAnonymous, user), CitizenBasic(id, name, email, birthday, voteAnonymous, followAnonymous, user),
EntityCreatedAt by EntityCreatedAtImp() { EntityCreatedAt by EntityCreatedAtImp() {
var workgroups: List<WorkgroupSimple<CitizenRef>> = emptyList() var workgroups: List<WorkgroupAndRoles> = emptyList()
class WorkgroupAndRoles(
val roles: List<String>,
val workgroup: WorkgroupSimple<CitizenRef>
)
} }
open class CitizenBasic( open class CitizenBasic(

View File

@@ -1,5 +1,8 @@
package fr.dcproject.entity package fr.dcproject.entity
import fr.dcproject.entity.WorkgroupWithMembersI.Member
import fr.dcproject.entity.WorkgroupWithMembersI.Member.Role
import fr.postgresjson.entity.EntityI
import fr.postgresjson.entity.immutable.* import fr.postgresjson.entity.immutable.*
import fr.postgresjson.entity.mutable.EntityDeletedAt import fr.postgresjson.entity.mutable.EntityDeletedAt
import fr.postgresjson.entity.mutable.EntityDeletedAtImp import fr.postgresjson.entity.mutable.EntityDeletedAtImp
@@ -11,9 +14,8 @@ class Workgroup(
description: String, description: String,
logo: String? = null, logo: String? = null,
anonymous: Boolean = true, anonymous: Boolean = true,
owner: CitizenBasic,
createdBy: CitizenBasic, createdBy: CitizenBasic,
override var members: List<CitizenBasic> = emptyList() override var members: List<Member<CitizenBasic>> = emptyList()
) : WorkgroupWithAuthI<CitizenBasic>, ) : WorkgroupWithAuthI<CitizenBasic>,
WorkgroupSimple<CitizenBasic>( WorkgroupSimple<CitizenBasic>(
id, id,
@@ -21,7 +23,6 @@ class Workgroup(
description, description,
logo, logo,
anonymous, anonymous,
owner,
createdBy createdBy
), ),
EntityCreatedAt by EntityCreatedAtImp(), EntityCreatedAt by EntityCreatedAtImp(),
@@ -33,7 +34,6 @@ open class WorkgroupSimple<Z : CitizenRef>(
var description: String, var description: String,
var logo: String? = null, var logo: String? = null,
var anonymous: Boolean = true, var anonymous: Boolean = true,
var owner: Z,
createdBy: Z createdBy: Z
) : WorkgroupRef(id), ) : WorkgroupRef(id),
EntityCreatedBy<Z> by EntityCreatedByImp(createdBy), EntityCreatedBy<Z> by EntityCreatedByImp(createdBy),
@@ -45,19 +45,51 @@ open class WorkgroupRef(
interface WorkgroupWithAuthI<Z : CitizenWithUserI> : WorkgroupWithMembersI<Z>, EntityCreatedBy<Z>, EntityDeletedAt { interface WorkgroupWithAuthI<Z : CitizenWithUserI> : WorkgroupWithMembersI<Z>, EntityCreatedBy<Z>, EntityDeletedAt {
val anonymous: Boolean val anonymous: Boolean
val owner: Z
fun isMember(user: UserI): Boolean = fun isMember(user: UserI): Boolean = members.isMember(user)
members.map { it.user.id }.contains(user.id) || owner.user.id == user.id fun isMember(citizen: CitizenWithUserI): Boolean = members.isMember(citizen)
fun isMember(citizen: CitizenWithUserI): Boolean = fun hasRole(expectedRole: Role, user: UserI): Boolean = members.hasRole(expectedRole, user)
isMember(citizen.user) fun hasRole(expectedRole: Role, citizen: CitizenI): Boolean = members.hasRole(expectedRole, citizen)
fun getRoles(user: UserI): List<Role> = members.getRoles(user)
fun getRoles(citizen: CitizenI): List<Role> = members.getRoles(citizen)
} }
interface WorkgroupWithMembersI<Z : CitizenI> : WorkgroupI { interface WorkgroupWithMembersI<Z : CitizenI> : WorkgroupI {
var members: List<Z> var members: List<Member<Z>>
class Member<C : CitizenI> (
val citizen: C,
val roles: List<Role> = emptyList()
) : EntityI {
enum class Role {
MASTER,
MANAGER,
EDITOR,
REPORTER
}
}
} }
fun List<CitizenI>.asCitizen(citizen: CitizenI): Boolean = this.map { it.id }.contains(citizen.id) fun List<CitizenI>.hasCitizen(citizen: CitizenI): Boolean = this.map { it.id }.contains(citizen.id)
fun <Z : CitizenWithUserI> List<Member<Z>>.isMember(user: UserI): Boolean =
map { it.citizen.user.id }.contains(user.id)
fun <Z : CitizenI> List<Member<Z>>.isMember(citizen: CitizenI): Boolean =
map { it.citizen.id }.contains(citizen.id)
fun <Z : CitizenI> List<Member<Z>>.hasRole(expectedRole: Role, citizen: CitizenI): Boolean =
any { member -> member.citizen.id == citizen.id && member.roles.any { it == expectedRole } }
fun <Z : CitizenWithUserI> List<Member<Z>>.hasRole(expectedRole: Role, user: UserI): Boolean =
any { member -> member.citizen.user.id == user.id && member.roles.any { it == expectedRole } }
fun <Z : CitizenWithUserI> List<Member<Z>>.getRoles(user: UserI): List<Role> =
firstOrNull { it.citizen.user.id == user.id }?.roles ?: emptyList()
fun <Z : CitizenWithUserI> List<Member<Z>>.getRoles(citizen: CitizenI): List<Role> =
firstOrNull { it.citizen.id == citizen.id }?.roles ?: emptyList()
interface WorkgroupI : UuidEntityI interface WorkgroupI : UuidEntityI

View File

@@ -12,29 +12,21 @@ import java.util.*
import fr.dcproject.entity.Citizen as CitizenEntity import fr.dcproject.entity.Citizen as CitizenEntity
class Citizen(override var requester: Requester) : RepositoryI { class Citizen(override var requester: Requester) : RepositoryI {
fun findById(id: UUID, withUser: Boolean = false): CitizenEntity? { fun findById(id: UUID): CitizenEntity? = requester
return requester .getFunction("find_citizen_by_id_with_user_and_workgroups")
.getFunction(if (withUser) "find_citizen_by_id_with_user" else "find_citizen_by_id") .selectOne("id" to id)
.selectOne("id" to id)
}
fun findByUser(user: UserI): CitizenEntity? { fun findByUser(user: UserI): CitizenEntity? = requester
return requester .getFunction("find_citizen_by_user_id")
.getFunction("find_citizen_by_user_id") .selectOne("user_id" to user.id)
.selectOne("user_id" to user.id)
}
fun findByUsername(unsername: String): CitizenEntity? { fun findByUsername(unsername: String): CitizenEntity? = requester
return requester .getFunction("find_citizen_by_username")
.getFunction("find_citizen_by_username") .selectOne("username" to unsername)
.selectOne("username" to unsername)
}
fun findByEmail(email: String): CitizenEntity? { fun findByEmail(email: String): CitizenEntity? = requester
return requester .getFunction("find_citizen_by_email")
.getFunction("find_citizen_by_email") .selectOne("email" to email)
.selectOne("email" to email)
}
fun find( fun find(
page: Int = 1, page: Int = 1,
@@ -42,8 +34,7 @@ class Citizen(override var requester: Requester) : RepositoryI {
sort: String? = null, sort: String? = null,
direction: Direction? = null, direction: Direction? = null,
search: String? = null search: String? = null
): Paginated<CitizenBasic> { ): Paginated<CitizenBasic> = requester
return requester
.getFunction("find_citizens") .getFunction("find_citizens")
.select( .select(
page, limit, page, limit,
@@ -51,17 +42,12 @@ class Citizen(override var requester: Requester) : RepositoryI {
"direction" to direction, "direction" to direction,
"search" to search "search" to search
) )
}
fun upsert(citizen: CitizenFull): CitizenEntity? { fun upsert(citizen: CitizenFull): CitizenEntity? = requester
return requester
.getFunction("upsert_citizen") .getFunction("upsert_citizen")
.selectOne("resource" to citizen) .selectOne("resource" to citizen)
}
fun insertWithUser(citizen: CitizenFull): CitizenEntity? { fun insertWithUser(citizen: CitizenFull): CitizenEntity? = requester
return requester
.getFunction("insert_citizen_with_user") .getFunction("insert_citizen_with_user")
.selectOne("resource" to citizen) .selectOne("resource" to citizen)
}
} }

View File

@@ -1,6 +1,7 @@
package fr.dcproject.repository package fr.dcproject.repository
import fr.dcproject.entity.* import fr.dcproject.entity.*
import fr.dcproject.entity.WorkgroupWithMembersI.Member
import fr.postgresjson.connexion.Paginated import fr.postgresjson.connexion.Paginated
import fr.postgresjson.connexion.Requester import fr.postgresjson.connexion.Requester
import fr.postgresjson.entity.Parameter import fr.postgresjson.entity.Parameter
@@ -44,36 +45,43 @@ class Workgroup(override var requester: Requester) : RepositoryI {
.getFunction("delete_workgroup") .getFunction("delete_workgroup")
.perform("id" to workgroup.id) .perform("id" to workgroup.id)
fun addMember(workgroup: WorkgroupI, member: CitizenI): List<CitizenBasic> = fun addMember(workgroup: WorkgroupI, member: Member<CitizenI>): Member<CitizenBasic>? =
addMembers(workgroup, listOf(member)) addMember(workgroup, member.citizen, member.roles)
fun addMembers(workgroup: WorkgroupI, members: List<CitizenI>): List<CitizenBasic> = requester fun addMember(workgroup: WorkgroupI, citizen: CitizenI, roles: List<Member.Role>): Member<CitizenBasic>? = requester
.getFunction("add_workgroup_member")
.selectOne(
"id" to workgroup.id,
"members" to Member(citizen, roles).serialize()
)
fun <Z : CitizenI> addMembers(workgroup: WorkgroupI, members: List<Member<Z>>): List<Member<CitizenBasic>> = requester
.getFunction("add_workgroup_members") .getFunction("add_workgroup_members")
.select( .select(
"id" to workgroup.id, "id" to workgroup.id,
"resource" to members.serialize() "members" to members.serialize()
) )
fun removeMember(workgroup: WorkgroupI, memberToDelete: CitizenI): List<CitizenBasic> = fun <Z : CitizenI> removeMember(workgroup: WorkgroupI, memberToDelete: Member<Z>): List<Member<CitizenBasic>> =
removeMembers(workgroup, listOf(memberToDelete)) removeMembers(workgroup, listOf(memberToDelete))
fun removeMembers(workgroup: WorkgroupI, membersToDelete: List<CitizenI>): List<CitizenBasic> = requester fun <Z : CitizenI> removeMembers(workgroup: WorkgroupI, membersToDelete: List<Member<Z>>): List<Member<CitizenBasic>> = requester
.getFunction("remove_workgroup_members") .getFunction("remove_workgroup_members")
.select( .select(
"id" to workgroup.id, "id" to workgroup.id,
"resource" to membersToDelete "members" to membersToDelete
) )
fun updateMembers(workgroup: WorkgroupI, members: List<CitizenI>): List<CitizenBasic> = requester fun <Z : CitizenI> updateMembers(workgroup: WorkgroupI, members: List<Member<Z>>): List<Member<CitizenBasic>> = requester
.getFunction("update_workgroup_members") .getFunction("update_workgroup_members")
.select( .select(
"id" to workgroup.id, "id" to workgroup.id,
"resource" to members "members" to members
) )
fun <W : WorkgroupWithMembersI<CitizenI>> updateMembers(workgroup: W): W { fun <W : WorkgroupWithMembersI<Z>, Z : CitizenI> updateMembers(workgroup: W): W {
updateMembers(workgroup, workgroup.members).let { updateMembers(workgroup, workgroup.members).let {
workgroup.members = it workgroup.members = it as List<Member<Z>>
} }
return workgroup return workgroup

View File

@@ -3,31 +3,27 @@ package fr.dcproject.routes
import fr.dcproject.citizen import fr.dcproject.citizen
import fr.dcproject.entity.CitizenRef import fr.dcproject.entity.CitizenRef
import fr.dcproject.entity.WorkgroupSimple import fr.dcproject.entity.WorkgroupSimple
import fr.dcproject.entity.WorkgroupWithMembersI.Member
import fr.dcproject.entity.WorkgroupWithMembersI.Member.Role
import fr.dcproject.repository.Workgroup.Filter import fr.dcproject.repository.Workgroup.Filter
import fr.dcproject.security.voter.WorkgroupVoter.Action.VIEW
import fr.dcproject.security.voter.WorkgroupVoter.Action.CREATE import fr.dcproject.security.voter.WorkgroupVoter.Action.CREATE
import fr.dcproject.security.voter.WorkgroupVoter.Action.UPDATE import fr.dcproject.security.voter.WorkgroupVoter.Action.UPDATE
import fr.dcproject.security.voter.WorkgroupVoter.ActionMembers.ADD as ADD_MEMBERS import fr.dcproject.security.voter.WorkgroupVoter.Action.VIEW
import fr.dcproject.security.voter.WorkgroupVoter.ActionMembers.UPDATE as UPDATE_MEMBERS
import fr.dcproject.security.voter.WorkgroupVoter.ActionMembers.REMOVE as REMOVE_MEMBERS
import fr.ktorVoter.assertCan import fr.ktorVoter.assertCan
import fr.dcproject.utils.toUUID
import fr.postgresjson.repository.RepositoryI import fr.postgresjson.repository.RepositoryI
import io.ktor.application.ApplicationCall import io.ktor.application.ApplicationCall
import io.ktor.application.call import io.ktor.application.call
import io.ktor.http.HttpStatusCode import io.ktor.http.HttpStatusCode
import io.ktor.locations.KtorExperimentalLocationsAPI import io.ktor.locations.*
import io.ktor.locations.Location
import io.ktor.locations.get
import io.ktor.locations.post
import io.ktor.locations.put
import io.ktor.locations.delete
import io.ktor.request.receive import io.ktor.request.receive
import io.ktor.response.respond import io.ktor.response.respond
import io.ktor.routing.Route import io.ktor.routing.Route
import java.util.* import java.util.*
import fr.dcproject.entity.Workgroup as WorkgroupEntity import fr.dcproject.entity.Workgroup as WorkgroupEntity
import fr.dcproject.repository.Workgroup as WorkgroupRepository import fr.dcproject.repository.Workgroup as WorkgroupRepository
import fr.dcproject.security.voter.WorkgroupVoter.ActionMembers.ADD as ADD_MEMBERS
import fr.dcproject.security.voter.WorkgroupVoter.ActionMembers.REMOVE as REMOVE_MEMBERS
import fr.dcproject.security.voter.WorkgroupVoter.ActionMembers.UPDATE as UPDATE_MEMBERS
@KtorExperimentalLocationsAPI @KtorExperimentalLocationsAPI
object WorkgroupsPaths { object WorkgroupsPaths {
@@ -54,8 +50,7 @@ object WorkgroupsPaths {
val name: String, val name: String,
val description: String, val description: String,
val logo: String?, val logo: String?,
val anonymous: Boolean?, val anonymous: Boolean?
val owner: UUID?
) )
suspend fun getNewWorkgroup(call: ApplicationCall): WorkgroupSimple<CitizenRef> = call.receive<Body>().run { suspend fun getNewWorkgroup(call: ApplicationCall): WorkgroupSimple<CitizenRef> = call.receive<Body>().run {
@@ -65,7 +60,6 @@ object WorkgroupsPaths {
description, description,
logo, logo,
anonymous ?: true, anonymous ?: true,
owner?.let { CitizenRef(it) } ?: call.citizen,
call.citizen call.citizen
) )
} }
@@ -77,8 +71,7 @@ object WorkgroupsPaths {
val name: String?, val name: String?,
val description: String?, val description: String?,
val logo: String?, val logo: String?,
val anonymous: Boolean?, val anonymous: Boolean?
val owner: UUID?
) )
suspend fun updateWorkgroup(call: ApplicationCall): Unit = call.receive<Body>().run { suspend fun updateWorkgroup(call: ApplicationCall): Unit = call.receive<Body>().run {
@@ -98,13 +91,18 @@ object WorkgroupsMembersPaths {
@Location("/workgroups/{workgroup}/members") @Location("/workgroups/{workgroup}/members")
class WorkgroupsMembersRequest(val workgroup: WorkgroupEntity) { class WorkgroupsMembersRequest(val workgroup: WorkgroupEntity) {
class Body : MutableList<Body.Item> by mutableListOf() { class Body : MutableList<Body.Item> by mutableListOf() {
class Item(id: String) { class Item(val citizen: CitizenRef, roles: List<String> = emptyList()) {
val id = id.toUUID() val roles: List<Role> = roles.map {
Role.valueOf(it)
}
} }
} }
suspend fun getMembers(call: ApplicationCall): List<CitizenRef> = call.receive<Body>().map { suspend fun getMembers(call: ApplicationCall): List<Member<CitizenRef>> = call.receive<Body>().map {
CitizenRef(it.id) Member(
citizen = it.citizen,
roles = it.roles
)
} }
} }
} }
@@ -155,8 +153,8 @@ fun Route.workgroup(repo: WorkgroupRepository) {
.let { members -> .let { members ->
assertCan(ADD_MEMBERS, it.workgroup) assertCan(ADD_MEMBERS, it.workgroup)
repo.addMembers(it.workgroup, members) repo.addMembers(it.workgroup, members)
}.let { }.let { members ->
call.respond(HttpStatusCode.Created, it) call.respond(HttpStatusCode.Created, members)
} }
} }
@@ -166,8 +164,8 @@ fun Route.workgroup(repo: WorkgroupRepository) {
.let { members -> .let { members ->
assertCan(REMOVE_MEMBERS, it.workgroup) assertCan(REMOVE_MEMBERS, it.workgroup)
repo.removeMembers(it.workgroup, members) repo.removeMembers(it.workgroup, members)
}.let { }.let { members ->
call.respond(HttpStatusCode.OK, it) call.respond(HttpStatusCode.OK, members)
} }
} }
@@ -177,8 +175,8 @@ fun Route.workgroup(repo: WorkgroupRepository) {
.let { members -> .let { members ->
assertCan(UPDATE_MEMBERS, it.workgroup) assertCan(UPDATE_MEMBERS, it.workgroup)
repo.updateMembers(it.workgroup, members) repo.updateMembers(it.workgroup, members)
}.let { }.let { members ->
call.respond(HttpStatusCode.OK, it) call.respond(HttpStatusCode.OK, members)
} }
} }
} }

View File

@@ -1,7 +1,7 @@
package fr.dcproject.security.voter package fr.dcproject.security.voter
import fr.dcproject.citizenOrNull
import fr.dcproject.entity.* import fr.dcproject.entity.*
import fr.dcproject.entity.WorkgroupWithMembersI.Member.Role
import fr.dcproject.user import fr.dcproject.user
import fr.ktorVoter.ActionI import fr.ktorVoter.ActionI
import fr.ktorVoter.Vote import fr.ktorVoter.Vote
@@ -46,11 +46,11 @@ class WorkgroupVoter : Voter {
} }
if (subject is WorkgroupWithAuthI<*>) { if (subject is WorkgroupWithAuthI<*>) {
if (action == Action.DELETE && user is UserI && subject.owner.user.id == user.id) { if (action == Action.DELETE && user is UserI && subject.hasRole(Role.MASTER, user)) {
return Vote.GRANTED return Vote.GRANTED
} }
if (action == Action.UPDATE && user is UserI && subject.owner.user.id == user.id) { if (action == Action.UPDATE && user is UserI && subject.hasRole(Role.MASTER, user)) {
return Vote.GRANTED return Vote.GRANTED
} }
@@ -61,32 +61,29 @@ class WorkgroupVoter : Voter {
} }
if (action == ActionMembers.ADD) { if (action == ActionMembers.ADD) {
val citizen = call.citizenOrNull
// TODO create ROLES // TODO create ROLES
return Vote.isGranted { return Vote.isGranted {
citizen != null && user is UserI &&
subject is WorkgroupWithMembersI<*> && subject is WorkgroupWithAuthI<*> &&
subject.members.asCitizen(citizen) subject.hasRole(Role.MASTER, user)
} }
} }
if (action == ActionMembers.UPDATE) { if (action == ActionMembers.UPDATE) {
val citizen = call.citizenOrNull
// TODO create ROLES // TODO create ROLES
return Vote.isGranted { return Vote.isGranted {
citizen != null && user is UserI &&
subject is WorkgroupWithMembersI<*> && subject is WorkgroupWithAuthI<*> &&
subject.members.asCitizen(citizen) subject.hasRole(Role.MASTER, user)
} }
} }
if (action == ActionMembers.REMOVE) { if (action == ActionMembers.REMOVE) {
val citizen = call.citizenOrNull
// TODO create ROLES // TODO create ROLES
return Vote.isGranted { return Vote.isGranted {
citizen != null && user is UserI &&
subject is WorkgroupWithMembersI<*> && subject is WorkgroupWithAuthI<*> &&
subject.members.asCitizen(citizen) subject.hasRole(Role.MASTER, user)
} }
} }

View File

@@ -844,6 +844,7 @@ paths:
/workgroups: /workgroups:
get: get:
summary: Get all Workgroup (Paginated)
security: security:
- {} - {}
- JWTAuth: [] - JWTAuth: []
@@ -864,6 +865,7 @@ paths:
items: items:
$ref: '#/components/schemas/WorkgroupSimple' $ref: '#/components/schemas/WorkgroupSimple'
post: post:
summary: Create new Workgroup
security: security:
- JWTAuth: [] - JWTAuth: []
tags: tags:
@@ -925,6 +927,7 @@ paths:
parameters: parameters:
- $ref: '#/components/parameters/workgroup' - $ref: '#/components/parameters/workgroup'
get: get:
summary: Get one workgroup by ID
security: security:
- {} - {}
- JWTAuth: [] - JWTAuth: []
@@ -939,6 +942,7 @@ paths:
allOf: allOf:
- $ref: '#/components/schemas/Workgroup' - $ref: '#/components/schemas/Workgroup'
put: put:
summary: Edit one workgroup
security: security:
- JWTAuth: [] - JWTAuth: []
tags: tags:
@@ -993,6 +997,7 @@ paths:
type: string type: string
format: uuid format: uuid
delete: delete:
summary: Delete one workgroup
security: security:
- JWTAuth: [] - JWTAuth: []
tags: tags:
@@ -1004,6 +1009,7 @@ paths:
parameters: parameters:
- $ref: '#/components/parameters/workgroup' - $ref: '#/components/parameters/workgroup'
post: post:
summary: Add members to the workgroup
security: security:
- JWTAuth: [] - JWTAuth: []
tags: tags:
@@ -1012,24 +1018,16 @@ paths:
content: content:
application/json: application/json:
schema: schema:
type: array $ref: '#/components/schemas/MembersRequest'
items:
type: object
properties:
id:
type: string
format: uuid
example: 6434f4f9-f570-f22a-c134-8668350651ff
responses: responses:
201: 201:
description: the list of members description: the list of members
content: content:
application/json: application/json:
schema: schema:
type: array $ref: '#/components/schemas/MembersResponse'
items:
$ref: '#/components/schemas/CitizenResponse'
put: put:
summary: Updates ALL members. PLEASE NOTE this removes all members who are not in request!
security: security:
- JWTAuth: [] - JWTAuth: []
tags: tags:
@@ -1038,24 +1036,16 @@ paths:
content: content:
application/json: application/json:
schema: schema:
type: array $ref: '#/components/schemas/MembersRequest'
items:
type: object
properties:
id:
type: string
format: uuid
example: 6434f4f9-f570-f22a-c134-8668350651ff
responses: responses:
201: 201:
description: the list of members description: the list of members
content: content:
application/json: application/json:
schema: schema:
type: array $ref: '#/components/schemas/MembersResponse'
items:
$ref: '#/components/schemas/CitizenResponse'
delete: delete:
summary: Delete members of workgroup
security: security:
- JWTAuth: [] - JWTAuth: []
tags: tags:
@@ -1064,23 +1054,14 @@ paths:
content: content:
application/json: application/json:
schema: schema:
type: array $ref: '#/components/schemas/MembersRequest'
items:
type: object
properties:
id:
type: string
format: uuid
example: 6434f4f9-f570-f22a-c134-8668350651ff
responses: responses:
200: 200:
description: the list of members description: the list of members
content: content:
application/json: application/json:
schema: schema:
type: array $ref: '#/components/schemas/MembersResponse'
items:
$ref: '#/components/schemas/CitizenResponse'
components: components:
responses: responses:
@@ -1708,8 +1689,6 @@ components:
anonymous: anonymous:
type: boolean type: boolean
example: false example: false
owner:
$ref: '#/components/schemas/CitizenResponse'
- $ref: '#/components/schemas/CreatedBy' - $ref: '#/components/schemas/CreatedBy'
- $ref: '#/components/schemas/DeletedAt' - $ref: '#/components/schemas/DeletedAt'
Workgroup: Workgroup:
@@ -1719,11 +1698,51 @@ components:
- type: object - type: object
properties: properties:
members: members:
type: array $ref: '#/components/schemas/MembersResponse'
items:
$ref: '#/components/schemas/CitizenResponse'
- $ref: '#/components/schemas/CreatedAt' - $ref: '#/components/schemas/CreatedAt'
- $ref: '#/components/schemas/UpdatedAt' - $ref: '#/components/schemas/UpdatedAt'
MembersRequest:
description: members of workgroup
type: array
items:
$ref: '#/components/schemas/MemberRequest'
MembersResponse:
description: members of workgroup
type: array
items:
$ref: '#/components/schemas/MemberResponse'
MemberResponse:
description: Member of workgroup
type: object
properties:
citizen:
$ref: '#/components/schemas/CitizenResponse'
roles:
type: array
items:
type: string
enum:
- MASTER
- MANAGER
- EDITOR
- REPORTER
example: MASTER
MemberRequest:
description: Member of workgroup
type: object
properties:
citizen:
$ref: '#/components/schemas/UuidEntity'
roles:
type: array
items:
type: string
enum:
- MASTER
- MANAGER
- EDITOR
- REPORTER
example: MASTER

View File

@@ -4,7 +4,6 @@ declare
_password text := crypt('azerty', gen_salt('bf', 8)); _password text := crypt('azerty', gen_salt('bf', 8));
multiple int = coalesce(current_setting('fixture.quantity.multiple', true), '50')::int; multiple int = coalesce(current_setting('fixture.quantity.multiple', true), '50')::int;
begin begin
delete from "user";
insert into "user" (id, username, password, blocked_at, roles) insert into "user" (id, username, password, blocked_at, roles)
select select
uuid_in(md5('user'||rn::text)::cstring), uuid_in(md5('user'||rn::text)::cstring),

View File

@@ -1,7 +1,6 @@
do do
$$ $$
begin begin
delete from citizen;
insert into citizen (id, name, birthday, user_id, vote_anonymous, follow_anonymous, email) insert into citizen (id, name, birthday, user_id, vote_anonymous, follow_anonymous, email)
select select
uuid_in(md5('citizen'||row_number() over ()::text)::cstring), uuid_in(md5('citizen'||row_number() over ()::text)::cstring),

View File

@@ -2,27 +2,37 @@ do
$$ $$
declare declare
citizen_count int = (select count(*) from citizen); citizen_count int = (select count(*) from citizen);
_roles text[] = $roles$
{
"MANAGER", "EDITOR", "REPORTER"
}
$roles$;
begin begin
delete from citizen_in_workgroup; insert into workgroup (id, created_by_id, name, description, anonymous)
delete from workgroup;
insert into workgroup (id, created_by_id, name, description, anonymous, owner_id)
select select
uuid_in(md5('workgroup'||rn::text)::cstring), uuid_in(md5('workgroup'||rn::text)::cstring),
z.id, z.id,
'name' || rn, 'name' || rn,
'description' || rn, 'description' || rn,
rn % 3 = 1, rn % 3 = 1
z.id
from (select *, row_number() over () rn from citizen) z; from (select *, row_number() over () rn from citizen) z;
insert into citizen_in_workgroup (citizen_id, workgroup_id) insert into citizen_in_workgroup (citizen_id, workgroup_id, roles)
select select
z.id, z.id,
w.id w.id,
'{MASTER}'
from (select *, row_number() over ()+5 % citizen_count rn from citizen) z from (select *, row_number() over ()+5 % citizen_count rn from citizen) z
join (select *, row_number() over () rn from workgroup) w using (rn); join (select *, row_number() over () rn from workgroup) w using (rn);
insert into citizen_in_workgroup (citizen_id, workgroup_id, roles)
select
z.id,
w.id,
_roles[(row_number() over () % 3)+1:(row_number() over () % 3)+1]
from (select *, row_number() over ()+10 % citizen_count rn from citizen) z
join (select *, row_number() over () rn from workgroup) w using (rn);
raise notice 'workgroup fixtures done'; raise notice 'workgroup fixtures done';
end; end;
$$; $$;

View File

@@ -11,9 +11,6 @@ declare
_citizen_count int = (select count(z) from citizen z); _citizen_count int = (select count(z) from citizen z);
_workgroup_count int = (select count(w) from workgroup w); _workgroup_count int = (select count(w) from workgroup w);
begin begin
delete from article_relations;
delete from article;
insert into article (id, version_id, created_by_id, workgroup_id, title, anonymous, content, description, tags, created_at, draft) insert into article (id, version_id, created_by_id, workgroup_id, title, anonymous, content, description, tags, created_at, draft)
select select
uuid_in(md5('article'||row_number() over ())::cstring), uuid_in(md5('article'||row_number() over ())::cstring),

View File

@@ -3,10 +3,6 @@ $$
declare declare
article_count int = (select count(*) from article); article_count int = (select count(*) from article);
begin begin
delete from article_in_title;
delete from title;
delete from constitution;
insert into constitution (id, version_id, created_by_id, title, anonymous, created_at) insert into constitution (id, version_id, created_by_id, title, anonymous, created_at)
select select
uuid_in(md5('constitution'||row_number() over ())::cstring), uuid_in(md5('constitution'||row_number() over ())::cstring),

View File

@@ -3,8 +3,6 @@ $$
declare declare
article_count int = (select count(*) from article); article_count int = (select count(*) from article);
begin begin
delete from follow;
insert into follow_article (id, created_by_id, target_id) insert into follow_article (id, created_by_id, target_id)
select select
uuid_in(md5('follow_article'||row_number() over ())::cstring), uuid_in(md5('follow_article'||row_number() over ())::cstring),

View File

@@ -3,8 +3,6 @@ $$
declare declare
article_count int = (select count(*) from article); article_count int = (select count(*) from article);
begin begin
delete from comment;
insert into comment_on_article (id, created_by_id, target_id, content) insert into comment_on_article (id, created_by_id, target_id, content)
select select
uuid_in(md5('comment_on_article'||row_number() over ())::cstring), uuid_in(md5('comment_on_article'||row_number() over ())::cstring),

View File

@@ -3,11 +3,6 @@ $$
declare declare
article_count int = (select count(*) from article); article_count int = (select count(*) from article);
begin begin
delete from vote_for_article;
delete from vote_for_constitution;
delete from vote_for_comment_on_article;
delete from vote_for_comment_on_constitution;
insert into vote_for_article (id, created_by_id, target_id, note, anonymous) insert into vote_for_article (id, created_by_id, target_id, note, anonymous)
select select
uuid_in(md5('vote_for_article'||row_number() over ())::cstring), uuid_in(md5('vote_for_article'||row_number() over ())::cstring),

View File

@@ -3,9 +3,6 @@ $$
declare declare
article_count int = (select count(*) from article); article_count int = (select count(*) from article);
begin begin
delete from opinion_on_article;
delete from opinion_choice;
insert into opinion_choice (id, name, target) insert into opinion_choice (id, name, target)
select select
uuid_in(md5('opinion_choice'||row_number() over ())::cstring), uuid_in(md5('opinion_choice'||row_number() over ())::cstring),

View File

@@ -1,6 +1,5 @@
#bin/bash #!/usr/bin/env bash
echo "Start fixtures" awk 'FNR==1{print "--------------------"}1' `dirname $0`/*.sql > ./allSQL.sql
awk 'FNR==1{print "--------------------"}1' ./*.sql > ./allSQL.sql
docker exec -i postgresql_dc-project psql dc-project dc-project -q -b -v "ON_ERROR_STOP=1" < ./allSQL.sql docker exec -i postgresql_dc-project psql dc-project dc-project -q -b -v "ON_ERROR_STOP=1" < ./allSQL.sql
rm ./allSQL.sql rm ./allSQL.sql
echo "End fixtures" echo "End fixtures"

View File

@@ -7,7 +7,7 @@ begin
from ( from (
select select
a.*, a.*,
find_citizen_by_id(a.created_by_id) as created_by, find_citizen_by_id_with_user(a.created_by_id) as created_by,
find_workgroup_by_id(a.workgroup_id) as workgroup, find_workgroup_by_id(a.workgroup_id) as workgroup,
count_vote(a.id) as votes, count_vote(a.id) as votes,
count_opinion(a.id) as opinions count_opinion(a.id) as opinions

View File

@@ -21,7 +21,7 @@ begin
from ( from (
select select
a.*, a.*,
find_citizen_by_id(a.created_by_id) as created_by, find_citizen_by_id_with_user(a.created_by_id) as created_by,
find_workgroup_by_id(a.workgroup_id) as workgroup, find_workgroup_by_id(a.workgroup_id) as workgroup,
count_vote(a.id) as votes, count_vote(a.id) as votes,
count_opinion(a.id) as opinions, count_opinion(a.id) as opinions,

View File

@@ -14,7 +14,7 @@ begin
from ( from (
select select
a.*, a.*,
find_citizen_by_id(a.created_by_id) as created_by, find_citizen_by_id_with_user(a.created_by_id) as created_by,
find_workgroup_by_id(a.workgroup_id) as workgroup, find_workgroup_by_id(a.workgroup_id) as workgroup,
count_vote(a.id) as votes count_vote(a.id) as votes
from article as a from article as a

View File

@@ -7,7 +7,7 @@ begin
from ( from (
select select
a.*, a.*,
find_citizen_by_id(a.created_by_id) as created_by, find_citizen_by_id_with_user(a.created_by_id) as created_by,
find_workgroup_by_id(a.workgroup_id) as workgroup, find_workgroup_by_id(a.workgroup_id) as workgroup,
count_vote(a.id) as votes count_vote(a.id) as votes
into resource into resource

View File

@@ -6,9 +6,7 @@ begin
select to_json(t) into resource select to_json(t) into resource
from ( from (
select select
z.*, z.*
find_user_by_id(z.user_id) as "user",
array_agg(find_workgroup_by_id_simple(ciw.workgroup_id)) as "workgroups"
from citizen as z from citizen as z
left join citizen_in_workgroup ciw on z.id = ciw.citizen_id left join citizen_in_workgroup ciw on z.id = ciw.citizen_id
where z.id = _id where z.id = _id

View File

@@ -7,8 +7,7 @@ begin
from ( from (
select select
z.*, z.*,
find_user_by_id(z.user_id) as "user", find_user_by_id(z.user_id) as "user"
array_agg(find_workgroup_by_id_simple(ciw.workgroup_id)) as "workgroups"
from citizen as z from citizen as z
left join citizen_in_workgroup ciw on z.id = ciw.citizen_id left join citizen_in_workgroup ciw on z.id = ciw.citizen_id
where z.id = _id where z.id = _id

View File

@@ -0,0 +1,22 @@
create or replace function find_citizen_by_id_with_user_and_workgroups(in id uuid, out resource json) language plpgsql as
$$
declare
_id alias for id;
begin
select to_json(t) into resource
from (
select
z.*,
find_user_by_id(z.user_id) as "user",
case when ciw.workgroup_id is null then '{}' else array_agg(json_build_object(
'roles', ciw.roles,
'workgroup', find_workgroup_by_id_simple(ciw.workgroup_id)
)) end as "workgroups"
from citizen as z
left join citizen_in_workgroup ciw on z.id = ciw.citizen_id
where z.id = _id
group by z.id, ciw.workgroup_id
) as t;
end;
$$;

View File

@@ -23,7 +23,7 @@ begin
email = excluded.email email = excluded.email
returning id into new_id; returning id into new_id;
select find_citizen_by_id(new_id) into resource; select find_citizen_by_id_with_user(new_id) into resource;
end; end;
$$; $$;

View File

@@ -10,7 +10,7 @@ begin
select select
com.*, com.*,
find_reference_by_id(com.target_id, com.target_reference) as target, find_reference_by_id(com.target_id, com.target_reference) as target,
find_citizen_by_id(com.created_by_id) as created_by, find_citizen_by_id_with_user(com.created_by_id) as created_by,
count_vote(com.id) as votes count_vote(com.id) as votes
from "comment" as com from "comment" as com
where id = _id where id = _id

View File

@@ -21,7 +21,7 @@ begin
select select
com.*, com.*,
find_reference_by_id(com.target_id, _reference) as target, find_reference_by_id(com.target_id, _reference) as target,
find_citizen_by_id(com.created_by_id) as created_by, find_citizen_by_id_with_user(com.created_by_id) as created_by,
count_vote(com.id) as votes count_vote(com.id) as votes
from "comment" as com from "comment" as com

View File

@@ -14,7 +14,7 @@ begin
com.*, com.*,
(select count(*) from "comment" c2 where c2.parents_ids @> array[com.id]) as children_count, (select count(*) from "comment" c2 where c2.parents_ids @> array[com.id]) as children_count,
find_reference_by_id(com.target_id, com.target_reference) as target, find_reference_by_id(com.target_id, com.target_reference) as target,
find_citizen_by_id(com.created_by_id) as created_by, find_citizen_by_id_with_user(com.created_by_id) as created_by,
count_vote(com.id) as votes count_vote(com.id) as votes
from "comment" as com from "comment" as com
where parent_id = _parent_id where parent_id = _parent_id

View File

@@ -15,7 +15,7 @@ begin
com.*, com.*,
(select count(c2) from "comment" c2 where c2.parent_comment_id = com.id) as children_count, (select count(c2) from "comment" c2 where c2.parent_comment_id = com.id) as children_count,
find_reference_by_id(com.target_id, com.target_reference) as target, find_reference_by_id(com.target_id, com.target_reference) as target,
find_citizen_by_id(com.created_by_id) as created_by, find_citizen_by_id_with_user(com.created_by_id) as created_by,
count_vote(com.id) as votes count_vote(com.id) as votes
from "comment" as com from "comment" as com
where com.parent_id = _target_id where com.parent_id = _target_id

View File

@@ -7,7 +7,7 @@ begin
from ( from (
select select
c.*, c.*,
find_citizen_by_id(c.created_by_id) as created_by, find_citizen_by_id_with_user(c.created_by_id) as created_by,
find_constitution_titles_by_id(c.id) as titles find_constitution_titles_by_id(c.id) as titles
into resource into resource
from constitution as c from constitution as c

View File

@@ -14,7 +14,7 @@ begin
from ( from (
select select
c.*, c.*,
find_citizen_by_id(c.created_by_id) as created_by, find_citizen_by_id_with_user(c.created_by_id) as created_by,
find_constitution_titles_by_id(c.id) as titles, find_constitution_titles_by_id(c.id) as titles,
zdb.score(c.ctid) _score zdb.score(c.ctid) _score
from constitution as c from constitution as c

View File

@@ -19,7 +19,7 @@ begin
select select
f.*, f.*,
json_build_object('id', f.target_id, 'reference', f.target_reference) as target, json_build_object('id', f.target_id, 'reference', f.target_reference) as target,
find_citizen_by_id(f.created_by_id) as created_by find_citizen_by_id_with_user(f.created_by_id) as created_by
from follow as f from follow as f
where f.created_by_id = _citizen_id where f.created_by_id = _citizen_id
and array[f.target_id] <@ _target_ids and array[f.target_id] <@ _target_ids
@@ -32,7 +32,7 @@ begin
select select
f.*, f.*,
json_build_object('id', f.target_id, 'reference', f.target_reference) as target, json_build_object('id', f.target_id, 'reference', f.target_reference) as target,
find_citizen_by_id(f.created_by_id) as created_by find_citizen_by_id_with_user(f.created_by_id) as created_by
from follow as f from follow as f
where f.created_by_id = _citizen_id where f.created_by_id = _citizen_id
and f.target_id = _target_id and f.target_id = _target_id

View File

@@ -13,7 +13,7 @@ begin
select select
f.*, f.*,
find_article_by_id(f.target_id) as target, find_article_by_id(f.target_id) as target,
find_citizen_by_id(f.created_by_id) as created_by find_citizen_by_id_with_user(f.created_by_id) as created_by
from follow as f from follow as f
where created_by_id = _created_by_id where created_by_id = _created_by_id
order by created_at desc, order by created_at desc,

View File

@@ -13,7 +13,7 @@ begin
select select
f.*, f.*,
json_build_object('id', f.target_id) as target, json_build_object('id', f.target_id) as target,
find_citizen_by_id(f.created_by_id) as created_by find_citizen_by_id_with_user(f.created_by_id) as created_by
from follow as f from follow as f
where created_by_id = _created_by_id where created_by_id = _created_by_id
order by created_at desc, order by created_at desc,

View File

@@ -13,7 +13,7 @@ begin
select select
f.*, f.*,
find_constitution_by_id(f.target_id) as target, find_constitution_by_id(f.target_id) as target,
find_citizen_by_id(f.created_by_id) as created_by find_citizen_by_id_with_user(f.created_by_id) as created_by
from follow as f from follow as f
where created_by_id = _created_by_id where created_by_id = _created_by_id
order by created_at desc, order by created_at desc,

View File

@@ -19,7 +19,7 @@ begin
select select
o.*, o.*,
find_reference_by_id(o.target_id, o.target_reference) as target, find_reference_by_id(o.target_id, o.target_reference) as target,
find_citizen_by_id(o.created_by_id) as created_by, find_citizen_by_id_with_user(o.created_by_id) as created_by,
to_json(ol) as choice to_json(ol) as choice
from opinion as o from opinion as o
join opinion_choice ol on o.choice_id = ol.id join opinion_choice ol on o.choice_id = ol.id

View File

@@ -12,7 +12,7 @@ begin
select select
o.*, o.*,
find_reference_by_id(o.target_id, o.target_reference) as target, find_reference_by_id(o.target_id, o.target_reference) as target,
find_citizen_by_id(o.created_by_id) as created_by, find_citizen_by_id_with_user(o.created_by_id) as created_by,
to_json(ol) as choice to_json(ol) as choice
from opinion as o from opinion as o
join opinion_choice ol on o.choice_id = ol.id join opinion_choice ol on o.choice_id = ol.id

View File

@@ -10,7 +10,7 @@ begin
select select
o.*, o.*,
find_reference_by_id(o.target_id, o.target_reference) as target, find_reference_by_id(o.target_id, o.target_reference) as target,
find_citizen_by_id(o.created_by_id) as created_by, find_citizen_by_id_with_user(o.created_by_id) as created_by,
to_json(ol) as choice to_json(ol) as choice
from opinion as o from opinion as o
join opinion_choice ol on o.choice_id = ol.id join opinion_choice ol on o.choice_id = ol.id

View File

@@ -10,7 +10,7 @@ begin
select select
o.*, o.*,
find_reference_by_id(o.target_id, o.target_reference) as target, find_reference_by_id(o.target_id, o.target_reference) as target,
find_citizen_by_id(o.created_by_id) as created_by, find_citizen_by_id_with_user(o.created_by_id) as created_by,
to_json(ol) as choice to_json(ol) as choice
from "opinion" as o from "opinion" as o
join opinion_choice ol on o.choice_id = ol.id join opinion_choice ol on o.choice_id = ol.id

View File

@@ -13,7 +13,7 @@ begin
select select
o.*, o.*,
find_reference_by_id(o.target_id, o.target_reference) as target, find_reference_by_id(o.target_id, o.target_reference) as target,
find_citizen_by_id(o.created_by_id) as created_by, find_citizen_by_id_with_user(o.created_by_id) as created_by,
to_json(ol) as choice to_json(ol) as choice
from "opinion" as o from "opinion" as o
join opinion_choice ol on o.choice_id = ol.id join opinion_choice ol on o.choice_id = ol.id

View File

@@ -13,7 +13,7 @@ begin
select select
v.*, v.*,
find_reference_by_id(v.target_id, v.target_reference) as target, find_reference_by_id(v.target_id, v.target_reference) as target,
find_citizen_by_id(v.created_by_id) as created_by find_citizen_by_id_with_user(v.created_by_id) as created_by
from vote as v from vote as v

View File

@@ -21,7 +21,7 @@ begin
select select
v.*, v.*,
find_reference_by_id(v.target_id, _reference) as target, find_reference_by_id(v.target_id, _reference) as target,
find_citizen_by_id(v.created_by_id) as created_by find_citizen_by_id_with_user(v.created_by_id) as created_by
from vote as v from vote as v

View File

@@ -0,0 +1,14 @@
create or replace function add_workgroup_member(in _id uuid, inout member json)
language plpgsql as
$$
begin
insert into citizen_in_workgroup (workgroup_id, citizen_id, roles)
values (
_id,
(member#>>'{citizen, id}')::uuid,
(select array_agg(t) from json_array_elements_text(member#>'{roles}') t)
)
on conflict (workgroup_id, citizen_id) do update set
roles = excluded.roles;
end;
$$;

View File

@@ -1,16 +1,11 @@
create or replace function add_workgroup_members(in _id uuid, inout resource json) create or replace function add_workgroup_members(in _id uuid, inout members json)
language plpgsql as language plpgsql as
$$ $$
begin begin
insert into citizen_in_workgroup (citizen_id, workgroup_id) perform add_workgroup_member(_id, b)
select from json_array_elements(members) b;
(z->>'id')::uuid,
_id::uuid
from json_array_elements(resource) z
where (z->>'id') is not null
on conflict do nothing;
select find_workgroup_members(_id) into resource; select find_workgroup_members(_id) into members;
end; end;
$$; $$;

View File

@@ -7,8 +7,7 @@ begin
from ( from (
select select
w.*, w.*,
find_citizen_by_id(w.created_by_id) as created_by, find_citizen_by_id_with_user(w.created_by_id) as created_by,
find_citizen_by_id(w.owner_id) as owner,
find_workgroup_members(w.id) as members find_workgroup_members(w.id) as members
into resource into resource
from workgroup as w from workgroup as w

View File

@@ -7,8 +7,7 @@ begin
from ( from (
select select
w.*, w.*,
json_build_object('id', w.created_by_id) as created_by, json_build_object('id', w.created_by_id) as created_by
json_build_object('id', w.owner_id) as owner
into resource into resource
from workgroup as w from workgroup as w
join citizen_in_workgroup ciw on w.id = ciw.workgroup_id join citizen_in_workgroup ciw on w.id = ciw.workgroup_id

View File

@@ -4,8 +4,9 @@ begin
select json_agg(t) into resource select json_agg(t) into resource
from ( from (
select select
z.*, w.id as id,
find_user_by_id(z.user_id) as "user" find_citizen_by_id_with_user(z.id) as citizen,
ciw.roles as roles
from citizen_in_workgroup as ciw from citizen_in_workgroup as ciw
join workgroup as w on ciw.workgroup_id = w.id join workgroup as w on ciw.workgroup_id = w.id
join citizen z on z.id = ciw.citizen_id join citizen z on z.id = ciw.citizen_id

View File

@@ -21,8 +21,7 @@ begin
from ( from (
select select
w.*, w.*,
find_citizen_by_id(w.created_by_id) as created_by, find_citizen_by_id_with_user(w.created_by_id) as created_by,
find_citizen_by_id(w.owner_id) as owner,
zdb.score(w.ctid) _score zdb.score(w.ctid) _score
from workgroup as w from workgroup as w
where deleted_at is null where deleted_at is null

View File

@@ -1,4 +1,4 @@
create or replace function remove_workgroup_members(in _id uuid, inout resource json) create or replace function remove_workgroup_members(in _id uuid, inout members json)
language plpgsql as language plpgsql as
$$ $$
begin begin
@@ -6,11 +6,11 @@ begin
where workgroup_id = _id where workgroup_id = _id
and citizen_id in ( and citizen_id in (
select select
(z->>'id')::uuid (b#>>'{citizen, id}')::uuid
from json_array_elements(resource) z from json_array_elements(members) b
); );
select find_workgroup_members(_id) into resource; select find_workgroup_members(_id) into members;
end end
$$; $$;

View File

@@ -1,25 +1,20 @@
create or replace function update_workgroup_members(in _id uuid, inout resource json) create or replace function update_workgroup_members(in _id uuid, inout members json)
language plpgsql as language plpgsql as
$$ $$
begin begin
insert into citizen_in_workgroup (citizen_id, workgroup_id) perform add_workgroup_member(_id, b)
select from json_array_elements(members) b;
(z->>'id')::uuid,
_id::uuid
from json_array_elements(resource) z
where (z->>'id') is not null
on conflict do nothing;
delete from citizen_in_workgroup delete from citizen_in_workgroup
where workgroup_id = _id where workgroup_id = _id
and citizen_id not in( and citizen_id not in(
select select
(z->>'id')::uuid (b#>>'{citizen, id}')::uuid
from json_array_elements(resource) z from json_array_elements(members) b
where (z->>'id') is not null where (b#>>'{citizen, id}')::uuid is not null
); );
select find_workgroup_members(_id) into resource; select find_workgroup_members(_id) into members;
end; end;
$$; $$;

View File

@@ -3,29 +3,39 @@ create or replace function upsert_workgroup(inout resource json)
$$ $$
declare declare
new_id uuid = coalesce((resource->>'id')::uuid, uuid_generate_v4()); new_id uuid = coalesce((resource->>'id')::uuid, uuid_generate_v4());
exists boolean = case when (select true from workgroup where id = new_id) is null then true else false end;
begin begin
insert into workgroup (id, created_by_id, name, description, anonymous, logo, owner_id) insert into workgroup (id, created_by_id, name, description, anonymous, logo)
select select
new_id, new_id,
(resource#>>'{created_by, id}')::uuid, (resource#>>'{created_by, id}')::uuid,
name, name,
description, description,
anonymous, anonymous,
logo, logo
(resource#>>'{owner, id}')::uuid
from json_populate_record(null::workgroup, resource) from json_populate_record(null::workgroup, resource)
on conflict (id) do update set on conflict (id) do update set
name = excluded.name, name = excluded.name,
description = excluded.description, description = excluded.description,
anonymous = excluded.anonymous, anonymous = excluded.anonymous,
logo = excluded.logo, logo = excluded.logo;
owner_id = excluded.owner_id;
-- insert into citizen_in_workgroup (citizen_id, workgroup_id) insert into citizen_in_workgroup (workgroup_id, citizen_id, roles)
-- select select
-- (resource->>'id')::uuid, new_id::uuid,
-- new_id::uuid citizen_id,
-- from json_populate_recordset(null::workgroup, resource->'members'); roles
from json_populate_recordset(null::citizen_in_workgroup, resource->'members') m;
-- insert master if no members
if (exists) then
insert into citizen_in_workgroup (workgroup_id, citizen_id, roles)
select
new_id::uuid,
(resource#>>'{created_by, id}')::uuid,
'{MASTER}'
on conflict do nothing;
end if;
select find_workgroup_by_id(new_id) into resource; select find_workgroup_by_id(new_id) into resource;
end; end;

View File

@@ -32,15 +32,15 @@ create table workgroup
name varchar(128) not null, name varchar(128) not null,
description text null, description text null,
anonymous boolean default false not null, anonymous boolean default false not null,
logo text null, logo text null
owner_id uuid not null references citizen (id)
); );
create table citizen_in_workgroup create table citizen_in_workgroup
( (
citizen_id uuid not null references citizen (id), citizen_id uuid not null references citizen (id),
workgroup_id uuid not null references workgroup (id), workgroup_id uuid not null references workgroup (id),
created_at timestamptz default now() not null, roles text[] not null,
created_at timestamptz default now() not null,
primary key (citizen_id, workgroup_id) primary key (citizen_id, workgroup_id)
); );

View File

@@ -1,6 +1,7 @@
package feature package feature
import fr.dcproject.entity.* import fr.dcproject.entity.*
import fr.dcproject.entity.WorkgroupWithMembersI.Member
import fr.dcproject.utils.toUUID import fr.dcproject.utils.toUUID
import io.cucumber.datatable.DataTable import io.cucumber.datatable.DataTable
import io.cucumber.java8.En import io.cucumber.java8.En
@@ -16,7 +17,10 @@ class WorkgroupSteps : En, KoinTest {
init { init {
When("I have members in workgroup {string}:") { workgroupId: String, members: DataTable -> When("I have members in workgroup {string}:") { workgroupId: String, members: DataTable ->
val membersRefs = members.asList() val membersRefs = members.asList()
.map { CitizenRef(it.toUUID()) } .map { Member(
citizen = CitizenRef(it.toUUID()),
roles = listOf(Member.Role.MASTER)
) }
get<WorkgroupRepository>().addMembers(WorkgroupRef(workgroupId.toUUID()), membersRefs) get<WorkgroupRepository>().addMembers(WorkgroupRef(workgroupId.toUUID()), membersRefs)
} }
@@ -42,16 +46,11 @@ class WorkgroupSteps : En, KoinTest {
} }
} }
val owner = data["owner"]?.let {
get<CitizenRepository>().findByUsername(it.toLowerCase().replace(' ', '-'))
} ?: creator
val workgroup = WorkgroupSimple<CitizenRef>( val workgroup = WorkgroupSimple<CitizenRef>(
id = UUID.fromString(data["id"] ?: UUID.randomUUID().toString()), id = UUID.fromString(data["id"] ?: UUID.randomUUID().toString()),
name = data["name"] ?: "Les Incoruptible", name = data["name"] ?: "Les Incoruptible",
description = data["description"] ?: "La vie est notre jeux", description = data["description"] ?: "La vie est notre jeux",
createdBy = creator, createdBy = creator,
owner = owner,
anonymous = (data["anonymous"] ?: false) == true anonymous = (data["anonymous"] ?: false) == true
) )

View File

@@ -55,15 +55,15 @@ internal class WorkgroupVoterTest {
createdBy = tesla, createdBy = tesla,
description = "Super desc", description = "Super desc",
name = "super name", name = "super name",
owner = tesla, anonymous = false,
anonymous = false members = listOf(WorkgroupWithMembersI.Member(tesla, listOf(WorkgroupWithMembersI.Member.Role.MASTER)))
) )
private val workgroupAnon = WorkgroupEntity( private val workgroupAnon = WorkgroupEntity(
createdBy = tesla, createdBy = tesla,
description = "Super desc", description = "Super desc",
name = "super name", name = "super name",
owner = tesla, members = listOf(WorkgroupWithMembersI.Member(tesla, listOf(WorkgroupWithMembersI.Member.Role.MASTER))),
anonymous = true anonymous = true
) )

View File

@@ -3,15 +3,23 @@ Feature: Workgroup
Scenario: Can get one workgroup Scenario: Can get one workgroup
Given I have citizen Stephen Hawking Given I have citizen Stephen Hawking
And I have citizen Sadi Carnot with ID "be3b0926-8628-4426-804a-75188a6eb315"
And I have citizen Joseph Fourier with ID "d9671eca-abaf-4b67-9230-3ece700c1ddb"
And I am authenticated as Stephen Hawking And I am authenticated as Stephen Hawking
And I have workgroup: And I have workgroup:
| id | ab469134-bf14-4856-b093-ae1aa990f977 | | id | ab469134-bf14-4856-b093-ae1aa990f977 |
| name | Les Mousquets | | name | Les Mousquets |
| created_by | Stephen Hawking |
And I have members in workgroup "ab469134-bf14-4856-b093-ae1aa990f977":
| be3b0926-8628-4426-804a-75188a6eb315 |
| d9671eca-abaf-4b67-9230-3ece700c1ddb |
When I send a GET request to "/workgroups/ab469134-bf14-4856-b093-ae1aa990f977" When I send a GET request to "/workgroups/ab469134-bf14-4856-b093-ae1aa990f977"
Then the response status code should be 200 Then the response status code should be 200
And the JSON should contain: And the response should contain object:
| id | ab469134-bf14-4856-b093-ae1aa990f977 | | $.id | ab469134-bf14-4856-b093-ae1aa990f977 |
| name | Les Mousquets | | $.name | Les Mousquets |
| $.members[0].citizen.name.first_name | Stephen |
| $.members[1].citizen.name.first_name | Sadi |
Scenario: Can create a workgroup Scenario: Can create a workgroup
Given I have citizen Werner Heisenberg Given I have citizen Werner Heisenberg
@@ -58,14 +66,19 @@ Feature: Workgroup
And I have citizen Alessandro Volta with ID "b5bac515-45d4-4aeb-9b6d-2627a0bbc419" And I have citizen Alessandro Volta with ID "b5bac515-45d4-4aeb-9b6d-2627a0bbc419"
And I am authenticated as Blaise Pascal And I am authenticated as Blaise Pascal
And I have workgroup: And I have workgroup:
| id | b0ea1922-3bc6-44e2-aa7c-40158998cfbb | | id | b0ea1922-3bc6-44e2-aa7c-40158998cfbb |
| name | Les bonobos | | name | Les bonobos |
| owner | Blaise Pascal |
When I send a POST request to "/workgroups/b0ea1922-3bc6-44e2-aa7c-40158998cfbb/members" with body: When I send a POST request to "/workgroups/b0ea1922-3bc6-44e2-aa7c-40158998cfbb/members" with body:
""" """
[ [
{"id":"6d883fe7-5fc0-4a50-8858-72230673eba4"}, {
{"id":"b5bac515-45d4-4aeb-9b6d-2627a0bbc419"} "citizen": {"id":"6d883fe7-5fc0-4a50-8858-72230673eba4"},
"roles": ["MASTER"]
},
{
"citizen": {"id":"b5bac515-45d4-4aeb-9b6d-2627a0bbc419"},
"roles": ["MASTER"]
}
] ]
""" """
Then the response status code should be 201 Then the response status code should be 201
@@ -76,22 +89,24 @@ Feature: Workgroup
And I have citizen Paul Dirac with ID "1baf48bb-02bc-4d8f-ac86-33335354f5e7" And I have citizen Paul Dirac with ID "1baf48bb-02bc-4d8f-ac86-33335354f5e7"
And I am authenticated as Heinrich Hertz And I am authenticated as Heinrich Hertz
And I have workgroup: And I have workgroup:
| id | b6c975df-dd44-4e99-adc1-f605746b0e11 | | id | b6c975df-dd44-4e99-adc1-f605746b0e11 |
| name | Les Tacos | | name | Les Tacos |
| owner | Heinrich Hertz |
And I have members in workgroup "b6c975df-dd44-4e99-adc1-f605746b0e11": And I have members in workgroup "b6c975df-dd44-4e99-adc1-f605746b0e11":
| 87909ba3-2069-431c-9924-219fd8411cf2 | | 87909ba3-2069-431c-9924-219fd8411cf2 |
| 1baf48bb-02bc-4d8f-ac86-33335354f5e7 | | 1baf48bb-02bc-4d8f-ac86-33335354f5e7 |
When I send a DELETE request to "/workgroups/b6c975df-dd44-4e99-adc1-f605746b0e11/members" with body: When I send a DELETE request to "/workgroups/b6c975df-dd44-4e99-adc1-f605746b0e11/members" with body:
""" """
[ [
{"id":"87909ba3-2069-431c-9924-219fd8411cf2"} {
"citizen": {"id":"87909ba3-2069-431c-9924-219fd8411cf2"},
"roles": ["MASTER"]
}
] ]
""" """
Then the response status code should be 200 Then the response status code should be 200
And the response should contain object: And the response should contain object:
| $.[0]id | 1baf48bb-02bc-4d8f-ac86-33335354f5e7 | | $.[0]citizen.id | 1baf48bb-02bc-4d8f-ac86-33335354f5e7 |
And the JSON should have 1 items And the JSON should have 2 items
Scenario: Can update members on workgroup Scenario: Can update members on workgroup
Given I have citizen Leon Foucault Given I have citizen Leon Foucault
@@ -100,21 +115,26 @@ Feature: Workgroup
And I have citizen Georg Ohm with ID "b49e20c1-8393-45d6-a6a0-3fa5c71cbdc1" And I have citizen Georg Ohm with ID "b49e20c1-8393-45d6-a6a0-3fa5c71cbdc1"
And I am authenticated as Leon Foucault And I am authenticated as Leon Foucault
And I have workgroup: And I have workgroup:
| id | 784fe6bc-7635-4ae2-b080-3a4743b998bf | | id | 784fe6bc-7635-4ae2-b080-3a4743b998bf |
| name | Les Tacos | | name | Les Tacos |
| owner | Leon Foucault |
And I have members in workgroup "784fe6bc-7635-4ae2-b080-3a4743b998bf": And I have members in workgroup "784fe6bc-7635-4ae2-b080-3a4743b998bf":
| be3b0926-8628-4426-804a-75188a6eb315 | | be3b0926-8628-4426-804a-75188a6eb315 |
| d9671eca-abaf-4b67-9230-3ece700c1ddb | | d9671eca-abaf-4b67-9230-3ece700c1ddb |
When I send a PUT request to "/workgroups/784fe6bc-7635-4ae2-b080-3a4743b998bf/members" with body: When I send a PUT request to "/workgroups/784fe6bc-7635-4ae2-b080-3a4743b998bf/members" with body:
""" """
[ [
{"id":"be3b0926-8628-4426-804a-75188a6eb315"}, {
{"id":"b49e20c1-8393-45d6-a6a0-3fa5c71cbdc1"} "citizen": {"id":"be3b0926-8628-4426-804a-75188a6eb315"},
"roles": ["MASTER"]
},
{
"citizen": {"id":"b49e20c1-8393-45d6-a6a0-3fa5c71cbdc1"},
"roles": ["MASTER"]
}
] ]
""" """
Then the response status code should be 200 Then the response status code should be 200
And the response should contain object: And the response should contain object:
| $.[0]id | be3b0926-8628-4426-804a-75188a6eb315 | | $.[0]citizen.id | be3b0926-8628-4426-804a-75188a6eb315 |
| $.[1]id | b49e20c1-8393-45d6-a6a0-3fa5c71cbdc1 | | $.[1]citizen.id | b49e20c1-8393-45d6-a6a0-3fa5c71cbdc1 |
And the JSON should have 2 items And the JSON should have 2 items

View File

@@ -19,7 +19,6 @@ begin
end if; end if;
created_workgroup := jsonb_set(created_workgroup::jsonb, '{created_by}'::text[], jsonb_build_object('id', _citizen_id::text), true)::json; created_workgroup := jsonb_set(created_workgroup::jsonb, '{created_by}'::text[], jsonb_build_object('id', _citizen_id::text), true)::json;
created_workgroup := jsonb_set(created_workgroup::jsonb, '{owner}'::text[], jsonb_build_object('id', _citizen_id::text), true)::json;
assert created_workgroup#>>'{created_by, id}' = _citizen_id::text, format('citizenId in workgroup must be the same as citizen, %s != %s', created_workgroup#>>'{created_by, id}', _citizen_id::text); assert created_workgroup#>>'{created_by, id}' = _citizen_id::text, format('citizenId in workgroup must be the same as citizen, %s != %s', created_workgroup#>>'{created_by, id}', _citizen_id::text);
-- upsert workgroup -- upsert workgroup

View File

@@ -16,19 +16,19 @@ declare
}'; }';
selected_workgroup json; selected_workgroup json;
members json; members json;
selected_citizen json;
begin begin
created_workgroup := jsonb_set(created_workgroup::jsonb, '{created_by}'::text[], jsonb_build_object('id', _citizen_id::text), true)::json; created_workgroup := jsonb_set(created_workgroup::jsonb, '{created_by}'::text[], jsonb_build_object('id', _citizen_id::text), true)::json;
created_workgroup := jsonb_set(created_workgroup::jsonb, '{owner}'::text[], jsonb_build_object('id', _citizen_id::text), true)::json;
assert created_workgroup#>>'{created_by, id}' = _citizen_id::text, format('citizenId in workgroup must be the same as citizen, %s != %s', created_workgroup#>>'{created_by, id}', _citizen_id::text); assert created_workgroup#>>'{created_by, id}' = _citizen_id::text, format('citizenId in workgroup must be the same as citizen, %s != %s', created_workgroup#>>'{created_by, id}', _citizen_id::text);
-- upsert workgroup -- upsert workgroup
select upsert_workgroup(created_workgroup) into created_workgroup; select upsert_workgroup(created_workgroup) into created_workgroup;
assert created_workgroup->>'description' is not null, 'description should not be null'; assert created_workgroup->>'description' is not null, 'description should not be null';
assert (created_workgroup->>'name') = 'Le groupe des vert', format('name must be equal to "Le groupe des vert", %s instead', created_workgroup->>'name'); assert (created_workgroup->>'name') = 'Le groupe des vert', format('name must be equal to "Le groupe des vert", %s instead', created_workgroup->>'name');
assert (created_workgroup#>>'{members, 0, citizen, id}') = _citizen_id::text, 'workgroup must have creator in members on creation';
-- insert another workgroup -- insert another workgroup
created_workgroup_2 := jsonb_set(created_workgroup_2::jsonb, '{created_by}'::text[], jsonb_build_object('id', _citizen_id::text), true)::json; created_workgroup_2 := jsonb_set(created_workgroup_2::jsonb, '{created_by}'::text[], jsonb_build_object('id', _citizen_id::text), true)::json;
created_workgroup_2 := jsonb_set(created_workgroup_2::jsonb, '{owner}'::text[], jsonb_build_object('id', _citizen_id::text), true)::json;
assert created_workgroup_2#>>'{created_by, id}' = _citizen_id::text, format('citizenId in workgroup must be the same as citizen, %s != %s', created_workgroup_2#>>'{created_by, id}', _citizen_id::text); assert created_workgroup_2#>>'{created_by, id}' = _citizen_id::text, format('citizenId in workgroup must be the same as citizen, %s != %s', created_workgroup_2#>>'{created_by, id}', _citizen_id::text);
select upsert_workgroup(created_workgroup_2) into created_workgroup_2; select upsert_workgroup(created_workgroup_2) into created_workgroup_2;
@@ -47,44 +47,61 @@ begin
-- add -- add
select m into members from add_workgroup_members((created_workgroup->>'id')::uuid, json_build_array( select m into members from add_workgroup_members((created_workgroup->>'id')::uuid, json_build_array(
json_build_object('id', _citizen_id2), json_build_object('citizen', json_build_object('id', _citizen_id2), 'roles', '{MASTER}'::text[]),
json_build_object('id', _citizen_id3) json_build_object('citizen', json_build_object('id', _citizen_id3), 'roles', '{MASTER}'::text[])
)) m; )) m;
assert json_array_length(members) = 2, 'The members count must be equal to 2'; assert json_array_length(members) = 3, 'The members count must be equal to 3';
assert members::jsonb @> jsonb_build_array(jsonb_build_object('id', _citizen_id3)), assert (members::jsonb) @> jsonb_build_array(jsonb_build_object(
'id', (created_workgroup->>'id'),
'citizen', jsonb_build_object('id', _citizen_id3),
'roles', jsonb_build_array('MASTER')
)),
'Members must contain citizen3'; 'Members must contain citizen3';
-- Check if "find_citizen_by_id" retrun workgroups of citizen -- Check if "find_citizen_by_id" retrun citizen
assert (select find_citizen_by_id(_citizen_id2)#>>'{workgroups, 0, id}') = (created_workgroup->>'id'), 'find_citizen_by_id must return workgroups'; assert (select find_citizen_by_id(_citizen_id2)#>>'{id}')::uuid = _citizen_id2, 'find_citizen_by_id must return citizen';
-- Check if "find_citizen_by_id_with_user_and_workgroups" retrun workgroups of citizen
select find_citizen_by_id_with_user_and_workgroups(_citizen_id3) into selected_citizen;
assert selected_citizen#>>'{workgroups, 0, roles, 0}' = 'MASTER', format('workgroup must have MASTER role, %s', selected_citizen#>>'{workgroups, 0, roles, 0}');
-- update -- update
select m into members from update_workgroup_members((created_workgroup->>'id')::uuid, json_build_array( select m into members from update_workgroup_members((created_workgroup->>'id')::uuid, json_build_array(
json_build_object('id', _citizen_id2), json_build_object('citizen', json_build_object('id', _citizen_id2), 'roles', '{MASTER}'::text[]),
json_build_object('id', _citizen_id) json_build_object('citizen', json_build_object('id', _citizen_id), 'roles', '{MASTER}'::text[])
)) m; )) m;
assert json_array_length(members) = 2, 'The members count must be equal to 2'; assert json_array_length(members) = 2, 'The members count must be equal to 2';
assert members::jsonb @> jsonb_build_array(jsonb_build_object('id', _citizen_id)), assert (members::jsonb) @> jsonb_build_array(jsonb_build_object(
'Members must contain citizen2'; 'id', (created_workgroup->>'id'),
assert not members::jsonb @> jsonb_build_array(jsonb_build_object('id', _citizen_id3)), 'citizen', jsonb_build_object('id', _citizen_id),
'Members must NOT contain citizen3'; 'roles', jsonb_build_array('MASTER')
)), 'Members must contain citizen1';
assert not (members::jsonb) @> jsonb_build_array(jsonb_build_object(
'citizen', jsonb_build_object('id', _citizen_id3)
)), 'Members must NOT contain citizen3';
-- remove -- remove
select m into members from remove_workgroup_members((created_workgroup->>'id')::uuid, json_build_array( select m into members from remove_workgroup_members((created_workgroup->>'id')::uuid, json_build_array(jsonb_build_object(
json_build_object('id', _citizen_id2) 'citizen', json_build_object('id', _citizen_id2)
)) m; ))) m;
assert json_array_length(members) = 1, 'The members count must be equal to 1'; assert json_array_length(members) = 1, 'The members count must be equal to 1';
assert members::jsonb @> jsonb_build_array(jsonb_build_object('id', _citizen_id)), assert (members::jsonb) @> jsonb_build_array(jsonb_build_object(
'Members must contain citizen1'; 'citizen', jsonb_build_object('id', _citizen_id)
assert not members::jsonb @> jsonb_build_array(jsonb_build_object('id', _citizen_id2)), )), 'Members must contain citizen1';
'Members must NOT contain citizen2'; assert not (members::jsonb) @> jsonb_build_array(jsonb_build_object(
'citizen', jsonb_build_object('id', _citizen_id2)
)), 'Members must NOT contain citizen2';
select m into members from find_workgroup_members((created_workgroup->>'id')::uuid) m; select m into members from find_workgroup_members((created_workgroup->>'id')::uuid) m;
assert json_array_length(members) = 1, 'The members count must be equal to 1'; assert json_array_length(members) = 1, 'The members count must be equal to 1';
assert members::jsonb @> jsonb_build_array(jsonb_build_object('id', _citizen_id)), assert (members::jsonb) @> jsonb_build_array(jsonb_build_object(
'Members must contain citizen1'; 'citizen', jsonb_build_object('id', _citizen_id)
assert not members::jsonb @> jsonb_build_array(jsonb_build_object('id', _citizen_id2)), )), 'Members must contain citizen1';
'Members must NOT contain citizen2'; assert not (members::jsonb) @> jsonb_build_array(jsonb_build_object(
'citizen', jsonb_build_object('id', _citizen_id2)
)), 'Members must NOT contain citizen2';
-- Check if find_workgroup_by_id return members -- Check if find_workgroup_by_id return members
select find_workgroup_by_id((created_workgroup->>'id')::uuid) into selected_workgroup; select find_workgroup_by_id((created_workgroup->>'id')::uuid) into selected_workgroup;
assert json_array_length(selected_workgroup->'members') = 1, 'Workgroup must have members'; assert json_array_length(selected_workgroup->'members') = 1, 'Workgroup must have members';