From 87273b603b489e6daa27267e14421fab2ed56221 Mon Sep 17 00:00:00 2001 From: Anton Kholomiov Date: Sat, 4 Nov 2023 18:11:53 +0300 Subject: [PATCH 1/2] Complete release v2 --- Changelog.md | 40 ++++++++++++++++++++++ docs/src/00-foreword.md | 17 ++++++--- mig-client/LICENSE | 30 ++++++++++++++++ mig-client/mig-client.cabal | 3 +- mig-client/package.yaml | 3 +- mig-extra/LICENSE | 30 ++++++++++++++++ mig-extra/mig-extra.cabal | 3 +- mig-extra/package.yaml | 3 +- mig-extra/src/Mig/Extra/Plugin/Auth.hs | 5 ++- mig-extra/src/Mig/Extra/Plugin/Trace.hs | 8 +++-- mig-server/LICENSE | 2 +- mig-server/mig-server.cabal | 10 +++--- mig-server/package.yaml | 11 +++--- mig-swagger-ui/LICENSE | 30 ++++++++++++++++ mig-swagger-ui/mig-swagger-ui.cabal | 10 ++++-- mig-swagger-ui/package.yaml | 11 ++++-- mig-swagger-ui/src/Mig/Swagger.hs | 3 ++ mig-wai/LICENSE | 30 ++++++++++++++++ mig-wai/mig-wai.cabal | 17 ++++----- mig-wai/package.yaml | 13 +++---- mig-wai/src/Mig/Server/Wai.hs | 11 ++++-- mig/LICENSE | 30 ++++++++++++++++ mig/mig.cabal | 9 +++-- mig/package.yaml | 9 +++-- mig/src/Mig/Core.hs | 23 ++++++------- mig/src/Mig/Core/Api.hs | 1 + mig/src/Mig/Core/Api/NormalForm/TreeApi.hs | 8 ++++- mig/src/Mig/Core/Class/Route.hs | 4 ++- mig/src/Mig/Core/Server.hs | 39 ++++++++++++--------- mig/src/Mig/Core/Server/Cache.hs | 16 +++++++++ mig/src/Mig/Core/ServerFun.hs | 10 +++--- mig/src/Mig/Core/Types/Http.hs | 2 +- 32 files changed, 356 insertions(+), 85 deletions(-) create mode 100644 Changelog.md create mode 100644 mig-client/LICENSE create mode 100644 mig-extra/LICENSE create mode 100644 mig-swagger-ui/LICENSE create mode 100644 mig-wai/LICENSE create mode 100644 mig/LICENSE diff --git a/Changelog.md b/Changelog.md new file mode 100644 index 0000000..9c4ffef --- /dev/null +++ b/Changelog.md @@ -0,0 +1,40 @@ +# Changelog + +## v2 + +The servers are augmented with info on API structure. It allows us to +build OpenApi schema, swagger and clients for servers. + +* OpenApi schema for servers + +* swagger servers support + +* clients from the same code as servers + +* redesign of internal types + +* redesign of DSL for routes + +* many ergonomic improvements + +* packages for extra utils + +* split of `mig` package to several packages: + + * `mig` - core + * `mig-wai` - rendering servers to wai apps + * `mig-client` - clients + * `mig-extra` - extra utils + * `mig-server` - mig servers with batteries + * `mig-swagger-ui` - swagger ui server + +* tutorial and quickstart guide on github pages + +* CI for repo with formatter, build and tests and update of docs on github pages + +Thanks to Ambros for contribution. + +## v1 + +Initial version. Servers as functions with type-safe dsl. + diff --git a/docs/src/00-foreword.md b/docs/src/00-foreword.md index 2d366b7..9cb8464 100644 --- a/docs/src/00-foreword.md +++ b/docs/src/00-foreword.md @@ -53,10 +53,19 @@ With cabal we can install it from Hackage: cabal install mig-server --lib ``` -With stack we can link to the repo in extra-deps - -``` -TODO: example here +With stack we can link to the repo in `extra-deps` (put it in your `stack.yaml`): + +```yaml +extra-deps: + - git: https://github.com/anton-k/mig + commit: + subdirs: + - mig + - mig-extra + - mig-client + - mig-wai + - mig-swagger-ui + - mig-server ``` ## Structure of the library diff --git a/mig-client/LICENSE b/mig-client/LICENSE new file mode 100644 index 0000000..4173eb7 --- /dev/null +++ b/mig-client/LICENSE @@ -0,0 +1,30 @@ +Copyright Anton Kholomiov (c) 2023 + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + * Neither the name of Author name here nor the names of other + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/mig-client/mig-client.cabal b/mig-client/mig-client.cabal index 08152f9..e9302ac 100644 --- a/mig-client/mig-client.cabal +++ b/mig-client/mig-client.cabal @@ -16,6 +16,7 @@ author: Anton Kholomiov maintainer: anton.kholomiov@gmail.com copyright: 2023 Anton Kholomiov license: BSD3 +license-file: LICENSE build-type: Simple extra-source-files: README.md @@ -49,7 +50,7 @@ library , http-client , http-media , http-types - , mig + , mig >=0.2 , mtl , text default-language: GHC2021 diff --git a/mig-client/package.yaml b/mig-client/package.yaml index 323f5a6..2807cb9 100644 --- a/mig-client/package.yaml +++ b/mig-client/package.yaml @@ -5,6 +5,7 @@ license: BSD3 author: "Anton Kholomiov" maintainer: "anton.kholomiov@gmail.com" copyright: "2023 Anton Kholomiov" +license-file: LICENSE extra-source-files: - README.md @@ -27,7 +28,7 @@ dependencies: - bytestring - containers - http-api-data -- mig +- mig >= 0.2 - http-client - http-media - mtl diff --git a/mig-extra/LICENSE b/mig-extra/LICENSE new file mode 100644 index 0000000..4173eb7 --- /dev/null +++ b/mig-extra/LICENSE @@ -0,0 +1,30 @@ +Copyright Anton Kholomiov (c) 2023 + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + * Neither the name of Author name here nor the names of other + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/mig-extra/mig-extra.cabal b/mig-extra/mig-extra.cabal index a07434d..23e3e05 100644 --- a/mig-extra/mig-extra.cabal +++ b/mig-extra/mig-extra.cabal @@ -15,6 +15,7 @@ author: Anton Kholomiov maintainer: anton.kholomiov@gmail.com copyright: 2023 Anton Kholomiov license: BSD3 +license-file: LICENSE build-type: Simple extra-source-files: README.md @@ -61,7 +62,7 @@ library , http-api-data , http-media , http-types - , mig + , mig >=0.2 , mig-client , openapi3 , template-haskell diff --git a/mig-extra/package.yaml b/mig-extra/package.yaml index 4ba3272..1daceee 100644 --- a/mig-extra/package.yaml +++ b/mig-extra/package.yaml @@ -5,6 +5,7 @@ license: BSD3 author: "Anton Kholomiov" maintainer: "anton.kholomiov@gmail.com" copyright: "2023 Anton Kholomiov" +license-file: LICENSE extra-source-files: - README.md @@ -32,7 +33,7 @@ default-extensions: dependencies: - base >= 4.7 && < 5 - data-default -- mig +- mig >= 0.2 - http-api-data - blaze-html - http-types diff --git a/mig-extra/src/Mig/Extra/Plugin/Auth.hs b/mig-extra/src/Mig/Extra/Plugin/Auth.hs index 0f546fc..2eb4bb7 100644 --- a/mig-extra/src/Mig/Extra/Plugin/Auth.hs +++ b/mig-extra/src/Mig/Extra/Plugin/Auth.hs @@ -7,12 +7,15 @@ module Mig.Extra.Plugin.Auth ( import Control.Monad.IO.Class import Mig.Core +-- | Authorization plugin interface data WithAuth m token resp = WithAuth { isValid :: token -> m Bool - -- ^ check that token is valid and return info + -- ^ check that token is valid , authFail :: token -> m resp + -- ^ how to respond on failure } +-- | Creates plugin that applies check of auth credentials which are passed as HTTP Header with name auth. withHeaderAuth :: forall m token resp. (IsResp resp, MonadIO m) => WithAuth m token resp -> Header "auth" token -> Plugin m withHeaderAuth env (Header token) = processResponse $ \getResp -> do isOk <- env.isValid token diff --git a/mig-extra/src/Mig/Extra/Plugin/Trace.hs b/mig-extra/src/Mig/Extra/Plugin/Trace.hs index 30b3cf9..9ab9cfd 100644 --- a/mig-extra/src/Mig/Extra/Plugin/Trace.hs +++ b/mig-extra/src/Mig/Extra/Plugin/Trace.hs @@ -1,9 +1,11 @@ {-| Debug utils for server. Simple logger for HTTP requests and responses -Also we can use real logging functions with ***By versions. -Simple variants are only for manual testing. It prints to stdout +Also we can use real logging functions with ***By versions of the logger functions. +Simple variants are only for local testing. It prints to stdout with no ordering of the concurrent prints. -It can be useful for fast setup of debug for your application. +It can be useful for fast setup of debug for your application. Example of the usage: + +> applyPlugin (logHttp V2) server -} module Mig.Extra.Plugin.Trace ( logReq, diff --git a/mig-server/LICENSE b/mig-server/LICENSE index c5b6c16..4173eb7 100644 --- a/mig-server/LICENSE +++ b/mig-server/LICENSE @@ -1,4 +1,4 @@ -Copyright Author name here (c) 2023 +Copyright Anton Kholomiov (c) 2023 All rights reserved. diff --git a/mig-server/mig-server.cabal b/mig-server/mig-server.cabal index 4742363..66f3094 100644 --- a/mig-server/mig-server.cabal +++ b/mig-server/mig-server.cabal @@ -56,6 +56,8 @@ description: With library mig we can build lightweight and composable ser * quick start guide at . * examples directory for more fun servers: at + . + * reference for the main functions: category: Web homepage: https://github.com/anton-k/mig#readme bug-reports: https://github.com/anton-k/mig/issues @@ -101,10 +103,10 @@ library , data-default , http-api-data , http-types - , mig - , mig-extra - , mig-swagger-ui - , mig-wai + , mig >=0.2 + , mig-extra >=0.1 + , mig-swagger-ui >=0.1 + , mig-wai >=0.1 , openapi3 , text , transformers diff --git a/mig-server/package.yaml b/mig-server/package.yaml index 1a0a219..27a139f 100644 --- a/mig-server/package.yaml +++ b/mig-server/package.yaml @@ -5,6 +5,7 @@ license: BSD3 author: "Anton Kholomiov" maintainer: "anton.kholomiov@gmail.com" copyright: "2023 Anton Kholomiov" +license-file: LICENSE extra-source-files: - README.md @@ -66,6 +67,8 @@ description: | * quick start guide at . * examples directory for more fun servers: at + . + * reference for the main functions: language: GHC2021 @@ -81,16 +84,16 @@ default-extensions: dependencies: - aeson - base >= 4.7 && < 5 -- mig -- mig-extra -- mig-wai +- mig >= 0.2 +- mig-extra >= 0.1 +- mig-wai >= 0.1 - http-types - text - http-api-data - openapi3 - blaze-html - warp -- mig-swagger-ui +- mig-swagger-ui >= 0.1 - data-default - transformers diff --git a/mig-swagger-ui/LICENSE b/mig-swagger-ui/LICENSE new file mode 100644 index 0000000..4173eb7 --- /dev/null +++ b/mig-swagger-ui/LICENSE @@ -0,0 +1,30 @@ +Copyright Anton Kholomiov (c) 2023 + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + * Neither the name of Author name here nor the names of other + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/mig-swagger-ui/mig-swagger-ui.cabal b/mig-swagger-ui/mig-swagger-ui.cabal index 4149be8..690ecc1 100644 --- a/mig-swagger-ui/mig-swagger-ui.cabal +++ b/mig-swagger-ui/mig-swagger-ui.cabal @@ -7,14 +7,18 @@ cabal-version: 1.12 name: mig-swagger-ui version: 0.1.0.0 synopsis: Swagger servers for mig library -description: Swagger servers for mig library +description: Appends swagger servers for mig servers. + Example of the usage. This code adds swagger server which is serverd on path "swagger-ui" to our server + . + > withSwagger def server category: Web homepage: https://github.com/anton-k/mig#readme bug-reports: https://github.com/anton-k/mig/issues author: Anton Kholomiov -maintainer: example@example.com +maintainer: anton.kholomiov@gmail.com copyright: 2023 Anton Kholomiov license: BSD3 +license-file: LICENSE build-type: Simple extra-source-files: README.md @@ -65,7 +69,7 @@ library , file-embed-lzma , http-api-data , lens - , mig + , mig >=0.2 , openapi3 , text default-language: GHC2021 diff --git a/mig-swagger-ui/package.yaml b/mig-swagger-ui/package.yaml index 0ae3f8a..8eae8a1 100644 --- a/mig-swagger-ui/package.yaml +++ b/mig-swagger-ui/package.yaml @@ -3,8 +3,9 @@ version: 0.1.0.0 github: "anton-k/mig" license: BSD3 author: "Anton Kholomiov" -maintainer: "example@example.com" +maintainer: "anton.kholomiov@gmail.com" copyright: "2023 Anton Kholomiov" +license-file: LICENSE # Metadata used when publishing your package synopsis: Swagger servers for mig library @@ -13,7 +14,11 @@ category: Web # To avoid duplicated efforts in documentation and dealing with the # complications of embedding Haddock markup inside cabal files, it is # common to point users to the README.md file. -description: Swagger servers for mig library +description: | + Appends swagger servers for mig servers. + Example of the usage. This code adds swagger server which is serverd on path "swagger-ui" to our server + . + > withSwagger def server language: GHC2021 @@ -30,7 +35,7 @@ dependencies: - bytestring - text - lens -- mig +- mig >= 0.2 - file-embed-lzma - blaze-html - blaze-markup diff --git a/mig-swagger-ui/src/Mig/Swagger.hs b/mig-swagger-ui/src/Mig/Swagger.hs index eb9cb65..783dc31 100644 --- a/mig-swagger-ui/src/Mig/Swagger.hs +++ b/mig-swagger-ui/src/Mig/Swagger.hs @@ -53,6 +53,9 @@ data DefaultInfo = DefaultInfo , version :: Text } +{-| Adds most common used info to OpenApi schema. Use this function +in the @mapSchema@ field of the @SwaggerConfig@. +-} addDefaultInfo :: DefaultInfo -> OpenApi -> OpenApi addDefaultInfo appInfo = OA.info diff --git a/mig-wai/LICENSE b/mig-wai/LICENSE new file mode 100644 index 0000000..4173eb7 --- /dev/null +++ b/mig-wai/LICENSE @@ -0,0 +1,30 @@ +Copyright Anton Kholomiov (c) 2023 + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + * Neither the name of Author name here nor the names of other + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/mig-wai/mig-wai.cabal b/mig-wai/mig-wai.cabal index fb2ddf1..0b4fd6c 100644 --- a/mig-wai/mig-wai.cabal +++ b/mig-wai/mig-wai.cabal @@ -7,21 +7,22 @@ cabal-version: 1.12 name: mig-wai version: 0.1.0.0 synopsis: Render mig-servers as wai-applications -description: Please see the README on GitHub at +description: Library to render mig-servers as WAI-applications. category: Web -homepage: https://github.com/githubuser/mig-wai#readme -bug-reports: https://github.com/githubuser/mig-wai/issues -author: Author name here -maintainer: example@example.com -copyright: 2023 Author name here +homepage: https://github.com/anton-k/mig#readme +bug-reports: https://github.com/anton-k/mig/issues +author: Anton Kholomiov +maintainer: anton.kholomiov@gmail.com +copyright: 2023 Anton Kholomiov license: BSD3 +license-file: LICENSE build-type: Simple extra-source-files: README.md source-repository head type: git - location: https://github.com/githubuser/mig-wai + location: https://github.com/anton-k/mig library exposed-modules: @@ -42,7 +43,7 @@ library , containers , data-default , exceptions - , mig + , mig >=0.2 , text , wai default-language: GHC2021 diff --git a/mig-wai/package.yaml b/mig-wai/package.yaml index ed0d541..5dd3371 100644 --- a/mig-wai/package.yaml +++ b/mig-wai/package.yaml @@ -1,10 +1,11 @@ name: mig-wai version: 0.1.0.0 -github: "githubuser/mig-wai" +github: "anton-k/mig" license: BSD3 -author: "Author name here" -maintainer: "example@example.com" -copyright: "2023 Author name here" +author: "Anton Kholomiov" +maintainer: "anton.kholomiov@gmail.com" +copyright: "2023 Anton Kholomiov" +license-file: LICENSE extra-source-files: - README.md @@ -16,7 +17,7 @@ category: Web # To avoid duplicated efforts in documentation and dealing with the # complications of embedding Haddock markup inside cabal files, it is # common to point users to the README.md file. -description: Please see the README on GitHub at +description: Library to render mig-servers as WAI-applications. language: GHC2021 @@ -30,7 +31,7 @@ dependencies: - base >= 4.7 && < 5 - bytestring - containers -- mig +- mig >= 0.2 - text - wai - exceptions diff --git a/mig-wai/src/Mig/Server/Wai.hs b/mig-wai/src/Mig/Server/Wai.hs index 11a90b4..76feb74 100644 --- a/mig-wai/src/Mig/Server/Wai.hs +++ b/mig-wai/src/Mig/Server/Wai.hs @@ -30,7 +30,9 @@ data ServerConfig = ServerConfig { maxBodySize :: Maybe Kilobytes -- ^ limit the request body size. By default it is unlimited. , cache :: Maybe CacheConfig + -- ^ LRU cache if needed (default is no cache) , findRoute :: FindRouteType + -- ^ API normal form and find route strategy (default is plain api finder) } instance Default ServerConfig where @@ -43,6 +45,11 @@ data FindRouteType | -- | no optimization (prefer it for small servers) PlainFinder +{-| Converts mig server to WAI-application. +Note that only IO-based servers are supported. To use custom monad +we can use @hoistServer@ function which renders monad to IO based or +the class @HasServer@ which defines such transformatio for several useful cases. +-} toApplication :: ServerConfig -> Server IO -> Wai.Application toApplication config = case config.cache of Just cacheConfig -> @@ -57,7 +64,7 @@ toApplication config = case config.cache of -- | Convert server to WAI-application toApplicationNoCache :: ServerConfig -> FindRoute nf IO -> Server IO -> Wai.Application toApplicationNoCache config findRoute server req procResponse = do - mResp <- handleError onErr (fromServer findRoute server) =<< fromRequest config.maxBodySize req + mResp <- handleServerError onErr (fromServer findRoute server) =<< fromRequest config.maxBodySize req procResponse $ toWaiResponse $ fromMaybe noResult mResp where noResult = badRequest @Text ("Server produces nothing" :: Text) @@ -69,7 +76,7 @@ toApplicationNoCache config findRoute server req procResponse = do toApplicationWithCache :: CacheConfig -> ServerConfig -> FindRoute nf IO -> Server IO -> Wai.Application toApplicationWithCache cacheConfig config findRoute server req procResponse = do cache <- newRouteCache cacheConfig - mResp <- handleError onErr (fromServerWithCache findRoute cache server) =<< fromRequest config.maxBodySize req + mResp <- handleServerError onErr (fromServerWithCache findRoute cache server) =<< fromRequest config.maxBodySize req procResponse $ toWaiResponse $ fromMaybe noResult mResp where noResult = badRequest @Text ("Server produces nothing" :: Text) diff --git a/mig/LICENSE b/mig/LICENSE new file mode 100644 index 0000000..4173eb7 --- /dev/null +++ b/mig/LICENSE @@ -0,0 +1,30 @@ +Copyright Anton Kholomiov (c) 2023 + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + * Neither the name of Author name here nor the names of other + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/mig/mig.cabal b/mig/mig.cabal index 6f22d13..90f44cc 100644 --- a/mig/mig.cabal +++ b/mig/mig.cabal @@ -7,16 +7,18 @@ cabal-version: 1.12 name: mig version: 0.2.0.0 synopsis: Build lightweight and composable servers -description: Core for the mig server library. +description: The Core for the mig server library. With library mig we can build lightweight and composable servers. There are only couple of combinators to assemble servers from parts. It supports generic handler functions as servant does. But strives to use more simple model for API. It does not go to describing Server API at type level which leads to simpler error messages. . - * quick start guide at + * quick start guide at . - * examples directory for more fun servers: at + * examples directory for more servers: at + . + * reference for the main functions: category: Web homepage: https://github.com/anton-k/mig#readme bug-reports: https://github.com/anton-k/mig/issues @@ -24,6 +26,7 @@ author: Anton Kholomiov maintainer: anton.kholomiov@gmail.com copyright: 2023 Anton Kholomiov license: BSD3 +license-file: LICENSE build-type: Simple extra-source-files: README.md diff --git a/mig/package.yaml b/mig/package.yaml index a06e40f..39fdbe0 100644 --- a/mig/package.yaml +++ b/mig/package.yaml @@ -5,6 +5,7 @@ license: BSD3 author: "Anton Kholomiov" maintainer: "anton.kholomiov@gmail.com" copyright: "2023 Anton Kholomiov" +license-file: LICENSE extra-source-files: - README.md @@ -17,16 +18,18 @@ category: Web # complications of embedding Haddock markup inside cabal files, it is # common to point users to the README.md file. description: | - Core for the mig server library. + The Core for the mig server library. With library mig we can build lightweight and composable servers. There are only couple of combinators to assemble servers from parts. It supports generic handler functions as servant does. But strives to use more simple model for API. It does not go to describing Server API at type level which leads to simpler error messages. . - * quick start guide at + * quick start guide at . - * examples directory for more fun servers: at + * examples directory for more servers: at + . + * reference for the main functions: default-extensions: - OverloadedStrings diff --git a/mig/src/Mig/Core.hs b/mig/src/Mig/Core.hs index 69ded9d..b28e8b5 100644 --- a/mig/src/Mig/Core.hs +++ b/mig/src/Mig/Core.hs @@ -12,9 +12,7 @@ The main features are: * expressive DSL to compose servers -* type-safe handlers - -* handlers are encoded with generic haskell functions +* type-safe handlers which are encoded with generic haskell functions * built on top of WAI and warp server libraries. @@ -31,26 +29,27 @@ Example of hello world server. To run example use library @mig-server@ which is > -- replies on a single route > server :: Server IO > server = -> "api" /. "v1" /. -> mconcat +> "api/v1" /. > [ "hello" /. hello > , "bye" /. bye > ] > > -- | Handler takes no inputs and marked as Get HTTP-request that returns Text. > hello :: Get (Resp Text) -> hello = Get $ pure $ ok "Hello World" +> hello = pure $ ok "Hello World" > -> -- | Handle with URL-param query and json body input as Post HTTP-request that returns Text. -> bye :: Query "name" Text -> Body ByeRequest -> Post (Resp Text) -> bye (Query name) (Body req) = Post $ -> pure $ ok $ "Bye to " <> name <> " " <> req.greeting +> -- | Handle with URL-param query and capture as Get HTTP-request that returns Text. +> bye :: Query "name" Text -> Capture "suffix" -> Post (Resp Text) +> bye (Query name) (Capture suffix) = +> pure $ ok $ "Bye to " <> name <> " " <> suffix References: -* quick start guide at +* quick start guide at + +* examples directory for more servers: at -* examples directory for more fun servers: at +* reference for the main functions: -} module Mig.Core ( module X, diff --git a/mig/src/Mig/Core/Api.hs b/mig/src/Mig/Core/Api.hs index 5c13652..8624df4 100644 --- a/mig/src/Mig/Core/Api.hs +++ b/mig/src/Mig/Core/Api.hs @@ -10,6 +10,7 @@ module Mig.Core.Api ( CaptureMap, flatApi, fromFlatApi, + MethodMap, OutputMediaMap (..), InputMediaMap (..), MediaMap (..), diff --git a/mig/src/Mig/Core/Api/NormalForm/TreeApi.hs b/mig/src/Mig/Core/Api/NormalForm/TreeApi.hs index edf6575..63c8bcf 100644 --- a/mig/src/Mig/Core/Api/NormalForm/TreeApi.hs +++ b/mig/src/Mig/Core/Api/NormalForm/TreeApi.hs @@ -20,12 +20,18 @@ import Mig.Core.Api (Api (..), Path (..), PathItem (..)) type CaptureMap = Map Text Text +{-| This form of API encodes path switch points as Map's so +it does not retraverse the routes and can find the right +branch on switch. In the plain api it tries the routes one by one +until it finds the right one. +-} data TreeApi a = WithStaticPath [Text] (TreeApi a) | WithCapturePath [Text] (TreeApi a) | SwitchApi (Maybe a) (Map Text (TreeApi a)) (Maybe (CaptureCase a)) deriving (Eq, Show, Functor) +-- | Capture case alternative data CaptureCase a = CaptureCase { name :: Text , api :: TreeApi a @@ -81,8 +87,8 @@ accumCapture !captures !names !path = [] -> Nothing ------------------------------------------------------------------------------------- --- convert to normal form +-- | Converts api to tree normal form toTreeApi :: Api a -> TreeApi a toTreeApi = joinPaths . \case diff --git a/mig/src/Mig/Core/Class/Route.hs b/mig/src/Mig/Core/Class/Route.hs index f7f7ddb..a00d085 100644 --- a/mig/src/Mig/Core/Class/Route.hs +++ b/mig/src/Mig/Core/Class/Route.hs @@ -1,6 +1,8 @@ {-# LANGUAGE UndecidableInstances #-} --- | Creation of routes from functions +{-| Creation of routes from functions. A route is a handler function +for single path of the server. +-} module Mig.Core.Class.Route ( Route (..), ToRoute (..), diff --git a/mig/src/Mig/Core/Server.hs b/mig/src/Mig/Core/Server.hs index a7a5491..785bb18 100644 --- a/mig/src/Mig/Core/Server.hs +++ b/mig/src/Mig/Core/Server.hs @@ -3,7 +3,7 @@ -- | Server definition module Mig.Core.Server ( Server (..), - FindRoute, + FindRoute (..), treeApiStrategy, plainApiStrategy, mapServerFun, @@ -59,11 +59,10 @@ Example: > server :: Server IO > server = -> "api" /. "v1" /. -> mconcat -> [ "foo" /. handleFoo -> , "bar" /. handleBar -> ] +> "api/v1" /. +> [ "foo" /. handleFoo +> , "bar" /. handleBar +> ] > > handleFoo :: Query "name" Int -> Get IO (Resp Json Text) > handleBar :: Post Json IO Text @@ -84,12 +83,12 @@ and other request types. To distinguish by HTTP-method we use corresponding constructors: Get, Post, Put, etc. Let's discuss the structure of the constructor. Let's take Get for example: -> type Get ty m a = Send GET ty m a -> newtype Send ty m a = Send (m a) +> type Get m a = Send GET m a +> newtype Send method m a = Send (m a) Let's look at the arguments of he type -* @ty@ - media-type of the response. it can be: Text, Html, Json, OctetStream, AnyMedia, FormUrlEncoded +* @method@ - type tag of the HTTP-method (GET, POST, PUT, DELETE, etc.) * @m@ - underlying server monad * @a@ - response type. It should be convertible to the type of the response (see @IsResp@ class). -} @@ -104,12 +103,17 @@ mapServerFun f (Server server) = Server $ fmap (\x -> Route x.info (f x.run)) se mapResponse :: (Functor m) => (Response -> Response) -> Server m -> Server m mapResponse f = mapServerFun $ \fun -> fmap (fmap f) . fun -data FindRoute nf m = FindRoute - { toNormalForm :: Api (Route m) -> nf (Route m) - , getPath :: [Text] -> nf (Route m) -> Maybe (Route m, Api.CaptureMap) +{-| API route finder strategy. The API can be transformed to some normal form +for faster route lookup. So far we have two normal forms. +One is plain Api type as it is. And another one is tree-structure +where path switches are encoded with Map's. +-} +data FindRoute normalForm m = FindRoute + { toNormalForm :: Api (Route m) -> normalForm (Route m) + , getPath :: [Text] -> normalForm (Route m) -> Maybe (Route m, Api.CaptureMap) } --- | Use TreeApi normal form +-- | Use TreeApi normal form. Path switches are encoded as Maps. treeApiStrategy :: FindRoute TreeApi.TreeApi m treeApiStrategy = FindRoute @@ -117,7 +121,7 @@ treeApiStrategy = , getPath = TreeApi.getPath } --- | Use plain api type +-- | Use plain api type. Just Api type as it is. plainApiStrategy :: FindRoute Api.Api m plainApiStrategy = FindRoute @@ -128,7 +132,7 @@ plainApiStrategy = {-| Converts server to server function. Server function can be used to implement low-level handlers in various server-libraries. -} -fromServer :: forall m nf. (Monad m) => FindRoute nf m -> Server m -> ServerFun m +fromServer :: forall m normalForm. (Monad m) => FindRoute normalForm m -> Server m -> ServerFun m fromServer strategy (Server server) = \req -> do case getRoute req of Just (routes, captureMap) -> routes.run req{capture = captureMap} @@ -146,7 +150,7 @@ fromServer strategy (Server server) = \req -> do in various server-libraries. This function also uses LRU-cache to cache fetching of the routes -} -fromServerWithCache :: forall m nf. (MonadIO m) => FindRoute nf m -> RouteCache m -> Server m -> ServerFun m +fromServerWithCache :: forall m normalForm. (MonadIO m) => FindRoute normalForm m -> RouteCache m -> Server m -> ServerFun m fromServerWithCache strategy cache (Server server) = \req -> do mRoute <- liftIO $ withCache cache getRouteCache (getCacheKey req) case mRoute of @@ -411,8 +415,9 @@ filterPath :: (Api.Path -> Bool) -> Server m -> Server m filterPath cond (Server a) = Server (Api.fromFlatApi $ filter (cond . fst) $ Api.flatApi a) +-- | Returns a list of all paths in the server getServerPaths :: Server m -> [Api.Path] -getServerPaths (Server a) = fmap fst $ Api.flatApi a +getServerPaths (Server a) = fmap fst $ Api.flatApi (fillCaptures a) {-| Links one route of the server to another so that every call to first path is redirected to the second path diff --git a/mig/src/Mig/Core/Server/Cache.hs b/mig/src/Mig/Core/Server/Cache.hs index 6091e73..cc18b33 100644 --- a/mig/src/Mig/Core/Server/Cache.hs +++ b/mig/src/Mig/Core/Server/Cache.hs @@ -18,33 +18,49 @@ import Mig.Core.Api (CaptureMap) import Mig.Core.Class.Route (Route) import Network.HTTP.Types.Method (Method) +-- | Cache config data CacheConfig = CacheConfig { size :: Int + -- ^ how many items are allowed in the cache , cacheFilter :: CacheKey -> Bool + -- ^ which route to cache } +-- | Route key identidfies the single item for caching data CacheKey = CacheKey { inputType :: ByteString + -- ^ value of "Content-Type" header , outputType :: ByteString + -- ^ value of "Accept" header , method :: Method + -- ^ http method , path :: [Text] + -- ^ path to route (includes inlined captures) } deriving (Show, Eq, Ord) +-- | Cache value data CacheValue m = CacheValue { captures :: CaptureMap + -- ^ extracted capture map from the path , route :: Route m + -- ^ route handler } +-- | Route cache data RouteCache m = RouteCache { cacheFilter :: CacheKey -> Bool + -- ^ which route to cache (if True the route is cached) , cache :: AtomicLRU CacheKey (CacheValue m) + -- ^ cache map } +-- | Allocates new cache newRouteCache :: CacheConfig -> IO (RouteCache m) newRouteCache config = RouteCache config.cacheFilter <$> Lru.newAtomicLRU (Just (fromIntegral config.size)) +-- | Caches the function of route finder withCache :: RouteCache m -> (CacheKey -> Maybe (CacheValue m)) -> CacheKey -> IO (Maybe (CacheValue m)) withCache (RouteCache cacheFilter cache) f key = do mCacheResult <- liftIO $ Lru.lookup key cache diff --git a/mig/src/Mig/Core/ServerFun.hs b/mig/src/Mig/Core/ServerFun.hs index ed4833b..1154918 100644 --- a/mig/src/Mig/Core/ServerFun.hs +++ b/mig/src/Mig/Core/ServerFun.hs @@ -5,7 +5,8 @@ The server is a function from @Request@ to @Response@. To use the mig library with some server library like wai/warp we need to provide conversion of type @ServerFun@ to the representarion of the given library. -We can convert mig server to @ServerFun@ with function @toServerFun@. +We can convert mig server to @ServerFun@ with function @fromServer@. +The @Maybe@ type in the result encodes missing routes. -} module Mig.Core.ServerFun ( ServerFun, @@ -20,7 +21,7 @@ module Mig.Core.ServerFun ( withOptionalHeader, withPathInfo, withFullPathInfo, - handleError, + handleServerError, ) where import Control.Monad @@ -151,10 +152,11 @@ withPathInfo act = \req -> act req.path req withFullPathInfo :: (Text -> ServerFun m) -> ServerFun m withFullPathInfo act = \req -> act (toFullPath req) req +-- | Runs response getter action and returns it for any input request sendResponse :: (Functor m) => m Response -> ServerFun m sendResponse act = const $ fmap Just act -- | Handle errors -handleError :: (Exception a, MonadCatch m) => (a -> ServerFun m) -> ServerFun m -> ServerFun m -handleError handler act = \req -> +handleServerError :: (Exception a, MonadCatch m) => (a -> ServerFun m) -> ServerFun m -> ServerFun m +handleServerError handler act = \req -> (act req) `catch` (\err -> handler err req) diff --git a/mig/src/Mig/Core/Types/Http.hs b/mig/src/Mig/Core/Types/Http.hs index 215dbc3..69c6a66 100644 --- a/mig/src/Mig/Core/Types/Http.hs +++ b/mig/src/Mig/Core/Types/Http.hs @@ -1,6 +1,6 @@ {-# LANGUAGE UndecidableInstances #-} --- | Core types and functions +-- | Core types and functions for HTTP module Mig.Core.Types.Http ( -- * types Request (..), From 24e5fdc112d062d4398e2223f91b4babadea7d38 Mon Sep 17 00:00:00 2001 From: Anton Kholomiov Date: Sat, 4 Nov 2023 18:14:12 +0300 Subject: [PATCH 2/2] Update todo --- todo.md | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/todo.md b/todo.md index f3bc6e0..4f25b2e 100644 --- a/todo.md +++ b/todo.md @@ -1,29 +1,7 @@ # TODO -Next release due: start of November. -Also see isssue for the new features to implement. Features, improvements and open problems for releases: -### Curent: v2 - -* add TH for deriving of all classes by type: `IsParam`, `IsBody`, `IsParaBody`, `IsReaderServer` - * deriveParam ''Type - * deriveBody ''Type -* try out Response as generic type a (without Resp) for special case of Json servers (use overlappable) -* support for LRU cache for routes (research on efficient route/api representation) - -## v2.2 - -add client support - -* write docs page on clients - -* add client examples - * generate client for JsonExample - * make example for client and server from the same definition: use Counter as example - * reuse JsonApi and hello-world - * client from scratch (without defined server) to test client-only apps - ## v3 * add capture all case