Big refactoring #77

Merged
flecomte merged 166 commits from refactoring-component-and-immutable into master 2021-03-24 19:06:07 +01:00
5 changed files with 246 additions and 1 deletions
Showing only changes of commit 4762275b5b - Show all commits

View File

@@ -347,4 +347,6 @@ dependencies {
testImplementation("io.mockk:mockk-agent-jvm:1.10.6") testImplementation("io.mockk:mockk-agent-jvm:1.10.6")
testImplementation("org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion") testImplementation("org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion")
testImplementation("com.thedeanda:lorem:2.1") testImplementation("com.thedeanda:lorem:2.1")
testImplementation("org.openapi4j:openapi-operation-validator:1.0.6")
testImplementation("org.openapi4j:openapi-parser:1.0.6")
} }

View File

@@ -1,7 +1,15 @@
package fr.dcproject.common.utils package fr.dcproject.common.utils
import java.net.URL
fun String.readResource(callback: (String) -> Unit = {}): String { fun String.readResource(callback: (String) -> Unit = {}): String {
val content = callback::class.java.getResource(this).readText() val content = callback::class.java.getResource(this)?.readText() ?: error("File not found")
callback(content)
return content
}
fun String.getResource(callback: (URL) -> Unit = {}): URL {
val content = callback::class.java.getResource(this) ?: error("File not found")
callback(content) callback(content)
return content return content
} }

View File

@@ -0,0 +1,188 @@
openapi: 3.0.2
info:
version: '0.1'
title: 'DC Project'
description: 'A free comunity program for create constitution'
paths:
/articles:
get:
summary: Get all articles
tags:
- article
operationId: getArticles
parameters:
- $ref: '#/components/parameters/page'
- $ref: '#/components/parameters/limit'
- $ref: '#/components/parameters/articleSort'
- $ref: '#/components/parameters/direction'
- $ref: '#/components/parameters/search'
- $ref: '#/components/parameters/createdBy'
- name: workgroup
in: query
description: ID of workgroup
example: 82a0e60a-bb55-dbc0-1c3d-0a804df2b5df
required: false
schema:
type: string
format: uuid
responses:
200:
description: The Article objects
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/Paginated'
- type: object
properties:
result:
type: array
items:
properties:
id:
type: string
format: uuid
title:
type: string
created_by:
type: object
properties:
id:
type: string
format: uuid
name:
type: object
properties:
first_name:
type: string
last_name:
type: string
email:
type: string
workgroup:
type: object
nullable: true
properties:
id:
type: string
format: uuid
name:
type: string
draft:
type: boolean
components:
parameters:
page:
name: page
in: query
description: The current page
example: 1
required: false
schema:
default: 1
type: integer
minimum: 1
limit:
name: limit
in: query
description: The number of object per page
example: 50
required: false
schema:
default: 50
type: integer
minimum: 1
maximum: 50
sort:
name: sort
in: query
description: The sort field name
example: first_name
required: false
schema:
type: string
articleSort:
name: sort
in: query
description: The sort field name
example: createdAt
required: false
schema:
type: string
enum:
- title
- createdAt
- vote
- popularity
direction:
name: direction
in: query
description: The sort direction
example: asc
required: false
schema:
type: string
default: asc
enum: [asc, desc]
search:
name: search
in: query
description: A text to seach
example: content50
required: false
schema:
type: string
createdBy:
name: createdBy
in: query
description: filter by Author
example: 4d673bfa-eaef-4290-b52f-85a9c8a7eba5
required: false
schema:
type: string
format: uuid
schemas:
UUID:
type: string
pattern: '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}'
description: UUID
format: uuid
example:
e74be8e4-6823-47c4-bd1b-789725b2fa8e
UuidEntity:
properties:
id:
$ref: '#/components/schemas/UUID'
Paginated:
properties:
result:
type: array
items:
$ref: '#/components/schemas/UuidEntity'
count:
type: integer
minimum: 0
example: 1
currentPage:
type: integer
minimum: 0
example: 1
limit:
type: integer
minimum: 0
example: 50
offset:
type: integer
minimum: 0
example: 1
total:
type: integer
minimum: 0
example: 1
servers:
- description: localhost
url: http://localhost:8080
- description: production
url: http://dc-project.fr

View File

@@ -14,6 +14,7 @@ import integration.steps.given.`Given I have articles`
import integration.steps.given.`Given I have citizen` import integration.steps.given.`Given I have citizen`
import integration.steps.given.`Given I have workgroup` import integration.steps.given.`Given I have workgroup`
import integration.steps.given.`authenticated as` import integration.steps.given.`authenticated as`
import integration.steps.then.`And schema must be valid`
import integration.steps.then.`And the response should contain list` import integration.steps.then.`And the response should contain list`
import integration.steps.then.`And the response should not contain` import integration.steps.then.`And the response should not contain`
import io.ktor.http.HttpStatusCode.Companion.OK import io.ktor.http.HttpStatusCode.Companion.OK
@@ -36,6 +37,7 @@ class `Article routes` : BaseTest() {
`And the response should contain pattern`("$.result[2].created_by.name.first_name", "firstName.+") `And the response should contain pattern`("$.result[2].created_by.name.first_name", "firstName.+")
`And the response should not contain`("$.result[3]") `And the response should not contain`("$.result[3]")
`And the response should contain list`("$.result", 3, 3) `And the response should contain list`("$.result", 3, 3)
`And schema must be valid`()
} }
} }
} }

View File

@@ -0,0 +1,45 @@
package integration.steps.then
import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.ObjectMapper
import fr.dcproject.common.utils.getResource
import io.ktor.request.contentType
import io.ktor.request.httpMethod
import io.ktor.request.uri
import io.ktor.server.testing.TestApplicationResponse
import org.openapi4j.core.model.v3.OAI3
import org.openapi4j.parser.OpenApi3Parser
import org.openapi4j.parser.model.v3.OpenApi3
import org.openapi4j.parser.model.v3.Schema
import org.openapi4j.schema.validator.ValidationContext
import org.openapi4j.schema.validator.ValidationData
import org.openapi4j.schema.validator.v3.SchemaValidator
import java.io.File
import kotlin.test.assertTrue
fun TestApplicationResponse.`And schema must be valid`() {
// Parse without validation, setting to true is strongly recommended for further data validation.
val api: OpenApi3 = OpenApi3Parser().parse(File("/openapi2.yaml".getResource().toURI()), true)
val mediaType = this.call.request.contentType()
val operation = this.call.request.httpMethod
val uri = this.call.request.uri
val status = this.call.response.status()
val schema: Schema = api
.getPath(uri)
.getOperation(operation.value.toLowerCase())
.getResponse(status?.value?.toString() ?: error("HttpStatus not found"))
.getContentMediaType(mediaType.toString())
.schema
val validationContext: ValidationContext<OAI3> = ValidationContext(api.context)
val jsonNode: JsonNode = schema.toNode()
val schemaValidator = SchemaValidator(validationContext, "", jsonNode)
val mapper = ObjectMapper()
val results = ValidationData<Unit>()
schemaValidator.validate(mapper.readTree(content), results)
assertTrue(results.isValid, results.results().toString())
}