git.iko.soy/deriving-openapi3
on 1dfae52 help sign in
9 files changed+62−254
since splitsnapshots
unifiedsplit
.github/workflows/build.yaml−63 .gitignore+1 Changelog.md−3 LICENSE−21 deriving-openapi3.cabal−55 package.yaml+23 runDoctests.sh−9 src/Deriving/OpenApi.hs+34−102 stack.yaml+4−1
.github/workflows/build.yamldeleted−63
@@ -1,63 +0,0 @@
1−name: Build
2−
3−on:
4− push:
5− branches: [master]
6− pull_request:
7− branches: [master]
8− release:
9− types: [prereleased]
10−
11−jobs:
12− check_versions:
13− runs-on: ubuntu-latest
14− steps:
15− - uses: actions/checkout@v2
16−
17− - name: Check library version
18− if: github.event_name == 'release'
19− run: |
20− if [ "$(awk -F":" '{if ($1 == "version") {gsub(/ /, "", $2); print $2}}' *.cabal)" != "${{ github.event.release.tag_name }}" ]; then
21− echo "::error file=action.yaml::Library version does not match release version."
22− exit 1
23− fi
24− Linux:
25− needs: check_versions
26− runs-on: ubuntu-latest
27− steps:
28− - uses: actions/checkout@v2
29−
30− - name: Cache
31− uses: actions/cache@v1
32− with:
33− path: |
34− ~/.stack
35− .stack-work
36− key: ${{ runner.os }}-stack-${{ hashFiles('stack.yaml') }}-${{ hashFiles('**.cabal') }}
37− restore-keys: |
38− ${{ runner.os }}-stack-${{ hashFiles('stack.yaml') }}
39− ${{ runner.os }}-stack
40−
41− - name: Build
42− shell: bash
43− run: |
44− stack build
45−
46− - name: doctests
47− shell: bash
48− run: |
49− ./runDoctests.sh
50−
51− - name: Check Formatting
52− shell: bash
53− run: |
54− stack install ormolu
55− ormolu -o -XTypeApplications -o -XCPP -m check $(find src -type f -name "*.hs")
56−
57− - name: Publish candidate
58− shell: bash
59− if: github.event_name == 'release'
60− env:
61− HACKAGE_KEY: ${{ secrets.HACKAGE_TOKEN }}
62− run: |
63− stack upload --pvp-bounds both --test-tarball --candidate .
.gitignore+1
@@ -0,0 +1 @@
1+openapi3-deriving.cabal
Changelog.mddeleted−3
@@ -1,3 +0,0 @@
1−# 0.1.0.0
2−
3−Initial release
LICENSEdeleted−21
@@ -1,21 +0,0 @@
1−MIT License
2−
3−Copyright (c) 2022 Ilya Kostyuchenko
4−
5−Permission is hereby granted, free of charge, to any person obtaining a copy
6−of this software and associated documentation files (the "Software"), to deal
7−in the Software without restriction, including without limitation the rights
8−to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9−copies of the Software, and to permit persons to whom the Software is
10−furnished to do so, subject to the following conditions:
11−
12−The above copyright notice and this permission notice shall be included in all
13−copies or substantial portions of the Software.
14−
15−THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16−IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17−FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18−AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19−LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20−OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21−SOFTWARE.
deriving-openapi3.cabaldeleted−55
@@ -1,55 +0,0 @@
1−cabal-version: 1.12
2−
3−-- This file has been generated from package.yaml by hpack version 0.34.5.
4−--
5−-- see: https://github.com/sol/hpack
6−
7−name: deriving-openapi3
8−version: 0.1.0.0
9−synopsis: DerivingVia for OpenAPI 3
10−description: DerivingVia for OpenAPI 3
11−category: JSON, Generics, OpenAPI
12−homepage: https://github.com/ilyakooo0/deriving-openapi3#readme
13−bug-reports: https://github.com/ilyakooo0/deriving-openapi3/issues
14−maintainer: Ilya Kostyuchenko <ilyakooo0@gmail.com>
15−license: MIT
16−license-file: LICENSE
17−build-type: Simple
18−extra-source-files:
19− Changelog.md
20−
21−source-repository head
22− type: git
23− location: https://github.com/ilyakooo0/deriving-openapi3
24−
25−library
26− exposed-modules:
27− Deriving.OpenApi
28− other-modules:
29− Paths_deriving_openapi3
30− hs-source-dirs:
31− src
32− default-extensions:
33− AllowAmbiguousTypes
34− ScopedTypeVariables
35− KindSignatures
36− DataKinds
37− TypeOperators
38− FlexibleInstances
39− TypeApplications
40− UndecidableInstances
41− TypeFamilies
42− PolyKinds
43− OverloadedStrings
44− DerivingStrategies
45− DerivingVia
46− FlexibleContexts
47− ghc-options: -Weverything -Wno-star-is-type -Wno-unsafe -Wno-prepositive-qualified-module -Wno-safe -Wno-missing-safe-haskell-mode -Wno-missing-import-lists -Wno-implicit-prelude
48− build-depends:
49− aeson
50− , base >=4.12
51− , deriving-aeson >=0.2.2
52− , lens
53− , openapi3
54− , text
55− default-language: Haskell2010
package.yamlnew file+23
@@ -0,0 +1,23 @@
1+name: openapi3-deriving
2+version: 0.1.0
3+synopsis: DerivingVia for OpenAPI 3
4+# description:
5+maintainer: Ilya Kostyuchenko <ilyakooo0@gmail.com>
6+github: ilyakooo0/openapi3-deriving
7+ghc-options: -Wall
8+dependencies:
9+ - base
10+ - openapi3
11+ - deriving-aeson
12+ - aeson
13+library:
14+ source-dirs: src
15+default-extensions:
16+ - AllowAmbiguousTypes
17+ - ScopedTypeVariables
18+ - KindSignatures
19+ - DataKinds
20+ - TypeOperators
21+ - FlexibleInstances
22+ - TypeApplications
23+ - UndecidableInstances
runDoctests.shdeleted−9
@@ -1,9 +0,0 @@
1−#!/bin/sh
2−
3−set -e
4−
5−EXTENSIONS="-XAllowAmbiguousTypes -XScopedTypeVariables -XKindSignatures -XDataKinds -XTypeOperators -XFlexibleInstances -XTypeApplications -XUndecidableInstances -XTypeFamilies -XPolyKinds"
6−
7−DEPENDENCIES="--package doctest --package yaml"
8−
9−stack exec $DEPENDENCIES -- find src -name '*.hs' -exec doctest $EXTENSIONS '{}' \;
src/Deriving/OpenApi.hs+34−102
@@ -1,139 +1,71 @@
1−{-# LANGUAGE CPP #-}
21{-# OPTIONS_GHC -Wno-orphans #-}
32
4−-- | __Example:__
5−--
6−-- >>> :set -XDerivingStrategies -XDerivingVia -XDeriveGeneric
7−--
8−-- >>> :{
9−-- data User = User
10−-- { userFirstName :: String,
11−-- userAge :: Maybe Integer
12−-- }
13−-- deriving stock (Generic)
14−-- deriving (FromJSON, ToJSON, ToSchema)
15−-- via CustomJSON
16−-- '[FieldLabelModifier '[StripPrefix "user", CamelToSnake], RejectUnknownFields]
17−-- User
18−-- showYaml = Data.ByteString.Char8.putStr . Data.Yaml.encode
19−-- :}
20−--
21−-- >>> showYaml $ toSchema (Proxy :: Proxy User)
22−-- type: object
23−-- properties:
24−-- first_name:
25−-- type: string
26−-- age:
27−-- type: integer
28−-- required:
29−-- - first_name
30−-- additionalProperties: false
313module Deriving.OpenApi
324 ( CustomOpenApi,
335 DatatypeNameModifier,
34− ToSchema,
356 module Deriving.Aeson,
367 )
378where
389
39−import Control.Lens
4010import qualified Data.Aeson.Types as A
11+import Data.Kind
4112import Data.OpenApi
42−import Data.OpenApi.Internal.ParamSchema
4313import Data.OpenApi.Internal.Schema
4414import Data.Proxy
45−import Data.Text (Text)
46−import qualified Data.Text as T
47−import Data.Typeable
4815import Deriving.Aeson
4916import GHC.Generics
5017import GHC.TypeLits
5118
52−data Description (desc :: Symbol) = Description
53−
54−instance (AesonOptions xs) => AesonOptions (Description f ': xs) where
55− aesonOptions = aesonOptions @xs
56−
57−instance KnownSymbol t => OpenApiOptionModifier (Description t) where
58− openApiSchemaModifier = schema . description <>~ Just (toTextLine @t)
59−
60−toTextLine :: forall s. KnownSymbol s => Text
61−toTextLine = "\n\n" <> T.pack (symbolVal (Proxy @s))
62−
63−-- $setup
64−-- >>> import qualified Data.Yaml
65−-- >>> import qualified Data.ByteString.Char8
66−
6719type CustomOpenApi = CustomJSON
6820
69−instance
70− (OpenApiOptionModifier xs, GToSchema (Rep x), Generic x, Typeable x, Typeable xs, Typeable k) =>
71− ToSchema (CustomJSON (xs :: k) x)
72− where
73− declareNamedSchema Proxy =
74− openApiSchemaModifier @xs
75− <$> genericDeclareNamedSchema (openApiOptionsModifier @xs defaultSchemaOptions) (Proxy @x)
76−
77−instance
78− (OpenApiOptionModifier xs, GToParamSchema (Rep x), Generic x) =>
79− ToParamSchema (CustomJSON (xs :: k) x)
80− where
81− toParamSchema Proxy =
82− genericToParamSchema (openApiOptionsModifier @xs defaultSchemaOptions) (Proxy @x)
83−
84−class OpenApiOptionModifier x where
85− openApiOptionsModifier :: SchemaOptions -> SchemaOptions
86− openApiOptionsModifier = id
87− openApiSchemaModifier :: NamedSchema -> NamedSchema
88− openApiSchemaModifier = id
21+instance (OpenApiOptions xs, GToSchema (Rep x), Generic x) => ToSchema (CustomJSON xs x) where
22+ declareNamedSchema Proxy = genericDeclareNamedSchema (openApiOptions @xs) (Proxy @x)
8923
9024data DatatypeNameModifier t
9125
92−instance (StringModifier f) => OpenApiOptionModifier (DatatypeNameModifier f) where
93− openApiOptionsModifier o = o {datatypeNameModifier = getStringModifier @f}
94−
95−instance (AesonOptions xs) => AesonOptions (DatatypeNameModifier f ': xs) where
96− aesonOptions = aesonOptions @xs
26+instance (StringModifier f, OpenApiOptions xs) => OpenApiOptions (DatatypeNameModifier f ': xs) where
27+ openApiOptions = (openApiOptions @xs) {datatypeNameModifier = getStringModifier @f}
9728
9829-- deriving-aeson-based instances
9930
100−instance OpenApiOptionModifier UnwrapUnaryRecords where
101− openApiOptionsModifier o = o {unwrapUnaryRecords = True}
31+class OpenApiOptions (xs :: [Type]) where
32+ openApiOptions :: SchemaOptions
10233
103−instance OpenApiOptionModifier OmitNothingFields
34+instance OpenApiOptions '[] where
35+ openApiOptions = defaultSchemaOptions
10436
105−#if MIN_VERSION_deriving_aeson(0,2,4)
106−instance OpenApiOptionModifier RejectUnknownFields where
107− openApiSchemaModifier = schema . additionalProperties .~ Just (AdditionalPropertiesAllowed False)
108−#endif
37+instance OpenApiOptions xs => OpenApiOptions (UnwrapUnaryRecords ': xs) where
38+ openApiOptions = (openApiOptions @xs) {unwrapUnaryRecords = True}
10939
110−instance StringModifier f => OpenApiOptionModifier (FieldLabelModifier f) where
111− openApiOptionsModifier o = o {fieldLabelModifier = getStringModifier @f}
40+instance OpenApiOptions xs => OpenApiOptions (OmitNothingFields ': xs) where
41+ openApiOptions = openApiOptions @xs
11242
113−instance StringModifier f => OpenApiOptionModifier (ConstructorTagModifier f) where
114− openApiOptionsModifier o = o {constructorTagModifier = getStringModifier @f}
43+instance OpenApiOptions xs => OpenApiOptions (RejectUnknownFields ': xs) where
44+ openApiOptions = openApiOptions @xs
11545
116−instance
117− TypeError ('Text "deriving-openapi3 does not currently the `TagSingleConstructors` modifier.") =>
118− OpenApiOptionModifier TagSingleConstructors
46+instance (StringModifier f, OpenApiOptions xs) => OpenApiOptions (FieldLabelModifier f ': xs) where
47+ openApiOptions = (openApiOptions @xs) {fieldLabelModifier = getStringModifier @f}
11948
120−instance OpenApiOptionModifier NoAllNullaryToStringTag where
121− openApiOptionsModifier o = o {allNullaryToStringTag = False}
49+instance (StringModifier f, OpenApiOptions xs) => OpenApiOptions (ConstructorTagModifier f ': xs) where
50+ openApiOptions = (openApiOptions @xs) {constructorTagModifier = getStringModifier @f}
12251
123−instance (KnownSymbol t, KnownSymbol c) => OpenApiOptionModifier (SumTaggedObject t c) where
124− openApiOptionsModifier o = o {sumEncoding = A.TaggedObject (symbolVal (Proxy @t)) (symbolVal (Proxy @c))}
52+instance
53+ (OpenApiOptions xs, TypeError ( 'Text "openapi3-deriving does not currently the `TagSingleConstructors` modifier.")) =>
54+ OpenApiOptions (TagSingleConstructors ': xs)
55+ where
56+ openApiOptions = undefined
12557
126−instance OpenApiOptionModifier SumUntaggedValue where
127− openApiOptionsModifier o = o {sumEncoding = A.UntaggedValue}
58+instance OpenApiOptions xs => OpenApiOptions (NoAllNullaryToStringTag ': xs) where
59+ openApiOptions = (openApiOptions @xs) {allNullaryToStringTag = False}
12860
129−instance OpenApiOptionModifier SumObjectWithSingleField where
130− openApiOptionsModifier o = o {sumEncoding = A.ObjectWithSingleField}
61+instance (KnownSymbol t, KnownSymbol c, OpenApiOptions xs) => OpenApiOptions (SumTaggedObject t c ': xs) where
62+ openApiOptions = (openApiOptions @xs) {sumEncoding = A.TaggedObject (symbolVal (Proxy @t)) (symbolVal (Proxy @c))}
13163
132−instance OpenApiOptionModifier SumTwoElemArray where
133− openApiOptionsModifier o = o {sumEncoding = A.TwoElemArray}
64+instance (OpenApiOptions xs) => OpenApiOptions (SumUntaggedValue ': xs) where
65+ openApiOptions = (openApiOptions @xs) {sumEncoding = A.UntaggedValue}
13466
135−instance OpenApiOptionModifier '[]
67+instance (OpenApiOptions xs) => OpenApiOptions (SumObjectWithSingleField ': xs) where
68+ openApiOptions = (openApiOptions @xs) {sumEncoding = A.ObjectWithSingleField}
13669
137−instance (OpenApiOptionModifier x, OpenApiOptionModifier xs) => OpenApiOptionModifier (x ': xs) where
138− openApiOptionsModifier = openApiOptionsModifier @xs . openApiOptionsModifier @x
139− openApiSchemaModifier = openApiSchemaModifier @xs . openApiSchemaModifier @x
70+instance (OpenApiOptions xs) => OpenApiOptions (SumTwoElemArray ': xs) where
71+ openApiOptions = (openApiOptions @xs) {sumEncoding = A.TwoElemArray}
stack.yaml+4−1
@@ -1,4 +1,7 @@
1−resolver: lts-18.23
1+resolver: lts-16.26
22
33packages:
44 - .
5+
6+extra-deps:
7+ - openapi3-3.0.0.1@sha256:205669d14c5b48d2275e0bac41d78c9bfdb925ef9a8e3265662ce51a9e6d8139,4641