From f79030efb8e2da87ad978d67d428ba50b545eb30 Mon Sep 17 00:00:00 2001 From: jaguililla Date: Mon, 1 Jan 2024 18:52:07 +0100 Subject: [PATCH 1/4] Set new release version --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 38884e4db5..8e5601a4fc 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,7 +5,7 @@ org.gradle.warning.mode=all org.gradle.console=plain # Gradle -version=3.4.6 +version=3.4.7 group=com.hexagonkt description=The atoms of your platform From 3979f73abe5c417beabb1d217c93d44c5b6f32d7 Mon Sep 17 00:00:00 2001 From: jaguililla Date: Mon, 1 Jan 2024 18:54:44 +0100 Subject: [PATCH 2/4] Refactor --- http/http/api/http.api | 12 ------------ templates/templates/api/templates.api | 17 ----------------- .../api/templates_freemarker.api | 7 ------- 3 files changed, 36 deletions(-) diff --git a/http/http/api/http.api b/http/http/api/http.api index 4400dc7aa7..3d4b5c3bcf 100644 --- a/http/http/api/http.api +++ b/http/http/api/http.api @@ -1,15 +1,3 @@ -public final class com/hexagonkt/http/HttpKt { - public static final fun checkHeaders (Lcom/hexagonkt/http/model/Headers;)V - public static final fun formatQueryString (Lcom/hexagonkt/http/model/QueryParameters;)Ljava/lang/String; - public static final fun getCHECKED_HEADERS ()Ljava/util/List; - public static final fun parseContentType (Ljava/lang/String;)Lcom/hexagonkt/http/model/ContentType; - public static final fun parseQueryString (Ljava/lang/String;)Lcom/hexagonkt/http/model/QueryParameters; - public static final fun toHttpFormat (Ljava/time/Instant;)Ljava/lang/String; - public static final fun toHttpFormat (Ljava/time/LocalDateTime;)Ljava/lang/String; - public static final fun urlDecode (Ljava/lang/String;)Ljava/lang/String; - public static final fun urlEncode (Ljava/lang/String;)Ljava/lang/String; -} - public final class com/hexagonkt/http/SslSettings { public fun ()V public fun (Ljava/net/URL;Ljava/lang/String;Ljava/net/URL;Ljava/lang/String;Z)V diff --git a/templates/templates/api/templates.api b/templates/templates/api/templates.api index 02ec15ac02..8dc83292e8 100644 --- a/templates/templates/api/templates.api +++ b/templates/templates/api/templates.api @@ -1,23 +1,6 @@ -public final class com/hexagonkt/templates/TemplateManager { - public static final field INSTANCE Lcom/hexagonkt/templates/TemplateManager; - public final fun getAdapters ()Ljava/util/Map; - public final fun getDefaultAdapter ()Lcom/hexagonkt/templates/TemplatePort; - public final fun render (Ljava/net/URL;Ljava/util/Map;Ljava/util/Locale;)Ljava/lang/String; - public static synthetic fun render$default (Lcom/hexagonkt/templates/TemplateManager;Ljava/net/URL;Ljava/util/Map;Ljava/util/Locale;ILjava/lang/Object;)Ljava/lang/String; - public final fun setAdapters (Ljava/util/Map;)V - public final fun setDefaultAdapter (Lcom/hexagonkt/templates/TemplatePort;)V -} - public abstract interface class com/hexagonkt/templates/TemplatePort { public abstract fun render (Ljava/lang/String;Ljava/util/Map;Ljava/util/Locale;)Ljava/lang/String; public abstract fun render (Ljava/lang/String;Ljava/util/Map;Ljava/util/Map;Ljava/util/Locale;)Ljava/lang/String; public abstract fun render (Ljava/net/URL;Ljava/util/Map;Ljava/util/Locale;)Ljava/lang/String; } -public final class com/hexagonkt/templates/TemplatePort$DefaultImpls { - public static fun render (Lcom/hexagonkt/templates/TemplatePort;Ljava/lang/String;Ljava/util/Map;Ljava/util/Locale;)Ljava/lang/String; - public static synthetic fun render$default (Lcom/hexagonkt/templates/TemplatePort;Ljava/lang/String;Ljava/util/Map;Ljava/util/Locale;ILjava/lang/Object;)Ljava/lang/String; - public static synthetic fun render$default (Lcom/hexagonkt/templates/TemplatePort;Ljava/lang/String;Ljava/util/Map;Ljava/util/Map;Ljava/util/Locale;ILjava/lang/Object;)Ljava/lang/String; - public static synthetic fun render$default (Lcom/hexagonkt/templates/TemplatePort;Ljava/net/URL;Ljava/util/Map;Ljava/util/Locale;ILjava/lang/Object;)Ljava/lang/String; -} - diff --git a/templates/templates_freemarker/api/templates_freemarker.api b/templates/templates_freemarker/api/templates_freemarker.api index e55ccc7a98..9fd96c2e36 100644 --- a/templates/templates_freemarker/api/templates_freemarker.api +++ b/templates/templates_freemarker/api/templates_freemarker.api @@ -1,10 +1,3 @@ -public final class com/hexagonkt/templates/freemarker/FreeMarkerAdapter : com/hexagonkt/templates/TemplatePort { - public fun ()V - public fun render (Ljava/lang/String;Ljava/util/Map;Ljava/util/Locale;)Ljava/lang/String; - public fun render (Ljava/lang/String;Ljava/util/Map;Ljava/util/Map;Ljava/util/Locale;)Ljava/lang/String; - public fun render (Ljava/net/URL;Ljava/util/Map;Ljava/util/Locale;)Ljava/lang/String; -} - public final class com/hexagonkt/templates/freemarker/FreeMarkerAdapter$AdapterTemplateLoader : freemarker/cache/URLTemplateLoader { public static final field INSTANCE Lcom/hexagonkt/templates/freemarker/FreeMarkerAdapter$AdapterTemplateLoader; } From db77a793821c95d54a45bd86d1c1122e165609b3 Mon Sep 17 00:00:00 2001 From: jaguililla Date: Mon, 1 Jan 2024 19:30:24 +0100 Subject: [PATCH 3/4] Fix expired certificates --- http/http/api/http.api | 12 ++++++++++++ http/http/src/test/resources/hexagonkt.p12 | Bin 3678 -> 4766 bytes .../src/test/resources/hexagonkt.p12 | Bin 3678 -> 4766 bytes .../src/main/resources/ssl/hexagonkt.p12 | Bin 3678 -> 4766 bytes .../src/main/resources/ssl/trust.p12 | Bin 1190 -> 1446 bytes templates/templates/api/templates.api | 17 +++++++++++++++++ .../api/templates_freemarker.api | 7 +++++++ 7 files changed, 36 insertions(+) diff --git a/http/http/api/http.api b/http/http/api/http.api index 3d4b5c3bcf..4400dc7aa7 100644 --- a/http/http/api/http.api +++ b/http/http/api/http.api @@ -1,3 +1,15 @@ +public final class com/hexagonkt/http/HttpKt { + public static final fun checkHeaders (Lcom/hexagonkt/http/model/Headers;)V + public static final fun formatQueryString (Lcom/hexagonkt/http/model/QueryParameters;)Ljava/lang/String; + public static final fun getCHECKED_HEADERS ()Ljava/util/List; + public static final fun parseContentType (Ljava/lang/String;)Lcom/hexagonkt/http/model/ContentType; + public static final fun parseQueryString (Ljava/lang/String;)Lcom/hexagonkt/http/model/QueryParameters; + public static final fun toHttpFormat (Ljava/time/Instant;)Ljava/lang/String; + public static final fun toHttpFormat (Ljava/time/LocalDateTime;)Ljava/lang/String; + public static final fun urlDecode (Ljava/lang/String;)Ljava/lang/String; + public static final fun urlEncode (Ljava/lang/String;)Ljava/lang/String; +} + public final class com/hexagonkt/http/SslSettings { public fun ()V public fun (Ljava/net/URL;Ljava/lang/String;Ljava/net/URL;Ljava/lang/String;Z)V diff --git a/http/http/src/test/resources/hexagonkt.p12 b/http/http/src/test/resources/hexagonkt.p12 index ec8895f6f31cc840dde54a6e71d36e58eec21765..1c2d9f88e05a2dcdadecf3bdd56ebee68e95fd64 100644 GIT binary patch delta 4602 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 k2TClM4WDiV6wC74ec<31+)eU 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 diff --git a/http/http_server/src/test/resources/hexagonkt.p12 b/http/http_server/src/test/resources/hexagonkt.p12 index ec8895f6f31cc840dde54a6e71d36e58eec21765..1c2d9f88e05a2dcdadecf3bdd56ebee68e95fd64 100644 GIT binary patch delta 4602 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 k2TClM4WDiV6wC74ec<31+)eU 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 diff --git a/http/http_test/src/main/resources/ssl/hexagonkt.p12 b/http/http_test/src/main/resources/ssl/hexagonkt.p12 index ec8895f6f31cc840dde54a6e71d36e58eec21765..1c2d9f88e05a2dcdadecf3bdd56ebee68e95fd64 100644 GIT binary patch delta 4602 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 k2TClM4WDiV6wC74ec<31+)eU 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 diff --git a/http/http_test/src/main/resources/ssl/trust.p12 b/http/http_test/src/main/resources/ssl/trust.p12 index cf8b98949d2f14da891aa24435acba99387a3c7d..c30072901bd43096fc34c42725a3a61da87f8642 100644 GIT binary patch delta 1389 zcmV-z1(N!v38o7_FoFf50s#Xsf(1+l2`Yw2hW8Bt2LYgh1w90U1vxN+1vM~&1u+H* zDuzgg_YDCD2B3lkA~1pl9s&UXFoFe831*?{L)*IeSvy5q+ID zzet&>#!3Em^COWXDY(!NMO0jvrD=Z&!#D_mr!Nm&f=wQcSR9bnzHC80P37NliumUJ zrB{j@=yO89^@mEK8NX^P7!`r?Yw4G4liEoV^Uqab&a2BO*qR)!9Q``2{A=Iq1<<$@ zf^~JJaS$Xv+Y0*z40P<$j2yN>YKXCv_|F=UkBS^5T)REkyL|=+J2U`r@qK@5{*i|N z1%y6XnN}|CZ_!>EeQ;eHaDVR}x5O@KyLNxl<Gk~0q>pq-zv0bn5_Bf$5H2lf{8wI!W%2mL-(bvgisJp&y; zQbhyaw+}KbCu?fYd`EF6-T^nHWMdSeeJay$ zg8azZWMXn1{9)Y@OPq;!+@%{14*uXfFHi%Q3MQ9qe#3J{3cir3W>YmRL(h?66V>n3IN3@pi z-Ypu(hj#YHpfRLbRbT5dSb_QYqo7a(IrP`f=?5u!{cIkQG3iTOq?q;(0_UWNGTxGJxr5{p>Gx*yRh>{52z87Jzt9Ql@k1ZnVRNCxhAG zoN$wxJ^pa^WQcv)_8t>Yy#^Oxb{wNf?nL?AY!ZXkepSceQf!ZqbBOX}bNt)3IaJ8T z2d&_z;AVf(b2W>vDW#7&qn3BnL(#8uJ=D?mrKE52K`@{8cXe;tjg=(_%xcD@yCO{~ zD#(%=In)UNcgKNU)-mp}FikKqFbxI?V1`HmWdj5P0R;dAAb-c`taW)M%#|eKE+Z4M v^Qd_kDw52b%w}#&Lg4dn2?P}T0Z#LjdecfVvw#g2f%1Th8) zDuzgg_YDCD2B3ljA~1pk9s&UXFoFb;KP(h)Gp&~~g~u17Xs{>n!IBO>9s;P5el zofSe@8YZIHpXs|ryV&P*S8z|2W&#i;R~0=jERSQXOwa2fyy&os1igKZ7sGG|;&f~F z@N)9@U`Kfu^UD7||S4U+|&Rvb#! z_z+DUbgYNVLF2zE$Bpd45P^TO1VUR%q8RRG|4B1i_Gv@yj4#=404m84eBVq>r7x74 zJi$EzyNa&=Pq|i)gH6C7vO*EtM&Za7<*>wrb)kYU*XQvnT~8*M$vRv_SxGTx=CO*X z^?JgXt(@qXNr)Qyngr?jBx<#mRUmn98s1aI9+X8bh@64F?J}04x5qw@b=_&DRD!DZB_AE(GnvyO= z<}lfG*E@Y2mMt$+r@-nOE#Qc?WcybS)_T@iS*y97b$-)>92Yk|Jg>^o$VgT8gr0-b zu}qeKXQGmECs%)q)KwF3LId06#V5s{{yCzPmTgiYEG6U~O%LSFNC9O71OfpC00bZ@E|A$5x7v-IFxr>i xpQQc}EVHkryfLAc0^rlPXB(LW6p~JQ ()V + public fun render (Ljava/lang/String;Ljava/util/Map;Ljava/util/Locale;)Ljava/lang/String; + public fun render (Ljava/lang/String;Ljava/util/Map;Ljava/util/Map;Ljava/util/Locale;)Ljava/lang/String; + public fun render (Ljava/net/URL;Ljava/util/Map;Ljava/util/Locale;)Ljava/lang/String; +} + public final class com/hexagonkt/templates/freemarker/FreeMarkerAdapter$AdapterTemplateLoader : freemarker/cache/URLTemplateLoader { public static final field INSTANCE Lcom/hexagonkt/templates/freemarker/FreeMarkerAdapter$AdapterTemplateLoader; } From 950b4df08670617040e2f1167782276d4064144a Mon Sep 17 00:00:00 2001 From: jaguililla Date: Wed, 24 Jan 2024 23:56:12 +0100 Subject: [PATCH 4/4] Small improvements and fixes * Any media type * Fix case types regex * Improve API documentation * Regroup some utility methods * Improve settings loading method * Move basic auth * Fix HTTP client errors * Add utility interfaces to extend HTTP handlers * Fix HTTP handlers context 'receive' method * Fix REST serialize callbacks * Improve REST testing tools --- .gitignore | 2 - .sdkmanrc | 3 + README.md | 8 +- contributing.md | 8 +- core/api/core.api | 3 +- .../main/kotlin/com/hexagonkt/core/Checks.kt | 30 +++- .../com/hexagonkt/core/CodedException.kt | 12 +- .../kotlin/com/hexagonkt/core/Exceptions.kt | 18 --- .../src/main/kotlin/com/hexagonkt/core/Jvm.kt | 5 +- .../com/hexagonkt/core/MultipleException.kt | 14 +- .../com/hexagonkt/core/media/MediaTypes.kt | 2 + .../kotlin/com/hexagonkt/core/text/Cases.kt | 4 +- .../kotlin/com/hexagonkt/core/ChecksTest.kt | 22 +++ .../com/hexagonkt/core/ExceptionsTest.kt | 22 --- .../test/kotlin/com/hexagonkt/core/JvmTest.kt | 3 +- .../com/hexagonkt/core/media/MediaTypeTest.kt | 2 + gradle.properties | 17 +- .../handlers/ExceptionHandlerTest.kt | 2 +- http/http/api/http.api | 2 + .../main/kotlin/com/hexagonkt/http/Http.kt | 4 + .../com/hexagonkt/http/client/HttpClient.kt | 13 +- .../hexagonkt/http/client/HttpClientTest.kt | 4 +- .../http/client/jetty/JettyClientAdapter.kt | 33 ++-- http/http_handlers/api/http_handlers.api | 30 +++- .../hexagonkt/http/handlers/AfterHandler.kt | 8 +- .../hexagonkt/http/handlers/BeforeHandler.kt | 8 +- .../http/handlers/ExceptionHandler.kt | 2 +- .../hexagonkt/http/handlers/FilterHandler.kt | 8 +- .../hexagonkt/http/handlers/HandlerBuilder.kt | 54 +++---- .../com/hexagonkt/http/handlers/Handlers.kt | 33 ++-- .../hexagonkt/http/handlers/HttpCallback.kt | 3 + .../hexagonkt/http/handlers/HttpContext.kt | 10 +- .../hexagonkt/http/handlers/HttpController.kt | 26 +++ .../http/handlers/HttpExceptionCallback.kt | 4 + .../hexagonkt/http/handlers/HttpHandler.kt | 2 +- .../com/hexagonkt/http/handlers/OnHandler.kt | 8 +- .../com/hexagonkt/http/handlers/Examples.kt | 4 +- .../http/handlers/HttpControllerTest.kt | 22 +++ .../com/hexagonkt/http/test/BaseTest.kt | 5 - .../http/test/examples/ClientTest.kt | 4 +- .../http/test/examples/FiltersTest.kt | 4 +- .../http/test/examples/SamplesTest.kt | 2 +- .../src/main/resources/assets/index.html | 10 +- .../main/kotlin/com/hexagonkt/rest/Rest.kt | 5 + .../rest/SerializeRequestCallback.kt | 16 +- .../rest/SerializeResponseCallback.kt | 19 ++- .../rest/SerializeRequestCallbackTest.kt | 37 +++++ .../rest/SerializeResponseCallbackTest.kt | 46 ++++++ http/rest_tools/build.gradle.kts | 11 +- .../com/hexagonkt/rest/tools/DynamicServer.kt | 4 +- .../kotlin/com/hexagonkt/rest/tools/Http.kt | 153 +++++++++--------- .../rest/tools/openapi/OpenApiHandler.kt | 4 +- .../rest/tools/openapi/VerifySpecCallback.kt | 5 +- .../hexagonkt/rest/tools/DynamicServerTest.kt | 45 ++++-- .../rest/tools/StateHttpClientTest.kt | 84 ++++++++++ .../tools/openapi/VerifySpecCallbackTest.kt | 24 +-- site/assets/img/architecture.svg | 2 +- site/mkdocs.yml | 10 +- site/mkdocs/main.html | 2 +- site/pages/index.md | 4 +- 60 files changed, 636 insertions(+), 315 deletions(-) create mode 100644 .sdkmanrc create mode 100644 http/http_handlers/src/main/kotlin/com/hexagonkt/http/handlers/HttpCallback.kt create mode 100644 http/http_handlers/src/main/kotlin/com/hexagonkt/http/handlers/HttpController.kt create mode 100644 http/http_handlers/src/main/kotlin/com/hexagonkt/http/handlers/HttpExceptionCallback.kt create mode 100644 http/http_handlers/src/test/kotlin/com/hexagonkt/http/handlers/HttpControllerTest.kt create mode 100644 http/rest/src/test/kotlin/com/hexagonkt/rest/SerializeRequestCallbackTest.kt create mode 100644 http/rest/src/test/kotlin/com/hexagonkt/rest/SerializeResponseCallbackTest.kt create mode 100644 http/rest_tools/src/test/kotlin/com/hexagonkt/rest/tools/StateHttpClientTest.kt diff --git a/.gitignore b/.gitignore index 5bfae36ff8..2952e0d9e6 100644 --- a/.gitignore +++ b/.gitignore @@ -40,9 +40,7 @@ hs_err_pid* # Other Tools kotlin-js-store/ node_modules/ -package-lock.json .env -.sdkmanrc # System Files .DS_Store diff --git a/.sdkmanrc b/.sdkmanrc new file mode 100644 index 0000000000..9584f14f9c --- /dev/null +++ b/.sdkmanrc @@ -0,0 +1,3 @@ +# Enable auto-env through the sdkman_auto_env config +# Add key=value pairs of SDKs to use below +java=21.0.1-graalce diff --git a/README.md b/README.md index 2e37349688..2ef834bdab 100644 --- a/README.md +++ b/README.md @@ -447,8 +447,8 @@ If you like this project and want to support it, the easiest way is to [give it If you feel like you can do more. You can contribute to the project in different ways: -* By using it and [spreading the word][@hexagon_kt]. -* Giving feedback by [Twitter][@hexagon_kt] or [Slack]. +* By using it and [spreading the word][@hexagontk]. +* Giving feedback by [X (Twitter)][@hexagontk] or [Slack]. * Requesting [new features or submitting bugs][issues]. * Voting for the features you want in the [issue tracker][issues] (using [reactions]). * And... Drum roll... Submitting [code or documentation][contributing]. @@ -457,14 +457,14 @@ To know what issues are currently open and be aware of the next features you can [Organization Board] at GitHub. You can ask any question, suggestion or complaint at the project's [Slack channel][Slack]. You can -be up-to-date of project's news following [@hexagon_kt] on Twitter. +be up-to-date of project's news following [@hexagontk] on X (Twitter). Thanks to all project's [contributors]! [![CodeTriage](https://www.codetriage.com/hexagonkt/hexagon/badges/users.svg)][CodeTriage] [give it a star]: https://github.com/hexagonkt/hexagon/stargazers -[@hexagon_kt]: https://twitter.com/hexagon_kt +[@hexagontk]: https://twitter.com/hexagontk [Slack]: https://kotlinlang.slack.com/messages/hexagon [issues]: https://github.com/hexagonkt/hexagon/issues [reactions]: https://github.com/blog/2119-add-reactions-to-pull-requests-issues-and-comments diff --git a/contributing.md b/contributing.md index fc815643ae..cbda36f021 100644 --- a/contributing.md +++ b/contributing.md @@ -7,12 +7,12 @@ process or picking a task and building the code. You can use the repository's [Discussions tab][discussion] to ask questions or resolve problems. You can also ask any question, make suggestions or complaints at the project's -[Slack channel][Slack]. You can also be up-to-date of project's news following [@hexagon_kt] on -Twitter. +[Slack channel][Slack]. You can also be up-to-date of project's news following [@hexagontk] on +X (Twitter). [discussion]: https://github.com/hexagonkt/hexagon/discussions [Slack]: https://kotlinlang.slack.com/messages/hexagon -[@hexagon_kt]: https://twitter.com/hexagon_kt +[@hexagontk]: https://twitter.com/hexagontk ## Report a Bug To file a bug, create an issue with the [bug template]. @@ -120,7 +120,7 @@ If you want to generate the documentation site, check the Hexagon's site module * Dev.to * Kotlin Slack * Reddit Kotlin - * Twitter + * X (Twitter) * Kotlin Weekly Newsletter * LinkedIn * Mailing lists (Awesome Kotlin, Kotlin Weekly) diff --git a/core/api/core.api b/core/api/core.api index f2b2c5f79f..f707a8e429 100644 --- a/core/api/core.api +++ b/core/api/core.api @@ -1,4 +1,5 @@ public final class com/hexagonkt/core/ChecksKt { + public static final fun check (Ljava/lang/String;[Lkotlin/jvm/functions/Function0;)V public static final fun checkSize (Ljava/util/Collection;Lkotlin/ranges/IntRange;)Ljava/util/Collection; public static final fun requireGreater (Ljava/lang/Object;Lkotlin/reflect/KProperty1;Ljava/lang/Object;)V public static final fun requireGreater (Ljava/lang/Object;Lkotlin/reflect/KProperty1;Lkotlin/reflect/KProperty1;)V @@ -103,7 +104,6 @@ public final class com/hexagonkt/core/DatesKt { } public final class com/hexagonkt/core/ExceptionsKt { - public static final fun check (Ljava/lang/String;[Lkotlin/jvm/functions/Function0;)V public static final fun filterStackTrace (Ljava/lang/Throwable;Ljava/lang/String;)[Ljava/lang/StackTraceElement; public static final fun getAssertEnabled ()Z public static final fun getFail ()Ljava/lang/Void; @@ -340,6 +340,7 @@ public final class com/hexagonkt/core/media/MediaTypeGroup : java/lang/Enum { public final class com/hexagonkt/core/media/MediaTypesKt { public static final fun extensionsOf (Lcom/hexagonkt/core/media/MediaType;)Ljava/util/List; + public static final fun getANY_MEDIA ()Lcom/hexagonkt/core/media/MediaType; public static final fun getAPPLICATION_7Z ()Lcom/hexagonkt/core/media/MediaType; public static final fun getAPPLICATION_AVRO ()Lcom/hexagonkt/core/media/MediaType; public static final fun getAPPLICATION_BZIP ()Lcom/hexagonkt/core/media/MediaType; diff --git a/core/src/main/kotlin/com/hexagonkt/core/Checks.kt b/core/src/main/kotlin/com/hexagonkt/core/Checks.kt index 5038539980..2970867f4e 100644 --- a/core/src/main/kotlin/com/hexagonkt/core/Checks.kt +++ b/core/src/main/kotlin/com/hexagonkt/core/Checks.kt @@ -63,12 +63,34 @@ fun T.requireLowerOrEquals( } /** - * [TODO](https://github.com/hexagonkt/hexagon/issues/271). + * Ensure a collection has a fixed number of elements. * - * @receiver . - * @param count . - * @return . + * @receiver Collection which size will be checked. + * @param count Required number of elements. + * @return Receiver reference (to allow call chaining). */ fun Collection.checkSize(count: IntRange): Collection = this.apply { check(size in count) { "$size items while expecting only $count element" } } + +/** + * Execute a list of code block collecting the exceptions they may throw, in case there is any + * error, it throws a [MultipleException] with all the thrown exceptions. + * + * @param message Error message. + * @param blocks Blocks of code executed and checked. + */ +fun check(message: String, vararg blocks: () -> Unit) { + val exceptions: List = blocks.mapNotNull { + try { + it() + null + } + catch(e: Exception) { + e + } + } + + if (exceptions.isNotEmpty()) + throw MultipleException(message, exceptions) +} diff --git a/core/src/main/kotlin/com/hexagonkt/core/CodedException.kt b/core/src/main/kotlin/com/hexagonkt/core/CodedException.kt index 1b9ab1b57c..f3cf5dbe42 100644 --- a/core/src/main/kotlin/com/hexagonkt/core/CodedException.kt +++ b/core/src/main/kotlin/com/hexagonkt/core/CodedException.kt @@ -3,11 +3,9 @@ package com.hexagonkt.core /** * Exception with a numeric code. * - * [TODO](https://github.com/hexagonkt/hexagon/issues/271). - * - * @property code . - * @property message . - * @property cause . + * @property code Exception code. + * @property message Error message. + * @property cause Parent exception. */ -class CodedException (val code: Int, message: String = "", cause: Throwable? = null) : - RuntimeException (message, cause) +class CodedException(val code: Int, message: String = "", cause: Throwable? = null) : + RuntimeException(message, cause) diff --git a/core/src/main/kotlin/com/hexagonkt/core/Exceptions.kt b/core/src/main/kotlin/com/hexagonkt/core/Exceptions.kt index d93f8426e3..144674aa95 100644 --- a/core/src/main/kotlin/com/hexagonkt/core/Exceptions.kt +++ b/core/src/main/kotlin/com/hexagonkt/core/Exceptions.kt @@ -44,21 +44,3 @@ fun Throwable.toText(prefix: String = ""): String = this.filterStackTrace(prefix).joinToString(eol, eol) { "\tat $it" } + if (this.cause == null) "" else "${eol}Caused by: " + (this.cause as Throwable).toText(prefix) - -/** - * [TODO](https://github.com/hexagonkt/hexagon/issues/271). - */ -fun check(message: String, vararg blocks: () -> Unit) { - val exceptions: List = blocks.mapNotNull { - try { - it() - null - } - catch(e: Exception) { - e - } - } - - if (exceptions.isNotEmpty()) - throw MultipleException(message, exceptions) -} diff --git a/core/src/main/kotlin/com/hexagonkt/core/Jvm.kt b/core/src/main/kotlin/com/hexagonkt/core/Jvm.kt index 9f3a3711e4..1ba4aeb608 100644 --- a/core/src/main/kotlin/com/hexagonkt/core/Jvm.kt +++ b/core/src/main/kotlin/com/hexagonkt/core/Jvm.kt @@ -1,5 +1,6 @@ package com.hexagonkt.core +import com.hexagonkt.core.text.SNAKE_CASE import com.hexagonkt.core.text.parseOrNull import java.io.Console import java.net.InetAddress @@ -13,7 +14,7 @@ import kotlin.reflect.KClass * Object with utilities to gather information about the running JVM. */ object Jvm { - private val systemSettingPattern: Regex by lazy { Regex("[a-zA-Z_]+[a-zA-Z0-9_]*") } + private val systemSettingPattern: Regex by lazy { SNAKE_CASE } /** Operating system name ('os.name' property). If `null` throws an exception. */ val os: String by lazy { os() } @@ -149,7 +150,7 @@ object Jvm { private fun systemSettingRaw(name: String): String? { val correctName = name.matches(systemSettingPattern) require(correctName) { "Setting name must match $systemSettingPattern" } - return System.getenv(name) ?: System.getProperty(name) + return System.getenv(name) ?: System.getenv(name.uppercase()) ?: System.getProperty(name) } /** Operating system name ('os.name' property). If `null` throws an exception. */ diff --git a/core/src/main/kotlin/com/hexagonkt/core/MultipleException.kt b/core/src/main/kotlin/com/hexagonkt/core/MultipleException.kt index 001556f963..9ab99a6f06 100644 --- a/core/src/main/kotlin/com/hexagonkt/core/MultipleException.kt +++ b/core/src/main/kotlin/com/hexagonkt/core/MultipleException.kt @@ -5,18 +5,20 @@ package com.hexagonkt.core * cause. * * A coded multiple exception should be created this way: + * ```kotlin * CodedException(400, "Many errors", MultipleException()) + * ``` * * To pass a list of causes + * ```kotlin * CodedException (500, "Error", *list) + * ``` * - * [TODO](https://github.com/hexagonkt/hexagon/issues/271). - * - * @property causes . - * @property message . + * @property causes List of causing exceptions. + * @property message Error message. */ -class MultipleException (val causes: List, message: String = "") : - RuntimeException (message, null) { +class MultipleException(val causes: List, message: String = "") : + RuntimeException(message, null) { constructor(vararg causes: Throwable) : this(causes.toList()) constructor(message: String, causes: List) : this(causes, message) diff --git a/core/src/main/kotlin/com/hexagonkt/core/media/MediaTypes.kt b/core/src/main/kotlin/com/hexagonkt/core/media/MediaTypes.kt index 7efb8de3b9..8b493182d7 100644 --- a/core/src/main/kotlin/com/hexagonkt/core/media/MediaTypes.kt +++ b/core/src/main/kotlin/com/hexagonkt/core/media/MediaTypes.kt @@ -9,6 +9,8 @@ import kotlin.io.path.extension val MEDIA_TYPE_FORMAT: Regex by lazy { """\*|([\w+.-]+)""".toRegex() } +val ANY_MEDIA: MediaType by lazy { MediaType(ANY, "*") } + val APPLICATION_AVRO: MediaType by lazy { MediaType(APPLICATION, "avro") } val APPLICATION_CBOR: MediaType by lazy { MediaType(APPLICATION, "cbor") } val APPLICATION_JSON: MediaType by lazy { MediaType(APPLICATION, "json") } diff --git a/core/src/main/kotlin/com/hexagonkt/core/text/Cases.kt b/core/src/main/kotlin/com/hexagonkt/core/text/Cases.kt index 673e27728e..7b7af40e2b 100644 --- a/core/src/main/kotlin/com/hexagonkt/core/text/Cases.kt +++ b/core/src/main/kotlin/com/hexagonkt/core/text/Cases.kt @@ -2,8 +2,8 @@ package com.hexagonkt.core.text val CAMEL_CASE: Regex by lazy { Regex("[a-z]+([A-Z][a-z0-9]+)+") } val PASCAL_CASE: Regex by lazy { Regex("([A-Z][a-z0-9]+)+") } -val SNAKE_CASE: Regex by lazy { Regex("[A-Za-z]+(_[A-Za-z0-9]+)+") } -val KEBAB_CASE: Regex by lazy { Regex("[A-Za-z]+(-[A-Za-z0-9]+)+") } +val SNAKE_CASE: Regex by lazy { Regex("[_A-Za-z]+[_A-Za-z0-9]*") } +val KEBAB_CASE: Regex by lazy { Regex("[\\-A-Za-z]+[\\-A-Za-z0-9]*") } fun String.camelToWords(): List = split("(?=\\p{Upper}\\p{Lower})".toRegex()).toWords() diff --git a/core/src/test/kotlin/com/hexagonkt/core/ChecksTest.kt b/core/src/test/kotlin/com/hexagonkt/core/ChecksTest.kt index 894f6f5bbd..5f75d54579 100644 --- a/core/src/test/kotlin/com/hexagonkt/core/ChecksTest.kt +++ b/core/src/test/kotlin/com/hexagonkt/core/ChecksTest.kt @@ -141,4 +141,26 @@ internal class ChecksTest { val e = assertFailsWith(block = block) assertEquals(message, e.message) } + + @Test fun `Check multiple errors`() { + val e = assertFailsWith { + check( + "Test multiple exceptions", + { require(false) { "Sample error" } }, + { println("Good block") }, + { error("Bad state") }, + ) + } + + assertEquals("Test multiple exceptions", e.message) + assertEquals(2, e.causes.size) + assertEquals("Sample error", e.causes[0].message) + assertEquals("Bad state", e.causes[1].message) + + check( + "No exception thrown", + { println("Good block") }, + { println("Shouldn't throw an exception") }, + ) + } } diff --git a/core/src/test/kotlin/com/hexagonkt/core/ExceptionsTest.kt b/core/src/test/kotlin/com/hexagonkt/core/ExceptionsTest.kt index bf8d445824..a42d717d59 100644 --- a/core/src/test/kotlin/com/hexagonkt/core/ExceptionsTest.kt +++ b/core/src/test/kotlin/com/hexagonkt/core/ExceptionsTest.kt @@ -58,26 +58,4 @@ internal class ExceptionsTest { assert(filteredTrace.contains("\tat ${ExceptionsTest::class.java.name}")) assertFalse(filteredTrace.contains("\tat org.junit.platform")) } - - @Test fun `Check multiple errors`() { - val e = assertFailsWith { - check( - "Test multiple exceptions", - { require(false) { "Sample error" } }, - { println("Good block") }, - { error("Bad state") }, - ) - } - - assertEquals("Test multiple exceptions", e.message) - assertEquals(2, e.causes.size) - assertEquals("Sample error", e.causes[0].message) - assertEquals("Bad state", e.causes[1].message) - - check( - "No exception thrown", - { println("Good block") }, - { println("Shouldn't throw an exception") }, - ) - } } diff --git a/core/src/test/kotlin/com/hexagonkt/core/JvmTest.kt b/core/src/test/kotlin/com/hexagonkt/core/JvmTest.kt index 42cdae7717..8c03346695 100644 --- a/core/src/test/kotlin/com/hexagonkt/core/JvmTest.kt +++ b/core/src/test/kotlin/com/hexagonkt/core/JvmTest.kt @@ -41,7 +41,7 @@ internal class JvmTest { assertEquals("z3", System.getProperty("s3")) val e = assertFailsWith { Jvm.loadSystemSettings(mapOf("1" to "v")) } - assertEquals("Property name must match [a-zA-Z_]+[a-zA-Z0-9_]* (1)", e.message) + assertEquals("Property name must match [_A-Za-z]+[_A-Za-z0-9]* (1)", e.message) } @Test fun `OS kind is fetched properly`() { @@ -213,6 +213,7 @@ internal class JvmTest { assert(Jvm.systemSetting("system_property") == "value") assert(Jvm.systemSetting("PATH").isNotEmpty()) + assert(Jvm.systemSetting("path").isNotEmpty()) assertNull(Jvm.systemSettingOrNull("_not_defined_")) System.setProperty("PATH", "path override") diff --git a/core/src/test/kotlin/com/hexagonkt/core/media/MediaTypeTest.kt b/core/src/test/kotlin/com/hexagonkt/core/media/MediaTypeTest.kt index 068ff4c886..17a5ed6510 100644 --- a/core/src/test/kotlin/com/hexagonkt/core/media/MediaTypeTest.kt +++ b/core/src/test/kotlin/com/hexagonkt/core/media/MediaTypeTest.kt @@ -19,6 +19,8 @@ internal class MediaTypeTest { } @Test fun `Media types without extensions are correct`() { + assertEquals("*/*", ANY_MEDIA.fullType) + assertEquals("multipart/alternative", MULTIPART_ALTERNATIVE.fullType) assertEquals("multipart/appledouble", MULTIPART_APPLEDOUBLE.fullType) assertEquals("multipart/digest", MULTIPART_DIGEST.fullType) diff --git a/gradle.properties b/gradle.properties index 8e5601a4fc..451842e10f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -33,34 +33,35 @@ iconsDirectory=content # VERSIONS kotlinVersion=1.9.22 dokkaVersion=1.9.10 -mockkVersion=1.13.8 +mockkVersion=1.13.9 junitVersion=5.10.1 -gatlingVersion=3.10.2 +gatlingVersion=3.10.3 jmhVersion=1.37 -mkdocsMaterialVersion=9.5.2 +mkdocsMaterialVersion=9.5.5 mermaidDokkaVersion=0.4.4 nativeToolsVersion=0.9.28 # http_server_netty -nettyVersion=4.1.104.Final +nettyVersion=4.1.106.Final nettyTcNativeVersion=2.0.62.Final # http_server_helidon -helidonVersion=4.0.2 +helidonVersion=4.0.3 # http_server_servlet servletVersion=6.0.0 jettyVersion=12.0.5 # rest_tools -swaggerRequestValidatorVersion=2.39.0 +swaggerRequestValidatorVersion=2.40.0 +vertxVersion=4.5.1 # logging -slf4jVersion=2.0.9 +slf4jVersion=2.0.11 logbackVersion=1.4.14 # serialization -jacksonVersion=2.16.0 +jacksonVersion=2.16.1 dslJsonVersion=2.0.2 # templates_freemarker diff --git a/handlers/src/test/kotlin/com/hexagonkt/handlers/ExceptionHandlerTest.kt b/handlers/src/test/kotlin/com/hexagonkt/handlers/ExceptionHandlerTest.kt index e91eebd254..7738a93ea4 100644 --- a/handlers/src/test/kotlin/com/hexagonkt/handlers/ExceptionHandlerTest.kt +++ b/handlers/src/test/kotlin/com/hexagonkt/handlers/ExceptionHandlerTest.kt @@ -49,7 +49,7 @@ internal class ExceptionHandlerTest { ChainHandler( ExceptionHandler(Exception::class, false) { c, _ -> c.with("ok") }, - ExceptionHandler(Exception::class, false) { c, _ -> error("Fail") }, + ExceptionHandler(Exception::class, false) { _, _ -> error("Fail") }, OnHandler { error("Error") } ) .process(EventContext("test", { true })) diff --git a/http/http/api/http.api b/http/http/api/http.api index 4400dc7aa7..75e73f26c0 100644 --- a/http/http/api/http.api +++ b/http/http/api/http.api @@ -1,4 +1,6 @@ public final class com/hexagonkt/http/HttpKt { + public static final fun basicAuth (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; + public static synthetic fun basicAuth$default (Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Ljava/lang/String; public static final fun checkHeaders (Lcom/hexagonkt/http/model/Headers;)V public static final fun formatQueryString (Lcom/hexagonkt/http/model/QueryParameters;)Ljava/lang/String; public static final fun getCHECKED_HEADERS ()Ljava/util/List; diff --git a/http/http/src/main/kotlin/com/hexagonkt/http/Http.kt b/http/http/src/main/kotlin/com/hexagonkt/http/Http.kt index 1cc564049f..1b7c98db46 100644 --- a/http/http/src/main/kotlin/com/hexagonkt/http/Http.kt +++ b/http/http/src/main/kotlin/com/hexagonkt/http/Http.kt @@ -4,6 +4,7 @@ import com.hexagonkt.core.GMT_ZONE import com.hexagonkt.core.assertEnabled import com.hexagonkt.core.Jvm import com.hexagonkt.core.media.MediaType +import com.hexagonkt.core.text.encodeToBase64 import com.hexagonkt.http.model.* import java.net.URLDecoder import java.net.URLEncoder @@ -20,6 +21,9 @@ val CHECKED_HEADERS: List by lazy { internal val HTTP_DATE_FORMATTER: DateTimeFormatter by lazy { RFC_1123_DATE_TIME.withZone(UTC) } +fun basicAuth(user: String, password: String = ""): String = + "$user:$password".encodeToBase64() + fun checkHeaders(headers: Headers) { if (!assertEnabled) return diff --git a/http/http_client/src/main/kotlin/com/hexagonkt/http/client/HttpClient.kt b/http/http_client/src/main/kotlin/com/hexagonkt/http/client/HttpClient.kt index 1883f8c64b..2272ca36e6 100644 --- a/http/http_client/src/main/kotlin/com/hexagonkt/http/client/HttpClient.kt +++ b/http/http_client/src/main/kotlin/com/hexagonkt/http/client/HttpClient.kt @@ -57,12 +57,21 @@ class HttpClient( error("HTTP client *MUST BE STARTED* before sending requests") else rootHandler - ?.process(request, attributes) + ?.process(request.setUp(), attributes) ?.let { if (it.exception != null) throw it.exception as Exception else it.response } - ?: adapter.send(request) + ?: adapter.send(request.setUp()) + + private fun HttpRequest.setUp(): HttpRequest { + return copy( + contentType = contentType ?: settings.contentType, + accept = accept.ifEmpty(settings::accept), + headers = settings.headers + headers, + authorization = authorization ?: settings.authorization, + ) + } fun sse(request: HttpRequest): Publisher = if (!started()) error("HTTP client *MUST BE STARTED* before sending requests") diff --git a/http/http_client/src/test/kotlin/com/hexagonkt/http/client/HttpClientTest.kt b/http/http_client/src/test/kotlin/com/hexagonkt/http/client/HttpClientTest.kt index d5a4bfb10a..a9ec50296e 100644 --- a/http/http_client/src/test/kotlin/com/hexagonkt/http/client/HttpClientTest.kt +++ b/http/http_client/src/test/kotlin/com/hexagonkt/http/client/HttpClientTest.kt @@ -111,7 +111,7 @@ internal class HttpClientTest { csvClient.request { get("/a").checkClient("/a", contentType = csv) - head("/a").checkClient("/a") + head("/a").checkClient("/a", contentType = csv) post("/a").checkClient("/a", contentType = csv) put("/a").checkClient("/a", contentType = csv) delete("/a").checkClient("/a", contentType = csv) @@ -120,7 +120,7 @@ internal class HttpClientTest { patch("/a").checkClient("/a", contentType = csv) get("/a", headers).checkClient("/a", headers = headers, contentType = csv) - head("/a", headers).checkClient("/a", headers = headers) + head("/a", headers).checkClient("/a", headers = headers, contentType = csv) get("/a", body = body).checkClient("/a", body, contentType = csv) options("/a", body).checkClient("/a", body, contentType = csv) post("/a", body).checkClient("/a", body, contentType = csv) diff --git a/http/http_client_jetty/src/main/kotlin/com/hexagonkt/http/client/jetty/JettyClientAdapter.kt b/http/http_client_jetty/src/main/kotlin/com/hexagonkt/http/client/jetty/JettyClientAdapter.kt index 6a776f44d3..f82ccb0018 100644 --- a/http/http_client_jetty/src/main/kotlin/com/hexagonkt/http/client/jetty/JettyClientAdapter.kt +++ b/http/http_client_jetty/src/main/kotlin/com/hexagonkt/http/client/jetty/JettyClientAdapter.kt @@ -48,21 +48,24 @@ open class JettyClientAdapter : HttpClientPort { protected lateinit var jettyClient: JettyHttpClient protected lateinit var httpClient: HttpClient + private lateinit var httpSettings: HttpClientSettings private var started: Boolean = false private val publisherExecutor = Executors.newSingleThreadExecutor() override fun startUp(client: HttpClient) { val clientConnector = ClientConnector() - clientConnector.sslContextFactory = sslContext(client.settings) + val settings = client.settings + clientConnector.sslContextFactory = sslContext(settings) val http2 = HTTP2(JettyHttp2Client(clientConnector)) val transport = HttpClientTransportDynamic(clientConnector, HTTP11, http2) jettyClient = JettyHttpClient(transport) httpClient = client + httpSettings = settings jettyClient.userAgentField = null // Disable default user agent header - jettyClient.isFollowRedirects = client.settings.followRedirects + jettyClient.isFollowRedirects = settings.followRedirects jettyClient.start() started = true } @@ -78,7 +81,7 @@ open class JettyClientAdapter : HttpClientPort { override fun send(request: HttpRequestPort): HttpResponsePort { val response = try { - createJettyRequest(httpClient, jettyClient, request).send() + createJettyRequest(jettyClient, request).send() } catch (e: ExecutionException) { val cause = e.cause @@ -109,7 +112,7 @@ open class JettyClientAdapter : HttpClientPort { headers = request.headers + Header("connection", "keep-alive") ) - createJettyRequest(httpClient, jettyClient, sseRequest) + createJettyRequest(jettyClient, sseRequest) .onResponseBegin { if (it.status !in 200 until 300) error("Invalid response: ${it.status}") @@ -140,9 +143,8 @@ open class JettyClientAdapter : HttpClientPort { ): HttpResponse { val bodyString = if (response is ContentResponse) response.contentAsString else "" - val settings = adapterHttpClient.settings - if (settings.useCookies) + if (httpSettings.useCookies) adapterHttpClient.cookies = adapterJettyClient.httpCookieStore.all().map { Cookie( it.name, @@ -177,18 +179,14 @@ open class JettyClientAdapter : HttpClientPort { ) private fun createJettyRequest( - adapterHttpClient: HttpClient, - adapterJettyClient: JettyHttpClient, - request: HttpRequestPort + adapterJettyClient: JettyHttpClient, request: HttpRequestPort ): Request { - val settings = adapterHttpClient.settings - val contentType = request.contentType ?: settings.contentType - val accept = request.accept.ifEmpty(settings::accept) - val authorization = request.authorization ?: settings.authorization - val baseUrl = settings.baseUrl + val contentType = request.contentType + val authorization = request.authorization + val baseUrl = httpSettings.baseUrl - if (settings.useCookies) { + if (httpSettings.useCookies) { val uri = (baseUrl ?: request.url()).toURI() addCookies(uri, adapterJettyClient.httpCookieStore, request.cookies) } @@ -202,11 +200,10 @@ open class JettyClientAdapter : HttpClientPort { it.put("content-type", contentType.text) if (authorization != null) it.put("authorization", authorization.text) - (settings.headers + request.headers).values - .forEach { (k, v) -> it.put(k, v.map(Any::toString)) } + request.headers.values.forEach { (k, v) -> it.put(k, v.map(Any::toString)) } } .body(createBody(request)) - .accept(*accept.map { it.text }.toTypedArray()) + .accept(*request.accept.map { it.text }.toTypedArray()) request.queryParameters .forEach { (k, v) -> v.strings().forEach { jettyRequest.param(k, it) } } diff --git a/http/http_handlers/api/http_handlers.api b/http/http_handlers/api/http_handlers.api index 1b36b35746..6eafd8212e 100644 --- a/http/http_handlers/api/http_handlers.api +++ b/http/http_handlers/api/http_handlers.api @@ -193,6 +193,9 @@ public final class com/hexagonkt/http/handlers/HandlersKt { public static synthetic fun process$default (Lkotlin/jvm/functions/Function1;Lcom/hexagonkt/http/model/HttpRequest;Ljava/util/Map;ILjava/lang/Object;)Lcom/hexagonkt/http/handlers/HttpContext; } +public abstract interface class com/hexagonkt/http/handlers/HttpCallback : kotlin/jvm/functions/Function1 { +} + public final class com/hexagonkt/http/handlers/HttpContext : com/hexagonkt/handlers/Context { public fun (Lcom/hexagonkt/handlers/Context;)V public fun (Lcom/hexagonkt/http/model/HttpCall;Lkotlin/jvm/functions/Function1;Ljava/util/List;ILjava/lang/Exception;Ljava/util/Map;Z)V @@ -259,8 +262,8 @@ public final class com/hexagonkt/http/handlers/HttpContext : com/hexagonkt/handl public static synthetic fun notFound$default (Lcom/hexagonkt/http/handlers/HttpContext;Ljava/lang/Object;Lcom/hexagonkt/http/model/Headers;Lcom/hexagonkt/http/model/ContentType;Ljava/util/List;Ljava/util/Map;ILjava/lang/Object;)Lcom/hexagonkt/http/handlers/HttpContext; public final fun ok (Ljava/lang/Object;Lcom/hexagonkt/http/model/Headers;Lcom/hexagonkt/http/model/ContentType;Ljava/util/List;Ljava/util/Map;)Lcom/hexagonkt/http/handlers/HttpContext; public static synthetic fun ok$default (Lcom/hexagonkt/http/handlers/HttpContext;Ljava/lang/Object;Lcom/hexagonkt/http/model/Headers;Lcom/hexagonkt/http/model/ContentType;Ljava/util/List;Ljava/util/Map;ILjava/lang/Object;)Lcom/hexagonkt/http/handlers/HttpContext; - public final fun receive (Ljava/lang/Object;Lcom/hexagonkt/http/model/Headers;Lcom/hexagonkt/http/model/ContentType;Ljava/util/List;Ljava/util/Map;)Lcom/hexagonkt/http/handlers/HttpContext; - public static synthetic fun receive$default (Lcom/hexagonkt/http/handlers/HttpContext;Ljava/lang/Object;Lcom/hexagonkt/http/model/Headers;Lcom/hexagonkt/http/model/ContentType;Ljava/util/List;Ljava/util/Map;ILjava/lang/Object;)Lcom/hexagonkt/http/handlers/HttpContext; + public final fun receive (Ljava/lang/Object;Lcom/hexagonkt/http/model/Headers;Lcom/hexagonkt/http/model/ContentType;Ljava/util/List;Ljava/util/List;Ljava/util/Map;)Lcom/hexagonkt/http/handlers/HttpContext; + public static synthetic fun receive$default (Lcom/hexagonkt/http/handlers/HttpContext;Ljava/lang/Object;Lcom/hexagonkt/http/model/Headers;Lcom/hexagonkt/http/model/ContentType;Ljava/util/List;Ljava/util/List;Ljava/util/Map;ILjava/lang/Object;)Lcom/hexagonkt/http/handlers/HttpContext; public final fun redirect (Lcom/hexagonkt/http/model/HttpStatus;Ljava/lang/String;Lcom/hexagonkt/http/model/Headers;Ljava/util/List;Ljava/util/Map;)Lcom/hexagonkt/http/handlers/HttpContext; public static synthetic fun redirect$default (Lcom/hexagonkt/http/handlers/HttpContext;Lcom/hexagonkt/http/model/HttpStatus;Ljava/lang/String;Lcom/hexagonkt/http/model/Headers;Ljava/util/List;Ljava/util/Map;ILjava/lang/Object;)Lcom/hexagonkt/http/handlers/HttpContext; public final fun send (Lcom/hexagonkt/http/model/HttpRequestPort;Ljava/util/Map;)Lcom/hexagonkt/http/handlers/HttpContext; @@ -279,6 +282,29 @@ public final class com/hexagonkt/http/handlers/HttpContext : com/hexagonkt/handl public synthetic fun with (Ljava/lang/Object;Lkotlin/jvm/functions/Function1;Ljava/util/List;ILjava/lang/Exception;Ljava/util/Map;Z)Lcom/hexagonkt/handlers/Context; } +public abstract interface class com/hexagonkt/http/handlers/HttpController : com/hexagonkt/http/handlers/HttpHandler { + public abstract fun addPrefix (Ljava/lang/String;)Lcom/hexagonkt/http/handlers/HttpHandler; + public abstract fun getCallback ()Lkotlin/jvm/functions/Function1; + public abstract fun getHandler ()Lcom/hexagonkt/http/handlers/HttpHandler; + public abstract fun getHandlerPredicate ()Lcom/hexagonkt/http/handlers/HttpPredicate; + public abstract fun getPredicate ()Lkotlin/jvm/functions/Function1; + public abstract fun process (Lcom/hexagonkt/handlers/Context;)Lcom/hexagonkt/handlers/Context; +} + +public final class com/hexagonkt/http/handlers/HttpController$DefaultImpls { + public static fun addPrefix (Lcom/hexagonkt/http/handlers/HttpController;Ljava/lang/String;)Lcom/hexagonkt/http/handlers/HttpHandler; + public static fun byMethod (Lcom/hexagonkt/http/handlers/HttpController;)Ljava/util/Map; + public static fun filter (Lcom/hexagonkt/http/handlers/HttpController;Lcom/hexagonkt/http/model/HttpMethod;)Lcom/hexagonkt/http/handlers/HttpHandler; + public static fun getCallback (Lcom/hexagonkt/http/handlers/HttpController;)Lkotlin/jvm/functions/Function1; + public static fun getHandlerPredicate (Lcom/hexagonkt/http/handlers/HttpController;)Lcom/hexagonkt/http/handlers/HttpPredicate; + public static fun getPredicate (Lcom/hexagonkt/http/handlers/HttpController;)Lkotlin/jvm/functions/Function1; + public static fun process (Lcom/hexagonkt/http/handlers/HttpController;Lcom/hexagonkt/handlers/Context;)Lcom/hexagonkt/handlers/Context; + public static fun process (Lcom/hexagonkt/http/handlers/HttpController;Lcom/hexagonkt/http/model/HttpRequestPort;)Lcom/hexagonkt/http/handlers/HttpContext; +} + +public abstract interface class com/hexagonkt/http/handlers/HttpExceptionCallback : kotlin/jvm/functions/Function2 { +} + public abstract interface class com/hexagonkt/http/handlers/HttpHandler : com/hexagonkt/handlers/Handler { public abstract fun addPrefix (Ljava/lang/String;)Lcom/hexagonkt/http/handlers/HttpHandler; public abstract fun byMethod ()Ljava/util/Map; diff --git a/http/http_handlers/src/main/kotlin/com/hexagonkt/http/handlers/AfterHandler.kt b/http/http_handlers/src/main/kotlin/com/hexagonkt/http/handlers/AfterHandler.kt index e59de2bd80..35a6ea0e17 100644 --- a/http/http_handlers/src/main/kotlin/com/hexagonkt/http/handlers/AfterHandler.kt +++ b/http/http_handlers/src/main/kotlin/com/hexagonkt/http/handlers/AfterHandler.kt @@ -9,7 +9,7 @@ import kotlin.reflect.KClass data class AfterHandler( override val handlerPredicate: HttpPredicate = HttpPredicate(), - val block: HttpCallback + val block: HttpCallbackType ) : HttpHandler, Handler by AfterHandler(handlerPredicate, toCallback(block)) { constructor( @@ -17,14 +17,14 @@ data class AfterHandler( pattern: String = "", exception: KClass? = null, status: HttpStatus? = null, - block: HttpCallback, + block: HttpCallbackType, ) : this(HttpPredicate(methods, pattern, exception, status), block) - constructor(method: HttpMethod, pattern: String = "", block: HttpCallback) : + constructor(method: HttpMethod, pattern: String = "", block: HttpCallbackType) : this(setOf(method), pattern, block = block) - constructor(pattern: String, block: HttpCallback) : + constructor(pattern: String, block: HttpCallbackType) : this(emptySet(), pattern, block = block) override fun addPrefix(prefix: String): HttpHandler = diff --git a/http/http_handlers/src/main/kotlin/com/hexagonkt/http/handlers/BeforeHandler.kt b/http/http_handlers/src/main/kotlin/com/hexagonkt/http/handlers/BeforeHandler.kt index 4261a919a8..5f61157291 100644 --- a/http/http_handlers/src/main/kotlin/com/hexagonkt/http/handlers/BeforeHandler.kt +++ b/http/http_handlers/src/main/kotlin/com/hexagonkt/http/handlers/BeforeHandler.kt @@ -9,7 +9,7 @@ import kotlin.reflect.KClass data class BeforeHandler( override val handlerPredicate: HttpPredicate = HttpPredicate(), - val block: HttpCallback, + val block: HttpCallbackType, ) : HttpHandler, Handler by BeforeHandler(handlerPredicate, toCallback(block)) { constructor( @@ -17,14 +17,14 @@ data class BeforeHandler( pattern: String = "", exception: KClass? = null, status: HttpStatus? = null, - block: HttpCallback, + block: HttpCallbackType, ) : this(HttpPredicate(methods, pattern, exception, status), block) - constructor(method: HttpMethod, pattern: String = "", block: HttpCallback) : + constructor(method: HttpMethod, pattern: String = "", block: HttpCallbackType) : this(setOf(method), pattern, block = block) - constructor(pattern: String, block: HttpCallback) : + constructor(pattern: String, block: HttpCallbackType) : this(emptySet(), pattern, block = block) override fun addPrefix(prefix: String): HttpHandler = diff --git a/http/http_handlers/src/main/kotlin/com/hexagonkt/http/handlers/ExceptionHandler.kt b/http/http_handlers/src/main/kotlin/com/hexagonkt/http/handlers/ExceptionHandler.kt index 2c123747e4..4acd949a8a 100644 --- a/http/http_handlers/src/main/kotlin/com/hexagonkt/http/handlers/ExceptionHandler.kt +++ b/http/http_handlers/src/main/kotlin/com/hexagonkt/http/handlers/ExceptionHandler.kt @@ -8,7 +8,7 @@ import kotlin.reflect.KClass data class ExceptionHandler( val exception: KClass, val clear: Boolean = true, - val block: HttpExceptionCallback + val block: HttpExceptionCallbackType ) : HttpHandler, Handler by ExceptionHandler(exception, clear, toCallback(block)) { override val handlerPredicate: HttpPredicate = HttpPredicate() diff --git a/http/http_handlers/src/main/kotlin/com/hexagonkt/http/handlers/FilterHandler.kt b/http/http_handlers/src/main/kotlin/com/hexagonkt/http/handlers/FilterHandler.kt index c26f2bebfd..7c5feeff5d 100644 --- a/http/http_handlers/src/main/kotlin/com/hexagonkt/http/handlers/FilterHandler.kt +++ b/http/http_handlers/src/main/kotlin/com/hexagonkt/http/handlers/FilterHandler.kt @@ -9,7 +9,7 @@ import kotlin.reflect.KClass data class FilterHandler( override val handlerPredicate: HttpPredicate = HttpPredicate(), - val block: HttpCallback + val block: HttpCallbackType ) : HttpHandler, Handler by FilterHandler(handlerPredicate, toCallback(block)) { constructor( @@ -17,14 +17,14 @@ data class FilterHandler( pattern: String = "", exception: KClass? = null, status: HttpStatus? = null, - block: HttpCallback, + block: HttpCallbackType, ) : this(HttpPredicate(methods, pattern, exception, status), block) - constructor(method: HttpMethod, pattern: String = "", block: HttpCallback) : + constructor(method: HttpMethod, pattern: String = "", block: HttpCallbackType) : this(setOf(method), pattern, block = block) - constructor(pattern: String, block: HttpCallback) : + constructor(pattern: String, block: HttpCallbackType) : this(emptySet(), pattern, block = block) override fun addPrefix(prefix: String): HttpHandler = diff --git a/http/http_handlers/src/main/kotlin/com/hexagonkt/http/handlers/HandlerBuilder.kt b/http/http_handlers/src/main/kotlin/com/hexagonkt/http/handlers/HandlerBuilder.kt index ee5b5f67b6..cd2d9a14ed 100644 --- a/http/http_handlers/src/main/kotlin/com/hexagonkt/http/handlers/HandlerBuilder.kt +++ b/http/http_handlers/src/main/kotlin/com/hexagonkt/http/handlers/HandlerBuilder.kt @@ -32,7 +32,7 @@ class HandlerBuilder(var handlers: List = emptyList()) { fun on( predicate: HttpPredicate = HttpPredicate(), - callback: HttpCallback + callback: HttpCallbackType ) { use(OnHandler(predicate, callback)) } @@ -42,22 +42,22 @@ class HandlerBuilder(var handlers: List = emptyList()) { pattern: String = "", exception: KClass? = null, status: HttpStatus? = null, - callback: HttpCallback, + callback: HttpCallbackType, ) { use(OnHandler(methods, pattern, exception, status, callback)) } - fun on(method: HttpMethod, pattern: String = "", callback: HttpCallback) { + fun on(method: HttpMethod, pattern: String = "", callback: HttpCallbackType) { use(OnHandler(method, pattern, callback)) } - fun on(pattern: String, callback: HttpCallback) { + fun on(pattern: String, callback: HttpCallbackType) { use(OnHandler(pattern, callback)) } fun filter( predicate: HttpPredicate = HttpPredicate(), - callback: HttpCallback + callback: HttpCallbackType ) { use(FilterHandler(predicate, callback)) } @@ -67,24 +67,24 @@ class HandlerBuilder(var handlers: List = emptyList()) { pattern: String = "", exception: KClass? = null, status: HttpStatus? = null, - callback: HttpCallback, + callback: HttpCallbackType, ) { use( FilterHandler(methods, pattern, exception, status, callback) ) } - fun filter(method: HttpMethod, pattern: String = "", callback: HttpCallback) { + fun filter(method: HttpMethod, pattern: String = "", callback: HttpCallbackType) { use(FilterHandler(method, pattern, callback)) } - fun filter(pattern: String, callback: HttpCallback) { + fun filter(pattern: String, callback: HttpCallbackType) { use(FilterHandler(pattern, callback)) } fun after( predicate: HttpPredicate = HttpPredicate(), - callback: HttpCallback + callback: HttpCallbackType ) { use(AfterHandler(predicate, callback)) } @@ -94,22 +94,22 @@ class HandlerBuilder(var handlers: List = emptyList()) { pattern: String = "", exception: KClass? = null, status: HttpStatus? = null, - callback: HttpCallback, + callback: HttpCallbackType, ) { use(AfterHandler(methods, pattern, exception, status, callback)) } - fun after(method: HttpMethod, pattern: String = "", callback: HttpCallback) { + fun after(method: HttpMethod, pattern: String = "", callback: HttpCallbackType) { use(AfterHandler(method, pattern, callback)) } - fun after(pattern: String, callback: HttpCallback) { + fun after(pattern: String, callback: HttpCallbackType) { use(AfterHandler(pattern, callback)) } fun before( predicate: HttpPredicate = HttpPredicate(), - callback: HttpCallback + callback: HttpCallbackType ) { use(BeforeHandler(predicate, callback)) } @@ -119,64 +119,64 @@ class HandlerBuilder(var handlers: List = emptyList()) { pattern: String = "", exception: KClass? = null, status: HttpStatus? = null, - callback: HttpCallback, + callback: HttpCallbackType, ) { use(BeforeHandler(methods, pattern, exception, status, callback)) } - fun before(method: HttpMethod, pattern: String = "", callback: HttpCallback) { + fun before(method: HttpMethod, pattern: String = "", callback: HttpCallbackType) { use(BeforeHandler(method, pattern, callback)) } - fun before(pattern: String, callback: HttpCallback) { + fun before(pattern: String, callback: HttpCallbackType) { use(BeforeHandler(pattern, callback)) } fun exception( - exception: KClass, clear: Boolean = true, callback: HttpExceptionCallback + exception: KClass, clear: Boolean = true, callback: HttpExceptionCallbackType ) { use(ExceptionHandler(exception, clear, callback)) } inline fun exception( - clear: Boolean = true, noinline callback: HttpExceptionCallback, + clear: Boolean = true, noinline callback: HttpExceptionCallbackType, ) { use(ExceptionHandler(T::class, clear, callback)) } - fun get(pattern: String = "", callback: HttpCallback) { + fun get(pattern: String = "", callback: HttpCallbackType) { use(Get(pattern, callback)) } - fun ws(pattern: String = "", callback: HttpCallback) { + fun ws(pattern: String = "", callback: HttpCallbackType) { use(Ws(pattern, callback)) } - fun head(pattern: String = "", callback: HttpCallback) { + fun head(pattern: String = "", callback: HttpCallbackType) { use(Head(pattern, callback)) } - fun post(pattern: String = "", callback: HttpCallback) { + fun post(pattern: String = "", callback: HttpCallbackType) { use(Post(pattern, callback)) } - fun put(pattern: String = "", callback: HttpCallback) { + fun put(pattern: String = "", callback: HttpCallbackType) { use(Put(pattern, callback)) } - fun delete(pattern: String = "", callback: HttpCallback) { + fun delete(pattern: String = "", callback: HttpCallbackType) { use(Delete(pattern, callback)) } - fun trace(pattern: String = "", callback: HttpCallback) { + fun trace(pattern: String = "", callback: HttpCallbackType) { use(Trace(pattern, callback)) } - fun options(pattern: String = "", callback: HttpCallback) { + fun options(pattern: String = "", callback: HttpCallbackType) { use(Options(pattern, callback)) } - fun patch(pattern: String = "", callback: HttpCallback) { + fun patch(pattern: String = "", callback: HttpCallbackType) { use(Patch(pattern, callback)) } } diff --git a/http/http_handlers/src/main/kotlin/com/hexagonkt/http/handlers/Handlers.kt b/http/http_handlers/src/main/kotlin/com/hexagonkt/http/handlers/Handlers.kt index 5bdd33f724..38c326ee97 100644 --- a/http/http_handlers/src/main/kotlin/com/hexagonkt/http/handlers/Handlers.kt +++ b/http/http_handlers/src/main/kotlin/com/hexagonkt/http/handlers/Handlers.kt @@ -7,14 +7,11 @@ import com.hexagonkt.handlers.Context import com.hexagonkt.http.model.* import com.hexagonkt.http.model.HttpMethod.* import com.hexagonkt.http.model.HttpProtocol.HTTP -import com.hexagonkt.http.model.HttpCall -import com.hexagonkt.http.model.HttpRequest -import java.lang.IllegalStateException import java.math.BigInteger import java.security.cert.X509Certificate -typealias HttpCallback = HttpContext.() -> HttpContext -typealias HttpExceptionCallback = HttpContext.(T) -> HttpContext +typealias HttpCallbackType = HttpContext.() -> HttpContext +typealias HttpExceptionCallbackType = HttpContext.(T) -> HttpContext private val logger: Logger by lazy { Logger(HttpHandler::class.java.packageName) } private val BODY_TYPES_NAMES: String by lazy { @@ -22,21 +19,21 @@ private val BODY_TYPES_NAMES: String by lazy { bodyTypes.joinToString(", ") { it.simpleName.toString() } } -internal fun toCallback(block: HttpCallback): (Context) -> Context = +internal fun toCallback(block: HttpCallbackType): (Context) -> Context = { context -> HttpContext(context).block() } internal fun toCallback( - block: HttpExceptionCallback + block: HttpExceptionCallbackType ): (Context, E) -> Context = { context, e -> HttpContext(context).block(e) } -fun HttpCallback.process( +fun HttpCallbackType.process( request: HttpRequest, attributes: Map<*, *> = emptyMap() ): HttpContext = this(HttpContext(request = request, attributes = attributes)) -fun HttpCallback.process( +fun HttpCallbackType.process( method: HttpMethod = GET, protocol: HttpProtocol = HTTP, host: String = "localhost", @@ -90,31 +87,31 @@ fun path(contextPath: String = "", handlers: List): PathHandler = PathHandler(contextPath, it) } -fun Get(pattern: String = "", callback: HttpCallback): OnHandler = +fun Get(pattern: String = "", callback: HttpCallbackType): OnHandler = OnHandler(GET, pattern, callback) -fun Ws(pattern: String = "", callback: HttpCallback): OnHandler = +fun Ws(pattern: String = "", callback: HttpCallbackType): OnHandler = Get(pattern, callback) -fun Head(pattern: String = "", callback: HttpCallback): OnHandler = +fun Head(pattern: String = "", callback: HttpCallbackType): OnHandler = OnHandler(HEAD, pattern, callback) -fun Post(pattern: String = "", callback: HttpCallback): OnHandler = +fun Post(pattern: String = "", callback: HttpCallbackType): OnHandler = OnHandler(POST, pattern, callback) -fun Put(pattern: String = "", callback: HttpCallback): OnHandler = +fun Put(pattern: String = "", callback: HttpCallbackType): OnHandler = OnHandler(PUT, pattern, callback) -fun Delete(pattern: String = "", callback: HttpCallback): OnHandler = +fun Delete(pattern: String = "", callback: HttpCallbackType): OnHandler = OnHandler(DELETE, pattern, callback) -fun Trace(pattern: String = "", callback: HttpCallback): OnHandler = +fun Trace(pattern: String = "", callback: HttpCallbackType): OnHandler = OnHandler(TRACE, pattern, callback) -fun Options(pattern: String = "", callback: HttpCallback): OnHandler = +fun Options(pattern: String = "", callback: HttpCallbackType): OnHandler = OnHandler(OPTIONS, pattern, callback) -fun Patch(pattern: String = "", callback: HttpCallback): OnHandler = +fun Patch(pattern: String = "", callback: HttpCallbackType): OnHandler = OnHandler(PATCH, pattern, callback) fun bodyToBytes(body: Any): ByteArray = diff --git a/http/http_handlers/src/main/kotlin/com/hexagonkt/http/handlers/HttpCallback.kt b/http/http_handlers/src/main/kotlin/com/hexagonkt/http/handlers/HttpCallback.kt new file mode 100644 index 0000000000..86f1203a78 --- /dev/null +++ b/http/http_handlers/src/main/kotlin/com/hexagonkt/http/handlers/HttpCallback.kt @@ -0,0 +1,3 @@ +package com.hexagonkt.http.handlers + +interface HttpCallback : (HttpContext) -> HttpContext diff --git a/http/http_handlers/src/main/kotlin/com/hexagonkt/http/handlers/HttpContext.kt b/http/http_handlers/src/main/kotlin/com/hexagonkt/http/handlers/HttpContext.kt index 68b3faec93..76dabeb0e2 100644 --- a/http/http_handlers/src/main/kotlin/com/hexagonkt/http/handlers/HttpContext.kt +++ b/http/http_handlers/src/main/kotlin/com/hexagonkt/http/handlers/HttpContext.kt @@ -275,10 +275,11 @@ data class HttpContext( ) fun receive( - body: Any = response.body, - headers: Headers = response.headers, - contentType: ContentType? = response.contentType, - cookies: List = response.cookies, + body: Any = request.body, + headers: Headers = request.headers, + contentType: ContentType? = request.contentType, + accept: List = request.accept, + cookies: List = request.cookies, attributes: Map<*, *> = this.attributes, ): HttpContext = send( @@ -286,6 +287,7 @@ data class HttpContext( body = body, headers = headers, contentType = contentType, + accept = accept, cookies = cookies, ), attributes diff --git a/http/http_handlers/src/main/kotlin/com/hexagonkt/http/handlers/HttpController.kt b/http/http_handlers/src/main/kotlin/com/hexagonkt/http/handlers/HttpController.kt new file mode 100644 index 0000000000..739e813053 --- /dev/null +++ b/http/http_handlers/src/main/kotlin/com/hexagonkt/http/handlers/HttpController.kt @@ -0,0 +1,26 @@ +package com.hexagonkt.http.handlers + +import com.hexagonkt.handlers.Context +import com.hexagonkt.http.model.HttpCall + +/** + * Utility to encapsulate a handler in a class. TODO + */ +interface HttpController : HttpHandler { + val handler: HttpHandler + + override val handlerPredicate: HttpPredicate + get() = handler.handlerPredicate + + override fun addPrefix(prefix: String): HttpHandler = + handler.addPrefix(prefix) + + override fun process(context: Context): Context = + handler.process(context) + + override val predicate: (Context) -> Boolean + get() = handler.predicate + + override val callback: (Context) -> Context + get() = handler.callback +} diff --git a/http/http_handlers/src/main/kotlin/com/hexagonkt/http/handlers/HttpExceptionCallback.kt b/http/http_handlers/src/main/kotlin/com/hexagonkt/http/handlers/HttpExceptionCallback.kt new file mode 100644 index 0000000000..cd8deba07d --- /dev/null +++ b/http/http_handlers/src/main/kotlin/com/hexagonkt/http/handlers/HttpExceptionCallback.kt @@ -0,0 +1,4 @@ +package com.hexagonkt.http.handlers + +// TODO Use this type to implement the RFC 7807 exception handler +interface HttpExceptionCallback : (HttpContext, T) -> HttpContext diff --git a/http/http_handlers/src/main/kotlin/com/hexagonkt/http/handlers/HttpHandler.kt b/http/http_handlers/src/main/kotlin/com/hexagonkt/http/handlers/HttpHandler.kt index b9fafda377..8f7dffd0a2 100644 --- a/http/http_handlers/src/main/kotlin/com/hexagonkt/http/handlers/HttpHandler.kt +++ b/http/http_handlers/src/main/kotlin/com/hexagonkt/http/handlers/HttpHandler.kt @@ -37,7 +37,7 @@ sealed interface HttpHandler : Handler { is BeforeHandler -> copy(handlerPredicate = handlerPredicate.clearMethods()) - is ExceptionHandler<*> -> + else -> this } diff --git a/http/http_handlers/src/main/kotlin/com/hexagonkt/http/handlers/OnHandler.kt b/http/http_handlers/src/main/kotlin/com/hexagonkt/http/handlers/OnHandler.kt index 0ef081b185..1a91d72476 100644 --- a/http/http_handlers/src/main/kotlin/com/hexagonkt/http/handlers/OnHandler.kt +++ b/http/http_handlers/src/main/kotlin/com/hexagonkt/http/handlers/OnHandler.kt @@ -9,7 +9,7 @@ import kotlin.reflect.KClass data class OnHandler( override val handlerPredicate: HttpPredicate = HttpPredicate(), - val block: HttpCallback, + val block: HttpCallbackType, ) : HttpHandler, Handler by OnHandler(handlerPredicate, toCallback(block)) { constructor( @@ -17,14 +17,14 @@ data class OnHandler( pattern: String = "", exception: KClass? = null, status: HttpStatus? = null, - block: HttpCallback, + block: HttpCallbackType, ) : this(HttpPredicate(methods, pattern, exception, status), block) - constructor(method: HttpMethod, pattern: String = "", block: HttpCallback) : + constructor(method: HttpMethod, pattern: String = "", block: HttpCallbackType) : this(setOf(method), pattern, block = block) - constructor(pattern: String, block: HttpCallback) : + constructor(pattern: String, block: HttpCallbackType) : this(emptySet(), pattern, block = block) override fun addPrefix(prefix: String): HttpHandler = diff --git a/http/http_handlers/src/test/kotlin/com/hexagonkt/http/handlers/Examples.kt b/http/http_handlers/src/test/kotlin/com/hexagonkt/http/handlers/Examples.kt index 0223ed4617..e8d4516f6e 100644 --- a/http/http_handlers/src/test/kotlin/com/hexagonkt/http/handlers/Examples.kt +++ b/http/http_handlers/src/test/kotlin/com/hexagonkt/http/handlers/Examples.kt @@ -1,6 +1,6 @@ package com.hexagonkt.http.handlers -import com.hexagonkt.core.text.encodeToBase64 +import com.hexagonkt.http.basicAuth import com.hexagonkt.http.model.* import com.hexagonkt.http.parseQueryString import kotlin.test.assertEquals @@ -38,7 +38,7 @@ internal fun HttpRequest.auth( val authorization = if (user != null || password != null) - "$user:$password".encodeToBase64() + basicAuth(user ?: "", password ?: "") else null diff --git a/http/http_handlers/src/test/kotlin/com/hexagonkt/http/handlers/HttpControllerTest.kt b/http/http_handlers/src/test/kotlin/com/hexagonkt/http/handlers/HttpControllerTest.kt new file mode 100644 index 0000000000..e343d3d445 --- /dev/null +++ b/http/http_handlers/src/test/kotlin/com/hexagonkt/http/handlers/HttpControllerTest.kt @@ -0,0 +1,22 @@ +package com.hexagonkt.http.handlers + +import com.hexagonkt.http.model.HttpRequest +import org.junit.jupiter.api.Test +import kotlin.test.assertEquals + +internal class HttpControllerTest { + + class TestController : HttpController { + override val handler: HttpHandler = Get { ok("Good") } + } + + @Test fun `A controller can be used as a handler`() { + + val path = path { + use(TestController()) + } + + val response = path.process(HttpRequest()) + assertEquals("Good", response.response.bodyString()) + } +} diff --git a/http/http_test/src/main/kotlin/com/hexagonkt/http/test/BaseTest.kt b/http/http_test/src/main/kotlin/com/hexagonkt/http/test/BaseTest.kt index 884e7af934..2347d38343 100644 --- a/http/http_test/src/main/kotlin/com/hexagonkt/http/test/BaseTest.kt +++ b/http/http_test/src/main/kotlin/com/hexagonkt/http/test/BaseTest.kt @@ -1,6 +1,5 @@ package com.hexagonkt.http.test -import com.hexagonkt.core.text.encodeToBase64 import com.hexagonkt.core.logging.LoggingLevel.DEBUG import com.hexagonkt.core.logging.LoggingLevel.OFF import com.hexagonkt.core.logging.LoggingManager @@ -64,10 +63,6 @@ abstract class BaseTest { assertResponseContains(response, OK_200, *content) } - // TODO Move to `http` module to share basic and digest auth among client and server - protected fun basicAuth(user: String, password: String? = null): String = - "Basic " + "$user:$password".encodeToBase64() - protected fun assertResponseEquals( response: HttpResponsePort?, status: HttpStatus = OK_200, content: String ) { diff --git a/http/http_test/src/main/kotlin/com/hexagonkt/http/test/examples/ClientTest.kt b/http/http_test/src/main/kotlin/com/hexagonkt/http/test/examples/ClientTest.kt index d4b0714324..4ca3df6ba5 100644 --- a/http/http_test/src/main/kotlin/com/hexagonkt/http/test/examples/ClientTest.kt +++ b/http/http_test/src/main/kotlin/com/hexagonkt/http/test/examples/ClientTest.kt @@ -17,7 +17,7 @@ import com.hexagonkt.http.model.HttpProtocol.HTTPS import com.hexagonkt.http.model.INTERNAL_SERVER_ERROR_500 import com.hexagonkt.http.model.OK_200 import com.hexagonkt.http.server.* -import com.hexagonkt.http.handlers.HttpCallback +import com.hexagonkt.http.handlers.HttpCallbackType import com.hexagonkt.http.handlers.HttpHandler import com.hexagonkt.http.handlers.path import com.hexagonkt.http.test.BaseTest @@ -42,7 +42,7 @@ abstract class ClientTest( final override val serverSettings: HttpServerSettings = HttpServerSettings(), ) : BaseTest() { - private var callback: HttpCallback = { this } + private var callback: HttpCallbackType = { this } override val handler: HttpHandler = path { post("*") { callback() } diff --git a/http/http_test/src/main/kotlin/com/hexagonkt/http/test/examples/FiltersTest.kt b/http/http_test/src/main/kotlin/com/hexagonkt/http/test/examples/FiltersTest.kt index eda6e4b79b..bd9fa4367d 100644 --- a/http/http_test/src/main/kotlin/com/hexagonkt/http/test/examples/FiltersTest.kt +++ b/http/http_test/src/main/kotlin/com/hexagonkt/http/test/examples/FiltersTest.kt @@ -1,13 +1,13 @@ package com.hexagonkt.http.test.examples import com.hexagonkt.core.text.decodeBase64 +import com.hexagonkt.http.basicAuth import com.hexagonkt.http.client.HttpClient import com.hexagonkt.http.client.HttpClientPort import com.hexagonkt.http.client.HttpClientSettings import com.hexagonkt.http.model.FORBIDDEN_403 import com.hexagonkt.http.model.UNAUTHORIZED_401 import com.hexagonkt.http.model.Header -import com.hexagonkt.http.model.Headers import com.hexagonkt.http.model.HttpMethod.PUT import com.hexagonkt.http.model.* import com.hexagonkt.http.server.HttpServerPort @@ -132,7 +132,7 @@ abstract class FiltersTest( private fun authorizedClient(user: String, password: String): HttpClient { val settings = HttpClientSettings( baseUrl = server.binding, - headers = Headers(Header("authorization", basicAuth(user, password))) + authorization = Authorization("basic", basicAuth(user, password)) ) return HttpClient(clientAdapter(), settings).apply { start() } } diff --git a/http/http_test/src/main/kotlin/com/hexagonkt/http/test/examples/SamplesTest.kt b/http/http_test/src/main/kotlin/com/hexagonkt/http/test/examples/SamplesTest.kt index 0c80eaeb6d..9d7d90111c 100644 --- a/http/http_test/src/main/kotlin/com/hexagonkt/http/test/examples/SamplesTest.kt +++ b/http/http_test/src/main/kotlin/com/hexagonkt/http/test/examples/SamplesTest.kt @@ -554,7 +554,7 @@ abstract class SamplesTest( @Test fun mockRequest() { // mockRequest // Test callback (basically, a handler without a predicate) - val callback: HttpCallback = { + val callback: HttpCallbackType = { val fakeAttribute = attributes["fake"] val fakeHeader = request.headers["fake"]?.value ok("Callback result $fakeAttribute $fakeHeader") diff --git a/http/http_test/src/main/resources/assets/index.html b/http/http_test/src/main/resources/assets/index.html index 57aa9e563d..b21e834a02 100644 --- a/http/http_test/src/main/resources/assets/index.html +++ b/http/http_test/src/main/resources/assets/index.html @@ -37,8 +37,8 @@ - - + + @@ -107,7 +107,7 @@ @@ -258,7 +258,7 @@

