HttpClientTool.kt

  1. package com.hexagonkt.rest.tools

  2. import com.hexagonkt.core.media.MediaType
  3. import com.hexagonkt.core.urlOf
  4. import com.hexagonkt.http.SslSettings
  5. import com.hexagonkt.http.client.HttpClient
  6. import com.hexagonkt.http.client.HttpClientPort
  7. import com.hexagonkt.http.client.HttpClientSettings
  8. import com.hexagonkt.http.handlers.BeforeHandler
  9. import com.hexagonkt.http.handlers.HttpHandler
  10. import com.hexagonkt.http.model.HttpRequest
  11. import com.hexagonkt.http.model.*
  12. import com.hexagonkt.http.model.HttpMethod.*
  13. import com.hexagonkt.http.model.HttpStatusType.SUCCESS
  14. import com.hexagonkt.http.patterns.createPathPattern
  15. import com.hexagonkt.rest.SerializeRequestCallback

  16. data class HttpClientTool(
  17.     val adapter: HttpClientPort,
  18.     val url: String? = null,
  19.     val httpContentType: ContentType? = null,
  20.     val httpAccept: List<ContentType> = emptyList(),
  21.     val httpHeaders: Map<String, *> = emptyMap<String, Any>(),
  22.     val sslSettings: SslSettings? = SslSettings(),
  23.     val handler: HttpHandler? = serializeHandler,
  24.     val authorization: Authorization? = null,
  25.     val followRedirects: Boolean = false
  26. ) {
  27.     companion object {
  28.         // TODO Use SerializeRequestHandler when created
  29.         val serializeHandler: HttpHandler = BeforeHandler("*", SerializeRequestCallback())
  30.     }

  31.     private val settings =
  32.         HttpClientSettings(
  33.             baseUrl = url?.let(::urlOf),
  34.             contentType = httpContentType,
  35.             accept = httpAccept,
  36.             useCookies = true,
  37.             headers = toHeaders(httpHeaders),
  38.             insecure = true,
  39.             sslSettings = sslSettings,
  40.             authorization = authorization,
  41.             followRedirects = followRedirects
  42.         )

  43.     private val client = HttpClient(adapter, settings, handler = handler)

  44.     private lateinit var lastRequest: HttpRequest
  45.     private lateinit var lastAttributes: Map<String, *>
  46.     private lateinit var lastResponse: HttpResponsePort

  47.     val request: HttpRequest get() = lastRequest
  48.     val attributes: Map<String, *> get() = lastAttributes
  49.     val response: HttpResponsePort get() = lastResponse
  50.     val status: HttpStatus get() = lastResponse.status
  51.     val body: Any get() = lastResponse.body
  52.     val cookies: Map<String, Cookie> get() = lastResponse.cookiesMap()
  53.     val headers: Headers get() = lastResponse.headers
  54.     val contentType: ContentType? get() = lastResponse.contentType

  55.     constructor(
  56.         adapter: HttpClientPort,
  57.         url: String? = null,
  58.         mediaType: MediaType,
  59.         accept: List<MediaType> = emptyList(),
  60.         headers: Map<String, *> = emptyMap<String, Any>(),
  61.         sslSettings: SslSettings? = SslSettings(),
  62.         handler: HttpHandler? = serializeHandler,
  63.     ) : this(
  64.         adapter,
  65.         url,
  66.         ContentType(mediaType),
  67.         accept.map(::ContentType),
  68.         headers,
  69.         sslSettings,
  70.         handler
  71.     )

  72.     fun start() {
  73.         if (!client.started())
  74.             client.start()
  75.     }

  76.     fun stop() {
  77.         if (client.started())
  78.             client.stop()
  79.     }

  80.     fun request(block: HttpClientTool.() -> Unit) {
  81.         client.request { block.invoke(this@HttpClientTool) }
  82.     }

  83.     fun assertStatus(status: HttpStatus) {
  84.         assert(status == lastResponse.status)
  85.     }

  86.     fun assertOk() {
  87.         assertStatus(OK_200)
  88.     }

  89.     fun assertStatus(statusType: HttpStatusType) {
  90.         assert(statusType == lastResponse.status.type)
  91.     }

  92.     fun assertSuccess() {
  93.         assertStatus(SUCCESS)
  94.     }

  95.     fun assertContentType(contentType: ContentType) {
  96.         assert(this.contentType == contentType)
  97.     }

  98.     fun assertContentType(mediaType: MediaType) {
  99.         assert(contentType == ContentType(mediaType))
  100.     }

  101.     fun assertBody(body: Any) {
  102.         assert(body == lastResponse.body)
  103.     }

  104.     fun assertBodyContains(vararg texts: String) {
  105.         texts.forEach { assert(lastResponse.bodyString().contains(it)) }
  106.     }

  107.     private fun toHeaders(map: Map<String, *>): Headers = Headers(
  108.         map.mapValues { (k, v) ->
  109.             Header(
  110.                 k,
  111.                 when (v) {
  112.                     is Collection<*> -> v.map { it.toString() }.toList()
  113.                     else -> listOf(v.toString())
  114.                 }
  115.             )
  116.         }
  117.     )

  118.     private fun send(
  119.         method: HttpMethod = GET,
  120.         path: String = "/",
  121.         body: Any = "",
  122.         formParameters: List<FormParameter> = emptyList(),
  123.         headers: Map<String, *> = emptyMap<String, Any>(),
  124.         parts: List<HttpPart> = emptyList(),
  125.         contentType: ContentType? = settings.contentType,
  126.         accept: List<ContentType> = settings.accept,
  127.         attributes: Map<String, Any> = emptyMap(),
  128.     ): HttpResponsePort =
  129.         client
  130.             .apply {
  131.                 if (!started())
  132.                     start()

  133.                 lastAttributes = attributes
  134.                 lastRequest = HttpRequest(
  135.                     method = method,
  136.                     path = path,
  137.                     body = body,
  138.                     headers = toHeaders(headers),
  139.                     formParameters = FormParameters(formParameters),
  140.                     parts = parts,
  141.                     contentType = contentType,
  142.                     accept = accept,
  143.                 )
  144.             }
  145.             .send(lastRequest, attributes = attributes)
  146.             .apply { lastResponse = this }

  147.     private fun send(
  148.         method: HttpMethod = GET,
  149.         path: Pair<String, Map<String, Any>>,
  150.         body: Any = "",
  151.         formParameters: List<FormParameter> = emptyList(),
  152.         headers: Map<String, *> = emptyMap<String, Any>(),
  153.         parts: List<HttpPart> = emptyList(),
  154.         contentType: ContentType? = settings.contentType,
  155.         accept: List<ContentType> = settings.accept,
  156.         attributes: Map<String, Any> = emptyMap(),
  157.     ): HttpResponsePort =
  158.         send(
  159.             method = method,
  160.             path = createPathPattern(path.first, false).insertParameters(path.second),
  161.             body = body,
  162.             formParameters = formParameters,
  163.             headers = toHeaders(headers),
  164.             parts = parts,
  165.             contentType = contentType,
  166.             accept = accept,
  167.             attributes = attributes
  168.                 + mapOf("pathPattern" to path.first, "pathParameters" to path.second),
  169.         )

  170.     fun get(
  171.         path: String = "/",
  172.         body: Any = "",
  173.         formParameters: List<FormParameter> = emptyList(),
  174.         headers: Map<String, *> = emptyMap<String, Any>(),
  175.         parts: List<HttpPart> = emptyList(),
  176.         contentType: ContentType? = settings.contentType,
  177.         accept: List<ContentType> = settings.accept,
  178.     ): HttpResponsePort =
  179.         send(GET, path, body, formParameters, headers, parts, contentType, accept)

  180.     fun put(
  181.         path: String = "/",
  182.         body: Any = "",
  183.         formParameters: List<FormParameter> = emptyList(),
  184.         headers: Map<String, *> = emptyMap<String, Any>(),
  185.         parts: List<HttpPart> = emptyList(),
  186.         contentType: ContentType? = settings.contentType,
  187.         accept: List<ContentType> = settings.accept,
  188.     ): HttpResponsePort =
  189.         send(PUT, path, body, formParameters, headers, parts, contentType, accept)

  190.     fun put(
  191.         path: String = "/",
  192.         formParameters: List<FormParameter> = emptyList(),
  193.         headers: Map<String, *> = emptyMap<String, Any>(),
  194.         parts: List<HttpPart> = emptyList(),
  195.         contentType: ContentType? = settings.contentType,
  196.         accept: List<ContentType> = settings.accept,
  197.         body: () -> Any,
  198.     ): HttpResponsePort =
  199.         put(path, body(), formParameters, headers, parts, contentType, accept)

  200.     fun post(
  201.         path: String = "/",
  202.         body: Any = "",
  203.         formParameters: List<FormParameter> = emptyList(),
  204.         headers: Map<String, *> = emptyMap<String, Any>(),
  205.         parts: List<HttpPart> = emptyList(),
  206.         contentType: ContentType? = settings.contentType,
  207.         accept: List<ContentType> = settings.accept,
  208.     ): HttpResponsePort =
  209.         send(POST, path, body, formParameters, headers, parts, contentType, accept)

  210.     fun post(
  211.         path: String = "/",
  212.         formParameters: List<FormParameter> = emptyList(),
  213.         headers: Map<String, *> = emptyMap<String, Any>(),
  214.         parts: List<HttpPart> = emptyList(),
  215.         contentType: ContentType? = settings.contentType,
  216.         accept: List<ContentType> = settings.accept,
  217.         body: () -> Any,
  218.     ): HttpResponsePort =
  219.         post(path, body(), formParameters, headers, parts, contentType, accept)

  220.     fun options(
  221.         path: String = "/",
  222.         body: Any = "",
  223.         formParameters: List<FormParameter> = emptyList(),
  224.         headers: Map<String, *> = emptyMap<String, Any>(),
  225.         parts: List<HttpPart> = emptyList(),
  226.         contentType: ContentType? = settings.contentType,
  227.         accept: List<ContentType> = settings.accept,
  228.     ): HttpResponsePort =
  229.         send(OPTIONS, path, body, formParameters, headers, parts, contentType, accept)

  230.     fun delete(
  231.         path: String = "/",
  232.         body: Any = "",
  233.         formParameters: List<FormParameter> = emptyList(),
  234.         headers: Map<String, *> = emptyMap<String, Any>(),
  235.         parts: List<HttpPart> = emptyList(),
  236.         contentType: ContentType? = settings.contentType,
  237.         accept: List<ContentType> = settings.accept,
  238.     ): HttpResponsePort =
  239.         send(DELETE, path, body, formParameters, headers, parts, contentType, accept)

  240.     fun patch(
  241.         path: String = "/",
  242.         body: Any = "",
  243.         formParameters: List<FormParameter> = emptyList(),
  244.         headers: Map<String, *> = emptyMap<String, Any>(),
  245.         parts: List<HttpPart> = emptyList(),
  246.         contentType: ContentType? = settings.contentType,
  247.         accept: List<ContentType> = settings.accept,
  248.     ): HttpResponsePort =
  249.         send(PATCH, path, body, formParameters, headers, parts, contentType, accept)

  250.     fun trace(
  251.         path: String = "/",
  252.         body: Any = "",
  253.         formParameters: List<FormParameter> = emptyList(),
  254.         headers: Map<String, *> = emptyMap<String, Any>(),
  255.         parts: List<HttpPart> = emptyList(),
  256.         contentType: ContentType? = settings.contentType,
  257.         accept: List<ContentType> = settings.accept,
  258.     ): HttpResponsePort =
  259.         send(TRACE, path, body, formParameters, headers, parts, contentType, accept)

  260.     fun get(
  261.         path: Pair<String, Map<String, Any>>,
  262.         body: Any = "",
  263.         formParameters: List<FormParameter> = emptyList(),
  264.         headers: Map<String, *> = emptyMap<String, Any>(),
  265.         parts: List<HttpPart> = emptyList(),
  266.         contentType: ContentType? = settings.contentType,
  267.         accept: List<ContentType> = settings.accept,
  268.     ): HttpResponsePort =
  269.         send(GET, path, body, formParameters, headers, parts, contentType, accept)

  270.     fun put(
  271.         path: Pair<String, Map<String, Any>>,
  272.         body: Any = "",
  273.         formParameters: List<FormParameter> = emptyList(),
  274.         headers: Map<String, *> = emptyMap<String, Any>(),
  275.         parts: List<HttpPart> = emptyList(),
  276.         contentType: ContentType? = settings.contentType,
  277.         accept: List<ContentType> = settings.accept,
  278.     ): HttpResponsePort =
  279.         send(PUT, path, body, formParameters, headers, parts, contentType, accept)

  280.     fun put(
  281.         path: Pair<String, Map<String, Any>>,
  282.         formParameters: List<FormParameter> = emptyList(),
  283.         parts: List<HttpPart> = emptyList(),
  284.         contentType: ContentType? = settings.contentType,
  285.         accept: List<ContentType> = settings.accept,
  286.         body: () -> Any,
  287.     ): HttpResponsePort =
  288.         put(path, body(), formParameters, Headers(), parts, contentType, accept)

  289.     fun post(
  290.         path: Pair<String, Map<String, Any>>,
  291.         body: Any = "",
  292.         formParameters: List<FormParameter> = emptyList(),
  293.         headers: Map<String, *> = emptyMap<String, Any>(),
  294.         parts: List<HttpPart> = emptyList(),
  295.         contentType: ContentType? = settings.contentType,
  296.         accept: List<ContentType> = settings.accept,
  297.     ): HttpResponsePort =
  298.         send(POST, path, body, formParameters, headers, parts, contentType, accept)

  299.     fun post(
  300.         path: Pair<String, Map<String, Any>>,
  301.         formParameters: List<FormParameter> = emptyList(),
  302.         headers: Map<String, *> = emptyMap<String, Any>(),
  303.         parts: List<HttpPart> = emptyList(),
  304.         contentType: ContentType? = settings.contentType,
  305.         accept: List<ContentType> = settings.accept,
  306.         body: () -> Any,
  307.     ): HttpResponsePort =
  308.         post(path, body(), formParameters, headers, parts, contentType, accept)

  309.     fun options(
  310.         path: Pair<String, Map<String, Any>>,
  311.         body: Any = "",
  312.         formParameters: List<FormParameter> = emptyList(),
  313.         headers: Map<String, *> = emptyMap<String, Any>(),
  314.         parts: List<HttpPart> = emptyList(),
  315.         contentType: ContentType? = settings.contentType,
  316.         accept: List<ContentType> = settings.accept,
  317.     ): HttpResponsePort =
  318.         send(OPTIONS, path, body, formParameters, headers, parts, contentType, accept)

  319.     fun delete(
  320.         path: Pair<String, Map<String, Any>>,
  321.         body: Any = "",
  322.         formParameters: List<FormParameter> = emptyList(),
  323.         headers: Map<String, *> = emptyMap<String, Any>(),
  324.         parts: List<HttpPart> = emptyList(),
  325.         contentType: ContentType? = settings.contentType,
  326.         accept: List<ContentType> = settings.accept,
  327.     ): HttpResponsePort =
  328.         send(DELETE, path, body, formParameters, headers, parts, contentType, accept)

  329.     fun patch(
  330.         path: Pair<String, Map<String, Any>>,
  331.         body: Any = "",
  332.         formParameters: List<FormParameter> = emptyList(),
  333.         headers: Map<String, *> = emptyMap<String, Any>(),
  334.         parts: List<HttpPart> = emptyList(),
  335.         contentType: ContentType? = settings.contentType,
  336.         accept: List<ContentType> = settings.accept,
  337.     ): HttpResponsePort =
  338.         send(PATCH, path, body, formParameters, headers, parts, contentType, accept)

  339.     fun trace(
  340.         path: Pair<String, Map<String, Any>>,
  341.         body: Any = "",
  342.         formParameters: List<FormParameter> = emptyList(),
  343.         headers: Map<String, *> = emptyMap<String, Any>(),
  344.         parts: List<HttpPart> = emptyList(),
  345.         contentType: ContentType? = settings.contentType,
  346.         accept: List<ContentType> = settings.accept,
  347.     ): HttpResponsePort =
  348.         send(TRACE, path, body, formParameters, headers, parts, contentType, accept)
  349. }