{"id":15040999,"url":"https://github.com/jntakpe/spring-restdocs-dsl","last_synced_at":"2026-01-20T01:57:42.981Z","repository":{"id":57720567,"uuid":"163886345","full_name":"jntakpe/spring-restdocs-dsl","owner":"jntakpe","description":"Provides a convenient way to document and test APIs with Spring REST Docs leveraging Kotlin type safe power.","archived":false,"fork":false,"pushed_at":"2019-12-13T11:19:30.000Z","size":220,"stargazers_count":2,"open_issues_count":10,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-02-12T17:45:28.934Z","etag":null,"topics":["kotlin","spring-rest-docs"],"latest_commit_sha":null,"homepage":null,"language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jntakpe.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-01-02T20:28:33.000Z","updated_at":"2020-04-07T22:56:53.000Z","dependencies_parsed_at":"2022-09-26T21:41:54.589Z","dependency_job_id":null,"html_url":"https://github.com/jntakpe/spring-restdocs-dsl","commit_stats":null,"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jntakpe%2Fspring-restdocs-dsl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jntakpe%2Fspring-restdocs-dsl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jntakpe%2Fspring-restdocs-dsl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jntakpe%2Fspring-restdocs-dsl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jntakpe","download_url":"https://codeload.github.com/jntakpe/spring-restdocs-dsl/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247478285,"owners_count":20945264,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["kotlin","spring-rest-docs"],"created_at":"2024-09-24T20:45:23.207Z","updated_at":"2026-01-20T01:57:42.939Z","avatar_url":"https://github.com/jntakpe.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Spring REST Docs DSL\n\n[![Build Status](https://travis-ci.com/jntakpe/spring-restdocs-dsl.svg?branch=master)](https://travis-ci.com/jntakpe/spring-restdocs-dsl)\n![License](https://img.shields.io/badge/license-Apache%202-blue.svg)\n\nProvides a convenient way to document and test APIs with [Spring REST Docs](https://spring.io/projects/spring-restdocs) leveraging Kotlin DSL.\n\nOur primary goal is to : \n* Document APIs using [Spring REST Docs](https://spring.io/projects/spring-restdocs)\n* Preserve coherent order between JSON and documentation\n* Make API documentation code more readable\n* Enable view filtering\n\nThis library comes with 3 levels of maturity ([AutoDsl](#AutoDsl), [Reflection](#Reflection) and [Standard](#Standard-API)),\neach one alleviating the boilerplate you need to write to document your API.\n\n## Index\n\n* [Installation](#Installation)\n* [Configuration](#Configuration)\n  * [Maven](#Maven)\n  * [Gradle](#Gradle)\n* [Usage](#Usage)\n  * [AutoDsl](#AutoDsl)\n  * [Reflection API](#Reflection)\n  * [Standard API](#Standard-API)\n  * [WebTestClient usage](#WebTestClient-usage)\n  * [Compared to vanilla RestDocs](#Standard-Spring-REST-Docs-usage)\n\n## Installation\n\nSpring REST Docs DSL depends on Kotlin standard library and Spring REST Docs.\n\nThe current release is [0.6.2](https://github.com/jntakpe/spring-restdocs-dsl/releases/tag/v0.6.2).\n\n## Configuration\n\n#### Maven\n\n```xml\n\u003cdependency\u003e\n  \u003cgroupId\u003ecom.github.jntakpe\u003c/groupId\u003e\n  \u003cartifactId\u003espring-restdocs-dsl\u003c/artifactId\u003e\n  \u003cversion\u003e0.6.2\u003c/version\u003e\n  \u003cscope\u003etest\u003c/scope\u003e\n\u003c/dependency\u003e\n```\n\n#### Gradle\n\n```groovy\ntestImplementation 'com.github.jntakpe:spring-restdocs-dsl:0.6.2'\n```\n\nIf you want to use autoDsl feature you must also add\n\n```groovy\ncompileOnly 'com.github.jntakpe:spring-restdocs-dsl-annotations:0.6.2'\ncompileOnly 'com.github.jntakpe:spring-restdocs-dsl-core:0.6.2'\ntestImplementation 'com.github.jntakpe:spring-restdocs-dsl-core:0.6.2'\nkapt 'com.github.jntakpe:spring-restdocs-dsl-processor:0.6.2'\n```\n\n## Usage\n\n### Sample\n\nGiven the following JSON document : \n\n```json\n{\n  \"module\" : \"\",\n  \"questions\" : [ {\n    \"label\" : \"\",\n    \"configuration\" : {\n      \"duration\" : \"\",\n      \"code\" : false,\n      \"multipleChoice\" : false\n    },\n    \"answerOptions\" : [{\n      \"label\" : \"\",\n      \"valid\" : false,\n      \"id\" : \"\"\n    }],\n    \"answers\" : [ ],\n    \"valid\" : false,\n    \"id\" : \"\"\n  }],\n  \"configuration\" : {\n    \"shuffled\" : false,\n    \"duration\" : \"\"\n  },\n  \"id\" : \"\"\n}\n```\n\n## AutoDsl\n\nAutoDsl generates some helper functions from your Kotlin classes.\n\n#### Configuration\n\nYou can configure AutoDsl globally thanks to `@EnableRestDocsAutoDsl` annotation. It has the following options :\n\n|  Name | Kind | Description | Default |\n|:-------:|:------:|:------:|:--------:|\n|  packages  | Array\u003cString\u003e | Packages containing classes to generate DSL from. As an alternative you can indivually mark such classes with @Doc annotation |    empty    |\n|  trimSuffixes | Array\u003cString\u003e | Trims suffixes of generated DSL function e.g. PetDto is generated as `pet {}` instead of ~~`petDto {}`~~ |  empty   |\n\n**Note:** Kapt is triggered before Kotlin compilation. If you use Intellij, kapt is not currently supported. To overcome this it is recommended\nto use Gradle to build your project. To do so choose ‘Gradle’ in ‘Settings \u003e Build, Execution, Deployment \u003e Build Tools \u003e Gradle \u003e Build.\n\n#### Usage\n\nKapt generates DSL functions you can then use like this :\n\n```kotlin\nval initDoc =  {\n    durationType = String::class\n    answerOption {\n        label = \"Option's label\"\n        valid = \"Indicates if the option is valid\"\n        id = \"Option's unique identifier\"\n    }\n    questionConfiguration {\n        duration = \"Question's maximum duration\"\n        code = \"Indicates if the label should be formatted as code\"\n        multipleChoice = \"Indicates if question accepts multiple answers\"\n    }\n    question {\n        label = \"Question's label\"\n        configuration = \"Object containing question's configuration\"\n        answerOptions = \"Array containing the different possible answer options for the question\"\n        answers = \"Array containing the answer given by an user\"\n        valid = \"Field indicating if the given answer is valid\"\n        id = \"Question's unique identifier\"\n    }\n    quizConfiguration {\n        duration = \"Duration of the quiz. Equivalent of the total duration of all questions\"\n        shuffled = \"Indicates if the questions should be shuffled\"\n    }\n    quiz {\n        module = \"Module related to the quiz\"\n        questions = \"Array containing quiz questions\"\n        configuration = \"Object containing quiz configuration\"\n        id = \"Quiz unique identifier\"\n    }\n}\n```\n\nWith AutoDsl you just have to type field's description. The rest is inferred thanks to reflection.  \n\n**Note about `initDoc`** : if you use IntelliJ and chose to run your tests using JUnit, you need to call `initDoc()` method\nin either a `@BeforeAll` or a `@BeforeEach` function ; otherwise it won't get evaluated. Using Gradle works thine.\n\n**Note about external classes :** in this example we use `java.time.Duration` which we don't own.\nAutoDsl identifies those classes alongside those not picked up by `EnableRestDocsAutoDsl.packages` as external classes.  \nIt then leaves you with 2 options regarding those classes :\n\n* Document their fields like others. In this case syntax differs a bit and uses [reflection](#Reflection) syntax.\nFor `java.time.Duration` a durationDoc field is generated which we would initialize like :\n```kotlin\ndurationDoc = root {\n    field(Duration::nano, \"Nanoseconds\")\n    field(Duration::seconds, \"seconds\")\n    field(Duration::units, \"Unit\")\n}\n```\n* Else you might have defined a custom way to serialize this type. In this case, for `java.time.Duration` as an example\nyou can simply use the durationType field and pass it a Kotlin type matching the Json type once serialized : \n```kotlin\ndurationType = String::class\n// or if you serialize it with nanos\ndurationType = Long::class\n```\n\n**Note :** in your tests you can import auto-generated FieldDescriptors e.g. given a Quiz class, you can import a quizDoc top-level property.\n\n## Reflection\n\nReflection API brings some syntactic sugar compared to [standard usage](#Standard-API). Especially it alleviates :\n\n* Path is inferred from given KProperty e.g. instead of ~~`QuizDTO::module.name`~~ you can just pass `QuizDTO::module` \n* Type is also inferred. You can just use the `field()` method instead of `string(), boolean(), json(), array()...`\n* View and optionality are inferred\n\n**Note :** in order to use it you must also add [kotlin-reflect](https://mvnrepository.com/artifact/org.jetbrains.kotlin/kotlin-reflect) to your test classpath.\n\nThis enables us to write this :\n\n```kotlin\nval answerOptionDoc by obj {\n    field(AnswerOptionDTO::label, \"Option's label\")\n    field(AnswerOptionDTO::valid, \"Indicates if the option is valid\")\n    field(AnswerOptionDTO::id, \"Option's unique identifier\")\n}\nval questionConfigurationDoc by obj {\n    field\u003cString\u003e(QuestionConfigurationDTO::duration, \"Question's maximum duration\")\n    field(QuestionConfigurationDTO::code, \"Indicates if the label should be formatted as code\")\n    field(QuestionConfigurationDTO::multipleChoice, \"Indicates if question accepts multiple answers\")\n}\nval questionDoc by obj {\n    field(QuestionDTO::label, \"Question's label\")\n    field(QuestionDTO::configuration, questionConfigurationDoc, \"Object containing question's configuration\")\n    field(QuestionDTO::answerOptions, answerOptionDoc, \"Array containing the different possible answer options for the question\")\n    field(QuestionDTO::answers, \"Array containing the answer given by an user\")\n    field(QuestionDTO::valid, \"Field indicating if the given answer is valid\")\n    field(QuestionDTO::id, \"Question's unique identifier\")\n}\nval quizConfigurationDoc by obj {\n    field\u003cString\u003e(QuizConfigurationDTO::duration, \"Duration of the quiz. Equivalent of the total duration of all questions\")\n    field(QuizConfigurationDTO::shuffled, \"Indicates if the questions should be shuffled\")\n}\nval quizDoc by obj {\n    field(QuizDTO::module, \"Module related to the quiz\")\n    field(QuizDTO::questions, questionDoc, \"Array containing quiz questions\")\n    field(QuizDTO::configuration, quizConfigurationDoc, \"Object containing quiz configuration\")\n    field(QuizDTO::id, \"Quiz unique identifier\")\n}\n```\n\nIf you need to document an array of something (e.g. QuizDTO) you can use : \n\n```kotlin\n// reusing previously defined quizDoc\nval quizzesDoc by arr\u003cQuizDTO\u003e(quizDoc) // Description will be inferred from reified type\n// or if you want to explicitly define the description\nval explicitQuizzesDoc by arr\u003cQuizDTO\u003e(quizDoc, \"An array of quizzes\")\n```\n\nIf you need to enforce JSON type of a field e.g. `java.time.Duration` you can used reified `field()` method like :\n```kotlin\nfield\u003cString\u003e(QuestionConfigurationDTO::duration, \"Question's maximum duration\")\n```\n\n## Standard API\n\nUsing the standard Kotlin DSL, we write : \n\n```kotlin\nprivate fun quizResponse() = responseFields(quizDesc())\n    \nprivate fun quizDesc() = root {\n    string(QuizDTO::module.name, \"Module related to the quiz\")\n    array(QuizDTO::questions.name, \"Array containing quiz questions\") {\n        fields += questionDesc()\n    }\n    json(QuizDTO::configuration.name, \"Object containing quiz configuration\") {\n        string(QuizConfigurationDTO::duration.name, \"Duration of the quiz. Equivalent of the total duration of all questions\")\n        boolean(QuizConfigurationDTO::shuffled.name, \"Indicates if the questions should be shuffled\")\n    }\n    string(QuizDTO::id.name, \"Quiz unique identifier\")\n}\n\nprivate fun questionDesc() = root {\n    string(QuestionDTO::label.name, \"Question's label\")\n    json(QuestionDTO::configuration.name, \"Object containing question's configuration\") {\n        string(QuestionConfigurationDTO::duration.name, \"Question's maximum duration\")\n        boolean(QuestionConfigurationDTO::code.name, \"Indicates if the label should be formatted as code\")\n        boolean(QuestionConfigurationDTO::multipleChoice.name, \"Indicates if question accepts multiple answers\")\n    }\n    array(QuestionDTO::answerOptions.name, \"Array containing the different possible answer options for the question\") {\n        string(AnswerOptionDTO::label.name, \"Option's label\")\n        boolean(AnswerOptionDTO::valid.name, \"Indicates if the option is valid\")\n        string(AnswerOptionDTO::id.name, \"Option's unique identifier\")\n    }\n    array(QuestionDTO::answers.name, \"Array containing the answer given by an user\")\n    boolean(QuestionDTO::valid.name, \"Field indicating if the given answer is valid\")\n    string(QuestionDTO::id.name, \"Question's unique identifier\")\n}\n```\n\nIt feels natural and close to JSON syntax !\n\n### WebTestClient usage\n\nIn order to use those FieldDescriptors in our tests, some helpers are also provided :\n```kotlin\n// given our quizDoc previously written\nquizDoc.asList\u003cQuizDTO\u003e() // An array of quizzes\nquizDoc.asReq() // In request payload\nquizDoc.asResp() // In response payload\nquizDoc.asList\u003cQuizDTO\u003e().asResp() // Array of quizzes in response payload\nquizDoc.asList\u003cQuizDTO\u003e(\"A list of quizzes\").asReq() // Array of quizzes with explicit description in request payload\n```\n\n### Standard Spring REST Docs usage\n\nUsing standard Spring REST Docs, we write :\n\n```kotlin\n private fun quizResponse() = responseFields(quizDesc())\n\nprivate fun quizDesc() = mutableListOf(\n        fieldWithPath(QuizDTO::id.name).type(STRING).description(\"Quiz unique identifier\"),\n        fieldWithPath(QuizDTO::questions.name).type(ARRAY).description(\"Array containing the quiz questions\")\n)\n        .apply { addAll(questionDesc(\"${QuizDTO::questions.name}[].\")) }\n        .apply {\n            addAll(listOf(\n                    fieldWithPath(QuizDTO::module.name).type(STRING).description(\"Module related to the quiz\"),\n                    fieldWithPath(QuizDTO::configuration.name).type(OBJECT).description(\"Object containing quiz configuration\"),\n                    fieldWithPath(\"${QuizDTO::configuration.name}.${QuizConfigurationDTO::duration.name}\").type(STRING).description(\"Duration of the quiz. Equivalent of the total duration of all questions\"),\n                    fieldWithPath(\"${QuizDTO::configuration.name}.${QuizConfigurationDTO::shuffled.name}\").type(BOOLEAN).description(\"Indicates if the questions should be shuffled\")\n            ))\n        }\n\nfun questionDesc(prefix: String) = listOf(\n        fieldWithPath(\"$prefix${QuestionDTO::label.name}\").type(STRING).description(\"Question's label\"),\n        fieldWithPath(\"$prefix${QuestionDTO::configuration.name}\").type(OBJECT).description(\"Object containing question's configuration\"),\n        fieldWithPath(\"$prefix${QuestionDTO::configuration.name}.${QuestionConfigurationDTO::duration.name}\").type(STRING).description(\"Question's maximum duration\"),\n        fieldWithPath(\"$prefix${QuestionDTO::configuration.name}.${QuestionConfigurationDTO::code.name}\").type(BOOLEAN).description(\"Indicates if the label should be formatted as code\"),\n        fieldWithPath(\"$prefix${QuestionDTO::configuration.name}.${QuestionConfigurationDTO::multipleChoice.name}\").type(BOOLEAN).description(\"Indicates if question accepts multiple answers\"),\n        fieldWithPath(\"$prefix${QuestionDTO::answerOptions.name}\").type(ARRAY).description(\"Array containing the different possible answer options for the question\"),\n        fieldWithPath(\"$prefix${QuestionDTO::answerOptions.name}[].${AnswerOptionDTO::label.name}\").type(STRING).description(\"Option's label\"),\n        fieldWithPath(\"$prefix${QuestionDTO::answerOptions.name}[].${AnswerOptionDTO::valid.name}\").type(BOOLEAN).description(\"Indicates if the option is valid\"),\n        fieldWithPath(\"$prefix${QuestionDTO::answerOptions.name}[].${AnswerOptionDTO::id.name}\").type(STRING).description(\"Option's unique identifier\"),\n        fieldWithPath(\"$prefix${QuestionDTO::answers.name}\").type(ARRAY).description(\"Array containing the given answer options identifiers\"),\n        fieldWithPath(\"$prefix${QuestionDTO::valid.name}\").type(BOOLEAN).description(\"Field indicating if the given answer is valid\"),\n        fieldWithPath(\"$prefix${QuestionDTO::id.name}\").type(STRING).description(\"Question's unique identifier\")\n)\n```\n\nThe previous code has few majors flaws : \n* It's cumbersome to write\n* The fields ordering is hard to maintain\n* The field prefix has to be explicit\n\n## For contributors\n\n#### Debugging kapt\n\n* In order to trigger kapt you need to execute `./gradlew kaptKotlin`\n* To enable debugging add `kapt.use.worker.api=true` and `org.gradle.caching=false` to your `gradle.properties` file\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjntakpe%2Fspring-restdocs-dsl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjntakpe%2Fspring-restdocs-dsl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjntakpe%2Fspring-restdocs-dsl/lists"}