Skip to content

Commit 0911ebb

Browse files
authored
Merge pull request #2 from biocad/maksbotan/uverb
UVerb support
2 parents e1e3e76 + 2bcb46e commit 0911ebb

File tree

7 files changed

+201
-21
lines changed

7 files changed

+201
-21
lines changed

.travis.yml

+19-13
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
# This Travis job script has been generated by a script via
22
#
3-
# haskell-ci 'cabal.project'
3+
# haskell-ci 'cabal.project' '--config' 'cabal.haskell-ci'
44
#
55
# To regenerate the script (for example after adjusting tested-with) run
66
#
77
# haskell-ci regenerate
88
#
99
# For more information, see https://github.com/haskell-CI/haskell-ci
1010
#
11-
# version: 0.10.1
11+
# version: 0.10.3
1212
#
1313
version: ~> 1.0
1414
language: c
@@ -17,6 +17,9 @@ dist: xenial
1717
git:
1818
# whether to recursively clone submodules
1919
submodules: false
20+
branches:
21+
only:
22+
- master
2023
cache:
2124
directories:
2225
- $HOME/.cabal/packages
@@ -33,8 +36,11 @@ before_cache:
3336
- rm -rfv $CABALHOME/packages/head.hackage
3437
jobs:
3538
include:
36-
- compiler: ghc-8.8.3
37-
addons: {"apt":{"sources":[{"sourceline":"deb http://ppa.launchpad.net/hvr/ghc/ubuntu xenial main","key_url":"https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x063dab2bdc0b3f9fcebc378bff3aeacef6f88286"}],"packages":["ghc-8.8.3","cabal-install-3.2"]}}
39+
- compiler: ghc-8.10.2
40+
addons: {"apt":{"sources":[{"sourceline":"deb http://ppa.launchpad.net/hvr/ghc/ubuntu xenial main","key_url":"https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x063dab2bdc0b3f9fcebc378bff3aeacef6f88286"}],"packages":["ghc-8.10.2","cabal-install-3.2"]}}
41+
os: linux
42+
- compiler: ghc-8.8.4
43+
addons: {"apt":{"sources":[{"sourceline":"deb http://ppa.launchpad.net/hvr/ghc/ubuntu xenial main","key_url":"https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x063dab2bdc0b3f9fcebc378bff3aeacef6f88286"}],"packages":["ghc-8.8.4","cabal-install-3.2"]}}
3844
os: linux
3945
- compiler: ghc-8.6.5
4046
addons: {"apt":{"sources":[{"sourceline":"deb http://ppa.launchpad.net/hvr/ghc/ubuntu xenial main","key_url":"https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x063dab2bdc0b3f9fcebc378bff3aeacef6f88286"}],"packages":["ghc-8.6.5","cabal-install-3.2"]}}
@@ -96,10 +102,6 @@ install:
96102
- echo 'package example' >> cabal.project
97103
- "echo ' ghc-options: -Werror=missing-methods' >> cabal.project"
98104
- |
99-
echo "source-repository-package" >> cabal.project
100-
echo " type: git" >> cabal.project
101-
echo " location: https://github.com/biocad/openapi3/" >> cabal.project
102-
echo " tag: bd9df532f2381c4b22fe86ef722715088f5cfa68" >> cabal.project
103105
- "for pkg in $($HCPKG list --simple-output); do echo $pkg | sed 's/-[^-]*$//' | (grep -vE -- '^(example|servant-openapi3)$' || true) | sed 's/^/constraints: /' | sed 's/$/ installed/' >> cabal.project.local; done"
104106
- cat cabal.project || true
105107
- cat cabal.project.local || true
@@ -132,10 +134,6 @@ script:
132134
- echo 'package example' >> cabal.project
133135
- "echo ' ghc-options: -Werror=missing-methods' >> cabal.project"
134136
- |
135-
echo "source-repository-package" >> cabal.project
136-
echo " type: git" >> cabal.project
137-
echo " location: https://github.com/biocad/openapi3/" >> cabal.project
138-
echo " tag: bd9df532f2381c4b22fe86ef722715088f5cfa68" >> cabal.project
139137
- "for pkg in $($HCPKG list --simple-output); do echo $pkg | sed 's/-[^-]*$//' | (grep -vE -- '^(example|servant-openapi3)$' || true) | sed 's/^/constraints: /' | sed 's/$/ installed/' >> cabal.project.local; done"
140138
- cat cabal.project || true
141139
- cat cabal.project.local || true
@@ -155,6 +153,14 @@ script:
155153
# Building without installed constraints for packages in global-db...
156154
- rm -f cabal.project.local
157155
- ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks all
156+
# Constraint sets
157+
- rm -rf cabal.project.local
158+
# Constraint set servant-0.17
159+
- if [ $HCNUMVER -lt 81000 ] ; then ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks --constraint='servant ==0.17.*' all ; fi
160+
# Constraint set servant-0.18
161+
- if [ $HCNUMVER -ge 80800 ] ; then ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks --constraint='servant ==0.18' all ; fi
162+
# Constraint set servant-0.18.1
163+
- if [ $HCNUMVER -ge 80800 ] ; then ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks --constraint='servant ==0.18.1' all ; fi
158164

