mirror of
https://github.com/aelve/guide.git
synced 2024-11-25 07:52:52 +03:00
Generate swagger.json for branches (#380)
* Generate swagger.json for branches * Fix * Fix again * Fix once more * [skip ci] Regenerate swagger.json * Don't log debug info to stdout * Push even if the branch is ahead * Fix * Empty * [skip ci] Regenerate swagger.json * Don't use "skip ci"
This commit is contained in:
parent
a100905cbf
commit
ce62066598
27
.travis.yml
27
.travis.yml
@ -47,10 +47,31 @@ jobs:
|
||||
- curl -sSL https://get.haskellstack.org/ | sh
|
||||
# travis_retry works around https://github.com/commercialhaskell/stack/issues/4888
|
||||
- travis_retry stack setup
|
||||
# Decrypt the GitHub deploy key (travis_key.enc)
|
||||
- openssl aes-256-cbc -k "$travis_key_password" -d -md sha256 -a -in travis_key.enc -out travis_key
|
||||
- echo "Host github.com" > ~/.ssh/config
|
||||
- echo " IdentityFile $(pwd)/travis_key" >> ~/.ssh/config
|
||||
- chmod 400 travis_key
|
||||
- git remote set-url origin git@github.com:aelve/guide.git
|
||||
script:
|
||||
# Build
|
||||
- stack --no-terminal build --test --no-run-tests --dependencies-only
|
||||
- stack --no-terminal build --test --no-run-tests
|
||||
# Regenerate Swagger and push to the same branch, even if the branch
|
||||
# is already ahead (which may happen if the previous build in the
|
||||
# pipeline also pushed to it)
|
||||
- |
|
||||
if [ "$TRAVIS_EVENT_TYPE" = "push" ]; then
|
||||
git checkout "$TRAVIS_BRANCH" && git pull
|
||||
stack exec -- guide api-docs > back/swagger.json
|
||||
git add back/swagger.json
|
||||
# Will only push if the commit was created successfully. Note
|
||||
# that we don't use "[skip ci]" in the commit message here
|
||||
# because then Mergify goes "oh, but Travis hasn't passed, so I
|
||||
# shouldn't merge this".
|
||||
(git commit -m "Regenerate swagger.json" && git push) || true
|
||||
git checkout "$TRAVIS_COMMIT"
|
||||
fi
|
||||
# Upload the Docker image
|
||||
- |
|
||||
if [ "$TRAVIS_EVENT_TYPE" = "push" ]; then
|
||||
@ -113,3 +134,9 @@ jobs:
|
||||
notifications:
|
||||
slack:
|
||||
secure: BgQpUYFmvXrf7HVBP/fefS/8UVwES800+fT+ufgJX8b2HMx2FvaWVsdv3ErKAryLE0B3fwmvforWugTdgLO3kq66YUgSt51SNQOBLkMVGubIoQsgvr3Ernu+Wpw1DyoMkXQH9q9O9rfCIc4IwkQCEHqu5SVRqdOd5px/CHFl/ktTI22JkT8ap/Be53qjlB2U2sWUf4GxYXq0V/gGF6fDwsUwTVKFb14RfSDrOgK5Vlce2GRf3gNr1C/j7A7EHIR/Z+rNd2hvv69cFw6TRc3s39QmP8XPe3SLZPIHTZ8vRveX1SZioMeEy747r5rHd9vylEjxWtVHhvP9fOt693+woXa8ZAl5uVRgB6S4mTWLZ+LAbqhaCmDGJYr9GrrBMoqWvJiMuBX3ZvHptsAc6O2l/fxZQU3otTE++SmHkhbyoDQkcPCjXPDUi/ZlnoLc5zfMAfApcsZZ8b9t47z12H0O4uDZd2YiNPiQJ1iUA6R879LH3pcxPB3RaoWsfXzv/klkKrU/V2K4SXD9j4/bmAFArlig+dar+Dm44L/a3/G7vbU1lQIa1bG0EqB36qgUS3UCkuy2ppti/JTHpkYx7HVF2BipoCjOVvfBl9G8RkvcQIhyuCfOGm7WL1TjrKVMccIEGJKhm7OO6wOZYCBfAI5zILxi8XEJAIvBm9NywhQlwxI=
|
||||
|
||||
# travis_key.enc decryption password, see:
|
||||
# http://markbucciarelli.com/posts/2019-01-26_how-to-push-to-github-from-travis-ci.html
|
||||
env:
|
||||
matrix:
|
||||
secure: RqWbR4JjNKL5+KS6um6R2xKRGMxYO4qxRdI/RFi5oHsjRwKAQPGATb00Zmd84V/jaVb+NOccG1qp0/SNVwAaFf6Yr1MO9aV3GKC0fDFwSffKLdtVO2yx/JHrRYZ2ymrjEkBGug1FMrHFGZsSLaZYsYKx974tBs04xwAmPv6Yby8bZTGJyknbuyIMeSe6XZIkinyKaw/Zay6QC2CtSLoX50gqY2/HX0fVBgWKDNStigH0UEEynSetRYm/PFRcoBwu3XmHW89Y9B0E/zgvnvFuAgMVmZSUZnD9wBRlnFvfC1pUaFZP8Tz2VZhcJ4xTRTq+r3t2IRTNQBIhlQseobQMxoTrv1Y/ZiEmO14CLSwy3yeX5vCiGmCaA3957FqnjTP07svbru/A0qWMyEtuBtpheCVbQSVvMUBnrl0txTMFiBb4dsJ7zWrp3f7RQ8SFB11FwpGv89d+FifOpXN42DWRXjU0fLCPs8S5iKODWkTSQ41vpGXXUoZdaUOUg1y6tZSoc8gs61KlhcDTrBI2ZCJMNY6c6JVE/BOnJrp6zqyKhY2znyJUodEvjPsy9iccmrt0bEZTVshzbW4Q9okQ26usNtwIJoHNDUdifpmMcobb/ATYEr+C7n9ztxRy9AnZLzp6SsCDlfSDDp32fr7762PMk+2jR9w7fMrR/PBMtDXNsYQ=
|
||||
|
@ -3,9 +3,11 @@ module Guide.Api
|
||||
module Guide.Api.Methods,
|
||||
module Guide.Api.Server,
|
||||
module Guide.Api.Types,
|
||||
module Guide.Api.Docs,
|
||||
)
|
||||
where
|
||||
|
||||
import Guide.Api.Methods
|
||||
import Guide.Api.Server
|
||||
import Guide.Api.Types
|
||||
import Guide.Api.Docs
|
||||
|
32
back/src/Guide/Api/Docs.hs
Normal file
32
back/src/Guide/Api/Docs.hs
Normal file
@ -0,0 +1,32 @@
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
|
||||
-- | Rendered documentation for the API.
|
||||
module Guide.Api.Docs
|
||||
(
|
||||
apiSwaggerDoc,
|
||||
apiSwaggerRendered,
|
||||
)
|
||||
where
|
||||
|
||||
import Imports
|
||||
|
||||
import Servant.Swagger (toSwagger)
|
||||
import Data.Swagger
|
||||
import qualified Data.Aeson.Encode.Pretty as AesonPretty
|
||||
|
||||
import Guide.Api.Types
|
||||
|
||||
----------------------------------------------------------------------------
|
||||
-- Swagger
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
-- | Swagger docs for the 'Api'.
|
||||
apiSwaggerDoc :: Swagger
|
||||
apiSwaggerDoc =
|
||||
toSwagger (Proxy @Api)
|
||||
& info.title .~ "Aelve Guide API"
|
||||
& info.version .~ "alpha"
|
||||
|
||||
-- | Pretty-printed @swagger.json@ for the 'Api'.
|
||||
apiSwaggerRendered :: Text
|
||||
apiSwaggerRendered = utf8ToText $ AesonPretty.encodePretty apiSwaggerDoc
|
@ -13,19 +13,18 @@ where
|
||||
|
||||
import Imports
|
||||
|
||||
import Data.Swagger.Lens hiding (format)
|
||||
import Network.Wai (Middleware, Request)
|
||||
import Network.Wai.Middleware.Cors (CorsResourcePolicy (..), corsOrigins,
|
||||
simpleCorsResourcePolicy)
|
||||
import Servant
|
||||
import Servant.API.Generic
|
||||
import Servant.Server.Generic
|
||||
import Servant.Swagger
|
||||
import Servant.Swagger.UI
|
||||
|
||||
import Guide.Api.Guider
|
||||
import Guide.Api.Methods
|
||||
import Guide.Api.Types
|
||||
import Guide.Api.Docs
|
||||
import Guide.Logger
|
||||
import Guide.Config
|
||||
import Guide.State
|
||||
@ -76,11 +75,13 @@ logException logger mbReq ex =
|
||||
-- Servant servers
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
-- Collect API and Swagger server to united 'FullApi'. First takes precedence in case of overlap.
|
||||
-- | Collect API and Swagger server to united 'FullApi'. First takes
|
||||
-- precedence in case of overlap.
|
||||
fullServer :: DB -> Logger -> Config -> Server FullApi
|
||||
fullServer db di config = apiServer db di config :<|> docServer
|
||||
|
||||
-- Collect api out of guiders and convert them to handlers. Type 'type Server api = ServerT api Handler' needed it.
|
||||
-- | Collect api out of guiders and convert them to handlers. Type 'type
|
||||
-- Server api = ServerT api Handler' needed it.
|
||||
apiServer :: DB -> Logger -> Config -> Server Api
|
||||
apiServer db di config = do
|
||||
requestDetails <- ask
|
||||
@ -89,11 +90,7 @@ apiServer db di config = do
|
||||
|
||||
-- | A 'Server' for Swagger docs.
|
||||
docServer :: Server (SwaggerSchemaUI "api" "swagger.json")
|
||||
docServer = swaggerSchemaUIServer doc
|
||||
where
|
||||
doc = toSwagger (Proxy @Api)
|
||||
& info.title .~ "Aelve Guide API"
|
||||
& info.version .~ "alpha"
|
||||
docServer = swaggerSchemaUIServer apiSwaggerDoc
|
||||
|
||||
----------------------------------------------------------------------------
|
||||
-- API handlers put together ('Site')
|
||||
|
@ -24,42 +24,10 @@ import qualified Options.Applicative as Opt
|
||||
|
||||
-- | All available commands
|
||||
data Command
|
||||
= RunServer -- ^ run server
|
||||
| DryRun -- ^ load database and exit
|
||||
| LoadPublic FilePath -- ^ load PublicDB, create base on it and exit
|
||||
|
||||
----------------------------------------------------------------------------
|
||||
-- Parsers
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
{-
|
||||
To see help run command:
|
||||
$ guide --help
|
||||
Usage: guide [-v|--version] [COMMAND]
|
||||
|
||||
Available options:
|
||||
-h,--help Show this help text
|
||||
-v,--version Show Guide version
|
||||
|
||||
Available commands:
|
||||
run Run server
|
||||
dry-run Load database and exit
|
||||
load-public Load PublicDB, create base on it and exit
|
||||
|
||||
NOTE:
|
||||
Command 'guide' is the same as 'guide run'
|
||||
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
$ guide load-public --help
|
||||
Usage: guide load-public (-p|--path FILEPATH)
|
||||
Load PublicDB, create base on it and exit
|
||||
|
||||
Available options:
|
||||
-h,--help Show this help text
|
||||
-p,--path FILEPATH Public DB file name
|
||||
|
||||
-}
|
||||
= RunServer -- ^ Run server
|
||||
| DryRun -- ^ Load database and exit
|
||||
| LoadPublic FilePath -- ^ Load PublicDB, create base on it and exit
|
||||
| ApiDocs -- ^ Show docs for the backend API
|
||||
|
||||
-- | Parse the command line of the application.
|
||||
--
|
||||
@ -72,10 +40,14 @@ parseCommandLine = Opt.execParser
|
||||
-- | All possible commands.
|
||||
commandsParser :: Parser Command
|
||||
commandsParser = Opt.subparser
|
||||
$ Opt.command "run" (infoP (pure RunServer) "Start server")
|
||||
<> Opt.command "dry-run" (infoP (pure DryRun) "Load database and exit")
|
||||
$ Opt.command "run"
|
||||
(infoP (pure RunServer) "Start server")
|
||||
<> Opt.command "dry-run"
|
||||
(infoP (pure DryRun) "Load database and exit")
|
||||
<> Opt.command "load-public"
|
||||
(infoP loadPublicParser "Load PublicDB, create base on it and exit")
|
||||
<> Opt.command "api-docs"
|
||||
(infoP (pure ApiDocs) "Show swagger.json for the backend API")
|
||||
where
|
||||
infoP parser desc = Opt.info (Opt.helper <*> parser) $ Opt.progDesc desc
|
||||
|
||||
|
@ -16,11 +16,10 @@ where
|
||||
|
||||
import Imports hiding ((.=))
|
||||
|
||||
-- JSON
|
||||
import Data.Aeson as Aeson
|
||||
import Data.Aeson.Encode.Pretty as Aeson hiding (Config)
|
||||
-- Default
|
||||
import Data.Default
|
||||
import Say (sayErr)
|
||||
|
||||
import Guide.Utils
|
||||
|
||||
@ -125,7 +124,7 @@ readConfig = do
|
||||
let filename = "config.json"
|
||||
exists <- doesFileExist filename
|
||||
unless exists $ do
|
||||
putStrLn "config.json doesn't exist, creating it"
|
||||
sayErr "config.json doesn't exist, creating it"
|
||||
BSL.writeFile filename (Aeson.encodePretty (def :: Config))
|
||||
contents <- toLazyByteString <$> BS.readFile filename
|
||||
case Aeson.eitherDecode' contents of
|
||||
|
@ -15,6 +15,7 @@ module Guide.Main
|
||||
runServer,
|
||||
dryRun,
|
||||
loadPublic,
|
||||
apiDocs,
|
||||
)
|
||||
where
|
||||
|
||||
@ -47,7 +48,7 @@ import System.Signal
|
||||
-- HVect
|
||||
import Data.HVect hiding (length)
|
||||
|
||||
import Guide.Api (runApiServer)
|
||||
import Guide.Api (runApiServer, apiSwaggerRendered)
|
||||
import Guide.App
|
||||
import Guide.Cli
|
||||
import Guide.Config
|
||||
@ -124,6 +125,7 @@ runCommand config = \case
|
||||
RunServer -> runServer config
|
||||
DryRun -> dryRun config
|
||||
LoadPublic path -> loadPublic config path
|
||||
ApiDocs -> apiDocs config
|
||||
|
||||
----------------------------------------------------------------------------
|
||||
-- Commands
|
||||
@ -167,6 +169,11 @@ loadPublic config path = withLogger config $ \logger ->
|
||||
logDebugIO logger "PublicDB imported to GlobalState"
|
||||
exitSuccess
|
||||
|
||||
-- | Dump API docs to the output.
|
||||
apiDocs :: Config -> IO ()
|
||||
apiDocs config = withLogger config $ \_logger ->
|
||||
T.putStrLn apiSwaggerRendered
|
||||
|
||||
----------------------------------------------------------------------------
|
||||
-- Helpers
|
||||
----------------------------------------------------------------------------
|
||||
|
1211
back/swagger.json
Normal file
1211
back/swagger.json
Normal file
File diff suppressed because it is too large
Load Diff
71
travis_key.enc
Normal file
71
travis_key.enc
Normal file
@ -0,0 +1,71 @@
|
||||
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
|
Loading…
Reference in New Issue
Block a user