HttpClientTool.kt

  1. package com.hexagontk.rest.tools

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

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

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

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

  43.     private lateinit var lastRequest: HttpRequest
  44.     private lateinit var lastResponse: HttpResponsePort

  45.     val request: HttpRequest get() = lastRequest
  46.     val response: HttpResponsePort get() = lastResponse
  47.     val status: Int get() = lastResponse.status
  48.     val body: Any get() = lastResponse.body
  49.     val cookies: Map<String, Cookie> get() = lastResponse.cookiesMap()
  50.     val headers: Headers get() = lastResponse.headers
  51.     val contentType: ContentType? get() = lastResponse.contentType

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

  69.     fun start() {
  70.         if (!client.started())
  71.             client.start()
  72.     }

  73.     fun stop() {
  74.         if (client.started())
  75.             client.stop()
  76.     }

  77.     fun request(block: HttpClientTool.() -> Unit) {
  78.         client.request { block.invoke(this@HttpClientTool) }
  79.     }

  80.     fun assertStatus(status: Int) {
  81.         assert(status == lastResponse.status)
  82.     }

  83.     fun assertOk() {
  84.         assertStatus(OK_200)
  85.     }

  86.     fun assertSuccess() {
  87.         assert(lastResponse.status in SUCCESS)
  88.     }

  89.     fun assertContentType(contentType: ContentType) {
  90.         assert(this.contentType?.text == contentType.text)
  91.     }

  92.     fun assertContentType(mediaType: MediaType) {
  93.         assertContentType(ContentType(mediaType))
  94.     }

  95.     fun assertBody(body: Any) {
  96.         assert(body == lastResponse.body)
  97.     }

  98.     fun assertBodyContains(vararg texts: String) {
  99.         texts.forEach { assert(lastResponse.bodyString().contains(it)) }
  100.     }

  101.     private fun toHeaders(map: Map<String, *>): Headers = Headers(
  102.         map.flatMap { (k, v) ->
  103.             when (v) {
  104.                 is Collection<*> -> v.map { Header(k, it) }.toList()
  105.                 else -> listOf(Header(k, v))
  106.             }
  107.         }
  108.     )

  109.     private fun send(
  110.         method: HttpMethod,
  111.         path: String?,
  112.         body: Any = "",
  113.         formParameters: List<HttpField> = emptyList(),
  114.         headers: Map<String, *> = emptyMap<String, Any>(),
  115.         parts: List<HttpPart> = emptyList(),
  116.         contentType: ContentType? = settings.contentType,
  117.         accept: List<ContentType> = settings.accept,
  118.         pathPattern: String? = null,
  119.         pathParameters: Map<String, Any> = emptyMap(),
  120.     ): HttpResponsePort =
  121.         client
  122.             .apply {
  123.                 if (!started())
  124.                     start()

  125.                 val (pattern, patternPath) = when {
  126.                     pathPattern != null ->
  127.                         createPathPattern(pathPattern)
  128.                             .let { it to it.insertParameters(pathParameters) }

  129.                     else -> null to (path ?: "")
  130.                 }

  131.                 lastRequest = HttpRequest(
  132.                     method = method,
  133.                     path = patternPath,
  134.                     body = body,
  135.                     headers = toHeaders(headers),
  136.                     formParameters = Parameters(formParameters),
  137.                     parts = parts,
  138.                     contentType = contentType,
  139.                     accept = accept,
  140.                     pathPattern = pattern,
  141.                     pathParameters = pathParameters,
  142.                 )
  143.             }
  144.             .send(lastRequest)
  145.             .apply { lastResponse = this }

  146.     private fun send(
  147.         method: HttpMethod,
  148.         pathPattern: String,
  149.         pathParameters: Map<String, Any>,
  150.         body: Any = "",
  151.         formParameters: List<HttpField> = 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.     ): HttpResponsePort =
  157.         send(
  158.             method,
  159.             null,
  160.             body,
  161.             formParameters,
  162.             headers,
  163.             parts,
  164.             contentType,
  165.             accept,
  166.             pathPattern,
  167.             pathParameters,
  168.         )

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

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

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

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

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

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

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

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

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

  259.     fun get(
  260.         path: String,
  261.         pathParameters: Map<String, Any>,
  262.         body: Any = "",
  263.         formParameters: List<HttpField> = 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, pathParameters, body, formParameters, headers, parts, contentType, accept)

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

  281.     fun put(
  282.         path: String,
  283.         pathParameters: Map<String, Any>,
  284.         formParameters: List<HttpField> = emptyList(),
  285.         parts: List<HttpPart> = emptyList(),
  286.         contentType: ContentType? = settings.contentType,
  287.         accept: List<ContentType> = settings.accept,
  288.         body: () -> Any,
  289.     ): HttpResponsePort =
  290.         put(
  291.             path,
  292.             pathParameters,
  293.             body(),
  294.             formParameters,
  295.             mapOf<String, Any>(),
  296.             parts,
  297.             contentType,
  298.             accept
  299.         )

  300.     fun post(
  301.         path: String,
  302.         pathParameters: Map<String, Any>,
  303.         body: Any = "",
  304.         formParameters: List<HttpField> = emptyList(),
  305.         headers: Map<String, *> = emptyMap<String, Any>(),
  306.         parts: List<HttpPart> = emptyList(),
  307.         contentType: ContentType? = settings.contentType,
  308.         accept: List<ContentType> = settings.accept,
  309.     ): HttpResponsePort =
  310.         send(POST, path, pathParameters, body, formParameters, headers, parts, contentType, accept)

  311.     fun post(
  312.         path: String,
  313.         pathParameters: Map<String, Any>,
  314.         formParameters: List<HttpField> = emptyList(),
  315.         headers: Map<String, *> = emptyMap<String, Any>(),
  316.         parts: List<HttpPart> = emptyList(),
  317.         contentType: ContentType? = settings.contentType,
  318.         accept: List<ContentType> = settings.accept,
  319.         body: () -> Any,
  320.     ): HttpResponsePort =
  321.         post(path, pathParameters, body(), formParameters, headers, parts, contentType, accept)

  322.     fun options(
  323.         path: String,
  324.         pathParameters: Map<String, Any>,
  325.         body: Any = "",
  326.         formParameters: List<HttpField> = emptyList(),
  327.         headers: Map<String, *> = emptyMap<String, Any>(),
  328.         parts: List<HttpPart> = emptyList(),
  329.         contentType: ContentType? = settings.contentType,
  330.         accept: List<ContentType> = settings.accept,
  331.     ): HttpResponsePort =
  332.         send(
  333.             OPTIONS, path, pathParameters, body, formParameters, headers, parts, contentType, accept
  334.         )

  335.     fun delete(
  336.         path: String,
  337.         pathParameters: Map<String, Any>,
  338.         body: Any = "",
  339.         formParameters: List<HttpField> = emptyList(),
  340.         headers: Map<String, *> = emptyMap<String, Any>(),
  341.         parts: List<HttpPart> = emptyList(),
  342.         contentType: ContentType? = settings.contentType,
  343.         accept: List<ContentType> = settings.accept,
  344.     ): HttpResponsePort =
  345.         send(
  346.             DELETE, path, pathParameters, body, formParameters, headers, parts, contentType, accept
  347.         )

  348.     fun patch(
  349.         path: String,
  350.         pathParameters: Map<String, Any>,
  351.         body: Any = "",
  352.         formParameters: List<HttpField> = emptyList(),
  353.         headers: Map<String, *> = emptyMap<String, Any>(),
  354.         parts: List<HttpPart> = emptyList(),
  355.         contentType: ContentType? = settings.contentType,
  356.         accept: List<ContentType> = settings.accept,
  357.     ): HttpResponsePort =
  358.         send(PATCH, path, pathParameters, body, formParameters, headers, parts, contentType, accept)

  359.     fun trace(
  360.         path: String,
  361.         pathParameters: Map<String, Any>,
  362.         body: Any = "",
  363.         formParameters: List<HttpField> = emptyList(),
  364.         headers: Map<String, *> = emptyMap<String, Any>(),
  365.         parts: List<HttpPart> = emptyList(),
  366.         contentType: ContentType? = settings.contentType,
  367.         accept: List<ContentType> = settings.accept,
  368.     ): HttpResponsePort =
  369.         send(TRACE, path, pathParameters, body, formParameters, headers, parts, contentType, accept)
  370. }