159-
# REGENDATA ("0.10.1",["cabal.project"])
165+
# REGENDATA ("0.10.3",["cabal.project","--config","cabal.haskell-ci"])
160166
# EOF

cabal.haskell-ci

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
branches: master
2+
3+
constraint-set servant-0.17
4+
ghc: >= 8.0 && <8.10
5+
constraints: servant ==0.17.*
6+
7+
constraint-set servant-0.18
8+
ghc: >= 8.8 && <8.12
9+
constraints: servant ==0.18
10+
11+
constraint-set servant-0.18.1
12+
ghc: >= 8.8 && <8.12
13+
constraints: servant ==0.18.1

cabal.project

-5
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,3 @@ packages:
22
servant-openapi3.cabal,
33
example/example.cabal
44
tests: true
5-
6-
source-repository-package
7-
type: git
8-
location: https://github.com/biocad/openapi3/
9-
tag: bd9df532f2381c4b22fe86ef722715088f5cfa68

example/example.cabal

+2-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ data-files:
1616
tested-with:
1717
GHC ==8.4.4
1818
|| ==8.6.5
19-
|| ==8.8.3
19+
|| ==8.8.4
20+
|| ==8.10.2
2021

2122
library
2223
ghc-options: -Wall

servant-openapi3.cabal

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: servant-openapi3
2-
version: 2.0.0.1
2+
version: 2.0.1.0
33
synopsis: Generate a Swagger/OpenAPI/OAS 3.0 specification for your servant API.
44
description:
55
Swagger is a project used to describe and document RESTful APIs. The core of the
@@ -31,7 +31,8 @@ cabal-version: 1.18
3131
tested-with:
3232
GHC ==8.4.4
3333
|| ==8.6.5
34-
|| ==8.8.3
34+
|| ==8.8.4
35+
|| ==8.10.2
3536

3637
extra-source-files:
3738
README.md

src/Servant/OpenApi/Internal.hs

