From 4c1ab796e469cf094e180964e0613caa0aefc31d Mon Sep 17 00:00:00 2001 From: Fabrice Lecomte Date: Sat, 20 Mar 2021 01:14:39 +0100 Subject: [PATCH] Test openapi schema of /constitutions/* --- .../constitution/routes/CreateConstitution.kt | 18 ++ .../constitution/routes/FindConstitutions.kt | 32 ++- .../constitution/routes/GetConstitution.kt | 65 +++++- src/main/resources/openapi2.yaml | 206 ++++++++++++++++++ .../kotlin/integration/Constitution routes.kt | 31 ++- 5 files changed, 347 insertions(+), 5 deletions(-) diff --git a/src/main/kotlin/fr/dcproject/component/constitution/routes/CreateConstitution.kt b/src/main/kotlin/fr/dcproject/component/constitution/routes/CreateConstitution.kt index a9b44a0..f0e4a43 100644 --- a/src/main/kotlin/fr/dcproject/component/constitution/routes/CreateConstitution.kt +++ b/src/main/kotlin/fr/dcproject/component/constitution/routes/CreateConstitution.kt @@ -20,6 +20,7 @@ import io.ktor.locations.Location import io.ktor.locations.post import io.ktor.response.respond import io.ktor.routing.Route +import org.joda.time.DateTime import java.util.UUID @KtorExperimentalLocationsAPI @@ -110,6 +111,23 @@ object CreateConstitution { val anonymous: Boolean = c.anonymous val draft: Boolean = c.draft val versionId: UUID = c.versionId + val createdAt: DateTime = c.createdAt + val createdBy: Any = c.createdBy.let { c -> + object { + val id: UUID = c.id + val name: Any = c.name.let { n -> + object { + val firstName: String = n.firstName + val lastName: String = n.lastName + } + } + val user: Any = c.user.let { u -> + object { + val username: String = u.username + } + } + } + } } ) } diff --git a/src/main/kotlin/fr/dcproject/component/constitution/routes/FindConstitutions.kt b/src/main/kotlin/fr/dcproject/component/constitution/routes/FindConstitutions.kt index a2bbc44..a19651a 100644 --- a/src/main/kotlin/fr/dcproject/component/constitution/routes/FindConstitutions.kt +++ b/src/main/kotlin/fr/dcproject/component/constitution/routes/FindConstitutions.kt @@ -1,5 +1,6 @@ package fr.dcproject.component.constitution.routes +import fr.dcproject.common.dto.toOutput import fr.dcproject.common.security.assert import fr.dcproject.component.auth.citizenOrNull import fr.dcproject.component.constitution.ConstitutionAccessControl @@ -8,11 +9,14 @@ import fr.dcproject.routes.PaginatedRequest import fr.dcproject.routes.PaginatedRequestI import fr.postgresjson.repository.RepositoryI 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 FindConstitutions { @@ -29,7 +33,33 @@ object FindConstitutions { get { val constitutions = repo.find(it.page, it.limit, it.sort, it.direction, it.search) ac.assert { canView(constitutions.result, citizenOrNull) } - call.respond(constitutions) + call.respond( + HttpStatusCode.OK, + constitutions.toOutput { c -> + object { + val id: UUID = c.id + val title: String = c.title + val versionId: UUID = c.versionId + val createdAt: DateTime = c.createdAt + val createdBy: Any = c.createdBy.let { c -> + object { + val id: UUID = c.id + val name: Any = c.name.let { n -> + object { + val firstName: String = n.firstName + val lastName: String = n.lastName + } + } + val user: Any = c.user.let { u -> + object { + val username: String = u.username + } + } + } + } + } + } + ) } } } diff --git a/src/main/kotlin/fr/dcproject/component/constitution/routes/GetConstitution.kt b/src/main/kotlin/fr/dcproject/component/constitution/routes/GetConstitution.kt index 39ea032..4da8944 100644 --- a/src/main/kotlin/fr/dcproject/component/constitution/routes/GetConstitution.kt +++ b/src/main/kotlin/fr/dcproject/component/constitution/routes/GetConstitution.kt @@ -7,11 +7,13 @@ import fr.dcproject.component.constitution.database.ConstitutionRef import fr.dcproject.component.constitution.database.ConstitutionRepository import io.ktor.application.call import io.ktor.features.NotFoundException +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 @@ -25,7 +27,68 @@ object GetConstitution { get { val constitution = constitutionRepo.findById(it.constitution.id) ?: throw NotFoundException("Unable to find constitution ${it.constitution.id}") ac.assert { canView(constitution, citizenOrNull) } - call.respond(constitution) + call.respond( + HttpStatusCode.OK, + constitution.let { c -> + object { + val id: UUID = c.id + val title: String = c.title + val titles: List = c.titles.map { t -> + object { + val id: UUID = t.id + val name: String = t.name + val rank: Int = t.rank + val articles: List = t.articles.map { a -> + val id = a.id + val title = a.title + val createdBy = a.createdBy.let { cr -> + object { + val id: UUID = cr.id + val name: Any = cr.name.let { n -> + object { + val firstName: String = n.firstName + val lastName: String = n.lastName + } + } + val user: Any = cr.user.let { u -> + object { + val username: String = u.username + } + } + } + } + val workgroup: Any? = a.workgroup?.let { w -> + object { + val id = w.id + val name = w.name + } + } + } + } + } + val anonymous: Boolean = c.anonymous + val draft: Boolean = c.draft + val versionId: UUID = c.versionId + val createdAt: DateTime = c.createdAt + val createdBy: Any = c.createdBy.let { c -> + object { + val id: UUID = c.id + val name: Any = c.name.let { n -> + object { + val firstName: String = n.firstName + val lastName: String = n.lastName + } + } + val user: Any = c.user.let { u -> + object { + val username: String = u.username + } + } + } + } + } + } + ) } } } diff --git a/src/main/resources/openapi2.yaml b/src/main/resources/openapi2.yaml index b4be58a..2fe14c6 100644 --- a/src/main/resources/openapi2.yaml +++ b/src/main/resources/openapi2.yaml @@ -662,6 +662,73 @@ paths: 401: $ref: '#/components/responses/401' + /constitutions: + get: + summary: Get all constitutions + tags: + - constitution + operationId: getConstitutions + parameters: + - $ref: '#/components/parameters/page' + - $ref: '#/components/parameters/limit' + - $ref: '#/components/parameters/sort' + - $ref: '#/components/parameters/direction' + - $ref: '#/components/parameters/search' + responses: + 200: + description: The Constitution objects + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Paginated' + - type: object + properties: + result: + type: array + items: + $ref: '#/components/schemas/ConstitutionListingResponse' + post: + security: + - JWTAuth: [ ] + summary: Create new Constitution + tags: + - constitution + operationId: insertConstitution + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ConstitutionRequest' + + responses: + 201: + description: Constitution created + content: + application/json: + schema: + $ref: '#/components/schemas/ConstitutionResponse' + 401: + $ref: '#/components/responses/401' + 400: + $ref: '#/components/responses/400' + /constitutions/{constitution}: + parameters: + - $ref: '#/components/parameters/constitution' + get: + summary: Get all constitutions + tags: + - constitution + operationId: getConstitutions + responses: + 200: + description: The Constitution objects + content: + application/json: + schema: + $ref: '#/components/schemas/ConstitutionResponse' + components: parameters: page: @@ -781,6 +848,12 @@ components: application/json: schema: description: noting + 400: + description: BadReqest + content: + application/json: + schema: + description: noting schemas: UUID: @@ -962,6 +1035,9 @@ components: type: object nullable: true additionalProperties: false + required: + - id + - name properties: id: type: string @@ -1139,6 +1215,136 @@ components: minimum: 0 score: type: number + ConstitutionRequest: + additionalProperties: false + description: The versionId field must be defined for update contitution + type: object + required: + - title + properties: + id: + $ref: '#/components/schemas/UUID' + title: + type: string + example: + Constitution for the liberty + titles: + type: array + default: [ ] + items: + additionalProperties: false + type: object + required: + - name + properties: + id: + $ref: '#/components/schemas/UUID' + name: + type: string + example: + The liberties + articles: + type: array + items: + required: + - id + properties: + id: + $ref: '#/components/schemas/UUID' + anonymous: + type: boolean + default: true + draft: + type: boolean + default: false + versionId: + $ref: '#/components/schemas/UUID' + ConstitutionResponse: + additionalProperties: false + type: object + required: + - id + - title + - titles + - anonymous + - draft + - versionId + - createdBy + - createdAt + properties: + id: + $ref: '#/components/schemas/UUID' + title: + type: string + example: + Constitution for the liberty + titles: + type: array + default: [ ] + items: + additionalProperties: false + type: object + required: + - id + - name + - rank + - articles + properties: + id: + $ref: '#/components/schemas/UUID' + name: + type: string + example: + The liberties + rank: + type: integer + minimum: 0 + example: 0 + createdBy: + $ref: '#/components/schemas/CitizenCreator' + createdAt: + type: string + format: 'date-time' + articles: + type: array + items: + $ref: '#/components/schemas/ArticleListingResponse' + anonymous: + type: boolean + default: true + draft: + type: boolean + default: false + versionId: + $ref: '#/components/schemas/UUID' + createdBy: + $ref: '#/components/schemas/CitizenCreator' + createdAt: + type: string + format: 'date-time' + ConstitutionListingResponse: + additionalProperties: false + type: object + required: + - id + - title + - versionId + - createdAt + - createdBy + properties: + id: + $ref: '#/components/schemas/UUID' + title: + type: string + example: + Constitution for the liberty + versionId: + $ref: '#/components/schemas/UUID' + createdBy: + $ref: '#/components/schemas/CitizenCreator' + createdAt: + type: string + format: 'date-time' securitySchemes: JWTAuth: diff --git a/src/test/kotlin/integration/Constitution routes.kt b/src/test/kotlin/integration/Constitution routes.kt index 62404d3..da77887 100644 --- a/src/test/kotlin/integration/Constitution routes.kt +++ b/src/test/kotlin/integration/Constitution routes.kt @@ -1,5 +1,6 @@ package integration +import integration.steps.`when`.Validate import integration.steps.then.`And have property` import integration.steps.then.`And the response should not be null` import integration.steps.then.`Then the response should be` @@ -12,6 +13,8 @@ import integration.steps.given.`Given I have citizen` import integration.steps.given.`Given I have constitution` import integration.steps.given.`Given I have constitutions` import integration.steps.given.`authenticated as` +import io.ktor.http.HttpStatusCode.Companion.BadRequest +import io.ktor.http.HttpStatusCode.Companion.Created import io.ktor.http.HttpStatusCode.Companion.OK import org.junit.jupiter.api.Tag import org.junit.jupiter.api.Tags @@ -70,17 +73,39 @@ class `Constitution routes` : BaseTest() { "anonymous":true, "titles":[ { - "name":"plop", - "rank":0 + "name":"plop" } ] } """) - } `Then the response should be` OK and { + } `Then the response should be` Created and { `And the response should not be null`() `And have property`("$.versionId") `whish contains` "15814bb6-8d90-4c6a-a456-c3939a8ec75e" `And have property`("$.title") `whish contains` "Hello world!" } } } + + @Test + fun `I cannot create an constitution if bad request`() { + withIntegrationApplication { + `Given I have citizen`("Henri", "Poincaré") + `When I send a POST request`("/constitutions", Validate.ALL - Validate.REQUEST_BODY) { + `authenticated as`("Henri", "Poincaré") + `with body`(""" + { + "versionId":"15814bb6-8d90-4c6a-a456-c3939a8ec75e", + "title":"Hello world!", + "anonymous":true, + "titles":[ + { + "name":"plop", + "wrongField":0 + } + ] + } + """) + } `Then the response should be` BadRequest + } + } }