DocsField

restdocs에서 사용하는 FieldDescriptorClass를 상속하여,

직접 custom 할 수 있는 형태로 가공하고 infix function을 사용하여, builder pattern과 같은 형태로 사용 가능함.

parameterdescription
type타입 ex) String, Number…
means설명
example예시
defaultValue기본 값
formattedAs포멧 (날짜 등)
isOptional필수 여부

위의 parameter를 조합하여, 하나의 field를 생성함.

ex) "createdAt" type STRING means "회원 생성일자" formattedAs DocsFormatter.DATETIME defaultValue datetimeNowToString()

DocsField.kt
package com.example.kotlinapiserverguide.restDocs.constant  
  
import org.springframework.restdocs.payload.FieldDescriptor  
  
open class DocsField : FieldDescriptor {  
  
    constructor(path: String?) : super(path) {  
        this.attributes(DocsAttributeKeys.DEPTH.set(this.depth, this.path))  
    }  
  
    constructor(path: String, docsField: DocsField) : super(path) {  
        this.type(docsField.type)  
        this.description(docsField.description)  
        this.depth = docsField.depth  
        this.format = docsField.format  
        this.default = docsField.default  
        this.example = docsField.example  
        if (docsField.isIgnored) this.ignored()  
        if (docsField.isOptional) this.optional()  
    }  
    var depth: Int = 1  
  
    protected open var default: String?  
        get() = this.attributes.getOrDefault(DocsAttributeKeys.DEFAULT.code, " ") as String  
        set(value) {  
            this.attributes(DocsAttributeKeys.DEFAULT.set(value))  
        }  
  
    protected open var format: String?  
        get() = this.attributes.getOrDefault(DocsAttributeKeys.FORMAT.code, " ") as String  
        set(value) {  
            this.attributes(DocsAttributeKeys.FORMAT.set(value))  
        }  
  
    protected open var example: String?  
        get() = this.attributes.getOrDefault(DocsAttributeKeys.EXAMPLE.code, " ") as String  
        set(value) {  
            this.attributes(DocsAttributeKeys.EXAMPLE.set(value))  
        }  
  
    open infix fun means(value: String): DocsField {  
        this.description(value)  
        return this  
    }  
  
    open infix fun defaultValue(value: String): DocsField {  
        this.default = value  
        return this  
    }  
  
    open infix fun formattedAs(value: String): DocsField {  
        this.format = value  
        return this  
    }  
  
    open infix fun example(value: String): DocsField {  
        this.example = value  
        return this  
    }  
  
    open infix fun isOptional(value: Boolean): DocsField {  
        if (value) this.optional()  
        return this  
    }  
  
    open infix fun isIgnored(value: Boolean): DocsField {  
        if (value) this.ignored()  
        return this  
    }  
  
    // is Last  
    open infix fun fields(fields: Array<DocsField>): Array<DocsField> {  
        val parentsDepth = this.depth  
        val childFieldDepth = this.depth + 1  
        val childFields: Array<DocsField> = fields  
            .map { DocsField("${this.path}.${it.path}", it) }  
            .toTypedArray()  
  
        childFields.forEach {  
            it.depth = childFieldDepth  
            (1..parentsDepth).forEach { depth -> it.attributes(DocsAttributeKeys.DEPTH.set(depth, "&nbsp;")) }  
            it.attributes(DocsAttributeKeys.DEPTH.set(childFieldDepth, it.path.split(".").last()))  
        }  
  
        return arrayOf(this, *childFields)  
    }  
  
}

RestDocsFieldInfix

시작점을 구분하기 위해서 String.typeinfix fuction을 정의하여, type을 맨 처음 정의하도록 강제하고 field 정의 방식을 확립.

RestDocsFieldInfix.kt
package com.example.kotlinapiserverguide.restDocs.infix  
  
import com.example.kotlinapiserverguide.restDocs.constant.DocsField  
import com.example.kotlinapiserverguide.restDocs.constant.DocsFieldType  
import org.springframework.restdocs.payload.JsonFieldType  
  
private fun createField(value: String, type: JsonFieldType, optional: Boolean = false): DocsField {  
    val docsField = DocsField(value)  
    docsField.type(type)  
    docsField.description("")  
    if (optional) docsField.optional()  
    return docsField  
}  
  
infix fun String.type(docsFieldType: DocsFieldType): DocsField {  
    return createField(this, docsFieldType.type)  
}

RestDocsPathParameterInfix

pathParameter의 경우에는 기존의 DocsField Class를 사용하지 않고 타입을 지정할 필요가 없기 때문에 type이 아닌 바로 means(설명 또는 의미)로 시작할 수 있는 infix function을 따로 정의.

RestDocsPathParameterInfix.kt
package com.example.kotlinapiserverguide.restDocs.infix  
  
import org.springframework.restdocs.request.ParameterDescriptor  
import org.springframework.restdocs.request.RequestDocumentation  
  
private fun createField(name: String, description: String): ParameterDescriptor {  
    return RequestDocumentation.parameterWithName(name)  
        .description(description)  
}  
  
infix fun String.means(description: String): ParameterDescriptor {  
    return createField(this, description)  
}