+55
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,15 @@
1111
#if __GLASGOW_HASKELL__ >= 806
1212
{-# LANGUAGE UndecidableInstances #-}
1313
#endif
14+
{-# OPTIONS_GHC -Wno-orphans #-}
1415
module Servant.OpenApi.Internal where
1516

1617
import Prelude ()
1718
import Prelude.Compat
1819

20+
#if MIN_VERSION_servant(0,18,1)
21+
import Control.Applicative ((<|>))
22+
#endif
1923
import Control.Lens
2024
import Data.Aeson
2125
import Data.Foldable (toList)
@@ -183,6 +187,57 @@ instance OpenApiMethod 'OPTIONS where openApiMethod _ = options
183187
instance OpenApiMethod 'HEAD where openApiMethod _ = head_
184188
instance OpenApiMethod 'PATCH where openApiMethod _ = patch
185189

190+
#if MIN_VERSION_servant(0,18,1)
191+
instance HasOpenApi (UVerb method cs '[]) where
192+
toOpenApi _ = mempty
193+
194+
-- | @since <2.0.1.0>
195+
instance
196+
{-# OVERLAPPABLE #-}
197+
( ToSchema a,
198+
HasStatus a,
199+
AllAccept cs,
200+
OpenApiMethod method,
201+
HasOpenApi (UVerb method cs as)
202+
) =>
203+
HasOpenApi (UVerb method cs (a ': as))
204+
where
205+
toOpenApi _ =
206+
toOpenApi (Proxy :: Proxy (Verb method (StatusOf a) cs a))
207+
`combineSwagger` toOpenApi (Proxy :: Proxy (UVerb method cs as))
208+
where
209+
-- workaround for https://github.com/GetShopTV/swagger2/issues/218
210+
combinePathItem :: PathItem -> PathItem -> PathItem
211+
combinePathItem s t = PathItem
212+
{ _pathItemGet = _pathItemGet s <> _pathItemGet t
213+
, _pathItemPut = _pathItemPut s <> _pathItemPut t
214+
, _pathItemPost = _pathItemPost s <> _pathItemPost t
215+
, _pathItemDelete = _pathItemDelete s <> _pathItemDelete t
216+
, _pathItemOptions = _pathItemOptions s <> _pathItemOptions t
217+
, _pathItemHead = _pathItemHead s <> _pathItemHead t
218+
, _pathItemPatch = _pathItemPatch s <> _pathItemPatch t
219+
, _pathItemTrace = _pathItemTrace s <> _pathItemTrace t
220+
, _pathItemParameters = _pathItemParameters s <> _pathItemParameters t
221+
, _pathItemSummary = _pathItemSummary s <|> _pathItemSummary t
222+
, _pathItemDescription = _pathItemDescription s <|> _pathItemDescription t
223+
, _pathItemServers = _pathItemServers s <> _pathItemServers t
224+
}
225+
226+
combineSwagger :: OpenApi -> OpenApi -> OpenApi
227+
combineSwagger s t = OpenApi
228+
{ _openApiInfo = _openApiInfo s <> _openApiInfo t
229+
, _openApiServers = _openApiServers s <> _openApiServers t
230+
, _openApiPaths = InsOrdHashMap.unionWith combinePathItem (_openApiPaths s) (_openApiPaths t)
231+
, _openApiComponents = _openApiComponents s <> _openApiComponents t
232+
, _openApiSecurity = _openApiSecurity s <> _openApiSecurity t
233+
, _openApiTags = _openApiTags s <> _openApiTags t
234+
, _openApiExternalDocs = _openApiExternalDocs s <|> _openApiExternalDocs t
235+
}
236+
237+
instance ToSchema a => ToSchema (WithStatus s a) where
238+
declareNamedSchema _ = declareNamedSchema (Proxy :: Proxy a)
239+
#endif
240+
186241
instance {-# OVERLAPPABLE #-} (ToSchema a, AllAccept cs, KnownNat status, OpenApiMethod method) => HasOpenApi (Verb method status cs a) where
187242
toOpenApi _ = toOpenApi (Proxy :: Proxy (Verb method status cs (Headers '[] a)))
188243

test/Servant/OpenApiSpec.hs

+109
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
1+
{-# LANGUAGE CPP #-}
12
{-# LANGUAGE DataKinds #-}
23
{-# LANGUAGE DeriveDataTypeable #-}
34
{-# LANGUAGE DeriveGeneric #-}
45
{-# LANGUAGE OverloadedStrings #-}
56
{-# LANGUAGE QuasiQuotes #-}
67
{-# LANGUAGE TypeOperators #-}
78
{-# LANGUAGE PackageImports #-}
9+
#if MIN_VERSION_servant(0,18,1)
10+
{-# LANGUAGE TypeFamilies #-}
11+
#endif
812
module Servant.OpenApiSpec where
913

1014
import Control.Lens
@@ -37,6 +41,9 @@ spec = describe "HasOpenApi" $ do
3741
it "Comprehensive API" $ do
3842
let _x = toOpenApi comprehensiveAPI
3943
True `shouldBe` True -- type-level test
44+
#if MIN_VERSION_servant(0,18,1)
45+
it "UVerb API" $ checkOpenApi uverbOpenApi uverbAPI
46+
#endif
4047

4148
main :: IO ()
4249
main = hspec spec
@@ -418,3 +425,105 @@ getPostAPI = [aesonQQ|
418425
}
419426
|]
420427

428+
-- =======================================================================
429+
-- UVerb API
430+
-- =======================================================================
431+
432+
#if MIN_VERSION_servant(0,18,1)
433+
434+
data FisxUser = FisxUser {name :: String}
435+
deriving (Eq, Show, Generic)
436+
437+
instance ToSchema FisxUser
438+
439+
instance HasStatus FisxUser where
440+
type StatusOf FisxUser = 203
441+
442+
data ArianUser = ArianUser
443+
deriving (Eq, Show, Generic)
444+
445+
instance ToSchema ArianUser
446+
447+
type UVerbAPI = "fisx" :> UVerb 'GET '[JSON] '[FisxUser, WithStatus 303 String]
448+
:<|> "arian" :> UVerb 'POST '[JSON] '[WithStatus 201 ArianUser]
449+
450+
uverbOpenApi :: OpenApi
451+
uverbOpenApi = toOpenApi (Proxy :: Proxy UVerbAPI)
452+
453+
uverbAPI :: Value
454+
uverbAPI = [aesonQQ|
455+
{
456+
"openapi": "3.0.0",
457+
"info": {
458+
"version": "",
459+
"title": ""
460+
},
461+
"components": {
462+
"schemas": {
463+
"ArianUser": {
464+
"type": "string",
465+
"enum": [
466+
"ArianUser"
467+
]
468+
},
469+
"FisxUser": {
470+
"required": [
471+
"name"
472+
],
473+
"type": "object",
474+
"properties": {
475+
"name": {
476+
"type": "string"
477+
}
478+
}
479+
}
480+
}
481+
},
482+
"paths": {
483+
"/arian": {
484+
"post": {
485+
"responses": {
486+
"201": {
487+
"content": {
488+
"application/json;charset=utf-8": {
489+
"schema": {
490+
"$ref": "#/components/schemas/ArianUser"
491+
}
492+
}
493+
},
494+
"description": ""
495+
}
496+
}
497+
}
498+
},
499+
"/fisx": {
500+
"get": {
501+
"responses": {
502+
"303": {
503+
"content": {
504+
"application/json;charset=utf-8": {
505+
"schema": {
506+
"type": "string"
507+
}
508+
}
509+
},
510+
"description": ""
511+
},
512+
"203": {
513+
"content": {
514+
"application/json;charset=utf-8": {
515+
"schema": {
516+
"$ref": "#/components/schemas/FisxUser"
517+
}
518+
}
519+
},
520+
"description": ""
521+
}
522+
}
523+
}
524+
}
525+
}
526+
}
527+
|]
528+
529+
#endif

0 commit comments

Comments
 (0)