Contribute

Community

@@ -291,7 +291,7 @@

Community

aria-label="Fork hexagonkt/hexagon on GitHub">Fork + href="https://twitter.com/share?text=Hexagon Toolkit">Post diff --git a/http/rest/src/main/kotlin/com/hexagonkt/rest/Rest.kt b/http/rest/src/main/kotlin/com/hexagonkt/rest/Rest.kt index 0dbf8e0265..41b1ff3528 100644 --- a/http/rest/src/main/kotlin/com/hexagonkt/rest/Rest.kt +++ b/http/rest/src/main/kotlin/com/hexagonkt/rest/Rest.kt @@ -1,9 +1,14 @@ package com.hexagonkt.rest +import com.hexagonkt.core.media.ANY_MEDIA import com.hexagonkt.core.media.MediaType +import com.hexagonkt.http.model.ContentType import com.hexagonkt.http.model.HttpBase import com.hexagonkt.serialization.* +val anyContentType = ContentType(ANY_MEDIA) +val emptyBodies = setOf("", ByteArray(0)) + fun HttpBase.bodyList(): List<*> = bodyString().parseList(mediaType()) diff --git a/http/rest/src/main/kotlin/com/hexagonkt/rest/SerializeRequestCallback.kt b/http/rest/src/main/kotlin/com/hexagonkt/rest/SerializeRequestCallback.kt index d4f3cfe6d5..4ca94062f6 100644 --- a/http/rest/src/main/kotlin/com/hexagonkt/rest/SerializeRequestCallback.kt +++ b/http/rest/src/main/kotlin/com/hexagonkt/rest/SerializeRequestCallback.kt @@ -1,15 +1,21 @@ package com.hexagonkt.rest +import com.hexagonkt.http.handlers.HttpCallback import com.hexagonkt.http.handlers.HttpContext import com.hexagonkt.serialization.SerializationManager import com.hexagonkt.serialization.serialize -class SerializeRequestCallback: (HttpContext) -> HttpContext { +class SerializeRequestCallback : HttpCallback { - // TODO Short circuit if body is empty - override fun invoke(context: HttpContext): HttpContext = - context.request.contentType?.mediaType + override fun invoke(context: HttpContext): HttpContext { + val requestBody = context.request.body + + if (requestBody in emptyBodies) + return context + + return context.request.contentType?.mediaType ?.let(SerializationManager::formatOfOrNull) - ?.let { context.receive(body = context.request.body.serialize(it)) } + ?.let { context.receive(body = requestBody.serialize(it)) } ?: context + } } diff --git a/http/rest/src/main/kotlin/com/hexagonkt/rest/SerializeResponseCallback.kt b/http/rest/src/main/kotlin/com/hexagonkt/rest/SerializeResponseCallback.kt index 31d7027940..bc23fbb3b7 100644 --- a/http/rest/src/main/kotlin/com/hexagonkt/rest/SerializeResponseCallback.kt +++ b/http/rest/src/main/kotlin/com/hexagonkt/rest/SerializeResponseCallback.kt @@ -1,22 +1,25 @@ package com.hexagonkt.rest +import com.hexagonkt.http.handlers.HttpCallback import com.hexagonkt.http.handlers.HttpContext -import com.hexagonkt.http.model.ContentType import com.hexagonkt.serialization.SerializationManager import com.hexagonkt.serialization.serialize -class SerializeResponseCallback: (HttpContext) -> HttpContext { +class SerializeResponseCallback: HttpCallback { - fun HttpContext.accept(): List = - request.accept.ifEmpty { response.contentType?.let(::listOf) ?: emptyList() } + override fun invoke(context: HttpContext): HttpContext { + val responseBody = context.response.body - // TODO Short circuit if body is empty - override fun invoke(context: HttpContext): HttpContext = - context.accept() + if (responseBody in emptyBodies) + return context + + return (context.request.accept - anyContentType) + .ifEmpty { context.response.contentType?.let(::listOf) ?: emptyList() } .associateWith { SerializationManager.formatOfOrNull(it.mediaType) } .mapNotNull { (k, v) -> v?.let { k to it } } .firstOrNull() - ?.let { (ct, sf) -> ct to context.response.body.serialize(sf) } + ?.let { (ct, sf) -> ct to responseBody.serialize(sf) } ?.let { (ct, c) -> context.send(body = c, contentType = ct) } ?: context + } } diff --git a/http/rest/src/test/kotlin/com/hexagonkt/rest/SerializeRequestCallbackTest.kt b/http/rest/src/test/kotlin/com/hexagonkt/rest/SerializeRequestCallbackTest.kt new file mode 100644 index 0000000000..83eda2fda8 --- /dev/null +++ b/http/rest/src/test/kotlin/com/hexagonkt/rest/SerializeRequestCallbackTest.kt @@ -0,0 +1,37 @@ +package com.hexagonkt.rest + +import com.hexagonkt.core.media.APPLICATION_JSON +import com.hexagonkt.core.media.APPLICATION_YAML +import com.hexagonkt.http.handlers.HttpContext +import com.hexagonkt.http.model.ContentType +import com.hexagonkt.serialization.SerializationManager +import com.hexagonkt.serialization.jackson.json.Json +import org.junit.jupiter.api.Test +import kotlin.test.assertEquals +import kotlin.test.assertSame + +internal class SerializeRequestCallbackTest { + + private val callback = SerializeRequestCallback() + + @Test fun `Serialize empty request callback creates the proper response`() { + val stringContext = HttpContext().send(body = "") + assertSame(stringContext, callback(stringContext)) + + val binaryContext = HttpContext().send(body = ByteArray(0)) + assertSame(binaryContext, callback(binaryContext)) + } + + @Test fun `Serialize request callback creates the proper response`() { + SerializationManager.formats = setOf(Json) + val body = mapOf("key" to "value") + + val yaml = ContentType(APPLICATION_YAML) + val yamlContext = HttpContext().receive(body = body, contentType = yaml) + assertSame(yamlContext, callback(yamlContext)) + + val json = ContentType(APPLICATION_JSON) + val jsonContext = HttpContext().receive(body = body, contentType = json) + assertEquals(jsonContext.receive("{\n \"key\" : \"value\"\n}"), callback(jsonContext)) + } +} diff --git a/http/rest/src/test/kotlin/com/hexagonkt/rest/SerializeResponseCallbackTest.kt b/http/rest/src/test/kotlin/com/hexagonkt/rest/SerializeResponseCallbackTest.kt new file mode 100644 index 0000000000..47f4f98a6c --- /dev/null +++ b/http/rest/src/test/kotlin/com/hexagonkt/rest/SerializeResponseCallbackTest.kt @@ -0,0 +1,46 @@ +package com.hexagonkt.rest + +import com.hexagonkt.core.media.APPLICATION_JSON +import com.hexagonkt.core.media.APPLICATION_YAML +import com.hexagonkt.http.handlers.HttpContext +import com.hexagonkt.http.model.ContentType +import com.hexagonkt.serialization.SerializationManager +import com.hexagonkt.serialization.jackson.json.Json +import org.junit.jupiter.api.Test +import kotlin.test.assertEquals +import kotlin.test.assertSame + +internal class SerializeResponseCallbackTest { + + private val callback = SerializeResponseCallback() + + @Test fun `Serialize empty response callback creates the proper response`() { + val stringContext = HttpContext().send(body = "") + assertSame(stringContext, callback(stringContext)) + + val binaryContext = HttpContext().send(body = ByteArray(0)) + assertSame(binaryContext, callback(binaryContext)) + } + + @Test fun `Serialize response callback creates the proper response`() { + SerializationManager.formats = setOf(Json) + val body = mapOf("key" to "value") + val jsonBody = "{\n \"key\" : \"value\"\n}" + + val yaml = ContentType(APPLICATION_YAML) + val yamlContext = HttpContext().send(body = body, contentType = yaml) + assertSame(yamlContext, callback(yamlContext)) + + val anyMedia = ContentType(APPLICATION_YAML) + val anyMediaContext = HttpContext().send(body = body, contentType = anyMedia) + assertSame(anyMediaContext, callback(anyMediaContext)) + + val json = ContentType(APPLICATION_JSON) + val jsonContext = HttpContext().send(body = body, contentType = json) + assertEquals(jsonContext.send(body = jsonBody), callback(jsonContext)) + + val acceptContext = HttpContext().send(body = body).receive(accept = listOf(json)) + val expectedContext = acceptContext.send(body = jsonBody, contentType = json) + assertEquals(expectedContext, callback(acceptContext)) + } +} diff --git a/http/rest_tools/build.gradle.kts b/http/rest_tools/build.gradle.kts index eecbd14b3a..167bda92f6 100644 --- a/http/rest_tools/build.gradle.kts +++ b/http/rest_tools/build.gradle.kts @@ -12,12 +12,21 @@ apply(from = "$rootDir/gradle/detekt.gradle") description = "Tools to test and document REST services." dependencies { + val guavaVersion = "33.0.0-jre" + val slf4jVersion = properties["slf4jVersion"] val swaggerRequestValidatorVersion = properties["swaggerRequestValidatorVersion"] + val vertxVersion = properties["vertxVersion"] "api"(project(":http:rest")) "api"(project(":http:http_server")) "api"(project(":http:http_client")) - "api"("com.atlassian.oai:swagger-request-validator-core:$swaggerRequestValidatorVersion") + "api"("io.vertx:vertx-openapi:$vertxVersion") + "api"("org.slf4j:slf4j-api:$slf4jVersion") + "api"("com.google.guava:guava:$guavaVersion") + "api"("com.atlassian.oai:swagger-request-validator-core:$swaggerRequestValidatorVersion") { + exclude(group = "org.slf4j") + exclude(group = "com.google.guava") + } "testImplementation"(project(":http:http_client_jetty")) "testImplementation"(project(":http:http_server_jetty")) diff --git a/http/rest_tools/src/main/kotlin/com/hexagonkt/rest/tools/DynamicServer.kt b/http/rest_tools/src/main/kotlin/com/hexagonkt/rest/tools/DynamicServer.kt index 98236979ba..ce3e681472 100644 --- a/http/rest_tools/src/main/kotlin/com/hexagonkt/rest/tools/DynamicServer.kt +++ b/http/rest_tools/src/main/kotlin/com/hexagonkt/rest/tools/DynamicServer.kt @@ -13,7 +13,7 @@ import java.net.URL * Server with dynamic handler (delegated to [path]). Root handler can be replaced at any time * without restarting the server. */ -data class DynamicServer( +data class DynamicHttpServer( private val adapter: HttpServerPort, private val settings: HttpServerSettings = HttpServerSettings(), var path: PathHandler = PathHandler(), @@ -25,7 +25,7 @@ data class DynamicServer( HttpServer(adapter, settings) { after("*", SerializeResponseCallback()) after(pattern = "*", status = NOT_FOUND_404) { - send(response = this@DynamicServer.path.process(request).response) + send(response = this@DynamicHttpServer.path.process(request).response) } } } diff --git a/http/rest_tools/src/main/kotlin/com/hexagonkt/rest/tools/Http.kt b/http/rest_tools/src/main/kotlin/com/hexagonkt/rest/tools/Http.kt index 1396f97986..12806a3730 100644 --- a/http/rest_tools/src/main/kotlin/com/hexagonkt/rest/tools/Http.kt +++ b/http/rest_tools/src/main/kotlin/com/hexagonkt/rest/tools/Http.kt @@ -15,7 +15,7 @@ import com.hexagonkt.http.model.HttpStatusType.SUCCESS import com.hexagonkt.http.patterns.createPathPattern import com.hexagonkt.rest.SerializeRequestCallback -data class Http( +data class StateHttpClient( val adapter: HttpClientPort, val url: String? = null, val httpContentType: ContentType? = null, @@ -23,6 +23,8 @@ data class Http( val httpHeaders: Map = emptyMap(), val sslSettings: SslSettings? = SslSettings(), val handler: HttpHandler? = serializeHandler, + val authorization: Authorization? = null, + val followRedirects: Boolean = false ) { companion object { val serializeHandler: HttpHandler = BeforeHandler("*", SerializeRequestCallback()) @@ -37,6 +39,8 @@ data class Http( headers = toHeaders(httpHeaders), insecure = true, sslSettings = sslSettings, + authorization = authorization, + followRedirects = followRedirects ) private val client = HttpClient(adapter, settings, handler = handler) @@ -80,8 +84,8 @@ data class Http( client.stop() } - fun request(block: Http.() -> Unit) { - client.request { block.invoke(this@Http) } + fun request(block: StateHttpClient.() -> Unit) { + client.request { block.invoke(this@StateHttpClient) } } fun assertStatus(status: HttpStatus) { @@ -100,6 +104,14 @@ data class Http( assertStatus(SUCCESS) } + fun assertContentType(contentType: ContentType) { + assert(this.contentType == contentType) + } + + fun assertContentType(mediaType: MediaType) { + assert(contentType == ContentType(mediaType)) + } + fun assertBody(body: Any) { assert(body == lastResponse.body) } @@ -123,9 +135,9 @@ data class Http( private fun send( method: HttpMethod = GET, path: String = "/", - headers: Map = emptyMap(), body: Any = "", formParameters: List = emptyList(), + headers: Map = emptyMap(), parts: List = emptyList(), contentType: ContentType? = settings.contentType, accept: List = settings.accept, @@ -153,11 +165,10 @@ data class Http( private fun send( method: HttpMethod = GET, - pathPattern: String, - pathParameters: Map, - headers: Map = emptyMap(), + path: Pair>, body: Any = "", formParameters: List = emptyList(), + headers: Map = emptyMap(), parts: List = emptyList(), contentType: ContentType? = settings.contentType, accept: List = settings.accept, @@ -165,38 +176,38 @@ data class Http( ): HttpResponsePort = send( method = method, - path = createPathPattern(pathPattern, false).insertParameters(pathParameters), - headers = toHeaders(headers), + path = createPathPattern(path.first, false).insertParameters(path.second), body = body, formParameters = formParameters, + headers = toHeaders(headers), parts = parts, contentType = contentType, accept = accept, attributes = attributes - + mapOf("pathPattern" to pathPattern, "pathParameters" to pathParameters), + + mapOf("pathPattern" to path.first, "pathParameters" to path.second), ) fun get( path: String = "/", - headers: Map = emptyMap(), body: Any = "", formParameters: List = emptyList(), + headers: Map = emptyMap(), parts: List = emptyList(), contentType: ContentType? = settings.contentType, accept: List = settings.accept, ): HttpResponsePort = - send(GET, path, headers, body, formParameters, parts, contentType, accept) + send(GET, path, body, formParameters, headers, parts, contentType, accept) fun put( path: String = "/", body: Any = "", - headers: Map = emptyMap(), formParameters: List = emptyList(), + headers: Map = emptyMap(), parts: List = emptyList(), contentType: ContentType? = settings.contentType, accept: List = settings.accept, ): HttpResponsePort = - send(PUT, path, headers, body, formParameters, parts, contentType, accept) + send(PUT, path, body, formParameters, headers, parts, contentType, accept) fun put( path: String = "/", @@ -207,171 +218,169 @@ data class Http( accept: List = settings.accept, body: () -> Any, ): HttpResponsePort = - send(PUT, path, headers, body(), formParameters, parts, contentType, accept) + put(path, body(), formParameters, headers, parts, contentType, accept) fun post( path: String = "/", - headers: Map = emptyMap(), body: Any = "", formParameters: List = emptyList(), + headers: Map = emptyMap(), parts: List = emptyList(), contentType: ContentType? = settings.contentType, accept: List = settings.accept, ): HttpResponsePort = - send(POST, path, headers, body, formParameters, parts, contentType, accept) + send(POST, path, body, formParameters, headers, parts, contentType, accept) - fun options( + fun post( path: String = "/", + formParameters: List = emptyList(), headers: Map = emptyMap(), + parts: List = emptyList(), + contentType: ContentType? = settings.contentType, + accept: List = settings.accept, + body: () -> Any, + ): HttpResponsePort = + post(path, body(), formParameters, headers, parts, contentType, accept) + + fun options( + path: String = "/", body: Any = "", formParameters: List = emptyList(), + headers: Map = emptyMap(), parts: List = emptyList(), contentType: ContentType? = settings.contentType, accept: List = settings.accept, ): HttpResponsePort = - send(OPTIONS, path, headers, body, formParameters, parts, contentType, accept) + send(OPTIONS, path, body, formParameters, headers, parts, contentType, accept) fun delete( path: String = "/", - headers: Map = emptyMap(), body: Any = "", formParameters: List = emptyList(), + headers: Map = emptyMap(), parts: List = emptyList(), contentType: ContentType? = settings.contentType, accept: List = settings.accept, ): HttpResponsePort = - send(DELETE, path, headers, body, formParameters, parts, contentType, accept) + send(DELETE, path, body, formParameters, headers, parts, contentType, accept) fun patch( path: String = "/", - headers: Map = emptyMap(), body: Any = "", formParameters: List = emptyList(), + headers: Map = emptyMap(), parts: List = emptyList(), contentType: ContentType? = settings.contentType, accept: List = settings.accept, ): HttpResponsePort = - send(PATCH, path, headers, body, formParameters, parts, contentType, accept) + send(PATCH, path, body, formParameters, headers, parts, contentType, accept) fun trace( path: String = "/", - headers: Map = emptyMap(), body: Any = "", formParameters: List = emptyList(), + headers: Map = emptyMap(), parts: List = emptyList(), contentType: ContentType? = settings.contentType, accept: List = settings.accept, ): HttpResponsePort = - send(TRACE, path, headers, body, formParameters, parts, contentType, accept) + send(TRACE, path, body, formParameters, headers, parts, contentType, accept) fun get( - pathPattern: String, - pathParameters: Map, - headers: Map = emptyMap(), + path: Pair>, body: Any = "", formParameters: List = emptyList(), + headers: Map = emptyMap(), parts: List = emptyList(), contentType: ContentType? = settings.contentType, accept: List = settings.accept, ): HttpResponsePort = - send( - GET, pathPattern, pathParameters, headers, body, formParameters, parts, contentType, accept - ) + send(GET, path, body, formParameters, headers, parts, contentType, accept) fun put( - pathPattern: String, - pathParameters: Map, - headers: Map = emptyMap(), + path: Pair>, body: Any = "", formParameters: List = emptyList(), + headers: Map = emptyMap(), parts: List = emptyList(), contentType: ContentType? = settings.contentType, accept: List = settings.accept, ): HttpResponsePort = - send( - PUT, pathPattern, pathParameters, headers, body, formParameters, parts, contentType, accept - ) + send(PUT, path, body, formParameters, headers, parts, contentType, accept) fun put( - pathPattern: String, - pathParameters: Map, + path: Pair>, formParameters: List = emptyList(), parts: List = emptyList(), contentType: ContentType? = settings.contentType, accept: List = settings.accept, body: () -> Any, ): HttpResponsePort = - send( - PUT, pathPattern, pathParameters, Headers(), body(), formParameters, parts, contentType, accept - ) + put(path, body(), formParameters, Headers(), parts, contentType, accept) fun post( - pathPattern: String, - pathParameters: Map, - headers: Map = emptyMap(), + path: Pair>, body: Any = "", formParameters: List = emptyList(), + headers: Map = emptyMap(), parts: List = emptyList(), contentType: ContentType? = settings.contentType, accept: List = settings.accept, ): HttpResponsePort = - send( - POST, pathPattern, pathParameters, headers, body, formParameters, parts, contentType, accept - ) + send(POST, path, body, formParameters, headers, parts, contentType, accept) - fun options( - pathPattern: String, - pathParameters: Map, + fun post( + path: Pair>, + formParameters: List = emptyList(), headers: Map = emptyMap(), + parts: List = emptyList(), + contentType: ContentType? = settings.contentType, + accept: List = settings.accept, + body: () -> Any, + ): HttpResponsePort = + post(path, body(), formParameters, headers, parts, contentType, accept) + + fun options( + path: Pair>, body: Any = "", formParameters: List = emptyList(), + headers: Map = emptyMap(), parts: List = emptyList(), contentType: ContentType? = settings.contentType, accept: List = settings.accept, ): HttpResponsePort = - send( - OPTIONS, pathPattern, pathParameters, headers, body, formParameters, parts, contentType, accept - ) + send(OPTIONS, path, body, formParameters, headers, parts, contentType, accept) fun delete( - pathPattern: String, - pathParameters: Map, - headers: Map = emptyMap(), + path: Pair>, body: Any = "", formParameters: List = emptyList(), + headers: Map = emptyMap(), parts: List = emptyList(), contentType: ContentType? = settings.contentType, accept: List = settings.accept, ): HttpResponsePort = - send( - DELETE, pathPattern, pathParameters, headers, body, formParameters, parts, contentType, accept - ) + send(DELETE, path, body, formParameters, headers, parts, contentType, accept) fun patch( - pathPattern: String, - pathParameters: Map, - headers: Map = emptyMap(), + path: Pair>, body: Any = "", formParameters: List = emptyList(), + headers: Map = emptyMap(), parts: List = emptyList(), contentType: ContentType? = settings.contentType, accept: List = settings.accept, ): HttpResponsePort = - send( - PATCH, pathPattern, pathParameters, headers, body, formParameters, parts, contentType, accept - ) + send(PATCH, path, body, formParameters, headers, parts, contentType, accept) fun trace( - pathPattern: String, - pathParameters: Map, - headers: Map = emptyMap(), + path: Pair>, body: Any = "", formParameters: List = emptyList(), + headers: Map = emptyMap(), parts: List = emptyList(), contentType: ContentType? = settings.contentType, accept: List = settings.accept, ): HttpResponsePort = - send( - TRACE, pathPattern, pathParameters, headers, body, formParameters, parts, contentType, accept - ) + send(TRACE, path, body, formParameters, headers, parts, contentType, accept) } diff --git a/http/rest_tools/src/main/kotlin/com/hexagonkt/rest/tools/openapi/OpenApiHandler.kt b/http/rest_tools/src/main/kotlin/com/hexagonkt/rest/tools/openapi/OpenApiHandler.kt index 5207b23f43..45768a25a1 100644 --- a/http/rest_tools/src/main/kotlin/com/hexagonkt/rest/tools/openapi/OpenApiHandler.kt +++ b/http/rest_tools/src/main/kotlin/com/hexagonkt/rest/tools/openapi/OpenApiHandler.kt @@ -19,8 +19,6 @@ import io.swagger.v3.oas.models.security.SecurityScheme import io.swagger.v3.oas.models.security.SecurityScheme.Type import io.swagger.v3.parser.OpenAPIV3Parser -// TODO Validate bodies with vertx-json-schema -// TODO Check https://github.com/swagger-api/swagger-parser for route verification internal class OpenApiHandler(pathToSpec: String) { private val openAPIParser = OpenAPIV3Parser() @@ -48,7 +46,7 @@ internal class OpenApiHandler(pathToSpec: String) { private fun createHandler(method: HttpMethod, path: String, operation: Operation): HttpHandler = OnHandler(method, path, handleRequest(operation)) - private fun handleRequest(operation: Operation): HttpCallback = + private fun handleRequest(operation: Operation): HttpCallbackType = { verifyAuth(operation, this) ?: verifyParams(operation, this) diff --git a/http/rest_tools/src/main/kotlin/com/hexagonkt/rest/tools/openapi/VerifySpecCallback.kt b/http/rest_tools/src/main/kotlin/com/hexagonkt/rest/tools/openapi/VerifySpecCallback.kt index 8b2c8f7401..4defd44908 100644 --- a/http/rest_tools/src/main/kotlin/com/hexagonkt/rest/tools/openapi/VerifySpecCallback.kt +++ b/http/rest_tools/src/main/kotlin/com/hexagonkt/rest/tools/openapi/VerifySpecCallback.kt @@ -8,6 +8,7 @@ import com.atlassian.oai.validator.model.Request.Method import com.atlassian.oai.validator.model.SimpleRequest import com.atlassian.oai.validator.model.SimpleResponse import com.atlassian.oai.validator.report.ValidationReport +import com.hexagonkt.http.handlers.HttpCallback import com.hexagonkt.http.handlers.HttpContext import com.hexagonkt.http.model.ContentType import com.hexagonkt.http.model.HttpMethod @@ -17,8 +18,10 @@ import kotlin.jvm.optionals.getOrNull /** * Callback that verifies server calls comply with a given OpenAPI spec. + * + * TODO Use https://vertx.io/docs/vertx-openapi/java */ -class VerifySpecCallback(spec: URL) : (HttpContext) -> HttpContext { +class VerifySpecCallback(spec: URL) : HttpCallback { private val messagePrefix: String = "\n- " private val validator: OpenApiInteractionValidator = diff --git a/http/rest_tools/src/test/kotlin/com/hexagonkt/rest/tools/DynamicServerTest.kt b/http/rest_tools/src/test/kotlin/com/hexagonkt/rest/tools/DynamicServerTest.kt index 34d88089e6..fae33cf311 100644 --- a/http/rest_tools/src/test/kotlin/com/hexagonkt/rest/tools/DynamicServerTest.kt +++ b/http/rest_tools/src/test/kotlin/com/hexagonkt/rest/tools/DynamicServerTest.kt @@ -27,8 +27,8 @@ import kotlin.test.assertEquals import kotlin.test.assertTrue @TestInstance(PER_CLASS) -class DynamicServerTest { - private val dynamicServer: DynamicServer = DynamicServer(JettyServletAdapter()) +class DynamicHttpServerTest { + private val dynamicServer: DynamicHttpServer = DynamicHttpServer(JettyServletAdapter()) @BeforeAll fun `Set up mock services`() { dynamicServer.start() @@ -47,7 +47,8 @@ class DynamicServerTest { } } - Http(JettyClientAdapter(), "http://localhost:${dynamicServer.runtimePort}").request { + val url = "http://localhost:${dynamicServer.runtimePort}" + StateHttpClient(JettyClientAdapter(), url).request { get("/hello/mike") assertEquals(OK_200, response.status) } @@ -60,7 +61,8 @@ class DynamicServerTest { } } - Http(JettyClientAdapter(), "http://localhost:${dynamicServer.runtimePort}").request { + val url = "http://localhost:${dynamicServer.runtimePort}" + StateHttpClient(JettyClientAdapter(), url).request { get("/foo") assertEquals(OK_200, response.status) assertEquals("dynamic", response.body) @@ -89,10 +91,10 @@ class DynamicServerTest { val headers = mapOf("alfa" to "beta", "charlie" to listOf("delta", "echo")) val recordCallback = RecordCallback() val recordHandler = BeforeHandler("*", recordCallback) - val http = Http( + val http = StateHttpClient( adapter, httpHeaders = headers, - handler = PathHandler(recordHandler, Http.serializeHandler) + handler = PathHandler(recordHandler, StateHttpClient.serializeHandler) ) http.get("http://localhost:$port/hello/mike").assertBody("GET /hello/mike", headers) @@ -135,7 +137,7 @@ class DynamicServerTest { val url = "http://localhost:${dynamicServer.runtimePort}" val adapter = JettyClientAdapter() val headers = mapOf("alfa" to "beta", "charlie" to listOf("delta", "echo")) - val http = Http(adapter, url, httpHeaders = headers) + val http = StateHttpClient(adapter, url, httpHeaders = headers) http.get("/hello/mike").assertBody("GET /hello/mike", headers) http.get().assertBody("GET / ", headers) @@ -187,13 +189,14 @@ class DynamicServerTest { SerializationManager.formats = linkedSetOf(Json) val settings = HttpServerSettings(bindPort = 0) - val server = DynamicServer(JettyServletAdapter(), settings).apply(DynamicServer::start) + val serverAdapter = JettyServletAdapter() + val server = DynamicHttpServer(serverAdapter, settings).apply(DynamicHttpServer::start) val headers = mapOf("alfa" to "beta", "charlie" to listOf("delta", "echo")) val text = ContentType(TEXT_PLAIN) val json = ContentType(APPLICATION_JSON) val binding = server.binding.toString() val adapter = JettyClientAdapter() - val http = Http(adapter, url = binding, httpHeaders = headers, httpContentType = json) + val http = StateHttpClient(adapter, binding, json, httpHeaders = headers) server.path { before("*") { @@ -210,7 +213,7 @@ class DynamicServerTest { } http.request { - put("/data/{id}", mapOf("id" to 102030)) { + put("/data/102030") { object { val title = "Casino Royale" val tags = listOf("007", "action") @@ -220,6 +223,17 @@ class DynamicServerTest { assertOk() response.body.info("BODY: ") response.contentType.info("CONTENT TYPE: ") + + post("/data/102039") { + object { + val title = "Batman Begins" + val tags = listOf("DC", "action") + } + } + + assertOk() + response.body.info("BODY: ") + response.contentType.info("CONTENT TYPE: ") } http.request { @@ -260,21 +274,24 @@ class DynamicServerTest { } } - private fun Http.assertBody(expectedBody: String, checkedHeaders: Map) { + private fun StateHttpClient.assertBody(expectedBody: String, checkedHeaders: Map) { assertOk() assertSuccess() assertStatus(OK_200) assertStatus(SUCCESS) + assertContentType(ContentType(TEXT_PLAIN)) + assertContentType(TEXT_PLAIN) assertBodyContains(expectedBody) + assertEquals(ContentType(APPLICATION_JSON), request.contentType) + assertEquals(OK_200, status) + for ((k, v) in checkedHeaders.entries) { assertBodyContains(k, v.toString()) } } - private fun HttpResponsePort.assertBody( - expectedBody: String, checkedHeaders: Map) { - + private fun HttpResponsePort.assertBody(expectedBody: String, checkedHeaders: Map) { val bodyString = bodyString() assertEquals(OK_200, status) diff --git a/http/rest_tools/src/test/kotlin/com/hexagonkt/rest/tools/StateHttpClientTest.kt b/http/rest_tools/src/test/kotlin/com/hexagonkt/rest/tools/StateHttpClientTest.kt new file mode 100644 index 0000000000..201cda52f0 --- /dev/null +++ b/http/rest_tools/src/test/kotlin/com/hexagonkt/rest/tools/StateHttpClientTest.kt @@ -0,0 +1,84 @@ +package com.hexagonkt.rest.tools + +import com.hexagonkt.core.media.TEXT_PLAIN +import com.hexagonkt.http.client.jetty.JettyClientAdapter +import com.hexagonkt.http.model.ContentType +import com.hexagonkt.http.model.HttpMethod.POST +import com.hexagonkt.http.model.HttpMethod.PUT +import com.hexagonkt.http.model.HttpResponsePort +import com.hexagonkt.http.model.OK_200 +import com.hexagonkt.http.server.jetty.JettyServletAdapter +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestInstance +import org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +@TestInstance(PER_CLASS) +internal class StateHttpClientTest { + private val server: DynamicHttpServer = DynamicHttpServer(JettyServletAdapter()) + private val text = ContentType(TEXT_PLAIN) + + @BeforeAll fun `Set up mock services`() { + server.start() + } + + @AfterAll fun `Shut down mock services`() { + server.stop() + } + + @Test fun `Check all HTTP methods`() { + server.path { + before("*") { + ok("$method $path ${request.headers}", contentType = text) + } + + on(setOf(PUT, POST), "/bye/mike/{id}") { + ok("$method $path ${request.bodyString()} ${request.headers}", contentType = text) + } + } + + val url = "http://localhost:${server.runtimePort}" + val adapter = JettyClientAdapter() + val headers = mapOf("alfa" to "beta", "charlie" to listOf("delta", "echo")) + val params = mapOf("id" to 9) + val client = StateHttpClient(adapter, url, TEXT_PLAIN, headers = headers) + + client.get("/hello/mike/{id}" to params).assertBody("GET /hello/mike/9", headers) + client.put("/hello/mike/{id}" to params).assertBody("PUT /hello/mike/9", headers) + client.post("/hello/mike/{id}" to params).assertBody("POST /hello/mike/9", headers) + client.options("/hello/mike/{id}" to params).assertBody("OPTIONS /hello/mike/9", headers) + client.delete("/hello/mike/{id}" to params).assertBody("DELETE /hello/mike/9", headers) + client.patch("/hello/mike/{id}" to params).assertBody("PATCH /hello/mike/9", headers) + client.trace("/hello/mike/{id}" to params).assertBody("TRACE /hello/mike/9", headers) + + client.put("/bye/mike/{id}" to params) { "putLambdaBody" } + .assertBody("PUT /bye/mike/9 putLambdaBody", headers) + client.post("/bye/mike/{id}" to params) { "postLambdaBody" } + .assertBody("POST /bye/mike/9 postLambdaBody", headers) + + client.request { + get("/hello/mike").assertBody("GET /hello/mike", headers) + put("/hello/mike").assertBody("PUT /hello/mike", headers) + post("/hello/mike").assertBody("POST /hello/mike", headers) + options("/hello/mike").assertBody("OPTIONS /hello/mike", headers) + delete("/hello/mike").assertBody("DELETE /hello/mike", headers) + patch("/hello/mike").assertBody("PATCH /hello/mike", headers) + trace("/hello/mike").assertBody("TRACE /hello/mike", headers) + } + } + + private fun HttpResponsePort.assertBody(expectedBody: String, checkedHeaders: Map) { + val bodyString = bodyString() + + assertEquals(OK_200, status) + assertTrue(bodyString.startsWith(expectedBody)) + + for (entry in checkedHeaders.entries) { + assertTrue(bodyString.contains(entry.key)) + assertTrue(bodyString.contains(entry.value.toString())) + } + } +} diff --git a/http/rest_tools/src/test/kotlin/com/hexagonkt/rest/tools/openapi/VerifySpecCallbackTest.kt b/http/rest_tools/src/test/kotlin/com/hexagonkt/rest/tools/openapi/VerifySpecCallbackTest.kt index 7e41a874e5..aa20a408b0 100644 --- a/http/rest_tools/src/test/kotlin/com/hexagonkt/rest/tools/openapi/VerifySpecCallbackTest.kt +++ b/http/rest_tools/src/test/kotlin/com/hexagonkt/rest/tools/openapi/VerifySpecCallbackTest.kt @@ -24,6 +24,7 @@ internal class VerifySpecCallbackTest { // TODO Check commented code (it should throw validation errors) @Test fun `Requests not complying with spec return an error`() { verify(errors = listOf("ERROR: validation.request.path.missing [ ] No API path found that matches request ''. [] []")) + // 'status' query parameter with invalid value // verify( // HttpRequest( // path = "/pet/findByStatus", @@ -35,7 +36,7 @@ internal class VerifySpecCallbackTest { // body = listOf( // mapOf( // "name" to "Keka", -// "photoUrls" to listOf("http://example.com") +// "photoUrls" to listOf("https://example.com") // ) // ), // ), @@ -52,7 +53,7 @@ internal class VerifySpecCallbackTest { contentType = ContentType(APPLICATION_JSON), body = mapOf( "name" to "Keka", - "photoUrls" to listOf("http://example.com") + "photoUrls" to listOf("https://example.com") ), ), listOf("ERROR: validation.request.operation.notAllowed [ ] HEAD operation not allowed on path '/pet/1'. [] []") @@ -64,7 +65,7 @@ internal class VerifySpecCallbackTest { contentType = ContentType(APPLICATION_JSON), body = mapOf( "name" to "Keka", - "photoUrls" to listOf("http://example.com") + "photoUrls" to listOf("https://example.com") ) ), HttpResponse( @@ -73,12 +74,13 @@ internal class VerifySpecCallbackTest { body = listOf( mapOf( "name" to "Keka", - "photoUrls" to listOf("http://example.com") + "photoUrls" to listOf("https://example.com") ) ), ), listOf("ERROR: validation.response.body.schema.type [POST /pet RESPONSE] Instance type (array) does not match any allowed primitive type (allowed: [\"object\"]) [] []") ) + // TODO Request body required (should fail) // verify( // HttpRequest( // method = POST, @@ -112,7 +114,7 @@ internal class VerifySpecCallbackTest { contentType = ContentType(APPLICATION_JSON), body = mapOf( "name" to "Keka", - "photoUrls" to listOf("http://example.com") + "photoUrls" to listOf("https://example.com") ), ), ) @@ -143,7 +145,7 @@ internal class VerifySpecCallbackTest { body = listOf( mapOf( "name" to "Keka", - "photoUrls" to listOf("http://example.com") + "photoUrls" to listOf("https://example.com") ) ), ), @@ -155,7 +157,7 @@ internal class VerifySpecCallbackTest { contentType = ContentType(APPLICATION_JSON), body = mapOf( "name" to "Keka", - "photoUrls" to listOf("http://example.com") + "photoUrls" to listOf("https://example.com") ) ), HttpResponse( @@ -163,7 +165,7 @@ internal class VerifySpecCallbackTest { contentType = ContentType(APPLICATION_JSON), body = mapOf( "name" to "Keka", - "photoUrls" to listOf("http://example.com") + "photoUrls" to listOf("https://example.com") ), ), ) @@ -174,7 +176,7 @@ internal class VerifySpecCallbackTest { contentType = ContentType(APPLICATION_JSON), body = mapOf( "name" to "Keka", - "photoUrls" to listOf("http://example.com") + "photoUrls" to listOf("https://example.com") ) ), HttpResponse( @@ -182,7 +184,7 @@ internal class VerifySpecCallbackTest { contentType = ContentType(APPLICATION_JSON), body = mapOf( "name" to "Keka", - "photoUrls" to listOf("http://example.com") + "photoUrls" to listOf("https://example.com") ), ), ) @@ -193,7 +195,7 @@ internal class VerifySpecCallbackTest { contentType = ContentType(APPLICATION_JSON), body = mapOf( "name" to "Keka", - "photoUrls" to listOf("http://example.com") + "photoUrls" to listOf("https://example.com") ) ), HttpResponse(status = NOT_FOUND_404), diff --git a/site/assets/img/architecture.svg b/site/assets/img/architecture.svg index 80783e4bc4..e59f1ed143 100644 --- a/site/assets/img/architecture.svg +++ b/site/assets/img/architecture.svg @@ -1 +1 @@ - \ No newline at end of file + diff --git a/site/mkdocs.yml b/site/mkdocs.yml index 935e6dabe9..7535f41da7 100644 --- a/site/mkdocs.yml +++ b/site/mkdocs.yml @@ -71,7 +71,7 @@ nav: - Planning: planning.md - Contributing ↗: https://github.com/hexagonkt/hexagon/contribute - GitHub ↗: https://github.com/hexagonkt - - Dev.to ↗: https://dev.to/hexagonkt + - Dev.to ↗: https://dev.to/hexagontk - LibHunt ↗: https://kotlin.libhunt.com/hexagon-alternatives - StackShare ↗: https://stackshare.io/hexagon @@ -127,14 +127,14 @@ extra: provider: google property: G-BEKWF2E4DJ - twitter_user: hexagon_kt + twitter_user: hexagontk social: - icon: fontawesome/brands/github link: https://github.com/hexagonkt - icon: fontawesome/brands/dev - link: https://dev.to/hexagonkt - - icon: fontawesome/brands/twitter - link: https://twitter.com/hexagon_kt + link: https://dev.to/hexagontk + - icon: fontawesome/brands/x-twitter + link: https://twitter.com/hexagontk - icon: fontawesome/brands/slack link: https://kotlinlang.slack.com/messages/hexagon diff --git a/site/mkdocs/main.html b/site/mkdocs/main.html index 5b41d02be3..6130d174e9 100644 --- a/site/mkdocs/main.html +++ b/site/mkdocs/main.html @@ -128,7 +128,7 @@ aria-label="Fork {{ config.extra.repo }} on GitHub">Fork + href="https://twitter.com/intent/tweet?text=Hexagon Toolkit">Post {{ extracopyright }} diff --git a/site/pages/index.md b/site/pages/index.md index e94c33f531..5feba69bbe 100644 --- a/site/pages/index.md +++ b/site/pages/index.md @@ -180,7 +180,9 @@ graph TD http_client -->|uses| http_handlers web -->|uses| http_server web -->|uses| templates - rest -->|uses| http_server + rest -->|uses| http_handlers rest -->|uses| serialization rest_tools -->|uses| rest + rest_tools -->|uses| http_server + rest_tools -->|uses| http_client ```