From 2e3a6ec3eaac434006df9e8a18a92c2a8855c3f2 Mon Sep 17 00:00:00 2001 From: Owen Harvey Date: Wed, 20 Sep 2023 21:37:53 +1000 Subject: [PATCH] WPB-4240: Migrate from swagger2 to openapi3 (#3570) --------- Co-authored-by: Igor Ranieri Elland <54423+elland@users.noreply.github.com> Co-authored-by: Igor Ranieri --- changelog.d/4-docs/WPB-4240 | 1 + changelog.d/5-internal/WPB-4240 | 4 + libs/brig-types/brig-types.cabal | 2 +- libs/brig-types/default.nix | 4 +- .../test/unit/Test/Brig/Roundtrip.hs | 4 +- libs/deriving-swagger2/default.nix | 4 +- .../deriving-swagger2/deriving-swagger2.cabal | 4 +- .../deriving-swagger2/src/Deriving/Swagger.hs | 18 ++-- libs/extended/default.nix | 4 +- libs/extended/extended.cabal | 2 +- libs/extended/src/Servant/API/Extended.hs | 8 +- .../extended/src/Servant/API/Extended/RawM.hs | 6 +- libs/schema-profunctor/default.nix | 6 +- .../schema-profunctor/schema-profunctor.cabal | 4 +- libs/schema-profunctor/src/Data/Schema.hs | 34 +++--- .../test/unit/Test/Data/Schema.hs | 10 +- libs/types-common/default.nix | 4 +- libs/types-common/src/Data/Code.hs | 4 +- .../src/Data/CommaSeparatedList.hs | 6 +- libs/types-common/src/Data/Domain.hs | 2 +- libs/types-common/src/Data/Handle.hs | 2 +- libs/types-common/src/Data/Id.hs | 4 +- libs/types-common/src/Data/Json/Util.hs | 8 +- libs/types-common/src/Data/LegalHold.hs | 2 +- libs/types-common/src/Data/List1.hs | 6 +- libs/types-common/src/Data/Misc.hs | 10 +- libs/types-common/src/Data/Nonce.hs | 4 +- libs/types-common/src/Data/Qualified.hs | 14 ++- libs/types-common/src/Data/Range.hs | 13 ++- libs/types-common/src/Data/Text/Ascii.hs | 4 +- libs/types-common/types-common.cabal | 2 +- libs/wai-utilities/default.nix | 4 +- .../src/Network/Wai/Utilities/Headers.hs | 2 +- libs/wai-utilities/wai-utilities.cabal | 2 +- libs/wire-api-federation/default.nix | 4 +- .../src/Wire/API/Federation/Version.hs | 2 +- .../wire-api-federation.cabal | 2 +- libs/wire-api/default.nix | 10 +- libs/wire-api/src/Wire/API/Asset.hs | 6 +- libs/wire-api/src/Wire/API/Call/Config.hs | 2 +- libs/wire-api/src/Wire/API/Connection.hs | 4 +- libs/wire-api/src/Wire/API/Conversation.hs | 24 +++-- .../src/Wire/API/Conversation/Action.hs | 2 +- .../wire-api/src/Wire/API/Conversation/Bot.hs | 2 +- .../src/Wire/API/Conversation/Code.hs | 2 +- .../src/Wire/API/Conversation/Member.hs | 5 +- .../src/Wire/API/Conversation/Role.hs | 2 +- .../src/Wire/API/Conversation/Typing.hs | 2 +- libs/wire-api/src/Wire/API/CustomBackend.hs | 2 +- libs/wire-api/src/Wire/API/Deprecated.hs | 60 +++++++++++ libs/wire-api/src/Wire/API/Error.hs | 41 ++++--- libs/wire-api/src/Wire/API/Error/Brig.hs | 5 +- libs/wire-api/src/Wire/API/Error/Cannon.hs | 5 +- libs/wire-api/src/Wire/API/Error/Cargohold.hs | 5 +- libs/wire-api/src/Wire/API/Error/Empty.hs | 2 +- libs/wire-api/src/Wire/API/Error/Galley.hs | 34 ++++-- libs/wire-api/src/Wire/API/Error/Gundeck.hs | 5 +- .../src/Wire/API/Event/Conversation.hs | 7 +- .../src/Wire/API/Event/FeatureConfig.hs | 2 +- .../wire-api/src/Wire/API/Event/Federation.hs | 2 +- libs/wire-api/src/Wire/API/Event/Team.hs | 2 +- .../wire-api/src/Wire/API/FederationStatus.hs | 2 +- .../src/Wire/API/Internal/BulkPush.hs | 2 +- .../src/Wire/API/Internal/Notification.hs | 2 +- libs/wire-api/src/Wire/API/MLS/CipherSuite.hs | 4 +- .../wire-api/src/Wire/API/MLS/CommitBundle.hs | 2 +- libs/wire-api/src/Wire/API/MLS/Credential.hs | 6 +- libs/wire-api/src/Wire/API/MLS/Group.hs | 2 +- libs/wire-api/src/Wire/API/MLS/KeyPackage.hs | 2 +- libs/wire-api/src/Wire/API/MLS/Keys.hs | 2 +- libs/wire-api/src/Wire/API/MLS/Message.hs | 2 +- .../src/Wire/API/MLS/PublicGroupState.hs | 2 +- .../src/Wire/API/MLS/Serialisation.hs | 2 +- .../src/Wire/API/MLS/SubConversation.hs | 2 +- libs/wire-api/src/Wire/API/MLS/Welcome.hs | 2 +- .../src/Wire/API/MakesFederatedCall.hs | 48 +++++---- libs/wire-api/src/Wire/API/Message.hs | 6 +- libs/wire-api/src/Wire/API/Notification.hs | 4 +- libs/wire-api/src/Wire/API/OAuth.hs | 10 +- libs/wire-api/src/Wire/API/Properties.hs | 6 +- libs/wire-api/src/Wire/API/Provider/Bot.hs | 2 +- .../wire-api/src/Wire/API/Provider/Service.hs | 2 +- .../src/Wire/API/Provider/Service/Tag.hs | 36 ++++++- libs/wire-api/src/Wire/API/Push/V2/Token.hs | 4 +- libs/wire-api/src/Wire/API/RawJson.hs | 4 +- libs/wire-api/src/Wire/API/Routes/API.hs | 8 +- .../wire-api/src/Wire/API/Routes/AssetBody.hs | 6 +- libs/wire-api/src/Wire/API/Routes/Bearer.hs | 10 +- libs/wire-api/src/Wire/API/Routes/CSV.hs | 12 +++ libs/wire-api/src/Wire/API/Routes/Cookies.hs | 6 +- .../Wire/API/Routes/FederationDomainConfig.hs | 2 +- .../src/Wire/API/Routes/Internal/Brig.hs | 12 +-- .../API/Routes/Internal/Brig/Connection.hs | 2 +- .../src/Wire/API/Routes/Internal/Brig/EJPD.hs | 2 +- .../Wire/API/Routes/Internal/Brig/OAuth.hs | 2 +- .../API/Routes/Internal/Brig/SearchIndex.hs | 2 +- .../src/Wire/API/Routes/Internal/Cannon.hs | 8 +- .../src/Wire/API/Routes/Internal/Cargohold.hs | 8 +- .../src/Wire/API/Routes/Internal/Galley.hs | 8 +- .../Internal/Galley/ConversationsIntra.hs | 2 +- .../Galley/TeamFeatureNoConfigMulti.hs | 2 +- .../API/Routes/Internal/Galley/TeamsIntra.hs | 2 +- .../src/Wire/API/Routes/Internal/LegalHold.hs | 9 +- .../src/Wire/API/Routes/Internal/Spar.hs | 8 +- .../src/Wire/API/Routes/LowLevelStream.hs | 21 ++-- .../src/Wire/API/Routes/MultiTablePaging.hs | 9 +- .../Wire/API/Routes/MultiTablePaging/State.hs | 2 +- .../wire-api/src/Wire/API/Routes/MultiVerb.hs | 100 ++++++++++++------ libs/wire-api/src/Wire/API/Routes/Named.hs | 11 +- libs/wire-api/src/Wire/API/Routes/Public.hs | 41 +++---- .../src/Wire/API/Routes/Public/Brig.hs | 18 ++-- .../src/Wire/API/Routes/Public/Brig/Bot.hs | 2 +- .../src/Wire/API/Routes/Public/Brig/OAuth.hs | 2 +- .../src/Wire/API/Routes/Public/Cargohold.hs | 2 +- .../src/Wire/API/Routes/Public/Galley.hs | 2 +- .../src/Wire/API/Routes/Public/Galley/Bot.hs | 2 +- .../API/Routes/Public/Galley/Conversation.hs | 10 +- .../API/Routes/Public/Galley/CustomBackend.hs | 2 +- .../Wire/API/Routes/Public/Galley/Feature.hs | 2 +- .../API/Routes/Public/Galley/LegalHold.hs | 2 +- .../src/Wire/API/Routes/Public/Galley/MLS.hs | 3 +- .../API/Routes/Public/Galley/Messaging.hs | 2 +- .../src/Wire/API/Routes/Public/Galley/Team.hs | 2 +- .../Routes/Public/Galley/TeamConversation.hs | 2 +- .../API/Routes/Public/Galley/TeamMember.hs | 2 +- .../src/Wire/API/Routes/Public/Spar.hs | 8 +- .../src/Wire/API/Routes/Public/Util.hs | 2 +- .../src/Wire/API/Routes/QualifiedCapture.hs | 13 ++- libs/wire-api/src/Wire/API/Routes/Version.hs | 7 +- .../wire-api/src/Wire/API/Routes/Versioned.hs | 16 +-- .../wire-api/src/Wire/API/Routes/WebSocket.hs | 10 +- libs/wire-api/src/Wire/API/ServantProto.hs | 2 +- libs/wire-api/src/Wire/API/SwaggerHelper.hs | 66 +++++++++++- libs/wire-api/src/Wire/API/SwaggerServant.hs | 6 +- libs/wire-api/src/Wire/API/SystemSettings.hs | 4 +- libs/wire-api/src/Wire/API/Team.hs | 15 ++- .../src/Wire/API/Team/Conversation.hs | 2 +- libs/wire-api/src/Wire/API/Team/Feature.hs | 10 +- libs/wire-api/src/Wire/API/Team/Invitation.hs | 4 +- libs/wire-api/src/Wire/API/Team/LegalHold.hs | 6 +- .../src/Wire/API/Team/LegalHold/External.hs | 2 +- .../src/Wire/API/Team/LegalHold/Internal.hs | 2 +- libs/wire-api/src/Wire/API/Team/Member.hs | 10 +- libs/wire-api/src/Wire/API/Team/Permission.hs | 2 +- libs/wire-api/src/Wire/API/Team/Role.hs | 4 +- .../src/Wire/API/Team/SearchVisibility.hs | 2 +- libs/wire-api/src/Wire/API/Team/Size.hs | 2 +- libs/wire-api/src/Wire/API/Unreachable.hs | 2 +- libs/wire-api/src/Wire/API/User.hs | 8 +- libs/wire-api/src/Wire/API/User/Activation.hs | 4 +- libs/wire-api/src/Wire/API/User/Auth.hs | 4 +- .../src/Wire/API/User/Auth/LegalHold.hs | 2 +- .../wire-api/src/Wire/API/User/Auth/ReAuth.hs | 2 +- libs/wire-api/src/Wire/API/User/Auth/Sso.hs | 2 +- libs/wire-api/src/Wire/API/User/Client.hs | 6 +- .../Wire/API/User/Client/DPoPAccessToken.hs | 4 +- .../src/Wire/API/User/Client/Prekey.hs | 2 +- libs/wire-api/src/Wire/API/User/Handle.hs | 2 +- libs/wire-api/src/Wire/API/User/Identity.hs | 6 +- .../src/Wire/API/User/IdentityProvider.hs | 10 +- libs/wire-api/src/Wire/API/User/Orphans.hs | 10 +- libs/wire-api/src/Wire/API/User/Password.hs | 4 +- libs/wire-api/src/Wire/API/User/Profile.hs | 2 +- libs/wire-api/src/Wire/API/User/RichInfo.hs | 2 +- libs/wire-api/src/Wire/API/User/Saml.hs | 2 +- libs/wire-api/src/Wire/API/User/Scim.hs | 10 +- libs/wire-api/src/Wire/API/User/Search.hs | 8 +- libs/wire-api/src/Wire/API/UserMap.hs | 4 +- libs/wire-api/src/Wire/API/Wrapped.hs | 4 +- .../unit/Test/Wire/API/Roundtrip/Aeson.hs | 4 +- .../test/unit/Test/Wire/API/Swagger.hs | 4 +- libs/wire-api/wire-api.cabal | 7 +- nix/haskell-pins.nix | 16 +-- nix/manual-overrides.nix | 2 +- services/brig/brig.cabal | 4 +- services/brig/default.nix | 8 +- services/brig/src/Brig/API/Internal.hs | 2 +- services/brig/src/Brig/API/Public.hs | 7 +- services/brig/src/Brig/API/Public/Swagger.hs | 14 ++- services/brig/src/Brig/User/EJPD.hs | 2 +- services/spar/default.nix | 8 +- services/spar/spar.cabal | 4 +- services/spar/test/Arbitrary.hs | 2 +- services/spar/test/Test/Spar/APISpec.hs | 2 +- tools/fedcalls/default.nix | 10 +- tools/fedcalls/fedcalls.cabal | 7 +- tools/fedcalls/src/Main.hs | 88 ++++++++------- tools/stern/default.nix | 8 +- tools/stern/src/Stern/API/Routes.hs | 8 +- tools/stern/src/Stern/Types.hs | 8 +- tools/stern/stern.cabal | 4 +- 191 files changed, 890 insertions(+), 579 deletions(-) create mode 100644 changelog.d/4-docs/WPB-4240 create mode 100644 changelog.d/5-internal/WPB-4240 create mode 100644 libs/wire-api/src/Wire/API/Deprecated.hs diff --git a/changelog.d/4-docs/WPB-4240 b/changelog.d/4-docs/WPB-4240 new file mode 100644 index 00000000000..d7dd76196ec --- /dev/null +++ b/changelog.d/4-docs/WPB-4240 @@ -0,0 +1 @@ +Updating the route documentation from Swagger 2 to OpenAPI 3. \ No newline at end of file diff --git a/changelog.d/5-internal/WPB-4240 b/changelog.d/5-internal/WPB-4240 new file mode 100644 index 00000000000..bca7dcb1fc6 --- /dev/null +++ b/changelog.d/5-internal/WPB-4240 @@ -0,0 +1,4 @@ +Updating the route documentation library from swagger2 to openapi3. + +This also introduced a breaking change in how we track what federation calls each route makes. +The openapi3 library doesn't support extension fields, and as such tags are being used instead in a similar way. \ No newline at end of file diff --git a/libs/brig-types/brig-types.cabal b/libs/brig-types/brig-types.cabal index fb0afa4af77..faac2030515 100644 --- a/libs/brig-types/brig-types.cabal +++ b/libs/brig-types/brig-types.cabal @@ -156,8 +156,8 @@ test-suite brig-types-tests , brig-types , bytestring-conversion >=0.3.1 , imports + , openapi3 , QuickCheck >=2.9 - , swagger2 >=2.5 , tasty , tasty-hunit , tasty-quickcheck diff --git a/libs/brig-types/default.nix b/libs/brig-types/default.nix index 49028b0de48..173b83591b0 100644 --- a/libs/brig-types/default.nix +++ b/libs/brig-types/default.nix @@ -13,8 +13,8 @@ , gitignoreSource , imports , lib +, openapi3 , QuickCheck -, swagger2 , tasty , tasty-hunit , tasty-quickcheck @@ -47,8 +47,8 @@ mkDerivation { base bytestring-conversion imports + openapi3 QuickCheck - swagger2 tasty tasty-hunit tasty-quickcheck diff --git a/libs/brig-types/test/unit/Test/Brig/Roundtrip.hs b/libs/brig-types/test/unit/Test/Brig/Roundtrip.hs index 9ea421c6c2f..13cfc3570e6 100644 --- a/libs/brig-types/test/unit/Test/Brig/Roundtrip.hs +++ b/libs/brig-types/test/unit/Test/Brig/Roundtrip.hs @@ -20,7 +20,7 @@ module Test.Brig.Roundtrip where import Data.Aeson (FromJSON, ToJSON, parseJSON, toJSON) import Data.Aeson.Types (parseEither) import Data.ByteString.Conversion -import Data.Swagger (ToSchema, validatePrettyToJSON) +import Data.OpenApi (ToSchema, validatePrettyToJSON) import Imports import Test.Tasty (TestTree) import Test.Tasty.QuickCheck (Arbitrary, counterexample, testProperty, (.&&.), (===)) @@ -40,7 +40,7 @@ testRoundTrip = testProperty msg trip testRoundTripWithSwagger :: forall a. - (Arbitrary a, Typeable a, ToJSON a, FromJSON a, ToSchema a, Eq a, Show a) => + (Arbitrary a, ToJSON a, FromJSON a, ToSchema a, Eq a, Show a) => TestTree testRoundTripWithSwagger = testProperty msg (trip .&&. scm) where diff --git a/libs/deriving-swagger2/default.nix b/libs/deriving-swagger2/default.nix index fdf39de254a..5359dbec579 100644 --- a/libs/deriving-swagger2/default.nix +++ b/libs/deriving-swagger2/default.nix @@ -8,12 +8,12 @@ , gitignoreSource , imports , lib -, swagger2 +, openapi3 }: mkDerivation { pname = "deriving-swagger2"; version = "0.1.0"; src = gitignoreSource ./.; - libraryHaskellDepends = [ base extra imports swagger2 ]; + libraryHaskellDepends = [ base extra imports openapi3 ]; license = lib.licenses.agpl3Only; } diff --git a/libs/deriving-swagger2/deriving-swagger2.cabal b/libs/deriving-swagger2/deriving-swagger2.cabal index 4d68184d8c4..6e5b3f9de4a 100644 --- a/libs/deriving-swagger2/deriving-swagger2.cabal +++ b/libs/deriving-swagger2/deriving-swagger2.cabal @@ -62,9 +62,9 @@ library -Wredundant-constraints -Wunused-packages build-depends: - base >=4 && <5 + base >=4 && <5 , extra , imports - , swagger2 >=0.6 + , openapi3 default-language: GHC2021 diff --git a/libs/deriving-swagger2/src/Deriving/Swagger.hs b/libs/deriving-swagger2/src/Deriving/Swagger.hs index 3f0fc3b56f9..95a0c121a3e 100644 --- a/libs/deriving-swagger2/src/Deriving/Swagger.hs +++ b/libs/deriving-swagger2/src/Deriving/Swagger.hs @@ -1,3 +1,5 @@ +{-# LANGUAGE RankNTypes #-} + -- This file is part of the Wire Server implementation. -- -- Copyright (C) 2022 Wire Swiss GmbH @@ -22,10 +24,10 @@ module Deriving.Swagger where import Data.Char qualified as Char import Data.Kind (Constraint) import Data.List.Extra (stripSuffix) +import Data.OpenApi.Internal.Schema (GToSchema) +import Data.OpenApi.Internal.TypeShape +import Data.OpenApi.Schema import Data.Proxy (Proxy (..)) -import Data.Swagger (SchemaOptions, ToSchema (..), constructorTagModifier, defaultSchemaOptions, fieldLabelModifier, genericDeclareNamedSchema) -import Data.Swagger.Internal.Schema (GToSchema) -import Data.Swagger.Internal.TypeShape (TypeHasSimpleShape) import GHC.Generics (Generic (Rep)) import GHC.TypeLits (ErrorMessage (Text), KnownSymbol, Symbol, TypeError, symbolVal) import Imports @@ -81,6 +83,7 @@ import Imports -- | A newtype wrapper which gives ToSchema instances with modified options. -- 't' has to have an instance of the 'SwaggerOptions' class. newtype CustomSwagger t a = CustomSwagger {unCustomSwagger :: a} + deriving (Generic, Typeable) class SwaggerOptions xs where swaggerOptions :: SchemaOptions @@ -94,14 +97,7 @@ instance (StringModifier f, SwaggerOptions xs) => SwaggerOptions (FieldLabelModi instance (StringModifier f, SwaggerOptions xs) => SwaggerOptions (ConstructorTagModifier f ': xs) where swaggerOptions = (swaggerOptions @xs) {constructorTagModifier = getStringModifier @f} -instance - ( SwaggerOptions t, - Generic a, - GToSchema (Rep a), - TypeHasSimpleShape a "genericDeclareNamedSchemaUnrestricted" - ) => - ToSchema (CustomSwagger t a) - where +instance (SwaggerOptions t, Generic a, Typeable a, GToSchema (Rep a), Typeable (CustomSwagger t a), TypeHasSimpleShape a "genericDeclareNamedSchemaUnrestricted") => ToSchema (CustomSwagger t a) where declareNamedSchema _ = genericDeclareNamedSchema (swaggerOptions @t) (Proxy @a) -- ** Specify __what__ to modify diff --git a/libs/extended/default.nix b/libs/extended/default.nix index d2fd00ab9cb..b44a955a35f 100644 --- a/libs/extended/default.nix +++ b/libs/extended/default.nix @@ -27,8 +27,8 @@ , servant , servant-client , servant-client-core +, servant-openapi3 , servant-server -, servant-swagger , temporary , text , tinylog @@ -60,8 +60,8 @@ mkDerivation { servant servant-client servant-client-core + servant-openapi3 servant-server - servant-swagger text tinylog unliftio diff --git a/libs/extended/extended.cabal b/libs/extended/extended.cabal index 2271f8d1312..389b59b9447 100644 --- a/libs/extended/extended.cabal +++ b/libs/extended/extended.cabal @@ -98,8 +98,8 @@ library , servant , servant-client , servant-client-core + , servant-openapi3 , servant-server - , servant-swagger , text , tinylog , unliftio diff --git a/libs/extended/src/Servant/API/Extended.hs b/libs/extended/src/Servant/API/Extended.hs index 322b029f1b4..c1e87f38beb 100644 --- a/libs/extended/src/Servant/API/Extended.hs +++ b/libs/extended/src/Servant/API/Extended.hs @@ -31,8 +31,8 @@ import Network.Wai import Servant.API import Servant.API.ContentTypes import Servant.API.Modifiers +import Servant.OpenApi import Servant.Server.Internal -import Servant.Swagger import Prelude () -- | Like 'ReqBody'', but takes parsers that throw 'ServerError', not 'String'. @tag@ is used @@ -108,10 +108,10 @@ instance Right v -> pure v instance - HasSwagger (ReqBody' '[Required, Strict] cts a :> api) => - HasSwagger (ReqBodyCustomError cts tag a :> api) + HasOpenApi (ReqBody' '[Required, Strict] cts a :> api) => + HasOpenApi (ReqBodyCustomError cts tag a :> api) where - toSwagger Proxy = toSwagger (Proxy @(ReqBody' '[Required, Strict] cts a :> api)) + toOpenApi Proxy = toOpenApi (Proxy @(ReqBody' '[Required, Strict] cts a :> api)) instance RoutesToPaths rest => RoutesToPaths (ReqBodyCustomError' mods list tag a :> rest) where getRoutes = getRoutes @rest diff --git a/libs/extended/src/Servant/API/Extended/RawM.hs b/libs/extended/src/Servant/API/Extended/RawM.hs index 9f1e1a6395f..f5108d12329 100644 --- a/libs/extended/src/Servant/API/Extended/RawM.hs +++ b/libs/extended/src/Servant/API/Extended/RawM.hs @@ -10,11 +10,11 @@ import Data.Proxy import Imports import Network.Wai import Servant.API (Raw) +import Servant.OpenApi import Servant.Server hiding (respond) import Servant.Server.Internal.Delayed import Servant.Server.Internal.RouteResult import Servant.Server.Internal.Router -import Servant.Swagger type ApplicationM m = Request -> (Response -> IO ResponseReceived) -> m ResponseReceived @@ -51,8 +51,8 @@ instance HasServer RawM context where hoistServerWithContext _ _ f srvM req respond = f (srvM req respond) -instance HasSwagger RawM where - toSwagger _ = toSwagger (Proxy @Raw) +instance HasOpenApi RawM where + toOpenApi _ = toOpenApi (Proxy @Raw) instance RoutesToPaths RawM where getRoutes = [] diff --git a/libs/schema-profunctor/default.nix b/libs/schema-profunctor/default.nix index a498d97378b..bede1bdeae6 100644 --- a/libs/schema-profunctor/default.nix +++ b/libs/schema-profunctor/default.nix @@ -14,8 +14,8 @@ , insert-ordered-containers , lens , lib +, openapi3 , profunctors -, swagger2 , tasty , tasty-hunit , text @@ -34,8 +34,8 @@ mkDerivation { containers imports lens + openapi3 profunctors - swagger2 text transformers vector @@ -47,7 +47,7 @@ mkDerivation { imports insert-ordered-containers lens - swagger2 + openapi3 tasty tasty-hunit text diff --git a/libs/schema-profunctor/schema-profunctor.cabal b/libs/schema-profunctor/schema-profunctor.cabal index c9c534c0165..236a68a841b 100644 --- a/libs/schema-profunctor/schema-profunctor.cabal +++ b/libs/schema-profunctor/schema-profunctor.cabal @@ -69,8 +69,8 @@ library , containers , imports , lens + , openapi3 , profunctors - , swagger2 >=2 && <2.9 , text , transformers , vector @@ -139,8 +139,8 @@ test-suite schemas-tests , imports , insert-ordered-containers , lens + , openapi3 , schema-profunctor - , swagger2 , tasty , tasty-hunit , text diff --git a/libs/schema-profunctor/src/Data/Schema.hs b/libs/schema-profunctor/src/Data/Schema.hs index 6ff30f7ed38..9ae1187481f 100644 --- a/libs/schema-profunctor/src/Data/Schema.hs +++ b/libs/schema-profunctor/src/Data/Schema.hs @@ -100,12 +100,11 @@ import Data.List.NonEmpty (NonEmpty) import Data.List.NonEmpty qualified as NonEmpty import Data.Map qualified as Map import Data.Monoid hiding (Product) +import Data.OpenApi qualified as S +import Data.OpenApi.Declare qualified as S import Data.Profunctor (Star (..)) import Data.Proxy (Proxy (..)) import Data.Set qualified as Set -import Data.Swagger qualified as S -import Data.Swagger.Declare qualified as S -import Data.Swagger.Internal qualified as S import Data.Text qualified as T import Data.Text.Lazy qualified as TL import Data.Vector qualified as V @@ -624,7 +623,7 @@ text name = (A.withText (T.unpack name) pure) (pure . A.String) where - d = mempty & S.type_ ?~ S.SwaggerString + d = mempty & S.type_ ?~ S.OpenApiString -- | A schema for a textual value with possible failure. parsedText :: @@ -764,7 +763,7 @@ instance HasSchemaRef doc => HasField doc SwaggerDoc where where f ref = mempty - & S.type_ ?~ S.SwaggerObject + & S.type_ ?~ S.OpenApiObject & S.properties . at name ?~ ref & S.required .~ [name] @@ -780,8 +779,8 @@ instance HasSchemaRef ndoc => HasArray ndoc SwaggerDoc where f :: S.Referenced S.Schema -> S.Schema f ref = mempty - & S.type_ ?~ S.SwaggerArray - & S.items ?~ S.SwaggerItemsObject ref + & S.type_ ?~ S.OpenApiArray + & S.items ?~ S.OpenApiItemsObject ref instance HasSchemaRef ndoc => HasMap ndoc SwaggerDoc where mkMap = fmap f . schemaRef @@ -789,7 +788,7 @@ instance HasSchemaRef ndoc => HasMap ndoc SwaggerDoc where f :: S.Referenced S.Schema -> S.Schema f ref = mempty - & S.type_ ?~ S.SwaggerObject + & S.type_ ?~ S.OpenApiObject & S.additionalProperties ?~ S.AdditionalPropertiesSchema ref class HasMinItems s a where @@ -799,19 +798,19 @@ instance HasMinItems SwaggerDoc (Maybe Integer) where minItems = declared . S.minItems instance HasEnum Text NamedSwaggerDoc where - mkEnum = mkSwaggerEnum S.SwaggerString + mkEnum = mkSwaggerEnum S.OpenApiString instance HasEnum Integer NamedSwaggerDoc where - mkEnum = mkSwaggerEnum S.SwaggerInteger + mkEnum = mkSwaggerEnum S.OpenApiInteger instance HasEnum Natural NamedSwaggerDoc where - mkEnum = mkSwaggerEnum S.SwaggerInteger + mkEnum = mkSwaggerEnum S.OpenApiInteger instance HasEnum Bool NamedSwaggerDoc where - mkEnum = mkSwaggerEnum S.SwaggerBoolean + mkEnum = mkSwaggerEnum S.OpenApiBoolean mkSwaggerEnum :: - S.SwaggerType 'S.SwaggerKindSchema -> + S.OpenApiType -> Text -> [A.Value] -> NamedSwaggerDoc @@ -839,11 +838,12 @@ class ToSchema a where -- Newtype wrappers for deriving via newtype Schema a = Schema {getSchema :: a} + deriving (Generic) schemaToSwagger :: forall a. ToSchema a => Proxy a -> Declare S.NamedSchema schemaToSwagger _ = runDeclare (schemaDoc (schema @a)) -instance ToSchema a => S.ToSchema (Schema a) where +instance (Typeable a, ToSchema a) => S.ToSchema (Schema a) where declareNamedSchema _ = schemaToSwagger (Proxy @a) -- | JSON serialiser for an instance of 'ToSchema'. @@ -920,8 +920,14 @@ instance S.HasSchema d S.Schema => S.HasSchema (SchemaP d v w a b) S.Schema wher instance S.HasDescription NamedSwaggerDoc (Maybe Text) where description = declared . S.schema . S.description +instance S.HasDeprecated NamedSwaggerDoc (Maybe Bool) where + deprecated = declared . S.schema . S.deprecated + instance {-# OVERLAPPABLE #-} S.HasDescription s a => S.HasDescription (WithDeclare s) a where description = declared . S.description +instance {-# OVERLAPPABLE #-} S.HasDeprecated s a => S.HasDeprecated (WithDeclare s) a where + deprecated = declared . S.deprecated + instance {-# OVERLAPPABLE #-} S.HasExample s a => S.HasExample (WithDeclare s) a where example = declared . S.example diff --git a/libs/schema-profunctor/test/unit/Test/Data/Schema.hs b/libs/schema-profunctor/test/unit/Test/Data/Schema.hs index 5ee7af68a77..d29b69b2365 100644 --- a/libs/schema-profunctor/test/unit/Test/Data/Schema.hs +++ b/libs/schema-profunctor/test/unit/Test/Data/Schema.hs @@ -27,10 +27,10 @@ import Data.Aeson.QQ import Data.Aeson.Types qualified as A import Data.HashMap.Strict.InsOrd qualified as InsOrdHashMap import Data.List.NonEmpty (NonEmpty ((:|))) +import Data.OpenApi qualified as S +import Data.OpenApi.Declare qualified as S import Data.Proxy import Data.Schema hiding (getName) -import Data.Swagger qualified as S -import Data.Swagger.Declare qualified as S import Data.Text qualified as Text import Imports import Test.Tasty @@ -290,7 +290,7 @@ testNonEmptySchema = Nothing -> assertFailure "expected schema to have a property called 'nl'" Just (S.Ref _) -> assertFailure "expected property 'nl' to have inline schema" Just (S.Inline nlSch) -> do - assertEqual "type should be Array" (Just S.SwaggerArray) (nlSch ^. S.type_) + assertEqual "type should be Array" (Just S.OpenApiArray) (nlSch ^. S.type_) assertEqual "minItems should be 1" (Just 1) (nlSch ^. S.minItems) testRefField :: TestTree @@ -332,7 +332,7 @@ testEnumType = assertEqual "Text enum has Swagger type \"string\"" (s1 ^. S.type_) - (Just S.SwaggerString) + (Just S.OpenApiString) let e2 :: ValueSchema NamedSwaggerDoc Integer e2 = enum @Integer "IntEnum" (element (3 :: Integer) (3 :: Integer)) @@ -340,7 +340,7 @@ testEnumType = assertEqual "Integer enum has Swagger type \"integer\"" (s2 ^. S.type_) - (Just S.SwaggerInteger) + (Just S.OpenApiInteger) testNullable :: TestTree testNullable = diff --git a/libs/types-common/default.nix b/libs/types-common/default.nix index 8b4da990200..a5c57f5f05d 100644 --- a/libs/types-common/default.nix +++ b/libs/types-common/default.nix @@ -32,6 +32,7 @@ , lens-datetime , lib , mime +, openapi3 , optparse-applicative , pem , protobuf @@ -40,7 +41,6 @@ , random , schema-profunctor , servant-server -, swagger2 , tagged , tasty , tasty-hunit @@ -86,6 +86,7 @@ mkDerivation { lens lens-datetime mime + openapi3 optparse-applicative pem protobuf @@ -94,7 +95,6 @@ mkDerivation { random schema-profunctor servant-server - swagger2 tagged tasty tasty-hunit diff --git a/libs/types-common/src/Data/Code.hs b/libs/types-common/src/Data/Code.hs index 1820d85f403..ba176629701 100644 --- a/libs/types-common/src/Data/Code.hs +++ b/libs/types-common/src/Data/Code.hs @@ -31,11 +31,11 @@ import Data.Aeson.TH import Data.Bifunctor (Bifunctor (first)) import Data.ByteString.Conversion import Data.Json.Util +import Data.OpenApi qualified as S +import Data.OpenApi.ParamSchema import Data.Proxy (Proxy (Proxy)) import Data.Range import Data.Schema -import Data.Swagger qualified as S -import Data.Swagger.ParamSchema import Data.Text (pack) import Data.Text.Ascii import Data.Text.Encoding (encodeUtf8) diff --git a/libs/types-common/src/Data/CommaSeparatedList.hs b/libs/types-common/src/Data/CommaSeparatedList.hs index 8e3ebd0edd8..8c13c49f4cf 100644 --- a/libs/types-common/src/Data/CommaSeparatedList.hs +++ b/libs/types-common/src/Data/CommaSeparatedList.hs @@ -22,9 +22,9 @@ module Data.CommaSeparatedList where import Control.Lens ((?~)) import Data.Bifunctor qualified as Bifunctor import Data.ByteString.Conversion (FromByteString, List, fromList, parser, runParser) +import Data.OpenApi import Data.Proxy (Proxy (..)) import Data.Range (Bounds, Range) -import Data.Swagger (CollectionFormat (CollectionCSV), SwaggerItems (SwaggerItemsPrimitive), SwaggerType (SwaggerString), ToParamSchema (..), items, type_) import Data.Text qualified as Text import Data.Text.Encoding (encodeUtf8) import Imports @@ -40,10 +40,10 @@ instance FromByteString (List a) => FromHttpApiData (CommaSeparatedList a) where CommaSeparatedList . fromList <$> Bifunctor.first Text.pack (runParser parser $ encodeUtf8 t) instance ToParamSchema (CommaSeparatedList a) where - toParamSchema _ = mempty & type_ ?~ SwaggerString + toParamSchema _ = mempty & type_ ?~ OpenApiString -- | TODO: is this obsoleted by the instances in "Data.Range"? instance (ToParamSchema a, ToParamSchema (Range n m [a])) => ToParamSchema (Range n m (CommaSeparatedList a)) where toParamSchema _ = toParamSchema (Proxy @(Range n m [a])) - & items ?~ SwaggerItemsPrimitive (Just CollectionCSV) (toParamSchema (Proxy @a)) + & items ?~ OpenApiItemsArray [Inline $ toParamSchema (Proxy @a)] diff --git a/libs/types-common/src/Data/Domain.hs b/libs/types-common/src/Data/Domain.hs index 8f96dc18bcb..6f9d0884405 100644 --- a/libs/types-common/src/Data/Domain.hs +++ b/libs/types-common/src/Data/Domain.hs @@ -31,8 +31,8 @@ import Data.ByteString qualified as BS import Data.ByteString.Builder qualified as Builder import Data.ByteString.Char8 qualified as BS.Char8 import Data.ByteString.Conversion +import Data.OpenApi qualified as S import Data.Schema -import Data.Swagger qualified as S import Data.Text qualified as Text import Data.Text.Encoding qualified as Text.E import Imports hiding (isAlphaNum) diff --git a/libs/types-common/src/Data/Handle.hs b/libs/types-common/src/Data/Handle.hs index 0d1e5220076..29d1570cc32 100644 --- a/libs/types-common/src/Data/Handle.hs +++ b/libs/types-common/src/Data/Handle.hs @@ -31,8 +31,8 @@ import Data.Bifunctor (Bifunctor (first)) import Data.ByteString qualified as BS import Data.ByteString.Conversion (FromByteString (parser), ToByteString) import Data.Hashable (Hashable) +import Data.OpenApi qualified as S import Data.Schema -import Data.Swagger qualified as S import Data.Text qualified as Text import Data.Text.Encoding qualified as Text.E import Imports diff --git a/libs/types-common/src/Data/Id.hs b/libs/types-common/src/Data/Id.hs index cae7c686ce7..528725f888b 100644 --- a/libs/types-common/src/Data/Id.hs +++ b/libs/types-common/src/Data/Id.hs @@ -71,11 +71,11 @@ import Data.ByteString.Lazy qualified as L import Data.Char qualified as Char import Data.Default (Default (..)) import Data.Hashable (Hashable) +import Data.OpenApi qualified as S +import Data.OpenApi.Internal.ParamSchema (ToParamSchema (..)) import Data.ProtocolBuffers.Internal import Data.Proxy import Data.Schema -import Data.Swagger qualified as S -import Data.Swagger.Internal.ParamSchema (ToParamSchema (..)) import Data.Text qualified as T import Data.Text.Encoding (decodeUtf8, encodeUtf8) import Data.Text.Lazy (toStrict) diff --git a/libs/types-common/src/Data/Json/Util.hs b/libs/types-common/src/Data/Json/Util.hs index 62e7168d728..408dfe41cbc 100644 --- a/libs/types-common/src/Data/Json/Util.hs +++ b/libs/types-common/src/Data/Json/Util.hs @@ -62,8 +62,8 @@ import Data.ByteString.Builder qualified as BB import Data.ByteString.Conversion qualified as BS import Data.ByteString.Lazy qualified as L import Data.Fixed +import Data.OpenApi qualified as S import Data.Schema -import Data.Swagger qualified as S import Data.Text qualified as Text import Data.Text.Encoding qualified as Text import Data.Text.Encoding.Error qualified as Text @@ -161,7 +161,7 @@ instance ToJSONObject A.Object where instance S.ToParamSchema A.Object where toParamSchema _ = - mempty & S.type_ ?~ S.SwaggerString + mempty & S.type_ ?~ S.OpenApiString instance ToSchema A.Object where schema = @@ -209,7 +209,7 @@ instance ToHttpApiData Base64ByteString where toUrlPiece = Text.decodeUtf8With Text.lenientDecode . B64U.encodeUnpadded . fromBase64ByteString instance S.ToParamSchema Base64ByteString where - toParamSchema _ = mempty & S.type_ ?~ S.SwaggerString + toParamSchema _ = mempty & S.type_ ?~ S.OpenApiString -- base64("example") ~> "ZXhhbXBsZQo=" base64SchemaN :: ValueSchema NamedSwaggerDoc ByteString @@ -245,7 +245,7 @@ instance ToHttpApiData Base64ByteStringL where toUrlPiece = toUrlPiece . base64ToStrict instance S.ToParamSchema Base64ByteStringL where - toParamSchema _ = mempty & S.type_ ?~ S.SwaggerString + toParamSchema _ = mempty & S.type_ ?~ S.OpenApiString base64SchemaLN :: ValueSchema NamedSwaggerDoc LByteString base64SchemaLN = L.toStrict .= fmap L.fromStrict base64SchemaN diff --git a/libs/types-common/src/Data/LegalHold.hs b/libs/types-common/src/Data/LegalHold.hs index 7b328820e6c..02955c03f3d 100644 --- a/libs/types-common/src/Data/LegalHold.hs +++ b/libs/types-common/src/Data/LegalHold.hs @@ -20,8 +20,8 @@ module Data.LegalHold where import Cassandra.CQL import Control.Lens ((?~)) import Data.Aeson hiding (constructorTagModifier) +import Data.OpenApi qualified as S import Data.Schema -import Data.Swagger qualified as S import Imports import Test.QuickCheck diff --git a/libs/types-common/src/Data/List1.hs b/libs/types-common/src/Data/List1.hs index f77d578fed9..8a1d31555d2 100644 --- a/libs/types-common/src/Data/List1.hs +++ b/libs/types-common/src/Data/List1.hs @@ -25,8 +25,8 @@ import Cassandra import Data.Aeson import Data.List.NonEmpty (NonEmpty) import Data.List.NonEmpty qualified as N +import Data.OpenApi qualified as Swagger import Data.Schema as S -import Data.Swagger qualified as Swagger import Imports import Test.QuickCheck (Arbitrary) import Test.QuickCheck.Instances () @@ -72,8 +72,8 @@ instance ToSchema a => ToSchema (List1 a) where instance Swagger.ToParamSchema (List1 a) where toParamSchema _ = mempty - { Swagger._paramSchemaType = Just Swagger.SwaggerArray, - Swagger._paramSchemaMinLength = Just 1 + { Swagger._schemaType = Just Swagger.OpenApiArray, + Swagger._schemaMinLength = Just 1 } instance (Cql a) => Cql (List1 a) where diff --git a/libs/types-common/src/Data/Misc.hs b/libs/types-common/src/Data/Misc.hs index 1b81d37aa31..8acd18dee2b 100644 --- a/libs/types-common/src/Data/Misc.hs +++ b/libs/types-common/src/Data/Misc.hs @@ -77,9 +77,9 @@ import Data.ByteString.Char8 (unpack) import Data.ByteString.Conversion import Data.ByteString.Lazy (toStrict) import Data.IP (IP (IPv4, IPv6), toIPv4, toIPv6b) +import Data.OpenApi qualified as S import Data.Range import Data.Schema -import Data.Swagger qualified as S import Data.Text qualified as Text import Data.Text.Encoding (decodeUtf8, encodeUtf8) import GHC.TypeLits (Nat) @@ -100,7 +100,7 @@ newtype IpAddr = IpAddr {ipAddr :: IP} deriving (A.ToJSON, A.FromJSON, S.ToSchema) via (Schema IpAddr) instance S.ToParamSchema IpAddr where - toParamSchema _ = mempty & S.type_ ?~ S.SwaggerString + toParamSchema _ = mempty & S.type_ ?~ S.OpenApiString instance FromHttpApiData IpAddr where parseQueryParam p = first Text.pack (runParser parser (encodeUtf8 p)) @@ -296,7 +296,7 @@ data Rsa newtype Fingerprint a = Fingerprint { fingerprintBytes :: ByteString } - deriving stock (Eq, Show, Generic) + deriving stock (Eq, Show, Generic, Typeable) deriving newtype (FromByteString, ToByteString, NFData) deriving via @@ -314,7 +314,7 @@ deriving via deriving via (Schema (Fingerprint a)) instance - (ToSchema (Fingerprint a)) => + (Typeable (Fingerprint a), ToSchema (Fingerprint a)) => S.ToSchema (Fingerprint a) instance ToSchema (Fingerprint Rsa) where @@ -378,7 +378,7 @@ deriving via (Schema (PlainTextPassword' tag)) instance ToSchema (PlainTextPassw deriving via (Schema (PlainTextPassword' tag)) instance ToSchema (PlainTextPassword' tag) => ToJSON (PlainTextPassword' tag) -deriving via (Schema (PlainTextPassword' tag)) instance ToSchema (PlainTextPassword' tag) => S.ToSchema (PlainTextPassword' tag) +deriving via (Schema (PlainTextPassword' tag)) instance (KnownNat tag, ToSchema (PlainTextPassword' tag)) => S.ToSchema (PlainTextPassword' tag) instance Show (PlainTextPassword' minLen) where show _ = "PlainTextPassword' " diff --git a/libs/types-common/src/Data/Nonce.hs b/libs/types-common/src/Data/Nonce.hs index 91befc4c3e8..1f094bab764 100644 --- a/libs/types-common/src/Data/Nonce.hs +++ b/libs/types-common/src/Data/Nonce.hs @@ -31,10 +31,10 @@ import Data.Aeson qualified as A import Data.ByteString.Base64.URL qualified as Base64 import Data.ByteString.Conversion import Data.ByteString.Lazy (fromStrict, toStrict) +import Data.OpenApi qualified as S +import Data.OpenApi.ParamSchema import Data.Proxy (Proxy (Proxy)) import Data.Schema -import Data.Swagger qualified as S -import Data.Swagger.ParamSchema import Data.UUID as UUID (UUID, fromByteString, toByteString) import Data.UUID.V4 (nextRandom) import Imports diff --git a/libs/types-common/src/Data/Qualified.hs b/libs/types-common/src/Data/Qualified.hs index 964f91e1ef5..1c1ba088e10 100644 --- a/libs/types-common/src/Data/Qualified.hs +++ b/libs/types-common/src/Data/Qualified.hs @@ -48,15 +48,16 @@ module Data.Qualified ) where -import Control.Lens (Lens, lens, (?~)) +import Control.Lens (Lens, lens, over, (?~)) import Data.Aeson (FromJSON (..), ToJSON (..)) import Data.Bifunctor (first) import Data.Domain (Domain) import Data.Handle (Handle (..)) import Data.Id import Data.Map qualified as Map +import Data.OpenApi (deprecated) +import Data.OpenApi qualified as S import Data.Schema -import Data.Swagger qualified as S import Imports hiding (local) import Test.QuickCheck (Arbitrary (arbitrary)) @@ -163,8 +164,11 @@ isLocal loc = foldQualified loc (const True) (const False) ---------------------------------------------------------------------- -deprecatedSchema :: S.HasDescription doc (Maybe Text) => Text -> ValueSchema doc a -> ValueSchema doc a -deprecatedSchema new = doc . description ?~ ("Deprecated, use " <> new) +deprecatedSchema :: (S.HasDeprecated doc (Maybe Bool), S.HasDescription doc (Maybe Text)) => Text -> ValueSchema doc a -> ValueSchema doc a +deprecatedSchema new = + over doc $ + (description ?~ ("Deprecated, use " <> new)) + . (deprecated ?~ True) qualifiedSchema :: HasSchemaRef doc => @@ -198,7 +202,7 @@ instance KnownIdTag t => ToJSON (Qualified (Id t)) where instance KnownIdTag t => FromJSON (Qualified (Id t)) where parseJSON = schemaParseJSON -instance KnownIdTag t => S.ToSchema (Qualified (Id t)) where +instance (Typeable t, KnownIdTag t) => S.ToSchema (Qualified (Id t)) where declareNamedSchema = schemaToSwagger instance ToJSON (Qualified Handle) where diff --git a/libs/types-common/src/Data/Range.hs b/libs/types-common/src/Data/Range.hs index e4c5be14781..898df2142c1 100644 --- a/libs/types-common/src/Data/Range.hs +++ b/libs/types-common/src/Data/Range.hs @@ -74,13 +74,13 @@ import Data.List.NonEmpty (NonEmpty) import Data.List.NonEmpty qualified as N import Data.List1 (List1, toNonEmpty) import Data.Map qualified as Map +import Data.OpenApi (Schema, ToParamSchema (..)) +import Data.OpenApi qualified as S import Data.Proxy -import Data.Schema +import Data.Schema hiding (Schema) import Data.Sequence (Seq) import Data.Sequence qualified as Seq import Data.Set qualified as Set -import Data.Swagger (ParamSchema, ToParamSchema (..)) -import Data.Swagger qualified as S import Data.Text qualified as T import Data.Text.Ascii (AsciiChar, AsciiChars, AsciiText, fromAsciiChars) import Data.Text.Ascii qualified as Ascii @@ -152,6 +152,9 @@ numRangedSchemaDocModifier n m = S.schema %~ ((S.minimum_ ?~ fromIntegral n) . ( instance S.HasSchema d S.Schema => HasRangedSchemaDocModifier d [a] where rangedSchemaDocModifier _ = listRangedSchemaDocModifier +-- Sets are similar to lists, so use that as our defininition +instance S.HasSchema d S.Schema => HasRangedSchemaDocModifier d (Set a) where rangedSchemaDocModifier _ = listRangedSchemaDocModifier + instance S.HasSchema d S.Schema => HasRangedSchemaDocModifier d Text where rangedSchemaDocModifier _ = stringRangedSchemaDocModifier instance S.HasSchema d S.Schema => HasRangedSchemaDocModifier d String where rangedSchemaDocModifier _ = stringRangedSchemaDocModifier @@ -232,7 +235,7 @@ instance (KnownNat n, KnownNat m) => ToParamSchema (Range n m TL.Text) where & S.maxLength ?~ fromKnownNat (Proxy @n) & S.minLength ?~ fromKnownNat (Proxy @m) -instance S.ToSchema a => S.ToSchema (Range n m a) where +instance (KnownNat n, S.ToSchema a, KnownNat m) => S.ToSchema (Range n m a) where declareNamedSchema _ = S.declareNamedSchema (Proxy @a) @@ -316,7 +319,7 @@ rappend (Range a) (Range b) = Range (a <> b) rsingleton :: a -> Range 1 1 [a] rsingleton = Range . pure -rangedNumToParamSchema :: forall a n m t. (ToParamSchema a, Num a, KnownNat n, KnownNat m) => Proxy (Range n m a) -> ParamSchema t +rangedNumToParamSchema :: forall a n m. (ToParamSchema a, Num a, KnownNat n, KnownNat m) => Proxy (Range n m a) -> Schema rangedNumToParamSchema _ = toParamSchema (Proxy @a) & S.minimum_ ?~ fromKnownNat (Proxy @n) diff --git a/libs/types-common/src/Data/Text/Ascii.hs b/libs/types-common/src/Data/Text/Ascii.hs index 70712eabdba..0fac4b07e2f 100644 --- a/libs/types-common/src/Data/Text/Ascii.hs +++ b/libs/types-common/src/Data/Text/Ascii.hs @@ -86,8 +86,8 @@ import Data.ByteString.Base64.URL qualified as B64Url import Data.ByteString.Char8 qualified as C8 import Data.ByteString.Conversion import Data.Hashable (Hashable) +import Data.OpenApi qualified as S import Data.Schema -import Data.Swagger qualified as S import Data.Text qualified as Text import Data.Text.Encoding (decodeLatin1, decodeUtf8') import Imports @@ -156,7 +156,7 @@ instance AsciiChars c => ToJSON (AsciiText c) where instance AsciiChars c => FromJSON (AsciiText c) where parseJSON = schemaParseJSON -instance AsciiChars c => S.ToSchema (AsciiText c) where +instance (Typeable c, AsciiChars c) => S.ToSchema (AsciiText c) where declareNamedSchema = schemaToSwagger instance AsciiChars c => Cql (AsciiText c) where diff --git a/libs/types-common/types-common.cabal b/libs/types-common/types-common.cabal index 9f2eb9391c7..4ce602225f1 100644 --- a/libs/types-common/types-common.cabal +++ b/libs/types-common/types-common.cabal @@ -116,6 +116,7 @@ library , lens >=4.10 , lens-datetime >=0.3 , mime >=0.4.0.2 + , openapi3 , optparse-applicative >=0.10 , pem , protobuf >=0.2 @@ -124,7 +125,6 @@ library , random >=1.1 , schema-profunctor , servant-server - , swagger2 , tagged >=0.8 , tasty >=0.11 , tasty-hunit diff --git a/libs/wai-utilities/default.nix b/libs/wai-utilities/default.nix index 33988b17bfd..bc345ab3586 100644 --- a/libs/wai-utilities/default.nix +++ b/libs/wai-utilities/default.nix @@ -18,12 +18,12 @@ , lib , metrics-core , metrics-wai +, openapi3 , pipes , prometheus-client , schema-profunctor , servant-server , streaming-commons -, swagger2 , text , tinylog , types-common @@ -52,12 +52,12 @@ mkDerivation { kan-extensions metrics-core metrics-wai + openapi3 pipes prometheus-client schema-profunctor servant-server streaming-commons - swagger2 text tinylog types-common diff --git a/libs/wai-utilities/src/Network/Wai/Utilities/Headers.hs b/libs/wai-utilities/src/Network/Wai/Utilities/Headers.hs index 2cf2b2e644e..f1673e7de13 100644 --- a/libs/wai-utilities/src/Network/Wai/Utilities/Headers.hs +++ b/libs/wai-utilities/src/Network/Wai/Utilities/Headers.hs @@ -18,7 +18,7 @@ module Network.Wai.Utilities.Headers where import Data.ByteString.Conversion (FromByteString (..), ToByteString (..), fromByteString', toByteString') -import Data.Swagger.ParamSchema (ToParamSchema (..)) +import Data.OpenApi.ParamSchema (ToParamSchema (..)) import Data.Text as T import Imports import Servant (FromHttpApiData (..), Proxy (Proxy), ToHttpApiData (..)) diff --git a/libs/wai-utilities/wai-utilities.cabal b/libs/wai-utilities/wai-utilities.cabal index 44a3769dbf1..1c1ae75cbcc 100644 --- a/libs/wai-utilities/wai-utilities.cabal +++ b/libs/wai-utilities/wai-utilities.cabal @@ -86,12 +86,12 @@ library , kan-extensions , metrics-core >=0.1 , metrics-wai >=0.5.7 + , openapi3 , pipes >=4.1 , prometheus-client , schema-profunctor , servant-server , streaming-commons >=0.1 - , swagger2 , text >=0.11 , tinylog >=0.8 , types-common >=0.12 diff --git a/libs/wire-api-federation/default.nix b/libs/wire-api-federation/default.nix index 2ac0f43e549..0275616b33e 100644 --- a/libs/wire-api-federation/default.nix +++ b/libs/wire-api-federation/default.nix @@ -26,6 +26,7 @@ , lib , metrics-wai , mtl +, openapi3 , QuickCheck , schema-profunctor , servant @@ -34,7 +35,6 @@ , servant-server , singletons , singletons-th -, swagger2 , text , time , transformers @@ -66,6 +66,7 @@ mkDerivation { lens metrics-wai mtl + openapi3 QuickCheck schema-profunctor servant @@ -73,7 +74,6 @@ mkDerivation { servant-client-core servant-server singletons-th - swagger2 text time transformers diff --git a/libs/wire-api-federation/src/Wire/API/Federation/Version.hs b/libs/wire-api-federation/src/Wire/API/Federation/Version.hs index 3a433e3fb2a..0f3e113db95 100644 --- a/libs/wire-api-federation/src/Wire/API/Federation/Version.hs +++ b/libs/wire-api-federation/src/Wire/API/Federation/Version.hs @@ -21,10 +21,10 @@ module Wire.API.Federation.Version where import Control.Lens ((?~)) import Data.Aeson (FromJSON (..), ToJSON (..)) +import Data.OpenApi qualified as S import Data.Schema import Data.Set qualified as Set import Data.Singletons.TH -import Data.Swagger qualified as S import Imports import Wire.API.VersionInfo diff --git a/libs/wire-api-federation/wire-api-federation.cabal b/libs/wire-api-federation/wire-api-federation.cabal index 53862a2f92f..b19ff51c99d 100644 --- a/libs/wire-api-federation/wire-api-federation.cabal +++ b/libs/wire-api-federation/wire-api-federation.cabal @@ -97,6 +97,7 @@ library , lens , metrics-wai , mtl + , openapi3 , QuickCheck >=2.13 , schema-profunctor , servant >=0.16 @@ -104,7 +105,6 @@ library , servant-client-core , servant-server , singletons-th - , swagger2 , text >=0.11 , time >=1.8 , transformers diff --git a/libs/wire-api/default.nix b/libs/wire-api/default.nix index e6ec675d546..b76f3159a94 100644 --- a/libs/wire-api/default.nix +++ b/libs/wire-api/default.nix @@ -61,6 +61,7 @@ , metrics-wai , mime , mtl +, openapi3 , pem , polysemy , pretty @@ -81,13 +82,12 @@ , servant-client-core , servant-conduit , servant-multipart +, servant-openapi3 , servant-server -, servant-swagger , singletons , singletons-base , singletons-th , sop-core -, swagger2 , tagged , tasty , tasty-hspec @@ -167,6 +167,7 @@ mkDerivation { metrics-wai mime mtl + openapi3 pem polysemy proto-lens @@ -185,13 +186,12 @@ mkDerivation { servant-client-core servant-conduit servant-multipart + servant-openapi3 servant-server - servant-swagger singletons singletons-base singletons-th sop-core - swagger2 tagged text time @@ -239,6 +239,7 @@ mkDerivation { lens memory metrics-wai + openapi3 pem pretty process @@ -247,7 +248,6 @@ mkDerivation { schema-profunctor servant servant-server - swagger2 tasty tasty-hspec tasty-hunit diff --git a/libs/wire-api/src/Wire/API/Asset.hs b/libs/wire-api/src/Wire/API/Asset.hs index d8505a038f1..1658056c6d0 100644 --- a/libs/wire-api/src/Wire/API/Asset.hs +++ b/libs/wire-api/src/Wire/API/Asset.hs @@ -74,11 +74,11 @@ import Data.ByteString.Conversion import Data.ByteString.Lazy qualified as LBS import Data.Id import Data.Json.Util (UTCTimeMillis (fromUTCTimeMillis), toUTCTimeMillis) +import Data.OpenApi qualified as S import Data.Proxy import Data.Qualified import Data.SOP import Data.Schema -import Data.Swagger qualified as S import Data.Text qualified as T import Data.Text.Ascii (AsciiBase64Url) import Data.Text.Encoding qualified as T @@ -109,7 +109,7 @@ deriving via Schema (Asset' key) instance ToSchema (Asset' key) => (ToJSON (Asse deriving via Schema (Asset' key) instance ToSchema (Asset' key) => (FromJSON (Asset' key)) -deriving via Schema (Asset' key) instance ToSchema (Asset' key) => (S.ToSchema (Asset' key)) +deriving via Schema (Asset' key) instance (Typeable key, ToSchema (Asset' key)) => (S.ToSchema (Asset' key)) -- Generate expiry time with millisecond precision instance Arbitrary key => Arbitrary (Asset' key) where @@ -394,7 +394,7 @@ instance FromHttpApiData (AssetLocation Absolute) where instance S.ToParamSchema (AssetLocation r) where toParamSchema _ = mempty - & S.type_ ?~ S.SwaggerString + & S.type_ ?~ S.OpenApiString & S.format ?~ "url" -- | An asset as returned by the download API: if the asset is local, only a diff --git a/libs/wire-api/src/Wire/API/Call/Config.hs b/libs/wire-api/src/Wire/API/Call/Config.hs index a458891e34c..18289ca1706 100644 --- a/libs/wire-api/src/Wire/API/Call/Config.hs +++ b/libs/wire-api/src/Wire/API/Call/Config.hs @@ -81,8 +81,8 @@ import Data.ByteString.Conversion qualified as BC import Data.IP qualified as IP import Data.List.NonEmpty (NonEmpty) import Data.Misc (HttpsUrl (..), IpAddr (IpAddr), Port (..)) +import Data.OpenApi qualified as S import Data.Schema -import Data.Swagger qualified as S import Data.Text qualified as Text import Data.Text.Ascii import Data.Text.Encoding qualified as TE diff --git a/libs/wire-api/src/Wire/API/Connection.hs b/libs/wire-api/src/Wire/API/Connection.hs index a093c10c72e..138b6c3eb4b 100644 --- a/libs/wire-api/src/Wire/API/Connection.hs +++ b/libs/wire-api/src/Wire/API/Connection.hs @@ -45,10 +45,10 @@ import Control.Lens ((?~)) import Data.Aeson (FromJSON (..), ToJSON (..)) import Data.Id import Data.Json.Util (UTCTimeMillis) +import Data.OpenApi qualified as S import Data.Qualified (Qualified (qUnqualified), deprecatedSchema) import Data.Range import Data.Schema -import Data.Swagger qualified as S import Data.Text as Text import Imports import Servant.API @@ -142,7 +142,7 @@ data Relation deriving (FromJSON, ToJSON, S.ToSchema) via (Schema Relation) instance S.ToParamSchema Relation where - toParamSchema _ = mempty & S.type_ ?~ S.SwaggerString + toParamSchema _ = mempty & S.type_ ?~ S.OpenApiString -- | 'updateConnectionInternal', requires knowledge of the previous state (before -- 'MissingLegalholdConsent'), but the clients don't need that information. To avoid having diff --git a/libs/wire-api/src/Wire/API/Conversation.hs b/libs/wire-api/src/Wire/API/Conversation.hs index f28e178727a..bfe91520ec7 100644 --- a/libs/wire-api/src/Wire/API/Conversation.hs +++ b/libs/wire-api/src/Wire/API/Conversation.hs @@ -96,12 +96,13 @@ import Data.List.NonEmpty (NonEmpty) import Data.List1 import Data.Map qualified as Map import Data.Misc +import Data.OpenApi (deprecated) +import Data.OpenApi qualified as S import Data.Qualified import Data.Range (Range, fromRange, rangedSchema) import Data.SOP import Data.Schema import Data.Set qualified as Set -import Data.Swagger qualified as S import Data.UUID qualified as UUID import Data.UUID.V5 qualified as UUIDV5 import Imports @@ -567,14 +568,15 @@ instance ToSchema AccessRole where instance ToSchema AccessRoleLegacy where schema = - (S.schema . description ?~ desc) $ - enum @Text "AccessRoleLegacy" $ - mconcat - [ element "private" PrivateAccessRole, - element "team" TeamAccessRole, - element "activated" ActivatedAccessRole, - element "non_activated" NonActivatedAccessRole - ] + (S.schema . S.deprecated ?~ True) $ + (S.schema . description ?~ desc) $ + enum @Text "AccessRoleLegacy" $ + mconcat + [ element "private" PrivateAccessRole, + element "team" TeamAccessRole, + element "activated" ActivatedAccessRole, + element "non_activated" NonActivatedAccessRole + ] where desc = "Which users can join conversations (deprecated, use `access_role_v2` instead).\ @@ -670,7 +672,9 @@ newConvSchema sch = <$> newConvUsers .= ( fieldWithDocModifier "users" - (description ?~ usersDesc) + ( (deprecated ?~ True) + . (description ?~ usersDesc) + ) (array schema) <|> pure [] ) diff --git a/libs/wire-api/src/Wire/API/Conversation/Action.hs b/libs/wire-api/src/Wire/API/Conversation/Action.hs index 30156061710..4ede3530c6d 100644 --- a/libs/wire-api/src/Wire/API/Conversation/Action.hs +++ b/libs/wire-api/src/Wire/API/Conversation/Action.hs @@ -38,10 +38,10 @@ import Data.Aeson.KeyMap qualified as A import Data.Id import Data.Kind import Data.List.NonEmpty qualified as NonEmptyList +import Data.OpenApi qualified as S import Data.Qualified (Qualified) import Data.Schema hiding (tag) import Data.Singletons.TH -import Data.Swagger qualified as S import Data.Time.Clock import Imports import Wire.API.Conversation diff --git a/libs/wire-api/src/Wire/API/Conversation/Bot.hs b/libs/wire-api/src/Wire/API/Conversation/Bot.hs index 2fd4a442cb3..f46a83869d4 100644 --- a/libs/wire-api/src/Wire/API/Conversation/Bot.hs +++ b/libs/wire-api/src/Wire/API/Conversation/Bot.hs @@ -28,8 +28,8 @@ where import Data.Aeson qualified as A import Data.Id +import Data.OpenApi qualified as S import Data.Schema -import Data.Swagger qualified as S import Imports import Wire.API.Event.Conversation (Event) import Wire.API.User.Client.Prekey (Prekey) diff --git a/libs/wire-api/src/Wire/API/Conversation/Code.hs b/libs/wire-api/src/Wire/API/Conversation/Code.hs index b99b4012df2..51a142ddd09 100644 --- a/libs/wire-api/src/Wire/API/Conversation/Code.hs +++ b/libs/wire-api/src/Wire/API/Conversation/Code.hs @@ -40,8 +40,8 @@ import Data.ByteString.Conversion (toByteString') -- FUTUREWORK: move content of Data.Code here? import Data.Code as Code import Data.Misc +import Data.OpenApi qualified as S import Data.Schema -import Data.Swagger qualified as S import Imports import URI.ByteString qualified as URI import Wire.Arbitrary (Arbitrary, GenericUniform (..)) diff --git a/libs/wire-api/src/Wire/API/Conversation/Member.hs b/libs/wire-api/src/Wire/API/Conversation/Member.hs index 2bb1d7e3817..bb913a7ef38 100644 --- a/libs/wire-api/src/Wire/API/Conversation/Member.hs +++ b/libs/wire-api/src/Wire/API/Conversation/Member.hs @@ -38,9 +38,10 @@ import Control.Lens ((?~)) import Data.Aeson (FromJSON (..), ToJSON (..)) import Data.Aeson qualified as A import Data.Id +import Data.OpenApi (deprecated) +import Data.OpenApi qualified as S import Data.Qualified import Data.Schema -import Data.Swagger qualified as S import Imports import Test.QuickCheck qualified as QC import Wire.API.Conversation.Role @@ -141,7 +142,7 @@ instance ToSchema OtherMember where <* (qUnqualified . omQualifiedId) .= optional (field "id" schema) <*> omService .= maybe_ (optFieldWithDocModifier "service" (description ?~ desc) schema) <*> omConvRoleName .= (field "conversation_role" schema <|> pure roleNameWireAdmin) - <* const (0 :: Int) .= optional (fieldWithDocModifier "status" (description ?~ "deprecated") schema) -- TODO: remove + <* const (0 :: Int) .= optional (fieldWithDocModifier "status" ((deprecated ?~ True) . (description ?~ "deprecated")) schema) -- TODO: remove where desc = "The reference to the owning service, if the member is a 'bot'." diff --git a/libs/wire-api/src/Wire/API/Conversation/Role.hs b/libs/wire-api/src/Wire/API/Conversation/Role.hs index 1df79697f80..edb97c23f42 100644 --- a/libs/wire-api/src/Wire/API/Conversation/Role.hs +++ b/libs/wire-api/src/Wire/API/Conversation/Role.hs @@ -68,11 +68,11 @@ import Data.Aeson.TH qualified as A import Data.Attoparsec.Text import Data.ByteString.Conversion import Data.Hashable +import Data.OpenApi qualified as S import Data.Range (fromRange, genRangeText) import Data.Schema import Data.Set qualified as Set import Data.Singletons.TH -import Data.Swagger qualified as S import Deriving.Swagger qualified as S import GHC.TypeLits import Imports diff --git a/libs/wire-api/src/Wire/API/Conversation/Typing.hs b/libs/wire-api/src/Wire/API/Conversation/Typing.hs index 65e728c87c6..076dbde5e47 100644 --- a/libs/wire-api/src/Wire/API/Conversation/Typing.hs +++ b/libs/wire-api/src/Wire/API/Conversation/Typing.hs @@ -21,8 +21,8 @@ module Wire.API.Conversation.Typing where import Data.Aeson (FromJSON (..), ToJSON (..)) +import Data.OpenApi qualified as S import Data.Schema -import Data.Swagger qualified as S import Imports import Wire.Arbitrary (Arbitrary, GenericUniform (..)) diff --git a/libs/wire-api/src/Wire/API/CustomBackend.hs b/libs/wire-api/src/Wire/API/CustomBackend.hs index 73c3a525a06..f7c12e0140d 100644 --- a/libs/wire-api/src/Wire/API/CustomBackend.hs +++ b/libs/wire-api/src/Wire/API/CustomBackend.hs @@ -24,8 +24,8 @@ where import Control.Lens ((?~)) import Data.Misc (HttpsUrl) +import Data.OpenApi qualified as S import Data.Schema -import Data.Swagger qualified as S import Deriving.Aeson import Imports import Wire.Arbitrary (Arbitrary, GenericUniform (..)) diff --git a/libs/wire-api/src/Wire/API/Deprecated.hs b/libs/wire-api/src/Wire/API/Deprecated.hs new file mode 100644 index 00000000000..c68120be996 --- /dev/null +++ b/libs/wire-api/src/Wire/API/Deprecated.hs @@ -0,0 +1,60 @@ +-- This file is part of the Wire Server implementation. +-- +-- Copyright (C) 2022 Wire Swiss GmbH +-- +-- This program is free software: you can redistribute it and/or modify it under +-- the terms of the GNU Affero General Public License as published by the Free +-- Software Foundation, either version 3 of the License, or (at your option) any +-- later version. +-- +-- This program is distributed in the hope that it will be useful, but WITHOUT +-- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +-- FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more +-- details. +-- +-- You should have received a copy of the GNU Affero General Public License along +-- with this program. If not, see . + +module Wire.API.Deprecated + ( Deprecated, + ) +where + +import Control.Lens +import Data.Kind (Type) +import Data.Metrics.Servant +import Data.OpenApi hiding (HasServer) +import Imports +import Servant +import Servant.Client +import Servant.OpenApi + +-- Annotate that the route is deprecated +data Deprecated deriving (Typeable) + +-- All of these instances are very similar to the instances +-- for Summary. These don't impact the API directly, but are +-- for marking the deprecated flag in the openapi output. +instance HasLink sub => HasLink (Deprecated :> sub :: Type) where + type MkLink (Deprecated :> sub) a = MkLink sub a + toLink = + let simpleToLink toA _ = toLink toA (Proxy :: Proxy sub) + in simpleToLink + +instance HasOpenApi api => HasOpenApi (Deprecated :> api :: Type) where + toOpenApi _ = + toOpenApi (Proxy @api) + & allOperations . deprecated ?~ True + +instance HasServer api ctx => HasServer (Deprecated :> api) ctx where + type ServerT (Deprecated :> api) m = ServerT api m + route _ = route $ Proxy @api + hoistServerWithContext _ pc nt s = hoistServerWithContext (Proxy @api) pc nt s + +instance HasClient m api => HasClient m (Deprecated :> api) where + type Client m (Deprecated :> api) = Client m api + clientWithRoute pm _ = clientWithRoute pm (Proxy :: Proxy api) + hoistClientMonad pm _ f cl = hoistClientMonad pm (Proxy :: Proxy api) f cl + +instance (RoutesToPaths rest) => RoutesToPaths (Deprecated :> rest) where + getRoutes = getRoutes @rest diff --git a/libs/wire-api/src/Wire/API/Error.hs b/libs/wire-api/src/Wire/API/Error.hs index 304f11596ec..8946a785721 100644 --- a/libs/wire-api/src/Wire/API/Error.hs +++ b/libs/wire-api/src/Wire/API/Error.hs @@ -49,12 +49,13 @@ where import Control.Lens (at, (%~), (.~), (?~)) import Data.Aeson (FromJSON (..), ToJSON (..)) import Data.Aeson qualified as A +import Data.HashMap.Strict.InsOrd import Data.Kind import Data.Metrics.Servant +import Data.OpenApi qualified as S import Data.Proxy import Data.SOP import Data.Schema -import Data.Swagger qualified as S import Data.Text qualified as Text import Data.Text.Lazy qualified as LT import GHC.TypeLits @@ -65,7 +66,7 @@ import Network.Wai.Utilities.JSONResponse import Polysemy import Polysemy.Error import Servant -import Servant.Swagger +import Servant.OpenApi import Wire.API.Routes.MultiVerb import Wire.API.Routes.Named (Named) import Wire.API.Routes.Version @@ -185,23 +186,23 @@ instance (HasServer api ctx) => HasServer (CanThrowMany es :> api) ctx where hoistServerWithContext _ = hoistServerWithContext (Proxy @api) instance - (HasSwagger api, IsSwaggerError e) => - HasSwagger (CanThrow e :> api) + (HasOpenApi api, IsSwaggerError e) => + HasOpenApi (CanThrow e :> api) where - toSwagger _ = addToSwagger @e (toSwagger (Proxy @api)) + toOpenApi _ = addToOpenApi @e (toOpenApi (Proxy @api)) type instance SpecialiseToVersion v (CanThrowMany es :> api) = CanThrowMany es :> SpecialiseToVersion v api -instance HasSwagger api => HasSwagger (CanThrowMany '() :> api) where - toSwagger _ = toSwagger (Proxy @api) +instance HasOpenApi api => HasOpenApi (CanThrowMany '() :> api) where + toOpenApi _ = toOpenApi (Proxy @api) instance - (HasSwagger (CanThrowMany es :> api), IsSwaggerError e) => - HasSwagger (CanThrowMany '(e, es) :> api) + (HasOpenApi (CanThrowMany es :> api), IsSwaggerError e) => + HasOpenApi (CanThrowMany '(e, es) :> api) where - toSwagger _ = addToSwagger @e (toSwagger (Proxy @(CanThrowMany es :> api))) + toOpenApi _ = addToOpenApi @e (toOpenApi (Proxy @(CanThrowMany es :> api))) type family DeclaredErrorEffects api :: EffectRow where DeclaredErrorEffects (CanThrow e :> api) = (ErrorEffect e ': DeclaredErrorEffects api) @@ -211,15 +212,23 @@ type family DeclaredErrorEffects api :: EffectRow where DeclaredErrorEffects (Named n api) = DeclaredErrorEffects api DeclaredErrorEffects api = '[] -errorResponseSwagger :: forall e. KnownError e => S.Response +errorResponseSwagger :: forall e. (Typeable e, KnownError e) => S.Response errorResponseSwagger = mempty & S.description .~ (eMessage err <> " (label: `" <> eLabel err <> "`)") - & S.schema ?~ S.Inline (S.toSchema (Proxy @(SStaticError e))) + -- Defaulting this to JSON, as openapi3 needs something to map a schema against. + -- This _should_ be overridden with the actual media types once we are at the + -- point of rendering out the schemas for MultiVerb. + -- Check the instance of `S.HasOpenApi (MultiVerb method (cs :: [Type]) as r)` + & S.content .~ singleton mediaType mediaTypeObject where err = dynError @e + mediaType = contentType $ Proxy @JSON + mediaTypeObject = + mempty + & S.schema ?~ S.Inline (S.toSchema (Proxy @(SStaticError e))) -addErrorResponseToSwagger :: Int -> S.Response -> S.Swagger -> S.Swagger +addErrorResponseToSwagger :: Int -> S.Response -> S.OpenApi -> S.OpenApi addErrorResponseToSwagger code resp = S.allOperations . S.responses @@ -233,7 +242,7 @@ addErrorResponseToSwagger code resp = addRef (Just (S.Inline resp1)) = S.Inline (combineResponseSwagger resp1 resp) addRef (Just r@(S.Ref _)) = r -addStaticErrorToSwagger :: forall e. KnownError e => S.Swagger -> S.Swagger +addStaticErrorToSwagger :: forall e. (Typeable e, KnownError e) => S.OpenApi -> S.OpenApi addStaticErrorToSwagger = addErrorResponseToSwagger (fromIntegral (eCode (dynError @e))) @@ -244,7 +253,7 @@ type family MapError (e :: k) :: StaticError type family ErrorEffect (e :: k) :: Effect class IsSwaggerError e where - addToSwagger :: S.Swagger -> S.Swagger + addToOpenApi :: S.OpenApi -> S.OpenApi -- | An effect for a static error type with no data. type ErrorS e = Error (Tagged e ()) @@ -323,7 +332,7 @@ instance KnownError (MapError e) => AsConstructor '[] (ErrorResponse e) where toConstructor _ = Nil fromConstructor _ = dynError @(MapError e) -instance KnownError (MapError e) => IsSwaggerResponse (ErrorResponse e) where +instance (KnownError (MapError e), Typeable (MapError e)) => IsSwaggerResponse (ErrorResponse e) where responseSwagger = pure $ errorResponseSwagger @(MapError e) instance diff --git a/libs/wire-api/src/Wire/API/Error/Brig.hs b/libs/wire-api/src/Wire/API/Error/Brig.hs index 8544a58d50d..85a280b171c 100644 --- a/libs/wire-api/src/Wire/API/Error/Brig.hs +++ b/libs/wire-api/src/Wire/API/Error/Brig.hs @@ -17,6 +17,7 @@ module Wire.API.Error.Brig where +import Data.Data import Wire.API.Error data BrigError @@ -86,8 +87,8 @@ data BrigError | ServiceDisabled | InvalidBot -instance KnownError (MapError e) => IsSwaggerError (e :: BrigError) where - addToSwagger = addStaticErrorToSwagger @(MapError e) +instance (Typeable (MapError e), KnownError (MapError e)) => IsSwaggerError (e :: BrigError) where + addToOpenApi = addStaticErrorToSwagger @(MapError e) type instance MapError 'ServiceDisabled = 'StaticError 403 "service-disabled" "The desired service is currently disabled." diff --git a/libs/wire-api/src/Wire/API/Error/Cannon.hs b/libs/wire-api/src/Wire/API/Error/Cannon.hs index 7cdca830697..6dea237c1fc 100644 --- a/libs/wire-api/src/Wire/API/Error/Cannon.hs +++ b/libs/wire-api/src/Wire/API/Error/Cannon.hs @@ -17,14 +17,15 @@ module Wire.API.Error.Cannon where +import Data.Data import Wire.API.Error data CannonError = ClientGone | PresenceNotRegistered -instance KnownError (MapError e) => IsSwaggerError (e :: CannonError) where - addToSwagger = addStaticErrorToSwagger @(MapError e) +instance (Typeable (MapError e), KnownError (MapError e)) => IsSwaggerError (e :: CannonError) where + addToOpenApi = addStaticErrorToSwagger @(MapError e) type instance MapError 'ClientGone = 'StaticError 410 "general" "client gone" diff --git a/libs/wire-api/src/Wire/API/Error/Cargohold.hs b/libs/wire-api/src/Wire/API/Error/Cargohold.hs index 26087509d12..0c4f17015cc 100644 --- a/libs/wire-api/src/Wire/API/Error/Cargohold.hs +++ b/libs/wire-api/src/Wire/API/Error/Cargohold.hs @@ -17,6 +17,7 @@ module Wire.API.Error.Cargohold where +import Data.Typeable import Wire.API.Error data CargoholdError @@ -26,8 +27,8 @@ data CargoholdError | InvalidLength | NoMatchingAssetEndpoint -instance KnownError (MapError e) => IsSwaggerError (e :: CargoholdError) where - addToSwagger = addStaticErrorToSwagger @(MapError e) +instance (Typeable (MapError e), KnownError (MapError e)) => IsSwaggerError (e :: CargoholdError) where + addToOpenApi = addStaticErrorToSwagger @(MapError e) type instance MapError 'AssetNotFound = 'StaticError 404 "not-found" "Asset not found" diff --git a/libs/wire-api/src/Wire/API/Error/Empty.hs b/libs/wire-api/src/Wire/API/Error/Empty.hs index 474841ef1fd..290c75c978d 100644 --- a/libs/wire-api/src/Wire/API/Error/Empty.hs +++ b/libs/wire-api/src/Wire/API/Error/Empty.hs @@ -18,7 +18,7 @@ module Wire.API.Error.Empty where import Control.Lens ((.~)) -import Data.Swagger qualified as S +import Data.OpenApi qualified as S import Data.Text qualified as Text import GHC.TypeLits import Imports diff --git a/libs/wire-api/src/Wire/API/Error/Galley.hs b/libs/wire-api/src/Wire/API/Error/Galley.hs index 48aba881d42..3e7896b4544 100644 --- a/libs/wire-api/src/Wire/API/Error/Galley.hs +++ b/libs/wire-api/src/Wire/API/Error/Galley.hs @@ -37,11 +37,12 @@ import Control.Lens ((%~), (.~), (?~)) import Data.Aeson (FromJSON (..), ToJSON (..)) import Data.Containers.ListUtils import Data.Domain +import Data.HashMap.Strict.InsOrd (singleton) +import Data.OpenApi qualified as S import Data.Proxy import Data.Qualified import Data.Schema import Data.Singletons.TH (genSingletons) -import Data.Swagger qualified as S import Data.Tagged import GHC.TypeLits import Imports @@ -51,6 +52,7 @@ import Network.Wai.Utilities.JSONResponse import Polysemy import Polysemy.Error import Prelude.Singletons (Show_) +import Servant.API.ContentTypes (JSON, contentType) import Wire.API.Conversation.Role import Wire.API.Error import Wire.API.Error.Brig qualified as BrigError @@ -139,8 +141,8 @@ data GalleyError $(genSingletons [''GalleyError]) -instance KnownError (MapError e) => IsSwaggerError (e :: GalleyError) where - addToSwagger = addStaticErrorToSwagger @(MapError e) +instance (Typeable (MapError e), KnownError (MapError e)) => IsSwaggerError (e :: GalleyError) where + addToOpenApi = addStaticErrorToSwagger @(MapError e) instance KnownError (MapError e) => APIError (Tagged (e :: GalleyError) ()) where toResponse _ = toResponse $ dynError @(MapError e) @@ -324,7 +326,7 @@ type instance MapError 'VerificationCodeAuthFailed = 'StaticError 403 "code-auth type instance MapError 'VerificationCodeRequired = 'StaticError 403 "code-authentication-required" "Verification code required" instance IsSwaggerError AuthenticationError where - addToSwagger = + addToOpenApi = addStaticErrorToSwagger @(MapError 'ReAuthFailed) . addStaticErrorToSwagger @(MapError 'VerificationCodeAuthFailed) . addStaticErrorToSwagger @(MapError 'VerificationCodeRequired) @@ -351,7 +353,7 @@ data TeamFeatureError instance IsSwaggerError TeamFeatureError where -- Do not display in Swagger - addToSwagger = id + addToOpenApi = id type instance MapError 'AppLockInactivityTimeoutTooLow = 'StaticError 400 "inactivity-timeout-too-low" "Applock inactivity timeout must be at least 30 seconds" @@ -398,7 +400,7 @@ type instance ErrorEffect MLSProposalFailure = Error MLSProposalFailure -- Proposal failures are only reported generically in Swagger instance IsSwaggerError MLSProposalFailure where - addToSwagger = S.allOperations . S.description %~ Just . (<> desc) . fold + addToOpenApi = S.allOperations . S.description %~ Just . (<> desc) . fold where desc = "\n\n**Note**: this endpoint can execute proposals, and therefore \ @@ -449,11 +451,16 @@ instance ToSchema NonFederatingBackends where nonFederatingBackendsFromList instance IsSwaggerError NonFederatingBackends where - addToSwagger = + addToOpenApi = addErrorResponseToSwagger (HTTP.statusCode nonFederatingBackendsStatus) $ mempty & S.description .~ "Adding members to the conversation is not possible because the backends involved do not form a fully connected graph" - & S.schema ?~ S.Inline (S.toSchema (Proxy @NonFederatingBackends)) + & S.content .~ singleton mediaType mediaTypeObject + where + mediaType = contentType $ Proxy @JSON + mediaTypeObject = + mempty + & S.schema ?~ S.Inline (S.toSchema (Proxy @NonFederatingBackends)) type instance ErrorEffect NonFederatingBackends = Error NonFederatingBackends @@ -486,11 +493,18 @@ instance ToSchema UnreachableBackends where <$> (.backends) .= field "unreachable_backends" (array schema) instance IsSwaggerError UnreachableBackends where - addToSwagger = + addToOpenApi = addErrorResponseToSwagger (HTTP.statusCode unreachableBackendsStatus) $ mempty & S.description .~ "Some domains are unreachable" - & S.schema ?~ S.Inline (S.toSchema (Proxy @UnreachableBackends)) + -- Defaulting this to JSON, as openapi3 needs something to map a schema against. + -- This _should_ be overridden with the actual media types once we are at the + -- point of rendering out the schemas for MultiVerb. + -- Check the instance of `S.HasOpenApi (MultiVerb method (cs :: [Type]) as r)` + & S.content .~ singleton mediaType mediaTypeObject + where + mediaType = contentType $ Proxy @JSON + mediaTypeObject = mempty & S.schema ?~ S.Inline (S.toSchema (Proxy @UnreachableBackends)) type instance ErrorEffect UnreachableBackends = Error UnreachableBackends diff --git a/libs/wire-api/src/Wire/API/Error/Gundeck.hs b/libs/wire-api/src/Wire/API/Error/Gundeck.hs index f28432f45f1..ac9b6ce363f 100644 --- a/libs/wire-api/src/Wire/API/Error/Gundeck.hs +++ b/libs/wire-api/src/Wire/API/Error/Gundeck.hs @@ -17,6 +17,7 @@ module Wire.API.Error.Gundeck where +import Data.Typeable import Wire.API.Error data GundeckError @@ -28,8 +29,8 @@ data GundeckError | TokenNotFound | NotificationNotFound -instance KnownError (MapError e) => IsSwaggerError (e :: GundeckError) where - addToSwagger = addStaticErrorToSwagger @(MapError e) +instance (Typeable (MapError e), KnownError (MapError e)) => IsSwaggerError (e :: GundeckError) where + addToOpenApi = addStaticErrorToSwagger @(MapError e) type instance MapError 'AddTokenErrorNoBudget = 'StaticError 413 "sns-thread-budget-reached" "Too many concurrent calls to SNS; is SNS down?" diff --git a/libs/wire-api/src/Wire/API/Event/Conversation.hs b/libs/wire-api/src/Wire/API/Event/Conversation.hs index 2aeb06e131d..265bf5a5d8b 100644 --- a/libs/wire-api/src/Wire/API/Event/Conversation.hs +++ b/libs/wire-api/src/Wire/API/Event/Conversation.hs @@ -71,10 +71,11 @@ import Data.Aeson qualified as A import Data.Aeson.KeyMap qualified as KeyMap import Data.Id import Data.Json.Util +import Data.OpenApi (deprecated) +import Data.OpenApi qualified as S import Data.Qualified import Data.SOP import Data.Schema -import Data.Swagger qualified as S import Data.Time import Imports import Test.QuickCheck qualified as QC @@ -234,7 +235,9 @@ instance ToSchema SimpleMembers where .= optional ( fieldWithDocModifier "user_ids" - (description ?~ "deprecated") + ( (description ?~ "deprecated") + . (deprecated ?~ True) + ) (array schema) ) diff --git a/libs/wire-api/src/Wire/API/Event/FeatureConfig.hs b/libs/wire-api/src/Wire/API/Event/FeatureConfig.hs index e6982d8f45f..32e67dcfaf6 100644 --- a/libs/wire-api/src/Wire/API/Event/FeatureConfig.hs +++ b/libs/wire-api/src/Wire/API/Event/FeatureConfig.hs @@ -26,8 +26,8 @@ import Data.Aeson (toJSON) import Data.Aeson qualified as A import Data.Aeson.KeyMap qualified as KeyMap import Data.Json.Util (ToJSONObject (toJSONObject)) +import Data.OpenApi qualified as S import Data.Schema -import Data.Swagger qualified as S import GHC.TypeLits (KnownSymbol) import Imports import Test.QuickCheck.Gen (oneof) diff --git a/libs/wire-api/src/Wire/API/Event/Federation.hs b/libs/wire-api/src/Wire/API/Event/Federation.hs index 17d5120c0ce..55130a9ec84 100644 --- a/libs/wire-api/src/Wire/API/Event/Federation.hs +++ b/libs/wire-api/src/Wire/API/Event/Federation.hs @@ -9,8 +9,8 @@ import Data.Aeson qualified as A import Data.Aeson.KeyMap qualified as KeyMap import Data.Domain import Data.Json.Util (ToJSONObject (toJSONObject)) +import Data.OpenApi qualified as S import Data.Schema -import Data.Swagger qualified as S import Imports import Wire.Arbitrary diff --git a/libs/wire-api/src/Wire/API/Event/Team.hs b/libs/wire-api/src/Wire/API/Event/Team.hs index a6404dd851b..d5dac32eb39 100644 --- a/libs/wire-api/src/Wire/API/Event/Team.hs +++ b/libs/wire-api/src/Wire/API/Event/Team.hs @@ -42,8 +42,8 @@ import Data.Aeson.KeyMap qualified as KeyMap import Data.Aeson.Types (Parser) import Data.Id (ConvId, TeamId, UserId) import Data.Json.Util +import Data.OpenApi qualified as S import Data.Schema -import Data.Swagger qualified as S import Data.Time (UTCTime) import Imports import Test.QuickCheck qualified as QC diff --git a/libs/wire-api/src/Wire/API/FederationStatus.hs b/libs/wire-api/src/Wire/API/FederationStatus.hs index 257d95c96b3..b0e7a3c9859 100644 --- a/libs/wire-api/src/Wire/API/FederationStatus.hs +++ b/libs/wire-api/src/Wire/API/FederationStatus.hs @@ -10,8 +10,8 @@ import Data.Aeson (FromJSON (..), ToJSON (..), (.:)) import Data.Aeson qualified as A import Data.Aeson.Types qualified as A import Data.Domain +import Data.OpenApi qualified as S import Data.Schema -import Data.Swagger qualified as S import Imports import Wire.Arbitrary diff --git a/libs/wire-api/src/Wire/API/Internal/BulkPush.hs b/libs/wire-api/src/Wire/API/Internal/BulkPush.hs index 8c07c074a2c..0ffb9eec618 100644 --- a/libs/wire-api/src/Wire/API/Internal/BulkPush.hs +++ b/libs/wire-api/src/Wire/API/Internal/BulkPush.hs @@ -20,9 +20,9 @@ module Wire.API.Internal.BulkPush where import Control.Lens import Data.Aeson import Data.Id +import Data.OpenApi qualified as Swagger import Data.Schema (ValueSchema) import Data.Schema qualified as S -import Data.Swagger qualified as Swagger import Imports import Wire.API.Internal.Notification diff --git a/libs/wire-api/src/Wire/API/Internal/Notification.hs b/libs/wire-api/src/Wire/API/Internal/Notification.hs index 3c252180668..849c8125460 100644 --- a/libs/wire-api/src/Wire/API/Internal/Notification.hs +++ b/libs/wire-api/src/Wire/API/Internal/Notification.hs @@ -45,8 +45,8 @@ import Control.Lens (makeLenses) import Data.Aeson import Data.Id import Data.List1 +import Data.OpenApi qualified as Swagger import Data.Schema qualified as S -import Data.Swagger qualified as Swagger import Imports hiding (cs) import Wire.API.Notification diff --git a/libs/wire-api/src/Wire/API/MLS/CipherSuite.hs b/libs/wire-api/src/Wire/API/MLS/CipherSuite.hs index 4327cee928d..c6d93ac2e4e 100644 --- a/libs/wire-api/src/Wire/API/MLS/CipherSuite.hs +++ b/libs/wire-api/src/Wire/API/MLS/CipherSuite.hs @@ -25,10 +25,10 @@ import Crypto.Hash.Algorithms import Crypto.KDF.HKDF qualified as HKDF import Crypto.PubKey.Ed25519 qualified as Ed25519 import Data.Aeson (parseJSON, toJSON) +import Data.OpenApi qualified as S +import Data.OpenApi.Internal.Schema qualified as S import Data.Proxy import Data.Schema -import Data.Swagger qualified as S -import Data.Swagger.Internal.Schema qualified as S import Data.Word import Imports import Wire.API.MLS.Credential diff --git a/libs/wire-api/src/Wire/API/MLS/CommitBundle.hs b/libs/wire-api/src/Wire/API/MLS/CommitBundle.hs index a6c4e6753cd..8cff3683009 100644 --- a/libs/wire-api/src/Wire/API/MLS/CommitBundle.hs +++ b/libs/wire-api/src/Wire/API/MLS/CommitBundle.hs @@ -20,9 +20,9 @@ module Wire.API.MLS.CommitBundle where import Control.Lens (view, (.~), (?~)) import Data.Bifunctor (first) import Data.ByteString qualified as BS +import Data.OpenApi qualified as S import Data.ProtoLens (decodeMessage, encodeMessage) import Data.ProtoLens qualified (Message (defMessage)) -import Data.Swagger qualified as S import Data.Text qualified as T import Imports import Proto.Mls qualified diff --git a/libs/wire-api/src/Wire/API/MLS/Credential.hs b/libs/wire-api/src/Wire/API/MLS/Credential.hs index dc229102392..b3c92866f53 100644 --- a/libs/wire-api/src/Wire/API/MLS/Credential.hs +++ b/libs/wire-api/src/Wire/API/MLS/Credential.hs @@ -32,9 +32,9 @@ import Data.Binary.Parser import Data.Binary.Parser.Char8 import Data.Domain import Data.Id +import Data.OpenApi qualified as S import Data.Qualified import Data.Schema -import Data.Swagger qualified as S import Data.Text qualified as T import Data.UUID import Imports @@ -123,7 +123,7 @@ instance FromJSONKey SignatureSchemeTag where fromJSONKey = Aeson.FromJSONKeyTextParser parseSignatureScheme instance S.ToParamSchema SignatureSchemeTag where - toParamSchema _ = mempty & S.type_ ?~ S.SwaggerString + toParamSchema _ = mempty & S.type_ ?~ S.OpenApiString instance FromHttpApiData SignatureSchemeTag where parseQueryParam = note "Unknown signature scheme" . signatureSchemeFromName @@ -206,7 +206,7 @@ instance FromJSONKey SignaturePurpose where either fail pure . signaturePurposeFromName instance S.ToParamSchema SignaturePurpose where - toParamSchema _ = mempty & S.type_ ?~ S.SwaggerString + toParamSchema _ = mempty & S.type_ ?~ S.OpenApiString instance FromHttpApiData SignaturePurpose where parseQueryParam = first T.pack . signaturePurposeFromName diff --git a/libs/wire-api/src/Wire/API/MLS/Group.hs b/libs/wire-api/src/Wire/API/MLS/Group.hs index fbbefd015e1..4cff768d633 100644 --- a/libs/wire-api/src/Wire/API/MLS/Group.hs +++ b/libs/wire-api/src/Wire/API/MLS/Group.hs @@ -23,9 +23,9 @@ import Data.ByteArray (convert) import Data.ByteString.Conversion import Data.Id import Data.Json.Util +import Data.OpenApi qualified as S import Data.Qualified import Data.Schema -import Data.Swagger qualified as S import Imports import Wire.API.MLS.Serialisation import Wire.Arbitrary diff --git a/libs/wire-api/src/Wire/API/MLS/KeyPackage.hs b/libs/wire-api/src/Wire/API/MLS/KeyPackage.hs index 51a7267450f..a692179a9bc 100644 --- a/libs/wire-api/src/Wire/API/MLS/KeyPackage.hs +++ b/libs/wire-api/src/Wire/API/MLS/KeyPackage.hs @@ -49,9 +49,9 @@ import Data.ByteString qualified as B import Data.ByteString.Lazy qualified as LBS import Data.Id import Data.Json.Util +import Data.OpenApi qualified as S import Data.Qualified import Data.Schema -import Data.Swagger qualified as S import Imports hiding (cs) import Test.QuickCheck import Web.HttpApiData diff --git a/libs/wire-api/src/Wire/API/MLS/Keys.hs b/libs/wire-api/src/Wire/API/MLS/Keys.hs index 3bb54a9be20..9819b2c1f64 100644 --- a/libs/wire-api/src/Wire/API/MLS/Keys.hs +++ b/libs/wire-api/src/Wire/API/MLS/Keys.hs @@ -29,8 +29,8 @@ import Data.Aeson (FromJSON (..), ToJSON (..)) import Data.ByteArray import Data.Json.Util import Data.Map qualified as Map +import Data.OpenApi qualified as S import Data.Schema -import Data.Swagger qualified as S import Imports import Wire.API.MLS.Credential diff --git a/libs/wire-api/src/Wire/API/MLS/Message.hs b/libs/wire-api/src/Wire/API/MLS/Message.hs index 517ef2a7fa6..b24e8742988 100644 --- a/libs/wire-api/src/Wire/API/MLS/Message.hs +++ b/libs/wire-api/src/Wire/API/MLS/Message.hs @@ -51,9 +51,9 @@ import Data.Binary.Put import Data.ByteArray qualified as BA import Data.Json.Util import Data.Kind +import Data.OpenApi qualified as S import Data.Schema import Data.Singletons.TH -import Data.Swagger qualified as S import Imports hiding (cs) import Test.QuickCheck hiding (label) import Wire.API.Event.Conversation diff --git a/libs/wire-api/src/Wire/API/MLS/PublicGroupState.hs b/libs/wire-api/src/Wire/API/MLS/PublicGroupState.hs index 870b46f549d..fb467228d3b 100644 --- a/libs/wire-api/src/Wire/API/MLS/PublicGroupState.hs +++ b/libs/wire-api/src/Wire/API/MLS/PublicGroupState.hs @@ -22,7 +22,7 @@ import Data.Binary import Data.Binary.Get import Data.Binary.Put import Data.ByteString.Lazy qualified as LBS -import Data.Swagger qualified as S +import Data.OpenApi qualified as S import Imports import Test.QuickCheck hiding (label) import Wire.API.MLS.CipherSuite diff --git a/libs/wire-api/src/Wire/API/MLS/Serialisation.hs b/libs/wire-api/src/Wire/API/MLS/Serialisation.hs index 946bcbc7c39..f0e7f997c26 100644 --- a/libs/wire-api/src/Wire/API/MLS/Serialisation.hs +++ b/libs/wire-api/src/Wire/API/MLS/Serialisation.hs @@ -59,9 +59,9 @@ import Data.ByteString qualified as BS import Data.ByteString.Lazy qualified as LBS import Data.Json.Util import Data.Kind +import Data.OpenApi qualified as S import Data.Proxy import Data.Schema -import Data.Swagger qualified as S import Data.Text qualified as Text import Imports diff --git a/libs/wire-api/src/Wire/API/MLS/SubConversation.hs b/libs/wire-api/src/Wire/API/MLS/SubConversation.hs index 69ec37ade05..8d46721f7b5 100644 --- a/libs/wire-api/src/Wire/API/MLS/SubConversation.hs +++ b/libs/wire-api/src/Wire/API/MLS/SubConversation.hs @@ -26,8 +26,8 @@ import Control.Lens.Tuple (_1) import Control.Monad.Except import Data.Aeson (FromJSON (..), ToJSON (..)) import Data.Id +import Data.OpenApi qualified as S import Data.Schema -import Data.Swagger qualified as S import Data.Text qualified as T import Imports import Servant (FromHttpApiData (..), ToHttpApiData (toQueryParam)) diff --git a/libs/wire-api/src/Wire/API/MLS/Welcome.hs b/libs/wire-api/src/Wire/API/MLS/Welcome.hs index ac95b53dee0..2bb8e117eed 100644 --- a/libs/wire-api/src/Wire/API/MLS/Welcome.hs +++ b/libs/wire-api/src/Wire/API/MLS/Welcome.hs @@ -17,7 +17,7 @@ module Wire.API.MLS.Welcome where -import Data.Swagger qualified as S +import Data.OpenApi qualified as S import Imports hiding (cs) import Wire.API.MLS.CipherSuite import Wire.API.MLS.Commit diff --git a/libs/wire-api/src/Wire/API/MakesFederatedCall.hs b/libs/wire-api/src/Wire/API/MakesFederatedCall.hs index fbc133d6728..ba24fd4ee16 100644 --- a/libs/wire-api/src/Wire/API/MakesFederatedCall.hs +++ b/libs/wire-api/src/Wire/API/MakesFederatedCall.hs @@ -31,20 +31,22 @@ module Wire.API.MakesFederatedCall ) where +import Control.Lens ((<>~)) import Data.Aeson import Data.Constraint +import Data.HashSet.InsOrd (singleton) import Data.Kind import Data.Metrics.Servant +import Data.OpenApi qualified as S import Data.Proxy import Data.Schema -import Data.Swagger.Operation (addExtensions) import Data.Text qualified as T import GHC.TypeLits import Imports import Servant.API import Servant.Client +import Servant.OpenApi import Servant.Server -import Servant.Swagger import Test.QuickCheck (Arbitrary) import TransitiveAnns.Types import Unsafe.Coerce (unsafeCoerce) @@ -158,24 +160,32 @@ type instance -- | 'MakesFederatedCall' annotates the swagger documentation with an extension -- tag @x-wire-makes-federated-calls-to@. -instance (HasSwagger api, KnownSymbol name, KnownSymbol (ShowComponent comp)) => HasSwagger (MakesFederatedCall comp name :> api :: Type) where - toSwagger _ = - toSwagger (Proxy @api) - & addExtensions - mergeJSONArray - [ ( "wire-makes-federated-call-to", - Array - [ Array - [ String $ T.pack $ symbolVal $ Proxy @(ShowComponent comp), - String $ T.pack $ symbolVal $ Proxy @name - ] - ] +instance (HasOpenApi api, KnownSymbol name, KnownSymbol (ShowComponent comp)) => HasOpenApi (MakesFederatedCall comp name :> api :: Type) where + toOpenApi _ = + toOpenApi (Proxy @api) + -- Since extensions aren't in the openapi3 library yet, + -- and the PRs for their support seem be going no where quickly, I'm using + -- tags instead. https://github.com/biocad/openapi3/pull/43 + -- Basically, this is similar to the old system, except we don't have nested JSON to + -- work with. So I'm using the magic string and sticking the call name on the end + -- and sticking the component in the description. This ordering is important as we + -- can't have duplicate tag names on an object. + + -- Set the tags at the top of OpenApi object + & S.tags + <>~ singleton + ( S.Tag + name + (pure $ T.pack (symbolVal $ Proxy @(ShowComponent comp))) + Nothing ) - ] - -mergeJSONArray :: Value -> Value -> Value -mergeJSONArray (Array x) (Array y) = Array $ x <> y -mergeJSONArray _ _ = error "impossible! bug in construction of federated calls JSON" + -- Set the tags on the specific path we're looking at + -- This is where the tag is actually registered on the path + -- so it can be picked up by fedcalls. + & S.allOperations . S.tags <>~ setName + where + name = "wire-makes-federated-call-to-" <> T.pack (symbolVal $ Proxy @name) + setName = singleton name instance HasClient m api => HasClient m (MakesFederatedCall comp name :> api :: Type) where type Client m (MakesFederatedCall comp name :> api) = Client m api diff --git a/libs/wire-api/src/Wire/API/Message.hs b/libs/wire-api/src/Wire/API/Message.hs index e258cc3e74a..3b651796e21 100644 --- a/libs/wire-api/src/Wire/API/Message.hs +++ b/libs/wire-api/src/Wire/API/Message.hs @@ -67,6 +67,7 @@ import Data.Domain (Domain, domainText, mkDomain) import Data.Id import Data.Json.Util import Data.Map.Strict qualified as Map +import Data.OpenApi qualified as S import Data.ProtoLens qualified as ProtoLens import Data.ProtoLens.Field qualified as ProtoLens import Data.ProtocolBuffers qualified as Protobuf @@ -74,7 +75,6 @@ import Data.Qualified (Qualified (..)) import Data.Schema import Data.Serialize (runGet) import Data.Set qualified as Set -import Data.Swagger qualified as S import Data.Text.Read qualified as Reader import Data.UUID qualified as UUID import Imports @@ -553,7 +553,7 @@ data IgnoreMissing deriving (Show, Eq) instance S.ToParamSchema IgnoreMissing where - toParamSchema _ = mempty & S.type_ ?~ S.SwaggerString + toParamSchema _ = mempty & S.type_ ?~ S.OpenApiString instance FromHttpApiData IgnoreMissing where parseQueryParam = \case @@ -566,7 +566,7 @@ data ReportMissing | ReportMissingList (Set UserId) instance S.ToParamSchema ReportMissing where - toParamSchema _ = mempty & S.type_ ?~ S.SwaggerString + toParamSchema _ = mempty & S.type_ ?~ S.OpenApiString instance FromHttpApiData ReportMissing where parseQueryParam = \case diff --git a/libs/wire-api/src/Wire/API/Notification.hs b/libs/wire-api/src/Wire/API/Notification.hs index b404e05db61..1b7601bce10 100644 --- a/libs/wire-api/src/Wire/API/Notification.hs +++ b/libs/wire-api/src/Wire/API/Notification.hs @@ -47,10 +47,10 @@ import Data.HashMap.Strict.InsOrd qualified as InsOrdHashMap import Data.Id import Data.Json.Util import Data.List.NonEmpty (NonEmpty) +import Data.OpenApi (ToParamSchema (..)) +import Data.OpenApi qualified as S import Data.SOP import Data.Schema -import Data.Swagger (ToParamSchema (..)) -import Data.Swagger qualified as S import Data.Time.Clock (UTCTime) import Data.UUID qualified as UUID import Imports diff --git a/libs/wire-api/src/Wire/API/OAuth.hs b/libs/wire-api/src/Wire/API/OAuth.hs index b34b3ae38d6..293d154885c 100644 --- a/libs/wire-api/src/Wire/API/OAuth.hs +++ b/libs/wire-api/src/Wire/API/OAuth.hs @@ -31,11 +31,11 @@ import Data.ByteString.Lazy (toStrict) import Data.Either.Combinators (mapLeft) import Data.HashMap.Strict qualified as HM import Data.Id as Id +import Data.OpenApi (ToParamSchema (..)) +import Data.OpenApi qualified as S import Data.Range import Data.Schema import Data.Set qualified as Set -import Data.Swagger (ToParamSchema (..)) -import Data.Swagger qualified as S import Data.Text qualified as T import Data.Text.Ascii import Data.Text.Encoding qualified as TE @@ -45,7 +45,7 @@ import GHC.TypeLits (Nat, symbolVal) import Imports hiding (exp, head) import Prelude.Singletons (Show_) import Servant hiding (Handler, JSON, Tagged, addHeader, respond) -import Servant.Swagger.Internal.Orphans () +import Servant.OpenApi.Internal.Orphans () import Test.QuickCheck (Arbitrary (..)) import URI.ByteString import URI.ByteString.QQ qualified as URI.QQ @@ -641,8 +641,8 @@ data OAuthError | OAuthInvalidRefreshToken | OAuthInvalidGrant -instance KnownError (MapError e) => IsSwaggerError (e :: OAuthError) where - addToSwagger = addStaticErrorToSwagger @(MapError e) +instance (Typeable (MapError e), KnownError (MapError e)) => IsSwaggerError (e :: OAuthError) where + addToOpenApi = addStaticErrorToSwagger @(MapError e) type instance MapError 'OAuthClientNotFound = 'StaticError 404 "not-found" "OAuth client not found" diff --git a/libs/wire-api/src/Wire/API/Properties.hs b/libs/wire-api/src/Wire/API/Properties.hs index 70e03c71437..debcf9016d7 100644 --- a/libs/wire-api/src/Wire/API/Properties.hs +++ b/libs/wire-api/src/Wire/API/Properties.hs @@ -30,7 +30,7 @@ import Data.Aeson (FromJSON (..), ToJSON (..), Value) import Data.Aeson qualified as A import Data.ByteString.Conversion import Data.Hashable (Hashable) -import Data.Swagger qualified as S +import Data.OpenApi qualified as S import Data.Text.Ascii import Imports import Servant @@ -43,7 +43,7 @@ instance S.ToSchema PropertyKeysAndValues where declareNamedSchema _ = pure $ S.NamedSchema (Just "PropertyKeysAndValues") $ - mempty & S.type_ ?~ S.SwaggerObject + mempty & S.type_ ?~ S.OpenApiObject newtype PropertyKey = PropertyKey {propertyKeyName :: AsciiPrintable} @@ -64,7 +64,7 @@ newtype PropertyKey = PropertyKey instance S.ToParamSchema PropertyKey where toParamSchema _ = mempty - & S.type_ ?~ S.SwaggerString + & S.type_ ?~ S.OpenApiString & S.format ?~ "printable" -- | A raw, unparsed property value. diff --git a/libs/wire-api/src/Wire/API/Provider/Bot.hs b/libs/wire-api/src/Wire/API/Provider/Bot.hs index 0db3cabeaff..e8a1f5b1c4a 100644 --- a/libs/wire-api/src/Wire/API/Provider/Bot.hs +++ b/libs/wire-api/src/Wire/API/Provider/Bot.hs @@ -34,8 +34,8 @@ import Control.Lens (makeLenses) import Data.Aeson qualified as A import Data.Handle (Handle) import Data.Id +import Data.OpenApi qualified as S import Data.Schema -import Data.Swagger qualified as S import Imports import Wire.API.Conversation.Member (OtherMember (..)) import Wire.API.User.Profile (ColourId, Name) diff --git a/libs/wire-api/src/Wire/API/Provider/Service.hs b/libs/wire-api/src/Wire/API/Provider/Service.hs index 28a1e5609a1..c1110a58f25 100644 --- a/libs/wire-api/src/Wire/API/Provider/Service.hs +++ b/libs/wire-api/src/Wire/API/Provider/Service.hs @@ -61,11 +61,11 @@ import Data.Id import Data.Json.Util ((#)) import Data.List1 (List1) import Data.Misc (HttpsUrl (..), PlainTextPassword6) +import Data.OpenApi qualified as S import Data.PEM (PEM, pemParseBS, pemWriteLBS) import Data.Proxy import Data.Range (Range) import Data.Schema -import Data.Swagger qualified as S import Data.Text qualified as Text import Data.Text.Ascii import Data.Text.Encoding qualified as Text diff --git a/libs/wire-api/src/Wire/API/Provider/Service/Tag.hs b/libs/wire-api/src/Wire/API/Provider/Service/Tag.hs index 522c519ff87..07f0910ce5e 100644 --- a/libs/wire-api/src/Wire/API/Provider/Service/Tag.hs +++ b/libs/wire-api/src/Wire/API/Provider/Service/Tag.hs @@ -41,13 +41,18 @@ where import Data.Aeson (FromJSON (parseJSON), ToJSON (toJSON)) import Data.Aeson qualified as JSON +import Data.ByteString (toStrict) import Data.ByteString.Builder qualified as BB import Data.ByteString.Char8 qualified as C8 import Data.ByteString.Conversion -import Data.Range (Range, fromRange) +import Data.OpenApi qualified as S +import Data.Range (Range, fromRange, rangedSchema) import Data.Range qualified as Range +import Data.Schema import Data.Set qualified as Set +import Data.Text.Encoding (decodeUtf8With) import Data.Text.Encoding qualified as Text +import Data.Text.Encoding.Error (lenientDecode) import Data.Type.Ord import GHC.TypeLits (KnownNat, Nat) import Imports @@ -173,6 +178,16 @@ instance FromJSON ServiceTag where JSON.withText "ServiceTag" $ either fail pure . runParser parser . Text.encodeUtf8 +instance ToSchema ServiceTag where + schema = enum @Text "" . mconcat $ (\a -> element (decodeUtf8With lenientDecode $ toStrict $ toByteString a) a) <$> [minBound ..] + +instance S.ToParamSchema ServiceTag where + toParamSchema _ = + mempty + { S._schemaType = Just S.OpenApiString, + S._schemaEnum = Just (toJSON <$> [(minBound :: ServiceTag) ..]) + } + -------------------------------------------------------------------------------- -- Bounded ServiceTag Queries @@ -181,6 +196,19 @@ newtype QueryAnyTags (m :: Nat) (n :: Nat) = QueryAnyTags {queryAnyTagsRange :: Range m n (Set (QueryAllTags m n))} deriving stock (Eq, Show, Ord) +instance (m <= n) => S.ToParamSchema (QueryAnyTags m n) where + toParamSchema _ = + mempty + { S._schemaType = Just S.OpenApiString, + S._schemaEnum = Just (toJSON <$> [(minBound :: ServiceTag) ..]) + } + +instance (KnownNat n, KnownNat m, m <= n) => ToSchema (QueryAnyTags m n) where + schema = + let sch :: ValueSchema NamedSwaggerDoc (Range m n (Set (QueryAllTags m n))) + sch = fromRange .= rangedSchema (named "QueryAnyTags" $ set schema) + in queryAnyTagsRange .= (QueryAnyTags <$> sch) + instance (KnownNat m, KnownNat n, m <= n) => Arbitrary (QueryAnyTags m n) where arbitrary = QueryAnyTags <$> arbitrary @@ -236,6 +264,12 @@ instance (KnownNat m, KnownNat n, m <= n) => FromByteString (QueryAllTags m n) w rs <- either fail pure (Range.checkedEither (Set.fromList ts)) pure $! QueryAllTags rs +instance (KnownNat m, KnownNat n, m <= n) => ToSchema (QueryAllTags m n) where + schema = + let sch :: ValueSchema NamedSwaggerDoc (Range m n (Set ServiceTag)) + sch = fromRange .= rangedSchema (named "QueryAllTags" $ set schema) + in queryAllTagsRange .= fmap QueryAllTags sch + -------------------------------------------------------------------------------- -- ServiceTag Matchers diff --git a/libs/wire-api/src/Wire/API/Push/V2/Token.hs b/libs/wire-api/src/Wire/API/Push/V2/Token.hs index ee8e828670d..0cf7b292af4 100644 --- a/libs/wire-api/src/Wire/API/Push/V2/Token.hs +++ b/libs/wire-api/src/Wire/API/Push/V2/Token.hs @@ -47,10 +47,10 @@ import Data.Aeson qualified as A import Data.Attoparsec.ByteString (takeByteString) import Data.ByteString.Conversion import Data.Id +import Data.OpenApi (ToParamSchema) +import Data.OpenApi qualified as S import Data.SOP import Data.Schema -import Data.Swagger (ToParamSchema) -import Data.Swagger qualified as S import Generics.SOP qualified as GSOP import Imports import Servant diff --git a/libs/wire-api/src/Wire/API/RawJson.hs b/libs/wire-api/src/Wire/API/RawJson.hs index fd0517ea289..08529ded900 100644 --- a/libs/wire-api/src/Wire/API/RawJson.hs +++ b/libs/wire-api/src/Wire/API/RawJson.hs @@ -20,7 +20,7 @@ module Wire.API.RawJson where import Control.Lens -import Data.Swagger qualified as Swagger +import Data.OpenApi qualified as Swagger import Imports import Servant import Test.QuickCheck @@ -43,6 +43,6 @@ instance Swagger.ToSchema RawJson where declareNamedSchema _ = pure . Swagger.NamedSchema (Just "RawJson") $ mempty - & Swagger.type_ ?~ Swagger.SwaggerObject + & Swagger.type_ ?~ Swagger.OpenApiObject & Swagger.description ?~ "Any JSON as plain string. The object structure is not specified in this schema." diff --git a/libs/wire-api/src/Wire/API/Routes/API.hs b/libs/wire-api/src/Wire/API/Routes/API.hs index b43569bf76f..23ac38e6fed 100644 --- a/libs/wire-api/src/Wire/API/Routes/API.hs +++ b/libs/wire-api/src/Wire/API/Routes/API.hs @@ -31,14 +31,14 @@ where import Data.Domain import Data.Kind +import Data.OpenApi qualified as S import Data.Proxy -import Data.Swagger qualified as S import Imports import Polysemy import Polysemy.Error import Polysemy.Internal import Servant hiding (Union) -import Servant.Swagger +import Servant.OpenApi import Wire.API.Error import Wire.API.Routes.Named import Wire.API.Routes.Version @@ -47,8 +47,8 @@ class ServiceAPI service (v :: Version) where type ServiceAPIRoutes service type SpecialisedAPIRoutes v service :: Type type SpecialisedAPIRoutes v service = SpecialiseToVersion v (ServiceAPIRoutes service) - serviceSwagger :: HasSwagger (SpecialisedAPIRoutes v service) => S.Swagger - serviceSwagger = toSwagger (Proxy @(SpecialisedAPIRoutes v service)) + serviceSwagger :: HasOpenApi (SpecialisedAPIRoutes v service) => S.OpenApi + serviceSwagger = toOpenApi (Proxy @(SpecialisedAPIRoutes v service)) instance ServiceAPI VersionAPITag v where type ServiceAPIRoutes VersionAPITag = VersionAPI diff --git a/libs/wire-api/src/Wire/API/Routes/AssetBody.hs b/libs/wire-api/src/Wire/API/Routes/AssetBody.hs index 2b6989d308b..4998c10f538 100644 --- a/libs/wire-api/src/Wire/API/Routes/AssetBody.hs +++ b/libs/wire-api/src/Wire/API/Routes/AssetBody.hs @@ -25,13 +25,13 @@ where import Conduit import Data.ByteString.Lazy qualified as LBS -import Data.Swagger -import Data.Swagger.Internal.Schema +import Data.OpenApi +import Data.OpenApi.Internal.Schema import Imports import Network.HTTP.Media ((//)) import Servant import Servant.Conduit () -import Servant.Swagger.Internal.Orphans () +import Servant.OpenApi.Internal.Orphans () data MultipartMixed diff --git a/libs/wire-api/src/Wire/API/Routes/Bearer.hs b/libs/wire-api/src/Wire/API/Routes/Bearer.hs index 545db5254df..64a1baed79f 100644 --- a/libs/wire-api/src/Wire/API/Routes/Bearer.hs +++ b/libs/wire-api/src/Wire/API/Routes/Bearer.hs @@ -21,11 +21,11 @@ import Control.Lens ((<>~)) import Data.ByteString qualified as BS import Data.HashMap.Strict.InsOrd qualified as InsOrdHashMap import Data.Metrics.Servant -import Data.Swagger hiding (Header) +import Data.OpenApi hiding (HasServer, Header) import Data.Text.Encoding qualified as T import Imports import Servant -import Servant.Swagger +import Servant.OpenApi import Wire.API.Routes.Version newtype Bearer a = Bearer {unBearer :: a} @@ -47,9 +47,9 @@ type instance SpecialiseToVersion v (Bearer a :> api) = Bearer a :> SpecialiseToVersion v api -instance HasSwagger api => HasSwagger (Bearer a :> api) where - toSwagger _ = - toSwagger (Proxy @api) +instance HasOpenApi api => HasOpenApi (Bearer a :> api) where + toOpenApi _ = + toOpenApi (Proxy @api) & security <>~ [SecurityRequirement $ InsOrdHashMap.singleton "ZAuth" []] instance RoutesToPaths api => RoutesToPaths (Bearer a :> api) where diff --git a/libs/wire-api/src/Wire/API/Routes/CSV.hs b/libs/wire-api/src/Wire/API/Routes/CSV.hs index 0d09941545c..0345336c378 100644 --- a/libs/wire-api/src/Wire/API/Routes/CSV.hs +++ b/libs/wire-api/src/Wire/API/Routes/CSV.hs @@ -17,6 +17,10 @@ module Wire.API.Routes.CSV where +import Control.Lens +import Data.OpenApi qualified as O +import Data.OpenApi.Internal.Schema +import Imports import Network.HTTP.Media.MediaType import Servant.API @@ -24,3 +28,11 @@ data CSV instance Accept CSV where contentType _ = "text" // "csv" + +instance ToSchema CSV where + declareNamedSchema _ = + plain $ + mempty + & O.title ?~ "CSV" + & O.type_ ?~ O.OpenApiString + & O.format ?~ "text/csv" diff --git a/libs/wire-api/src/Wire/API/Routes/Cookies.hs b/libs/wire-api/src/Wire/API/Routes/Cookies.hs index 10383904ccb..2449f074c76 100644 --- a/libs/wire-api/src/Wire/API/Routes/Cookies.hs +++ b/libs/wire-api/src/Wire/API/Routes/Cookies.hs @@ -27,7 +27,7 @@ import Data.Text.Encoding qualified as T import GHC.TypeLits import Imports import Servant -import Servant.Swagger +import Servant.OpenApi import Web.Cookie (parseCookies) import Wire.API.Routes.Version @@ -63,8 +63,8 @@ type instance SpecialiseToVersion v (Cookies cs :> api) = Cookies cs :> SpecialiseToVersion v api -instance HasSwagger api => HasSwagger (Cookies cs :> api) where - toSwagger _ = toSwagger (Proxy @api) +instance HasOpenApi api => HasOpenApi (Cookies cs :> api) where + toOpenApi _ = toOpenApi (Proxy @api) class CookieArgs (cs :: [Type]) where -- example: AddArgs ["foo" :: Foo, "bar" :: Bar] a = Foo -> Bar -> a diff --git a/libs/wire-api/src/Wire/API/Routes/FederationDomainConfig.hs b/libs/wire-api/src/Wire/API/Routes/FederationDomainConfig.hs index 5ce5e7ca871..8530f78275a 100644 --- a/libs/wire-api/src/Wire/API/Routes/FederationDomainConfig.hs +++ b/libs/wire-api/src/Wire/API/Routes/FederationDomainConfig.hs @@ -26,8 +26,8 @@ where import Control.Lens ((?~)) import Data.Aeson (FromJSON, ToJSON) import Data.Domain (Domain) +import Data.OpenApi qualified as S import Data.Schema -import Data.Swagger qualified as S import GHC.Generics import Imports import Wire.API.User.Search (FederatedUserSearchPolicy) diff --git a/libs/wire-api/src/Wire/API/Routes/Internal/Brig.hs b/libs/wire-api/src/Wire/API/Routes/Internal/Brig.hs index 8a343a285b3..10308c8a58e 100644 --- a/libs/wire-api/src/Wire/API/Routes/Internal/Brig.hs +++ b/libs/wire-api/src/Wire/API/Routes/Internal/Brig.hs @@ -46,14 +46,14 @@ import Data.CommaSeparatedList import Data.Domain (Domain) import Data.Handle (Handle) import Data.Id as Id +import Data.OpenApi (HasInfo (info), HasTitle (title), OpenApi) +import Data.OpenApi qualified as S import Data.Qualified (Qualified) import Data.Schema hiding (swaggerDoc) -import Data.Swagger (HasInfo (info), HasTitle (title), Swagger) -import Data.Swagger qualified as S import Imports hiding (head) import Servant hiding (Handler, WithStatus, addHeader, respond) -import Servant.Swagger (HasSwagger (toSwagger)) -import Servant.Swagger.Internal.Orphans () +import Servant.OpenApi (HasOpenApi (toOpenApi)) +import Servant.OpenApi.Internal.Orphans () import Wire.API.Connection import Wire.API.Error import Wire.API.Error.Brig @@ -768,7 +768,7 @@ type FederationRemotesAPI = type FederationRemotesAPIDescription = "See https://docs.wire.com/understand/federation/backend-communication.html#configuring-remote-connections for background. " -swaggerDoc :: Swagger +swaggerDoc :: OpenApi swaggerDoc = - toSwagger (Proxy @API) + toOpenApi (Proxy @API) & info . title .~ "Wire-Server internal brig API" diff --git a/libs/wire-api/src/Wire/API/Routes/Internal/Brig/Connection.hs b/libs/wire-api/src/Wire/API/Routes/Internal/Brig/Connection.hs index f9226607259..7f3d76810cf 100644 --- a/libs/wire-api/src/Wire/API/Routes/Internal/Brig/Connection.hs +++ b/libs/wire-api/src/Wire/API/Routes/Internal/Brig/Connection.hs @@ -21,9 +21,9 @@ module Wire.API.Routes.Internal.Brig.Connection where import Data.Aeson (FromJSON, ToJSON) import Data.Id +import Data.OpenApi qualified as S import Data.Qualified import Data.Schema -import Data.Swagger qualified as S import Imports import Wire.API.Connection diff --git a/libs/wire-api/src/Wire/API/Routes/Internal/Brig/EJPD.hs b/libs/wire-api/src/Wire/API/Routes/Internal/Brig/EJPD.hs index efd26df2ee0..93db38b2974 100644 --- a/libs/wire-api/src/Wire/API/Routes/Internal/Brig/EJPD.hs +++ b/libs/wire-api/src/Wire/API/Routes/Internal/Brig/EJPD.hs @@ -28,7 +28,7 @@ where import Data.Aeson hiding (json) import Data.Handle (Handle) import Data.Id (TeamId, UserId) -import Data.Swagger (ToSchema) +import Data.OpenApi (ToSchema) import Deriving.Swagger (CamelToSnake, CustomSwagger (..), FieldLabelModifier, StripSuffix) import Imports hiding (head) import Test.QuickCheck (Arbitrary) diff --git a/libs/wire-api/src/Wire/API/Routes/Internal/Brig/OAuth.hs b/libs/wire-api/src/Wire/API/Routes/Internal/Brig/OAuth.hs index a8a2747af7d..8974da4c27c 100644 --- a/libs/wire-api/src/Wire/API/Routes/Internal/Brig/OAuth.hs +++ b/libs/wire-api/src/Wire/API/Routes/Internal/Brig/OAuth.hs @@ -20,7 +20,7 @@ module Wire.API.Routes.Internal.Brig.OAuth where import Data.Id (OAuthClientId) import Servant (JSON) import Servant hiding (Handler, JSON, Tagged, addHeader, respond) -import Servant.Swagger.Internal.Orphans () +import Servant.OpenApi.Internal.Orphans () import Wire.API.Error import Wire.API.OAuth import Wire.API.Routes.Named (Named (..)) diff --git a/libs/wire-api/src/Wire/API/Routes/Internal/Brig/SearchIndex.hs b/libs/wire-api/src/Wire/API/Routes/Internal/Brig/SearchIndex.hs index 6b45b977e68..0cca4948901 100644 --- a/libs/wire-api/src/Wire/API/Routes/Internal/Brig/SearchIndex.hs +++ b/libs/wire-api/src/Wire/API/Routes/Internal/Brig/SearchIndex.hs @@ -19,7 +19,7 @@ module Wire.API.Routes.Internal.Brig.SearchIndex where import Servant (JSON) import Servant hiding (Handler, JSON, Tagged, addHeader, respond) -import Servant.Swagger.Internal.Orphans () +import Servant.OpenApi.Internal.Orphans () import Wire.API.Routes.Named (Named (..)) type ISearchIndexAPI = diff --git a/libs/wire-api/src/Wire/API/Routes/Internal/Cannon.hs b/libs/wire-api/src/Wire/API/Routes/Internal/Cannon.hs index ff0fe916a1a..b8f1652bc7a 100644 --- a/libs/wire-api/src/Wire/API/Routes/Internal/Cannon.hs +++ b/libs/wire-api/src/Wire/API/Routes/Internal/Cannon.hs @@ -2,10 +2,10 @@ module Wire.API.Routes.Internal.Cannon where import Control.Lens ((.~)) import Data.Id -import Data.Swagger (HasInfo (info), HasTitle (title), Swagger) +import Data.OpenApi (HasInfo (info), HasTitle (title), OpenApi) import Imports import Servant -import Servant.Swagger (HasSwagger (toSwagger)) +import Servant.OpenApi (HasOpenApi (toOpenApi)) import Wire.API.Error import Wire.API.Error.Cannon import Wire.API.Internal.BulkPush @@ -59,7 +59,7 @@ type API = ) ) -swaggerDoc :: Swagger +swaggerDoc :: OpenApi swaggerDoc = - toSwagger (Proxy @API) + toOpenApi (Proxy @API) & info . title .~ "Wire-Server internal cannon API" diff --git a/libs/wire-api/src/Wire/API/Routes/Internal/Cargohold.hs b/libs/wire-api/src/Wire/API/Routes/Internal/Cargohold.hs index 825623ac9c6..cb9599b441e 100644 --- a/libs/wire-api/src/Wire/API/Routes/Internal/Cargohold.hs +++ b/libs/wire-api/src/Wire/API/Routes/Internal/Cargohold.hs @@ -18,10 +18,10 @@ module Wire.API.Routes.Internal.Cargohold where import Control.Lens -import Data.Swagger +import Data.OpenApi import Imports import Servant -import Servant.Swagger +import Servant.OpenApi import Wire.API.Routes.MultiVerb type InternalAPI = @@ -29,7 +29,7 @@ type InternalAPI = :> "status" :> MultiVerb 'GET '() '[RespondEmpty 200 "OK"] () -swaggerDoc :: Swagger +swaggerDoc :: OpenApi swaggerDoc = - toSwagger (Proxy @InternalAPI) + toOpenApi (Proxy @InternalAPI) & info . title .~ "Wire-Server internal cargohold API" diff --git a/libs/wire-api/src/Wire/API/Routes/Internal/Galley.hs b/libs/wire-api/src/Wire/API/Routes/Internal/Galley.hs index e9d4e7e834f..ccfcf69a617 100644 --- a/libs/wire-api/src/Wire/API/Routes/Internal/Galley.hs +++ b/libs/wire-api/src/Wire/API/Routes/Internal/Galley.hs @@ -19,13 +19,13 @@ module Wire.API.Routes.Internal.Galley where import Control.Lens ((.~)) import Data.Id as Id +import Data.OpenApi (OpenApi, info, title) import Data.Range -import Data.Swagger (Swagger, info, title) import GHC.TypeLits (AppendSymbol) import Imports hiding (head) import Servant hiding (JSON, WithStatus) import Servant qualified hiding (WithStatus) -import Servant.Swagger +import Servant.OpenApi import Wire.API.ApplyMods import Wire.API.Conversation.Role import Wire.API.Error @@ -426,7 +426,7 @@ type IFederationAPI = :> Get '[Servant.JSON] FederationStatus ) -swaggerDoc :: Swagger +swaggerDoc :: OpenApi swaggerDoc = - toSwagger (Proxy @InternalAPI) + toOpenApi (Proxy @InternalAPI) & info . title .~ "Wire-Server internal galley API" diff --git a/libs/wire-api/src/Wire/API/Routes/Internal/Galley/ConversationsIntra.hs b/libs/wire-api/src/Wire/API/Routes/Internal/Galley/ConversationsIntra.hs index cd81ed7473e..b644906cd95 100644 --- a/libs/wire-api/src/Wire/API/Routes/Internal/Galley/ConversationsIntra.hs +++ b/libs/wire-api/src/Wire/API/Routes/Internal/Galley/ConversationsIntra.hs @@ -26,9 +26,9 @@ where import Data.Aeson qualified as A import Data.Aeson.Types (FromJSON, ToJSON) import Data.Id (ConvId, UserId) +import Data.OpenApi qualified as Swagger import Data.Qualified import Data.Schema -import Data.Swagger qualified as Swagger import Imports data DesiredMembership = Included | Excluded diff --git a/libs/wire-api/src/Wire/API/Routes/Internal/Galley/TeamFeatureNoConfigMulti.hs b/libs/wire-api/src/Wire/API/Routes/Internal/Galley/TeamFeatureNoConfigMulti.hs index fdb40b05aec..9f96c0b024c 100644 --- a/libs/wire-api/src/Wire/API/Routes/Internal/Galley/TeamFeatureNoConfigMulti.hs +++ b/libs/wire-api/src/Wire/API/Routes/Internal/Galley/TeamFeatureNoConfigMulti.hs @@ -24,8 +24,8 @@ where import Data.Aeson qualified as A import Data.Id +import Data.OpenApi qualified as S import Data.Schema -import Data.Swagger qualified as S import Imports import Wire.API.Team.Feature qualified as Public diff --git a/libs/wire-api/src/Wire/API/Routes/Internal/Galley/TeamsIntra.hs b/libs/wire-api/src/Wire/API/Routes/Internal/Galley/TeamsIntra.hs index 09432560ea5..0bc3ae5a593 100644 --- a/libs/wire-api/src/Wire/API/Routes/Internal/Galley/TeamsIntra.hs +++ b/libs/wire-api/src/Wire/API/Routes/Internal/Galley/TeamsIntra.hs @@ -31,8 +31,8 @@ import Control.Lens ((?~)) import Data.Aeson import Data.Currency qualified as Currency import Data.Json.Util +import Data.OpenApi qualified as Swagger import Data.Schema qualified as S -import Data.Swagger qualified as Swagger import Data.Time (UTCTime) import Imports import Test.QuickCheck.Arbitrary (Arbitrary) diff --git a/libs/wire-api/src/Wire/API/Routes/Internal/LegalHold.hs b/libs/wire-api/src/Wire/API/Routes/Internal/LegalHold.hs index 69d114dca82..ffde2e561c3 100644 --- a/libs/wire-api/src/Wire/API/Routes/Internal/LegalHold.hs +++ b/libs/wire-api/src/Wire/API/Routes/Internal/LegalHold.hs @@ -19,11 +19,12 @@ module Wire.API.Routes.Internal.LegalHold where import Control.Lens import Data.Id +import Data.OpenApi (OpenApi) +import Data.OpenApi.Lens import Data.Proxy -import Data.Swagger import Imports import Servant.API hiding (Header, WithStatus) -import Servant.Swagger +import Servant.OpenApi import Wire.API.Team.Feature type InternalLegalHoldAPI = @@ -38,7 +39,7 @@ type InternalLegalHoldAPI = :> Put '[] NoContent ) -swaggerDoc :: Swagger +swaggerDoc :: OpenApi swaggerDoc = - toSwagger (Proxy @InternalLegalHoldAPI) + toOpenApi (Proxy @InternalLegalHoldAPI) & info . title .~ "Wire-Server internal legalhold API" diff --git a/libs/wire-api/src/Wire/API/Routes/Internal/Spar.hs b/libs/wire-api/src/Wire/API/Routes/Internal/Spar.hs index 63f2358f5e1..8cc2207031c 100644 --- a/libs/wire-api/src/Wire/API/Routes/Internal/Spar.hs +++ b/libs/wire-api/src/Wire/API/Routes/Internal/Spar.hs @@ -19,10 +19,10 @@ module Wire.API.Routes.Internal.Spar where import Control.Lens import Data.Id -import Data.Swagger +import Data.OpenApi import Imports import Servant -import Servant.Swagger +import Servant.OpenApi import Wire.API.User import Wire.API.User.Saml @@ -34,7 +34,7 @@ type InternalAPI = :<|> "scim" :> "userinfos" :> ReqBody '[JSON] UserSet :> Post '[JSON] ScimUserInfos ) -swaggerDoc :: Swagger +swaggerDoc :: OpenApi swaggerDoc = - toSwagger (Proxy @InternalAPI) + toOpenApi (Proxy @InternalAPI) & info . title .~ "Wire-Server internal spar API" diff --git a/libs/wire-api/src/Wire/API/Routes/LowLevelStream.hs b/libs/wire-api/src/Wire/API/Routes/LowLevelStream.hs index d9287bd5fa9..f39080b54f7 100644 --- a/libs/wire-api/src/Wire/API/Routes/LowLevelStream.hs +++ b/libs/wire-api/src/Wire/API/Routes/LowLevelStream.hs @@ -17,13 +17,13 @@ module Wire.API.Routes.LowLevelStream where -import Control.Lens (at, (.~), (?~)) +import Control.Lens (at, (.~), (?~), _Just) import Data.ByteString.Char8 as B8 import Data.CaseInsensitive qualified as CI import Data.HashMap.Strict.InsOrd qualified as InsOrdHashMap import Data.Metrics.Servant +import Data.OpenApi qualified as S import Data.Proxy -import Data.Swagger qualified as S import Data.Text qualified as Text import GHC.TypeLits import Imports @@ -33,10 +33,10 @@ import Network.Wai import Servant.API import Servant.API.ContentTypes import Servant.API.Status +import Servant.OpenApi as S +import Servant.OpenApi.Internal as S import Servant.Server hiding (respond) import Servant.Server.Internal -import Servant.Swagger as S -import Servant.Swagger.Internal as S import Wire.API.Routes.Version -- FUTUREWORK: make it possible to generate headers at runtime @@ -90,27 +90,30 @@ type instance LowLevelStream m s h d t instance - (Accept ctype, KnownNat status, KnownSymbol desc, SwaggerMethod method) => - HasSwagger (LowLevelStream method status headers desc ctype) + (S.ToSchema ctype, Accept ctype, KnownNat status, KnownSymbol desc, OpenApiMethod method) => + HasOpenApi (LowLevelStream method status headers desc ctype) where - toSwagger _ = + toOpenApi _ = mempty & S.paths . at "/" ?~ ( mempty & method ?~ ( mempty - & S.produces ?~ S.MimeList [contentType (Proxy @ctype)] & S.responses . S.responses .~ fmap S.Inline responses ) ) where - method = S.swaggerMethod (Proxy @method) + method = S.openApiMethod (Proxy @method) responses = InsOrdHashMap.singleton (fromIntegral (natVal (Proxy @status))) $ mempty & S.description .~ Text.pack (symbolVal (Proxy @desc)) + & S.content + .~ InsOrdHashMap.singleton + (contentType $ Proxy @ctype) + (mempty & S.schema . _Just . S._Inline .~ S.toSchema (Proxy @ctype)) instance RoutesToPaths (LowLevelStream method status headers desc ctype) where getRoutes = [] diff --git a/libs/wire-api/src/Wire/API/Routes/MultiTablePaging.hs b/libs/wire-api/src/Wire/API/Routes/MultiTablePaging.hs index e0438210f77..0fc48cdaf06 100644 --- a/libs/wire-api/src/Wire/API/Routes/MultiTablePaging.hs +++ b/libs/wire-api/src/Wire/API/Routes/MultiTablePaging.hs @@ -26,10 +26,10 @@ where import Control.Lens ((?~)) import Data.Aeson (FromJSON (..), ToJSON (..)) import Data.Kind +import Data.OpenApi qualified as S import Data.Proxy import Data.Range import Data.Schema -import Data.Swagger qualified as S import Data.Text qualified as Text import GHC.TypeLits import Imports @@ -77,7 +77,10 @@ deriving via deriving via Schema (GetMultiTablePageRequest name tables max def) instance - RequestSchemaConstraint name tables max def => S.ToSchema (GetMultiTablePageRequest name tables max def) + ( Typeable tables, + RequestSchemaConstraint name tables max def + ) => + S.ToSchema (GetMultiTablePageRequest name tables max def) instance RequestSchemaConstraint name tables max def => ToSchema (GetMultiTablePageRequest name tables max def) where schema = @@ -126,7 +129,7 @@ deriving via deriving via (Schema (MultiTablePage name resultsKey tables a)) instance - PageSchemaConstraints name resultsKey tables a => + (Typeable tables, Typeable a, PageSchemaConstraints name resultsKey tables a) => S.ToSchema (MultiTablePage name resultsKey tables a) instance diff --git a/libs/wire-api/src/Wire/API/Routes/MultiTablePaging/State.hs b/libs/wire-api/src/Wire/API/Routes/MultiTablePaging/State.hs index 197e44a959b..7d43b3009be 100644 --- a/libs/wire-api/src/Wire/API/Routes/MultiTablePaging/State.hs +++ b/libs/wire-api/src/Wire/API/Routes/MultiTablePaging/State.hs @@ -26,9 +26,9 @@ import Data.Attoparsec.ByteString qualified as AB import Data.ByteString qualified as BS import Data.ByteString.Base64.URL qualified as Base64Url import Data.Either.Combinators (mapLeft) +import Data.OpenApi qualified as S import Data.Proxy import Data.Schema -import Data.Swagger qualified as S import Data.Text qualified as Text import Data.Text.Encoding qualified as Text import GHC.TypeLits diff --git a/libs/wire-api/src/Wire/API/Routes/MultiVerb.hs b/libs/wire-api/src/Wire/API/Routes/MultiVerb.hs index db16fb8fc01..ed24bbfdbe5 100644 --- a/libs/wire-api/src/Wire/API/Routes/MultiVerb.hs +++ b/libs/wire-api/src/Wire/API/Routes/MultiVerb.hs @@ -41,6 +41,7 @@ module Wire.API.Routes.MultiVerb ResponseType, IsResponse (..), IsSwaggerResponse (..), + IsSwaggerResponseList (..), simpleResponseSwagger, combineResponseSwagger, ResponseTypes, @@ -54,18 +55,18 @@ import Control.Lens hiding (Context, (<|)) import Data.ByteString.Builder import Data.ByteString.Lazy qualified as LBS import Data.CaseInsensitive qualified as CI -import Data.Containers.ListUtils import Data.Either.Combinators (leftToMaybe) -import Data.HashMap.Strict.InsOrd (InsOrdHashMap) +import Data.HashMap.Strict.InsOrd (InsOrdHashMap, unionWith) import Data.HashMap.Strict.InsOrd qualified as InsOrdHashMap import Data.Kind import Data.Metrics.Servant +import Data.OpenApi hiding (HasServer, Response, contentType) +import Data.OpenApi qualified as S +import Data.OpenApi.Declare qualified as S import Data.Proxy import Data.SOP import Data.Sequence (Seq, (<|), pattern (:<|)) import Data.Sequence qualified as Seq -import Data.Swagger qualified as S -import Data.Swagger.Declare qualified as S import Data.Text qualified as Text import Data.Text.Encoding qualified as Text import Data.Typeable @@ -82,10 +83,10 @@ import Servant.API.ContentTypes import Servant.API.Status (KnownStatus (..)) import Servant.Client import Servant.Client.Core hiding (addHeader) +import Servant.OpenApi as S +import Servant.OpenApi.Internal as S import Servant.Server import Servant.Server.Internal -import Servant.Swagger as S -import Servant.Swagger.Internal as S import Servant.Types.SourceT type Declare = S.Declare (S.Definitions S.Schema) @@ -191,19 +192,25 @@ instance (AllMimeRender cs a, AllMimeUnrender cs a, KnownStatus s) => IsResponse Nothing -> empty Just f -> either UnrenderError UnrenderSuccess (f (responseBody output)) -simpleResponseSwagger :: forall a desc. (S.ToSchema a, KnownSymbol desc) => Declare S.Response +simpleResponseSwagger :: forall a cs desc. (S.ToSchema a, KnownSymbol desc, AllMime cs) => Declare S.Response simpleResponseSwagger = do ref <- S.declareSchemaRef (Proxy @a) + let resps :: InsOrdHashMap M.MediaType MediaTypeObject + resps = InsOrdHashMap.fromList $ (,MediaTypeObject (pure ref) Nothing mempty mempty) <$> cs pure $ mempty & S.description .~ Text.pack (symbolVal (Proxy @desc)) - & S.schema ?~ ref + & S.content .~ resps + where + cs :: [M.MediaType] + cs = allMime $ Proxy @cs instance (KnownSymbol desc, S.ToSchema a) => IsSwaggerResponse (Respond s desc a) where - responseSwagger = simpleResponseSwagger @a @desc + -- Defaulting this to JSON, as openapi3 needs something to map a schema against. + responseSwagger = simpleResponseSwagger @a @'[JSON] @desc type instance ResponseType (RespondAs ct s desc a) = a @@ -248,10 +255,10 @@ instance KnownStatus s => IsResponse cs (RespondAs '() s desc ()) where guard (responseStatusCode output == statusVal (Proxy @s)) instance - (KnownSymbol desc, S.ToSchema a) => + (KnownSymbol desc, S.ToSchema a, Accept ct) => IsSwaggerResponse (RespondAs (ct :: Type) s desc a) where - responseSwagger = simpleResponseSwagger @a @desc + responseSwagger = simpleResponseSwagger @a @'[ct] @desc instance (KnownSymbol desc) => @@ -348,8 +355,8 @@ instance -- FUTUREWORK: should we concatenate all the matching headers instead of just -- taking the first one? extractHeaders hs = do - let name = headerName @name - (hs0, hs1) = Seq.partition (\(h, _) -> h == name) hs + let name' = headerName @name + (hs0, hs1) = Seq.partition (\(h, _) -> h == name') hs x <- case hs0 of Seq.Empty -> empty ((_, h) :<| _) -> either (const empty) pure (parseHeader h) @@ -378,11 +385,11 @@ instance (KnownSymbol name, KnownSymbol desc, S.ToParamSchema a) => ToResponseHeader (DescHeader name desc a) where - toResponseHeader _ = (name, S.Header (Just desc) sch) + toResponseHeader _ = (name', S.Header (Just desc) Nothing Nothing Nothing Nothing Nothing mempty sch) where - name = Text.pack (symbolVal (Proxy @name)) + name' = Text.pack (symbolVal (Proxy @name)) desc = Text.pack (symbolVal (Proxy @desc)) - sch = S.toParamSchema (Proxy @a) + sch = pure $ Inline $ S.toParamSchema (Proxy @a) instance ToResponseHeader h => ToResponseHeader (OptHeader h) where toResponseHeader _ = toResponseHeader (Proxy @h) @@ -419,7 +426,7 @@ instance where responseSwagger = fmap - (S.headers .~ toAllResponseHeaders (Proxy @hs)) + (S.headers .~ fmap S.Inline (toAllResponseHeaders (Proxy @hs))) (responseSwagger @r) class IsSwaggerResponseList as where @@ -477,7 +484,17 @@ combineResponseSwagger :: S.Response -> S.Response -> S.Response combineResponseSwagger r1 r2 = r1 & S.description <>~ ("\n\n" <> r2 ^. S.description) - & S.schema . _Just . S._Inline %~ flip combineSwaggerSchema (r2 ^. S.schema . _Just . S._Inline) + & S.content %~ flip (unionWith combineMediaTypeObject) (r2 ^. S.content) + +combineMediaTypeObject :: S.MediaTypeObject -> S.MediaTypeObject -> S.MediaTypeObject +combineMediaTypeObject m1 m2 = + m1 & S.schema .~ merge (m1 ^. S.schema) (m2 ^. S.schema) + where + merge Nothing a = a + merge a Nothing = a + merge (Just (Inline a)) (Just (Inline b)) = pure $ Inline $ combineSwaggerSchema a b + merge a@(Just (Ref _)) _ = a + merge _ a@(Just (Ref _)) = a combineSwaggerSchema :: S.Schema -> S.Schema -> S.Schema combineSwaggerSchema s1 s2 @@ -698,44 +715,61 @@ instance fromUnion (S (S x)) = case x of {} instance - (SwaggerMethod method, IsSwaggerResponseList as) => - S.HasSwagger (MultiVerb method '() as r) + (OpenApiMethod method, IsSwaggerResponseList as) => + S.HasOpenApi (MultiVerb method '() as r) where - toSwagger _ = + toOpenApi _ = mempty - & S.definitions <>~ defs + & S.components . S.schemas <>~ defs & S.paths . at "/" ?~ ( mempty & method ?~ ( mempty - & S.responses . S.responses .~ fmap S.Inline responses + & S.responses . S.responses .~ refResps ) ) where - method = S.swaggerMethod (Proxy @method) - (defs, responses) = S.runDeclare (responseListSwagger @as) mempty + method = S.openApiMethod (Proxy @method) + (defs, resps) = S.runDeclare (responseListSwagger @as) mempty + refResps = S.Inline <$> resps instance - (SwaggerMethod method, IsSwaggerResponseList as, AllMime cs) => - S.HasSwagger (MultiVerb method (cs :: [Type]) as r) + (OpenApiMethod method, IsSwaggerResponseList as, AllMime cs) => + S.HasOpenApi (MultiVerb method (cs :: [Type]) as r) where - toSwagger _ = + toOpenApi _ = mempty - & S.definitions <>~ defs + & S.components . S.schemas <>~ defs & S.paths . at "/" ?~ ( mempty & method ?~ ( mempty - & S.produces ?~ S.MimeList (nubOrd cs) - & S.responses . S.responses .~ fmap S.Inline responses + & S.responses . S.responses .~ refResps ) ) where - method = S.swaggerMethod (Proxy @method) + method = S.openApiMethod (Proxy @method) + -- This has our content types. cs = allMime (Proxy @cs) - (defs, responses) = S.runDeclare (responseListSwagger @as) mempty + -- This has our schemas + (defs, resps) = S.runDeclare (responseListSwagger @as) mempty + -- We need to zip them together, and stick it all back into the contentMap + -- Since we have a single schema per type, and are only changing the content-types, + -- we should be able to pick a schema out of the resps' map, and then use it for + -- all of the values of cs + addMime :: S.Response -> S.Response + addMime resp = + resp + & S.content + %~ + -- pick out an element from the map, if any exist. + -- These will all have the same schemas, and we are reapplying the content types. + foldMap (\c -> InsOrdHashMap.fromList $ (,c) <$> cs) + . listToMaybe + . toList + refResps = S.Inline . addMime <$> resps class Typeable a => IsWaiBody a where responseToWai :: ResponseF a -> Wai.Response diff --git a/libs/wire-api/src/Wire/API/Routes/Named.hs b/libs/wire-api/src/Wire/API/Routes/Named.hs index 79136daebfa..f76ada19664 100644 --- a/libs/wire-api/src/Wire/API/Routes/Named.hs +++ b/libs/wire-api/src/Wire/API/Routes/Named.hs @@ -22,14 +22,15 @@ module Wire.API.Routes.Named where import Control.Lens ((%~)) import Data.Kind import Data.Metrics.Servant +import Data.OpenApi.Lens hiding (HasServer) +import Data.OpenApi.Operation import Data.Proxy -import Data.Swagger import GHC.TypeLits import Imports import Servant import Servant.Client import Servant.Client.Core (clientIn) -import Servant.Swagger +import Servant.OpenApi -- | See http://docs.wire.com/developer/developer/servant.html#named-and-internal-route-ids-in-swagger newtype Named name x = Named {unnamed :: x} @@ -46,9 +47,9 @@ instance {-# OVERLAPPABLE #-} KnownSymbol a => RenderableSymbol a where instance {-# OVERLAPPING #-} (RenderableSymbol a, RenderableSymbol b) => RenderableSymbol '(a, b) where renderSymbol = "(" <> (renderSymbol @a) <> ", " <> (renderSymbol @b) <> ")" -instance (HasSwagger api, RenderableSymbol name) => HasSwagger (Named name api) where - toSwagger _ = - toSwagger (Proxy @api) +instance (HasOpenApi api, RenderableSymbol name) => HasOpenApi (Named name api) where + toOpenApi _ = + toOpenApi (Proxy @api) & allOperations . description %~ (Just (dscr <> "\n\n") <>) where dscr :: Text diff --git a/libs/wire-api/src/Wire/API/Routes/Public.hs b/libs/wire-api/src/Wire/API/Routes/Public.hs index a9d5ab6646b..68886e65407 100644 --- a/libs/wire-api/src/Wire/API/Routes/Public.hs +++ b/libs/wire-api/src/Wire/API/Routes/Public.hs @@ -44,18 +44,19 @@ import Data.HashMap.Strict.InsOrd qualified as InsOrdHashMap import Data.Id as Id import Data.Kind import Data.Metrics.Servant +import Data.OpenApi hiding (HasServer, Header, Server) +import Data.OpenApi qualified as S import Data.Qualified -import Data.Swagger hiding (Header) import GHC.Base (Symbol) import GHC.TypeLits (KnownSymbol) import Imports hiding (All, head) import Network.Wai qualified as Wai import Servant hiding (Handler, JSON, addHeader, respond) import Servant.API.Modifiers +import Servant.OpenApi (HasOpenApi (toOpenApi)) import Servant.Server.Internal.Delayed import Servant.Server.Internal.DelayedIO import Servant.Server.Internal.Router (Router) -import Servant.Swagger (HasSwagger (toSwagger)) import Wire.API.OAuth qualified as OAuth import Wire.API.Routes.Version @@ -223,15 +224,15 @@ type ZHostValue = Text type ZOptHostHeader = Header' '[Servant.Optional, Strict] "Z-Host" ZHostValue -instance HasSwagger api => HasSwagger (ZHostOpt :> api) where - toSwagger _ = toSwagger (Proxy @api) +instance HasOpenApi api => HasOpenApi (ZHostOpt :> api) where + toOpenApi _ = toOpenApi (Proxy @api) type instance SpecialiseToVersion v (ZHostOpt :> api) = ZHostOpt :> SpecialiseToVersion v api -addZAuthSwagger :: Swagger -> Swagger +addZAuthSwagger :: OpenApi -> OpenApi addZAuthSwagger s = s - & securityDefinitions <>~ SecurityDefinitions (InsOrdHashMap.singleton "ZAuth" secScheme) + & S.components . S.securitySchemes <>~ SecurityDefinitions (InsOrdHashMap.singleton "ZAuth" secScheme) & security <>~ [SecurityRequirement $ InsOrdHashMap.singleton "ZAuth" []] where secScheme = @@ -244,11 +245,11 @@ type instance SpecialiseToVersion v (ZAuthServant t opts :> api) = ZAuthServant t opts :> SpecialiseToVersion v api -instance HasSwagger api => HasSwagger (ZAuthServant 'ZAuthUser _opts :> api) where - toSwagger _ = addZAuthSwagger (toSwagger (Proxy @api)) +instance HasOpenApi api => HasOpenApi (ZAuthServant 'ZAuthUser _opts :> api) where + toOpenApi _ = addZAuthSwagger (toOpenApi (Proxy @api)) -instance HasSwagger api => HasSwagger (ZAuthServant 'ZLocalAuthUser opts :> api) where - toSwagger _ = addZAuthSwagger (toSwagger (Proxy @api)) +instance HasOpenApi api => HasOpenApi (ZAuthServant 'ZLocalAuthUser opts :> api) where + toOpenApi _ = addZAuthSwagger (toOpenApi (Proxy @api)) instance HasLink endpoint => HasLink (ZAuthServant usr opts :> endpoint) where type MkLink (ZAuthServant _ _ :> endpoint) a = MkLink endpoint a @@ -256,10 +257,10 @@ instance HasLink endpoint => HasLink (ZAuthServant usr opts :> endpoint) where instance {-# OVERLAPPABLE #-} - HasSwagger api => - HasSwagger (ZAuthServant ztype _opts :> api) + HasOpenApi api => + HasOpenApi (ZAuthServant ztype _opts :> api) where - toSwagger _ = toSwagger (Proxy @api) + toOpenApi _ = toOpenApi (Proxy @api) instance ( HasContextEntry (ctx .++ DefaultErrorFormatters) ErrorFormatters, @@ -301,8 +302,8 @@ instance checkType :: Maybe ByteString -> Wai.Request -> DelayedIO () checkType token req = case (token, lookup "Z-Type" (Wai.requestHeaders req)) of - (Just t, value) - | value /= Just t -> + (Just t, v) + | v /= Just t -> delayedFail ServerError { errHTTPCode = 403, @@ -321,7 +322,7 @@ instance RoutesToPaths api => RoutesToPaths (ZHostOpt :> api) where getRoutes = getRoutes @api -- FUTUREWORK: Make a PR to the servant-swagger package with this instance -instance ToSchema a => ToSchema (Headers ls a) where +instance (Typeable ls, ToSchema a) => ToSchema (Headers ls a) where declareNamedSchema _ = declareNamedSchema (Proxy @a) data DescriptionOAuthScope (scope :: OAuth.OAuthScope) @@ -331,12 +332,12 @@ type instance DescriptionOAuthScope scope :> SpecialiseToVersion v api instance - (HasSwagger api, OAuth.IsOAuthScope scope) => - HasSwagger (DescriptionOAuthScope scope :> api) + (HasOpenApi api, OAuth.IsOAuthScope scope) => + HasOpenApi (DescriptionOAuthScope scope :> api) where - toSwagger _ = addScopeDescription @scope (toSwagger (Proxy @api)) + toOpenApi _ = addScopeDescription @scope (toOpenApi (Proxy @api)) -addScopeDescription :: forall scope. OAuth.IsOAuthScope scope => Swagger -> Swagger +addScopeDescription :: forall scope. OAuth.IsOAuthScope scope => OpenApi -> OpenApi addScopeDescription = allOperations . description %~ Just . (<> "\nOAuth scope: `" <> cs (toByteString (OAuth.toOAuthScope @scope)) <> "`") . fold instance (HasServer api ctx) => HasServer (DescriptionOAuthScope scope :> api) ctx where diff --git a/libs/wire-api/src/Wire/API/Routes/Public/Brig.hs b/libs/wire-api/src/Wire/API/Routes/Public/Brig.hs index 48e8c36fca4..b8b22b88a33 100644 --- a/libs/wire-api/src/Wire/API/Routes/Public/Brig.hs +++ b/libs/wire-api/src/Wire/API/Routes/Public/Brig.hs @@ -19,6 +19,7 @@ module Wire.API.Routes.Public.Brig where +import Control.Lens ((?~)) import Data.Aeson qualified as A (FromJSON, ToJSON, Value) import Data.ByteString.Conversion import Data.Code (Timeout) @@ -28,20 +29,21 @@ import Data.Handle import Data.Id as Id import Data.Misc (IpAddr) import Data.Nonce (Nonce) +import Data.OpenApi hiding (Contact, Header, Schema, ToSchema) +import Data.OpenApi qualified as S import Data.Qualified (Qualified (..)) import Data.Range import Data.SOP import Data.Schema as Schema -import Data.Swagger hiding (Contact, Header, Schema, ToSchema) -import Data.Swagger qualified as S import Generics.SOP qualified as GSOP import Imports hiding (head) import Network.Wai.Utilities import Servant (JSON) import Servant hiding (Handler, JSON, addHeader, respond) -import Servant.Swagger.Internal.Orphans () +import Servant.OpenApi.Internal.Orphans () import Wire.API.Call.Config (RTCConfiguration) import Wire.API.Connection hiding (MissingLegalholdConsent) +import Wire.API.Deprecated import Wire.API.Error import Wire.API.Error.Brig import Wire.API.Error.Empty @@ -564,6 +566,7 @@ type AccountAPI = :<|> Named "post-password-reset-key-deprecated" ( Summary "Complete a password reset." + :> Deprecated :> CanThrow 'PasswordResetInProgress :> CanThrow 'InvalidPasswordResetKey :> CanThrow 'InvalidPasswordResetCode @@ -577,6 +580,7 @@ type AccountAPI = :<|> Named "onboarding" ( Summary "Upload contacts and invoke matching." + :> Deprecated :> Description "DEPRECATED: the feature has been turned off, the end-point does \ \nothing and always returns '{\"results\":[],\"auto-connects\":[]}'." @@ -598,8 +602,9 @@ data DeprecatedMatchingResult = DeprecatedMatchingResult instance ToSchema DeprecatedMatchingResult where schema = - object + objectWithDocModifier "DeprecatedMatchingResult" + (S.deprecated ?~ True) $ DeprecatedMatchingResult <$ const [] .= field "results" (array (null_ @SwaggerDoc)) @@ -1344,8 +1349,9 @@ type CallingAPI = Named "get-calls-config" ( Summary - "[deprecated] Retrieve TURN server addresses and credentials for \ - \ IP addresses, scheme `turn` and transport `udp` only" + "Retrieve TURN server addresses and credentials for \ + \ IP addresses, scheme `turn` and transport `udp` only (deprecated)" + :> Deprecated :> ZUser :> ZConn :> "calls" diff --git a/libs/wire-api/src/Wire/API/Routes/Public/Brig/Bot.hs b/libs/wire-api/src/Wire/API/Routes/Public/Brig/Bot.hs index 285202fb993..7e3259d8fca 100644 --- a/libs/wire-api/src/Wire/API/Routes/Public/Brig/Bot.hs +++ b/libs/wire-api/src/Wire/API/Routes/Public/Brig/Bot.hs @@ -22,7 +22,7 @@ import Data.Id as Id import Imports import Servant (JSON) import Servant hiding (Handler, JSON, Tagged, addHeader, respond) -import Servant.Swagger.Internal.Orphans () +import Servant.OpenApi.Internal.Orphans () import Wire.API.Conversation.Bot import Wire.API.Error (CanThrow, ErrorResponse) import Wire.API.Error.Brig (BrigError (..)) diff --git a/libs/wire-api/src/Wire/API/Routes/Public/Brig/OAuth.hs b/libs/wire-api/src/Wire/API/Routes/Public/Brig/OAuth.hs index 0a4adf52401..a096c78d975 100644 --- a/libs/wire-api/src/Wire/API/Routes/Public/Brig/OAuth.hs +++ b/libs/wire-api/src/Wire/API/Routes/Public/Brig/OAuth.hs @@ -22,7 +22,7 @@ import Data.SOP import Imports hiding (exp, head) import Servant (JSON) import Servant hiding (Handler, JSON, Tagged, addHeader, respond) -import Servant.Swagger.Internal.Orphans () +import Servant.OpenApi.Internal.Orphans () import Wire.API.Error import Wire.API.OAuth import Wire.API.Routes.API diff --git a/libs/wire-api/src/Wire/API/Routes/Public/Cargohold.hs b/libs/wire-api/src/Wire/API/Routes/Public/Cargohold.hs index 1ce9dd600cc..a1dc8001504 100644 --- a/libs/wire-api/src/Wire/API/Routes/Public/Cargohold.hs +++ b/libs/wire-api/src/Wire/API/Routes/Public/Cargohold.hs @@ -24,7 +24,7 @@ import Data.Qualified import Data.SOP import Imports import Servant -import Servant.Swagger.Internal.Orphans () +import Servant.OpenApi.Internal.Orphans () import URI.ByteString import Wire.API.Asset import Wire.API.Error diff --git a/libs/wire-api/src/Wire/API/Routes/Public/Galley.hs b/libs/wire-api/src/Wire/API/Routes/Public/Galley.hs index d24c473738e..52ec0ee5022 100644 --- a/libs/wire-api/src/Wire/API/Routes/Public/Galley.hs +++ b/libs/wire-api/src/Wire/API/Routes/Public/Galley.hs @@ -21,7 +21,7 @@ module Wire.API.Routes.Public.Galley where import Servant hiding (WithStatus) -import Servant.Swagger.Internal.Orphans () +import Servant.OpenApi.Internal.Orphans () import Wire.API.Routes.API import Wire.API.Routes.Public.Galley.Bot import Wire.API.Routes.Public.Galley.Conversation diff --git a/libs/wire-api/src/Wire/API/Routes/Public/Galley/Bot.hs b/libs/wire-api/src/Wire/API/Routes/Public/Galley/Bot.hs index 2c4752fda43..3eb711a96c8 100644 --- a/libs/wire-api/src/Wire/API/Routes/Public/Galley/Bot.hs +++ b/libs/wire-api/src/Wire/API/Routes/Public/Galley/Bot.hs @@ -18,7 +18,7 @@ module Wire.API.Routes.Public.Galley.Bot where import Servant hiding (WithStatus) -import Servant.Swagger.Internal.Orphans () +import Servant.OpenApi.Internal.Orphans () import Wire.API.Error import Wire.API.Error.Galley import Wire.API.MakesFederatedCall diff --git a/libs/wire-api/src/Wire/API/Routes/Public/Galley/Conversation.hs b/libs/wire-api/src/Wire/API/Routes/Public/Galley/Conversation.hs index 58b42a4e4a1..79a5db7c59f 100644 --- a/libs/wire-api/src/Wire/API/Routes/Public/Galley/Conversation.hs +++ b/libs/wire-api/src/Wire/API/Routes/Public/Galley/Conversation.hs @@ -24,11 +24,12 @@ import Data.Range import Data.SOP (I (..), NS (..)) import Imports hiding (head) import Servant hiding (WithStatus) -import Servant.Swagger.Internal.Orphans () +import Servant.OpenApi.Internal.Orphans () import Wire.API.Conversation import Wire.API.Conversation.Code import Wire.API.Conversation.Role import Wire.API.Conversation.Typing +import Wire.API.Deprecated import Wire.API.Error import Wire.API.Error.Galley import Wire.API.Event.Conversation @@ -785,6 +786,7 @@ type ConversationAPI = :<|> Named "update-other-member-unqualified" ( Summary "Update membership of the specified user (deprecated)" + :> Deprecated :> Description "Use `PUT /conversations/:cnv_domain/:cnv/members/:usr_domain/:usr` instead" :> MakesFederatedCall 'Galley "on-conversation-updated" :> MakesFederatedCall 'Galley "on-mls-message-sent" @@ -835,6 +837,7 @@ type ConversationAPI = :<|> Named "update-conversation-name-deprecated" ( Summary "Update conversation name (deprecated)" + :> Deprecated :> Description "Use `/conversations/:domain/:conv/name` instead." :> MakesFederatedCall 'Galley "on-conversation-updated" :> MakesFederatedCall 'Galley "on-mls-message-sent" @@ -855,6 +858,7 @@ type ConversationAPI = :<|> Named "update-conversation-name-unqualified" ( Summary "Update conversation name (deprecated)" + :> Deprecated :> Description "Use `/conversations/:domain/:conv/name` instead." :> MakesFederatedCall 'Galley "on-conversation-updated" :> MakesFederatedCall 'Galley "on-mls-message-sent" @@ -898,6 +902,7 @@ type ConversationAPI = :<|> Named "update-conversation-message-timer-unqualified" ( Summary "Update the message timer for a conversation (deprecated)" + :> Deprecated :> Description "Use `/conversations/:domain/:cnv/message-timer` instead." :> MakesFederatedCall 'Galley "on-conversation-updated" :> MakesFederatedCall 'Galley "on-mls-message-sent" @@ -943,6 +948,7 @@ type ConversationAPI = :<|> Named "update-conversation-receipt-mode-unqualified" ( Summary "Update receipt mode for a conversation (deprecated)" + :> Deprecated :> Description "Use `PUT /conversations/:domain/:cnv/receipt-mode` instead." :> MakesFederatedCall 'Galley "on-conversation-updated" :> MakesFederatedCall 'Galley "on-mls-message-sent" @@ -1064,6 +1070,7 @@ type ConversationAPI = :<|> Named "get-conversation-self-unqualified" ( Summary "Get self membership properties (deprecated)" + :> Deprecated :> ZLocalUser :> "conversations" :> Capture' '[Description "Conversation ID"] "cnv" ConvId @@ -1073,6 +1080,7 @@ type ConversationAPI = :<|> Named "update-conversation-self-unqualified" ( Summary "Update self membership properties (deprecated)" + :> Deprecated :> Description "Use `/conversations/:domain/:conv/self` instead." :> CanThrow 'ConvNotFound :> ZLocalUser diff --git a/libs/wire-api/src/Wire/API/Routes/Public/Galley/CustomBackend.hs b/libs/wire-api/src/Wire/API/Routes/Public/Galley/CustomBackend.hs index 079858baa0e..607a6e62573 100644 --- a/libs/wire-api/src/Wire/API/Routes/Public/Galley/CustomBackend.hs +++ b/libs/wire-api/src/Wire/API/Routes/Public/Galley/CustomBackend.hs @@ -19,7 +19,7 @@ module Wire.API.Routes.Public.Galley.CustomBackend where import Data.Domain import Servant hiding (WithStatus) -import Servant.Swagger.Internal.Orphans () +import Servant.OpenApi.Internal.Orphans () import Wire.API.CustomBackend import Wire.API.Error import Wire.API.Error.Galley diff --git a/libs/wire-api/src/Wire/API/Routes/Public/Galley/Feature.hs b/libs/wire-api/src/Wire/API/Routes/Public/Galley/Feature.hs index eb8e6cd3075..7d460614080 100644 --- a/libs/wire-api/src/Wire/API/Routes/Public/Galley/Feature.hs +++ b/libs/wire-api/src/Wire/API/Routes/Public/Galley/Feature.hs @@ -20,7 +20,7 @@ module Wire.API.Routes.Public.Galley.Feature where import Data.Id import GHC.TypeLits import Servant hiding (WithStatus) -import Servant.Swagger.Internal.Orphans () +import Servant.OpenApi.Internal.Orphans () import Wire.API.ApplyMods import Wire.API.Conversation.Role import Wire.API.Error diff --git a/libs/wire-api/src/Wire/API/Routes/Public/Galley/LegalHold.hs b/libs/wire-api/src/Wire/API/Routes/Public/Galley/LegalHold.hs index 24848c46fd2..8a8b576f2c3 100644 --- a/libs/wire-api/src/Wire/API/Routes/Public/Galley/LegalHold.hs +++ b/libs/wire-api/src/Wire/API/Routes/Public/Galley/LegalHold.hs @@ -21,7 +21,7 @@ import Data.Id import GHC.Generics import Generics.SOP qualified as GSOP import Servant hiding (WithStatus) -import Servant.Swagger.Internal.Orphans () +import Servant.OpenApi.Internal.Orphans () import Wire.API.Conversation.Role import Wire.API.Error import Wire.API.Error.Galley diff --git a/libs/wire-api/src/Wire/API/Routes/Public/Galley/MLS.hs b/libs/wire-api/src/Wire/API/Routes/Public/Galley/MLS.hs index 0ae7d3feb10..116c1dc11c3 100644 --- a/libs/wire-api/src/Wire/API/Routes/Public/Galley/MLS.hs +++ b/libs/wire-api/src/Wire/API/Routes/Public/Galley/MLS.hs @@ -17,6 +17,7 @@ module Wire.API.Routes.Public.Galley.MLS where -import Servant +import Servant hiding (WithStatus) +import Servant.OpenApi.Internal.Orphans () type MLSAPI = EmptyAPI diff --git a/libs/wire-api/src/Wire/API/Routes/Public/Galley/Messaging.hs b/libs/wire-api/src/Wire/API/Routes/Public/Galley/Messaging.hs index b5f07834e7f..72aa70f4125 100644 --- a/libs/wire-api/src/Wire/API/Routes/Public/Galley/Messaging.hs +++ b/libs/wire-api/src/Wire/API/Routes/Public/Galley/Messaging.hs @@ -22,7 +22,7 @@ import Data.SOP import Generics.SOP qualified as GSOP import Imports import Servant hiding (WithStatus) -import Servant.Swagger.Internal.Orphans () +import Servant.OpenApi.Internal.Orphans () import Wire.API.Error import Wire.API.Error.Brig qualified as BrigError import Wire.API.Error.Galley diff --git a/libs/wire-api/src/Wire/API/Routes/Public/Galley/Team.hs b/libs/wire-api/src/Wire/API/Routes/Public/Galley/Team.hs index 3d3571dd23c..fd3fd392a4a 100644 --- a/libs/wire-api/src/Wire/API/Routes/Public/Galley/Team.hs +++ b/libs/wire-api/src/Wire/API/Routes/Public/Galley/Team.hs @@ -20,7 +20,7 @@ module Wire.API.Routes.Public.Galley.Team where import Data.Id import Imports import Servant hiding (WithStatus) -import Servant.Swagger.Internal.Orphans () +import Servant.OpenApi.Internal.Orphans () import Wire.API.Error import Wire.API.Error.Galley import Wire.API.Routes.MultiVerb diff --git a/libs/wire-api/src/Wire/API/Routes/Public/Galley/TeamConversation.hs b/libs/wire-api/src/Wire/API/Routes/Public/Galley/TeamConversation.hs index ce5fed146d5..a6a2c8aceb4 100644 --- a/libs/wire-api/src/Wire/API/Routes/Public/Galley/TeamConversation.hs +++ b/libs/wire-api/src/Wire/API/Routes/Public/Galley/TeamConversation.hs @@ -19,7 +19,7 @@ module Wire.API.Routes.Public.Galley.TeamConversation where import Data.Id import Servant hiding (WithStatus) -import Servant.Swagger.Internal.Orphans () +import Servant.OpenApi.Internal.Orphans () import Wire.API.Conversation.Role import Wire.API.Error import Wire.API.Error.Galley diff --git a/libs/wire-api/src/Wire/API/Routes/Public/Galley/TeamMember.hs b/libs/wire-api/src/Wire/API/Routes/Public/Galley/TeamMember.hs index 6d14ebc1483..4c71df03e49 100644 --- a/libs/wire-api/src/Wire/API/Routes/Public/Galley/TeamMember.hs +++ b/libs/wire-api/src/Wire/API/Routes/Public/Galley/TeamMember.hs @@ -23,7 +23,7 @@ import Data.Range import GHC.Generics import Generics.SOP qualified as GSOP import Servant hiding (WithStatus) -import Servant.Swagger.Internal.Orphans () +import Servant.OpenApi.Internal.Orphans () import Wire.API.Error import Wire.API.Error.Galley import Wire.API.Routes.CSV diff --git a/libs/wire-api/src/Wire/API/Routes/Public/Spar.hs b/libs/wire-api/src/Wire/API/Routes/Public/Spar.hs index 59b98ddc9eb..107ed1de9a5 100644 --- a/libs/wire-api/src/Wire/API/Routes/Public/Spar.hs +++ b/libs/wire-api/src/Wire/API/Routes/Public/Spar.hs @@ -25,11 +25,12 @@ import SAML2.WebSSO qualified as SAML import Servant import Servant.API.Extended import Servant.Multipart -import Servant.Swagger +import Servant.OpenApi import URI.ByteString qualified as URI import Web.Scim.Capabilities.MetaSchema as Scim.Meta import Web.Scim.Class.Auth as Scim.Auth import Web.Scim.Class.User as Scim.User +import Wire.API.Deprecated (Deprecated) import Wire.API.Error import Wire.API.Error.Brig import Wire.API.Routes.API @@ -57,7 +58,7 @@ type DeprecateSSOAPIV1 = \Details: https://docs.wire.com/understand/single-sign-on/trouble-shooting.html#can-i-use-the-same-sso-login-code-for-multiple-teams" type APISSO = - DeprecateSSOAPIV1 :> "metadata" :> SAML.APIMeta + DeprecateSSOAPIV1 :> Deprecated :> "metadata" :> SAML.APIMeta :<|> "metadata" :> Capture "team" TeamId :> SAML.APIMeta :<|> "initiate-login" :> APIAuthReqPrecheck :<|> "initiate-login" :> APIAuthReq @@ -82,6 +83,7 @@ type APIAuthReq = type APIAuthRespLegacy = DeprecateSSOAPIV1 + :> Deprecated :> "finalize-login" -- (SAML.APIAuthResp from here on, except for response) :> MultipartForm Mem SAML.AuthnResponseBody @@ -191,4 +193,4 @@ data SparAPITag instance ServiceAPI SparAPITag v where type ServiceAPIRoutes SparAPITag = SparAPI type SpecialisedAPIRoutes v SparAPITag = SparAPI - serviceSwagger = toSwagger (Proxy @SparAPI) + serviceSwagger = toOpenApi (Proxy @SparAPI) diff --git a/libs/wire-api/src/Wire/API/Routes/Public/Util.hs b/libs/wire-api/src/Wire/API/Routes/Public/Util.hs index 694230f7574..ab34186bb12 100644 --- a/libs/wire-api/src/Wire/API/Routes/Public/Util.hs +++ b/libs/wire-api/src/Wire/API/Routes/Public/Util.hs @@ -23,7 +23,7 @@ module Wire.API.Routes.Public.Util where import Control.Comonad import Data.SOP (I (..), NS (..)) import Servant -import Servant.Swagger.Internal.Orphans () +import Servant.OpenApi.Internal.Orphans () import Wire.API.Routes.MultiVerb instance diff --git a/libs/wire-api/src/Wire/API/Routes/QualifiedCapture.hs b/libs/wire-api/src/Wire/API/Routes/QualifiedCapture.hs index f54cccf4f36..9147e008fda 100644 --- a/libs/wire-api/src/Wire/API/Routes/QualifiedCapture.hs +++ b/libs/wire-api/src/Wire/API/Routes/QualifiedCapture.hs @@ -24,16 +24,16 @@ where import Data.Domain import Data.Kind import Data.Metrics.Servant +import Data.OpenApi hiding (HasServer, value) import Data.Qualified -import Data.Swagger import GHC.TypeLits import Imports import Servant import Servant.API.Description import Servant.API.Modifiers import Servant.Client.Core.HasClient +import Servant.OpenApi import Servant.Server.Internal.ErrorFormatter -import Servant.Swagger import Wire.API.Routes.Version -- | Capture a value qualified by a domain, with modifiers. @@ -56,16 +56,15 @@ type instance QualifiedCapture' mods capture a :> SpecialiseToVersion v api instance - ( Typeable a, - ToParamSchema a, - HasSwagger api, + ( ToParamSchema a, + HasOpenApi api, KnownSymbol capture, KnownSymbol (AppendSymbol capture "_domain"), KnownSymbol (FoldDescription mods) ) => - HasSwagger (QualifiedCapture' mods capture a :> api) + HasOpenApi (QualifiedCapture' mods capture a :> api) where - toSwagger _ = toSwagger (Proxy @(WithDomain mods capture a api)) + toOpenApi _ = toOpenApi (Proxy @(WithDomain mods capture a api)) instance ( KnownSymbol capture, diff --git a/libs/wire-api/src/Wire/API/Routes/Version.hs b/libs/wire-api/src/Wire/API/Routes/Version.hs index ea8bfc26f8b..a2a337b61df 100644 --- a/libs/wire-api/src/Wire/API/Routes/Version.hs +++ b/libs/wire-api/src/Wire/API/Routes/Version.hs @@ -52,15 +52,16 @@ import Data.Binary.Builder qualified as Builder import Data.ByteString.Conversion (ToByteString (builder), toByteString') import Data.ByteString.Lazy qualified as LBS import Data.Domain +import Data.OpenApi qualified as S import Data.Schema import Data.Singletons.Base.TH -import Data.Swagger qualified as S import Data.Text qualified as Text import Data.Text.Encoding as Text import GHC.TypeLits import Imports import Servant import Servant.API.Extended.RawM +import Wire.API.Deprecated import Wire.API.Routes.MultiVerb import Wire.API.Routes.Named import Wire.API.VersionInfo @@ -230,6 +231,10 @@ type instance SpecialiseToVersion v (Summary s :> api) = Summary s :> SpecialiseToVersion v api +type instance + SpecialiseToVersion v (Deprecated :> api) = + Deprecated :> SpecialiseToVersion v api + type instance SpecialiseToVersion v (Verb m s t r) = Verb m s t r diff --git a/libs/wire-api/src/Wire/API/Routes/Versioned.hs b/libs/wire-api/src/Wire/API/Routes/Versioned.hs index 1ca7bac0587..7707e3441e6 100644 --- a/libs/wire-api/src/Wire/API/Routes/Versioned.hs +++ b/libs/wire-api/src/Wire/API/Routes/Versioned.hs @@ -20,15 +20,15 @@ module Wire.API.Routes.Versioned where import Data.Aeson (FromJSON, ToJSON) import Data.Kind import Data.Metrics.Servant +import Data.OpenApi qualified as S import Data.Schema import Data.Singletons -import Data.Swagger qualified as S import GHC.TypeLits import Imports import Servant import Servant.API.ContentTypes -import Servant.Swagger -import Servant.Swagger.Internal +import Servant.OpenApi +import Servant.OpenApi.Internal import Wire.API.Routes.MultiVerb import Wire.API.Routes.Version @@ -63,12 +63,12 @@ type instance instance ( S.ToSchema (Versioned v a), - HasSwagger api, + HasOpenApi api, AllAccept cts ) => - HasSwagger (VersionedReqBody v cts a :> api) + HasOpenApi (VersionedReqBody v cts a :> api) where - toSwagger _ = toSwagger (Proxy @(ReqBody cts (Versioned v a) :> api)) + toOpenApi _ = toOpenApi (Proxy @(ReqBody cts (Versioned v a) :> api)) -------------------------------------------------------------------------------- -- Versioned responses @@ -92,7 +92,7 @@ instance (KnownSymbol desc, S.ToSchema a) => IsSwaggerResponse (VersionedRespond v s desc a) where - responseSwagger = simpleResponseSwagger @a @desc + responseSwagger = simpleResponseSwagger @a @'[JSON] @desc ------------------------------------------------------------------------------- -- Versioned newtype wrapper @@ -111,7 +111,7 @@ deriving via Schema (Versioned v a) instance ToSchema (Versioned v a) => FromJSO deriving via Schema (Versioned v a) instance ToSchema (Versioned v a) => ToJSON (Versioned v a) -- add version suffix to swagger schema to prevent collisions -instance (SingI v, ToSchema (Versioned v a)) => S.ToSchema (Versioned v a) where +instance (SingI v, ToSchema (Versioned v a), Typeable a, Typeable v) => S.ToSchema (Versioned v a) where declareNamedSchema _ = do S.NamedSchema n s <- schemaToSwagger (Proxy @(Versioned v a)) pure $ S.NamedSchema (fmap (<> toUrlPiece (demote @v)) n) s diff --git a/libs/wire-api/src/Wire/API/Routes/WebSocket.hs b/libs/wire-api/src/Wire/API/Routes/WebSocket.hs index 72354d95bc1..0405b58d094 100644 --- a/libs/wire-api/src/Wire/API/Routes/WebSocket.hs +++ b/libs/wire-api/src/Wire/API/Routes/WebSocket.hs @@ -21,16 +21,16 @@ import Control.Lens import Control.Monad.Trans.Resource import Data.HashMap.Strict.InsOrd import Data.Metrics.Servant +import Data.OpenApi hiding (HasServer) import Data.Proxy -import Data.Swagger import Imports import Network.Wai.Handler.WebSockets import Network.WebSockets +import Servant.OpenApi import Servant.Server hiding (respond) import Servant.Server.Internal.Delayed import Servant.Server.Internal.RouteResult import Servant.Server.Internal.Router -import Servant.Swagger import Wire.API.Routes.Version -- | A websocket that relates to a 'PendingConnection' @@ -65,8 +65,8 @@ instance HasServer WebSocketPending ctx where type instance SpecialiseToVersion v WebSocketPending = WebSocketPending -instance HasSwagger WebSocketPending where - toSwagger _ = +instance HasOpenApi WebSocketPending where + toOpenApi _ = mempty & paths . at "/" @@ -82,7 +82,7 @@ instance HasSwagger WebSocketPending where ) ) where - resps :: InsOrdHashMap HttpStatusCode (Referenced Data.Swagger.Response) + resps :: InsOrdHashMap HttpStatusCode (Referenced Data.OpenApi.Response) resps = mempty & at 101 ?~ Inline (mempty & description .~ "Connection upgraded.") diff --git a/libs/wire-api/src/Wire/API/ServantProto.hs b/libs/wire-api/src/Wire/API/ServantProto.hs index 3eb06458fab..6e2dbd6140b 100644 --- a/libs/wire-api/src/Wire/API/ServantProto.hs +++ b/libs/wire-api/src/Wire/API/ServantProto.hs @@ -19,7 +19,7 @@ module Wire.API.ServantProto where import Data.ByteString.Lazy qualified as LBS import Data.List.NonEmpty (NonEmpty (..)) -import Data.Swagger +import Data.OpenApi import Imports import Network.HTTP.Media ((//)) import Servant diff --git a/libs/wire-api/src/Wire/API/SwaggerHelper.hs b/libs/wire-api/src/Wire/API/SwaggerHelper.hs index 9e1927156c1..3e882b8ab5c 100644 --- a/libs/wire-api/src/Wire/API/SwaggerHelper.hs +++ b/libs/wire-api/src/Wire/API/SwaggerHelper.hs @@ -19,23 +19,33 @@ module Wire.API.SwaggerHelper where import Control.Lens import Data.Containers.ListUtils (nubOrd) -import Data.Swagger hiding (Contact, Header, Schema, ToSchema) -import Data.Swagger qualified as S +import Data.HashMap.Strict.InsOrd +import Data.OpenApi hiding (Contact, Header, Schema, ToSchema) +import Data.OpenApi qualified as S +import Data.Text qualified as T import Imports hiding (head) -cleanupSwagger :: Swagger -> Swagger +cleanupSwagger :: OpenApi -> OpenApi cleanupSwagger = (S.security %~ nub) -- sanitise definitions - . (S.definitions . traverse %~ sanitise) + . (S.components . S.schemas . traverse %~ sanitise) + -- strip the default errors + . ( S.allOperations + . S.responses + . S.responses + %~ foldrWithKey stripDefaultErrors mempty + ) -- sanitise general responses - . (S.responses . traverse . S.schema . _Just . S._Inline %~ sanitise) + . (S.components . S.responses . traverse . S.content . traverse . S.schema . _Just . S._Inline %~ sanitise) -- sanitise all responses of all paths . ( S.allOperations . S.responses . S.responses . traverse . S._Inline + . S.content + . traverse . S.schema . _Just . S._Inline @@ -47,3 +57,49 @@ cleanupSwagger = (S.properties . traverse . S._Inline %~ sanitise) . (S.required %~ nubOrd) . (S.enum_ . _Just %~ nub) + -- servant-openapi and servant-swagger both insert default responses with codes 404 and 400. + -- They have a simple structure that we can match against, and remove from the final structure. + stripDefaultErrors :: HttpStatusCode -> Referenced Response -> Responses' -> Responses' + stripDefaultErrors code resp resps = + case code of + 400 -> case resp ^? _Inline . S.description of + (Just desc) -> + if "Invalid " + `T.isPrefixOf` desc + && resp + ^? _Inline + . links + == pure mempty + && resp + ^? _Inline + . content + == pure mempty + && resp + ^? _Inline + . headers + == pure mempty + then resps + else insert code resp resps + Nothing -> insert code resp resps + 404 -> case resp ^? _Inline . S.description of + (Just desc) -> + if " not found" + `T.isSuffixOf` desc + && resp + ^? _Inline + . links + == pure mempty + && resp + ^? _Inline + . content + == pure mempty + && resp + ^? _Inline + . headers + == pure mempty + then resps + else insert code resp resps + Nothing -> insert code resp resps + _ -> insert code resp resps + +type Responses' = InsOrdHashMap HttpStatusCode (Referenced Response) diff --git a/libs/wire-api/src/Wire/API/SwaggerServant.hs b/libs/wire-api/src/Wire/API/SwaggerServant.hs index 89973fb59ae..5c3918cf39c 100644 --- a/libs/wire-api/src/Wire/API/SwaggerServant.hs +++ b/libs/wire-api/src/Wire/API/SwaggerServant.hs @@ -25,7 +25,7 @@ import Data.Metrics.Servant import Data.Proxy import Imports hiding (head) import Servant -import Servant.Swagger (HasSwagger (toSwagger)) +import Servant.OpenApi (HasOpenApi (toOpenApi)) -- | A type-level tag that lets us omit any branch from Swagger docs. -- @@ -34,8 +34,8 @@ import Servant.Swagger (HasSwagger (toSwagger)) -- it's only justification is laziness. data OmitDocs -instance HasSwagger (OmitDocs :> a) where - toSwagger _ = mempty +instance HasOpenApi (OmitDocs :> a) where + toOpenApi _ = mempty instance HasServer api ctx => HasServer (OmitDocs :> api) ctx where type ServerT (OmitDocs :> api) m = ServerT api m diff --git a/libs/wire-api/src/Wire/API/SystemSettings.hs b/libs/wire-api/src/Wire/API/SystemSettings.hs index d6098ac4ec5..d07d7152a44 100644 --- a/libs/wire-api/src/Wire/API/SystemSettings.hs +++ b/libs/wire-api/src/Wire/API/SystemSettings.hs @@ -19,10 +19,10 @@ module Wire.API.SystemSettings where import Control.Lens hiding ((.=)) import Data.Aeson qualified as A +import Data.OpenApi qualified as S import Data.Schema as Schema -import Data.Swagger qualified as S import Imports -import Servant.Swagger.Internal.Orphans () +import Servant.OpenApi.Internal.Orphans () import Test.QuickCheck import Wire.Arbitrary diff --git a/libs/wire-api/src/Wire/API/Team.hs b/libs/wire-api/src/Wire/API/Team.hs index fe3ed5e596c..13c09ab567b 100644 --- a/libs/wire-api/src/Wire/API/Team.hs +++ b/libs/wire-api/src/Wire/API/Team.hs @@ -67,7 +67,7 @@ module Wire.API.Team ) where -import Control.Lens (makeLenses, (?~)) +import Control.Lens (makeLenses, over, (?~)) import Data.Aeson (FromJSON, ToJSON, Value (..)) import Data.Aeson.Types (Parser) import Data.Attoparsec.ByteString qualified as Atto (Parser, string) @@ -76,9 +76,10 @@ import Data.ByteString.Conversion import Data.Code qualified as Code import Data.Id (TeamId, UserId) import Data.Misc (PlainTextPassword6) +import Data.OpenApi (HasDeprecated (deprecated)) +import Data.OpenApi qualified as S import Data.Range import Data.Schema -import Data.Swagger qualified as S import Data.Text.Encoding qualified as T import Imports import Test.QuickCheck.Gen (suchThat) @@ -118,7 +119,10 @@ instance ToSchema Team where <*> _teamSplashScreen .= (fromMaybe DefaultIcon <$> optField "splash_screen" schema) where desc = description ?~ "`binding` is deprecated, and should be ignored. The non-binding teams API is not used (and will not be supported from API version V4 onwards), and `binding` will always be `true`." - bindingDesc = description ?~ "Deprecated, please ignore." + bindingDesc v = + v + & description ?~ "Deprecated, please ignore." + & deprecated ?~ True -- | How a team "binds" its members (users) -- @@ -145,8 +149,9 @@ data TeamBinding instance ToSchema TeamBinding where schema = - enum @Bool "TeamBinding" $ - mconcat [element True Binding, element False NonBinding] + over doc (deprecated ?~ True) $ + enum @Bool "TeamBinding" $ + mconcat [element True Binding, element False NonBinding] -------------------------------------------------------------------------------- -- TeamList diff --git a/libs/wire-api/src/Wire/API/Team/Conversation.hs b/libs/wire-api/src/Wire/API/Team/Conversation.hs index ae020086104..3822a614923 100644 --- a/libs/wire-api/src/Wire/API/Team/Conversation.hs +++ b/libs/wire-api/src/Wire/API/Team/Conversation.hs @@ -35,8 +35,8 @@ where import Control.Lens (makeLenses, (?~)) import Data.Aeson qualified as A import Data.Id (ConvId) +import Data.OpenApi qualified as S import Data.Schema -import Data.Swagger qualified as S import Imports import Wire.Arbitrary (Arbitrary, GenericUniform (..)) diff --git a/libs/wire-api/src/Wire/API/Team/Feature.hs b/libs/wire-api/src/Wire/API/Team/Feature.hs index 5e3a3bf9f39..899f7491573 100644 --- a/libs/wire-api/src/Wire/API/Team/Feature.hs +++ b/libs/wire-api/src/Wire/API/Team/Feature.hs @@ -98,10 +98,10 @@ import Data.Either.Extra (maybeToEither) import Data.Id import Data.Kind import Data.Misc (HttpsUrl) +import Data.OpenApi qualified as S import Data.Proxy import Data.Schema import Data.Scientific (toBoundedInteger) -import Data.Swagger qualified as S import Data.Text qualified as T import Data.Text.Encoding qualified as T import Data.Text.Lazy qualified as TL @@ -266,7 +266,7 @@ deriving via (Schema (WithStatus cfg)) instance (ToSchema (WithStatus cfg)) => T deriving via (Schema (WithStatus cfg)) instance (ToSchema (WithStatus cfg)) => FromJSON (WithStatus cfg) -deriving via (Schema (WithStatus cfg)) instance (ToSchema (WithStatus cfg)) => S.ToSchema (WithStatus cfg) +deriving via (Schema (WithStatus cfg)) instance (ToSchema (WithStatus cfg), Typeable cfg) => S.ToSchema (WithStatus cfg) instance (ToSchema cfg, IsFeatureConfig cfg) => ToSchema (WithStatus cfg) where schema = @@ -296,7 +296,7 @@ deriving via (Schema (WithStatusPatch cfg)) instance (ToSchema (WithStatusPatch deriving via (Schema (WithStatusPatch cfg)) instance (ToSchema (WithStatusPatch cfg)) => FromJSON (WithStatusPatch cfg) -deriving via (Schema (WithStatusPatch cfg)) instance (ToSchema (WithStatusPatch cfg)) => S.ToSchema (WithStatusPatch cfg) +deriving via (Schema (WithStatusPatch cfg)) instance (ToSchema (WithStatusPatch cfg), Typeable cfg) => S.ToSchema (WithStatusPatch cfg) wsPatch :: Maybe FeatureStatus -> Maybe LockStatus -> Maybe cfg -> Maybe FeatureTTL -> WithStatusPatch cfg wsPatch = WithStatusBase @@ -1043,8 +1043,8 @@ data FeatureStatus instance S.ToParamSchema FeatureStatus where toParamSchema _ = mempty - { S._paramSchemaType = Just S.SwaggerString, - S._paramSchemaEnum = Just (A.String . toQueryParam <$> [(minBound :: FeatureStatus) ..]) + { S._schemaType = Just S.OpenApiString, + S._schemaEnum = Just (A.String . toQueryParam <$> [(minBound :: FeatureStatus) ..]) } instance FromHttpApiData FeatureStatus where diff --git a/libs/wire-api/src/Wire/API/Team/Invitation.hs b/libs/wire-api/src/Wire/API/Team/Invitation.hs index 8593c67ce97..44cc508ab69 100644 --- a/libs/wire-api/src/Wire/API/Team/Invitation.hs +++ b/libs/wire-api/src/Wire/API/Team/Invitation.hs @@ -32,9 +32,9 @@ import Data.Aeson qualified as A import Data.ByteString.Conversion import Data.Id import Data.Json.Util +import Data.OpenApi qualified as S import Data.SOP import Data.Schema -import Data.Swagger qualified as S import Data.Text.Encoding qualified as TE import Imports import Servant (FromHttpApiData (..), ToHttpApiData (..)) @@ -130,7 +130,7 @@ newtype InvitationLocation = InvitationLocation instance S.ToParamSchema InvitationLocation where toParamSchema _ = mempty - & S.type_ ?~ S.SwaggerString + & S.type_ ?~ S.OpenApiString & S.format ?~ "url" instance FromHttpApiData InvitationLocation where diff --git a/libs/wire-api/src/Wire/API/Team/LegalHold.hs b/libs/wire-api/src/Wire/API/Team/LegalHold.hs index d72dafb5da8..40fbb9a7af0 100644 --- a/libs/wire-api/src/Wire/API/Team/LegalHold.hs +++ b/libs/wire-api/src/Wire/API/Team/LegalHold.hs @@ -35,9 +35,9 @@ import Data.Aeson.Types qualified as A import Data.Id import Data.LegalHold import Data.Misc +import Data.OpenApi qualified as S hiding (info) import Data.Proxy import Data.Schema -import Data.Swagger qualified as S hiding (info) import Deriving.Aeson import Imports import Wire.API.Provider @@ -240,11 +240,11 @@ instance ToSchema LegalholdProtectee where pure $ S.NamedSchema (Just "LegalholdProtectee") $ mempty - & S.type_ ?~ S.SwaggerObject + & S.type_ ?~ S.OpenApiObject & S.properties . at "tag" ?~ S.Inline ( mempty - & S.type_ ?~ S.SwaggerString + & S.type_ ?~ S.OpenApiString & S.enum_ ?~ [ A.toJSON ("ProtectedUser" :: String), A.toJSON ("UnprotectedBot" :: String), diff --git a/libs/wire-api/src/Wire/API/Team/LegalHold/External.hs b/libs/wire-api/src/Wire/API/Team/LegalHold/External.hs index ea892087bfe..8dc5fd14366 100644 --- a/libs/wire-api/src/Wire/API/Team/LegalHold/External.hs +++ b/libs/wire-api/src/Wire/API/Team/LegalHold/External.hs @@ -34,7 +34,7 @@ where import Data.Aeson hiding (fieldLabelModifier) import Data.Id import Data.Json.Util ((#)) -import Data.Swagger +import Data.OpenApi import Imports import Wire.API.User.Client.Prekey import Wire.Arbitrary (Arbitrary, GenericUniform (..)) diff --git a/libs/wire-api/src/Wire/API/Team/LegalHold/Internal.hs b/libs/wire-api/src/Wire/API/Team/LegalHold/Internal.hs index cb3915f4a38..e706f472fc6 100644 --- a/libs/wire-api/src/Wire/API/Team/LegalHold/Internal.hs +++ b/libs/wire-api/src/Wire/API/Team/LegalHold/Internal.hs @@ -29,8 +29,8 @@ import Data.Aeson import Data.Id import Data.Json.Util import Data.Misc +import Data.OpenApi qualified as Swagger import Data.Schema qualified as Schema -import Data.Swagger qualified as Swagger import Imports import Wire.API.Provider import Wire.API.Provider.Service diff --git a/libs/wire-api/src/Wire/API/Team/Member.hs b/libs/wire-api/src/Wire/API/Team/Member.hs index d47061389b5..91e790aa66e 100644 --- a/libs/wire-api/src/Wire/API/Team/Member.hs +++ b/libs/wire-api/src/Wire/API/Team/Member.hs @@ -74,10 +74,10 @@ import Data.Json.Util import Data.Kind import Data.LegalHold (UserLegalHoldStatus (..), defUserLegalHoldStatus) import Data.Misc (PlainTextPassword6) +import Data.OpenApi (ToParamSchema (..)) +import Data.OpenApi.Schema qualified as S import Data.Proxy import Data.Schema -import Data.Swagger (ToParamSchema (..)) -import Data.Swagger.Schema qualified as S import GHC.TypeLits import Imports import Wire.API.Routes.MultiTablePaging (MultiTablePage (..)) @@ -132,7 +132,7 @@ deriving via deriving via (Schema (TeamMember' tag)) instance - (ToSchema (TeamMember' tag)) => + (ToSchema (TeamMember' tag), Typeable tag) => S.ToSchema (TeamMember' tag) mkTeamMember :: @@ -256,7 +256,7 @@ deriving via deriving via (Schema (TeamMemberList' tag)) instance - ToSchema (TeamMemberList' tag) => + (ToSchema (TeamMemberList' tag), Typeable tag) => S.ToSchema (TeamMemberList' tag) newTeamMemberList :: [TeamMember] -> ListType -> TeamMemberList @@ -348,7 +348,7 @@ deriving via deriving via (Schema (NewTeamMember' tag)) instance - (ToSchema (NewTeamMember' tag)) => + (ToSchema (NewTeamMember' tag), Typeable tag) => S.ToSchema (NewTeamMember' tag) deriving via (GenericUniform NewTeamMember) instance Arbitrary NewTeamMember diff --git a/libs/wire-api/src/Wire/API/Team/Permission.hs b/libs/wire-api/src/Wire/API/Team/Permission.hs index be29d6b46d1..49a9893b370 100644 --- a/libs/wire-api/src/Wire/API/Team/Permission.hs +++ b/libs/wire-api/src/Wire/API/Team/Permission.hs @@ -48,10 +48,10 @@ import Control.Error.Util qualified as Err import Control.Lens (makeLenses, (?~), (^.)) import Data.Aeson (FromJSON (..), ToJSON (..)) import Data.Bits (testBit, (.|.)) +import Data.OpenApi qualified as S import Data.Schema import Data.Set qualified as Set import Data.Singletons.Base.TH -import Data.Swagger qualified as S import Imports import Wire.API.Util.Aeson (CustomEncoded (..)) import Wire.Arbitrary (Arbitrary (arbitrary), GenericUniform (..)) diff --git a/libs/wire-api/src/Wire/API/Team/Role.hs b/libs/wire-api/src/Wire/API/Team/Role.hs index 424065e66c0..d4602394750 100644 --- a/libs/wire-api/src/Wire/API/Team/Role.hs +++ b/libs/wire-api/src/Wire/API/Team/Role.hs @@ -29,8 +29,8 @@ import Control.Lens ((?~)) import Data.Aeson import Data.Attoparsec.ByteString.Char8 (string) import Data.ByteString.Conversion (FromByteString (..), ToByteString (..)) +import Data.OpenApi qualified as S import Data.Schema -import Data.Swagger qualified as S import Data.Text qualified as T import Imports import Servant.API (FromHttpApiData, parseQueryParam) @@ -93,7 +93,7 @@ instance ToSchema Role where instance S.ToParamSchema Role where toParamSchema _ = mempty - & S.type_ ?~ S.SwaggerString + & S.type_ ?~ S.OpenApiString & S.enum_ ?~ fmap roleName [minBound .. maxBound] instance FromHttpApiData Role where diff --git a/libs/wire-api/src/Wire/API/Team/SearchVisibility.hs b/libs/wire-api/src/Wire/API/Team/SearchVisibility.hs index b41300a8b7a..76d530f6f15 100644 --- a/libs/wire-api/src/Wire/API/Team/SearchVisibility.hs +++ b/libs/wire-api/src/Wire/API/Team/SearchVisibility.hs @@ -24,8 +24,8 @@ module Wire.API.Team.SearchVisibility where import Control.Lens ((?~)) +import Data.OpenApi qualified as S import Data.Schema -import Data.Swagger qualified as S import Deriving.Aeson import Imports import Wire.Arbitrary (Arbitrary, GenericUniform (..)) diff --git a/libs/wire-api/src/Wire/API/Team/Size.hs b/libs/wire-api/src/Wire/API/Team/Size.hs index 811a7a094e6..ce0d8fe6468 100644 --- a/libs/wire-api/src/Wire/API/Team/Size.hs +++ b/libs/wire-api/src/Wire/API/Team/Size.hs @@ -22,8 +22,8 @@ where import Control.Lens ((?~)) import Data.Aeson qualified as A +import Data.OpenApi qualified as S import Data.Schema -import Data.Swagger qualified as S import Imports import Numeric.Natural diff --git a/libs/wire-api/src/Wire/API/Unreachable.hs b/libs/wire-api/src/Wire/API/Unreachable.hs index baf37558eff..54055ae6359 100644 --- a/libs/wire-api/src/Wire/API/Unreachable.hs +++ b/libs/wire-api/src/Wire/API/Unreachable.hs @@ -28,9 +28,9 @@ import Data.Aeson qualified as A import Data.Id import Data.List.NonEmpty import Data.List.NonEmpty qualified as NE +import Data.OpenApi qualified as S import Data.Qualified import Data.Schema -import Data.Swagger qualified as S import Imports newtype UnreachableUsers = UnreachableUsers {unreachableUsers :: NonEmpty (Qualified UserId)} diff --git a/libs/wire-api/src/Wire/API/User.hs b/libs/wire-api/src/Wire/API/User.hs index 9404502ca68..16be7999255 100644 --- a/libs/wire-api/src/Wire/API/User.hs +++ b/libs/wire-api/src/Wire/API/User.hs @@ -177,13 +177,13 @@ import Data.Json.Util (UTCTimeMillis, (#)) import Data.LegalHold (UserLegalHoldStatus) import Data.List.NonEmpty (NonEmpty (..)) import Data.Misc (PlainTextPassword6, PlainTextPassword8) +import Data.OpenApi qualified as S import Data.Qualified import Data.Range import Data.SOP import Data.Schema import Data.Schema qualified as Schema import Data.Set qualified as Set -import Data.Swagger qualified as S import Data.Text qualified as T import Data.Text.Ascii import Data.Text.Encoding qualified as T @@ -1819,7 +1819,7 @@ instance S.ToSchema ListUsersQuery where pure $ S.NamedSchema (Just "ListUsersQuery") $ mempty - & S.type_ ?~ S.SwaggerObject + & S.type_ ?~ S.OpenApiObject & S.description ?~ "exactly one of qualified_ids or qualified_handles must be provided." & S.properties .~ InsOrdHashMap.fromList [("qualified_ids", uids), ("qualified_handles", handles)] & S.example ?~ toJSON (ListUsersByIds [Qualified (Id UUID.nil) (Domain "example.com")]) @@ -1954,8 +1954,8 @@ instance FromByteString VerificationAction where instance S.ToParamSchema VerificationAction where toParamSchema _ = mempty - { S._paramSchemaType = Just S.SwaggerString, - S._paramSchemaEnum = Just (A.String . toQueryParam <$> [(minBound :: VerificationAction) ..]) + { S._schemaType = Just S.OpenApiString, + S._schemaEnum = Just (A.String . toQueryParam <$> [(minBound :: VerificationAction) ..]) } instance FromHttpApiData VerificationAction where diff --git a/libs/wire-api/src/Wire/API/User/Activation.hs b/libs/wire-api/src/Wire/API/User/Activation.hs index 7777b2c25b8..e14b30bc326 100644 --- a/libs/wire-api/src/Wire/API/User/Activation.hs +++ b/libs/wire-api/src/Wire/API/User/Activation.hs @@ -40,9 +40,9 @@ import Data.Aeson qualified as A import Data.Aeson.Types (Parser) import Data.ByteString.Conversion import Data.Data (Proxy (Proxy)) +import Data.OpenApi (ToParamSchema) +import Data.OpenApi qualified as S import Data.Schema -import Data.Swagger (ToParamSchema) -import Data.Swagger qualified as S import Data.Text.Ascii import Data.Tuple.Extra (fst3, snd3, thd3) import Imports diff --git a/libs/wire-api/src/Wire/API/User/Auth.hs b/libs/wire-api/src/Wire/API/User/Auth.hs index 8670a4bc20e..df15827e2e2 100644 --- a/libs/wire-api/src/Wire/API/User/Auth.hs +++ b/libs/wire-api/src/Wire/API/User/Auth.hs @@ -71,9 +71,9 @@ import Data.Handle (Handle) import Data.Id import Data.Json.Util import Data.Misc (PlainTextPassword6) +import Data.OpenApi qualified as S import Data.SOP import Data.Schema -import Data.Swagger qualified as S import Data.Text qualified as T import Data.Text.Encoding qualified as T import Data.Text.Lazy.Encoding qualified as LT @@ -554,7 +554,7 @@ utcToSetCookie c = } instance S.ToParamSchema UserTokenCookie where - toParamSchema _ = mempty & S.type_ ?~ S.SwaggerString + toParamSchema _ = mempty & S.type_ ?~ S.OpenApiString instance FromHttpApiData UserTokenCookie where parseHeader = utcFromSetCookie . parseSetCookie diff --git a/libs/wire-api/src/Wire/API/User/Auth/LegalHold.hs b/libs/wire-api/src/Wire/API/User/Auth/LegalHold.hs index 951b2c19ab2..b1f20c416a8 100644 --- a/libs/wire-api/src/Wire/API/User/Auth/LegalHold.hs +++ b/libs/wire-api/src/Wire/API/User/Auth/LegalHold.hs @@ -21,8 +21,8 @@ import Data.Aeson (FromJSON (..), ToJSON (..)) import Data.Aeson qualified as A import Data.Id import Data.Misc +import Data.OpenApi qualified as S import Data.Schema -import Data.Swagger qualified as S import Imports import Wire.API.User.Auth diff --git a/libs/wire-api/src/Wire/API/User/Auth/ReAuth.hs b/libs/wire-api/src/Wire/API/User/Auth/ReAuth.hs index 040698e848a..0892089a90d 100644 --- a/libs/wire-api/src/Wire/API/User/Auth/ReAuth.hs +++ b/libs/wire-api/src/Wire/API/User/Auth/ReAuth.hs @@ -25,8 +25,8 @@ import Data.Aeson (FromJSON (..), ToJSON (..)) import Data.Aeson qualified as A import Data.Code import Data.Misc +import Data.OpenApi qualified as S import Data.Schema -import Data.Swagger qualified as S import Imports import Wire.API.User diff --git a/libs/wire-api/src/Wire/API/User/Auth/Sso.hs b/libs/wire-api/src/Wire/API/User/Auth/Sso.hs index 6e061536e01..0c9daa86859 100644 --- a/libs/wire-api/src/Wire/API/User/Auth/Sso.hs +++ b/libs/wire-api/src/Wire/API/User/Auth/Sso.hs @@ -20,8 +20,8 @@ module Wire.API.User.Auth.Sso where import Data.Aeson (FromJSON (..), ToJSON (..)) import Data.Aeson qualified as A import Data.Id +import Data.OpenApi qualified as S import Data.Schema -import Data.Swagger qualified as S import Imports import Wire.API.User.Auth diff --git a/libs/wire-api/src/Wire/API/User/Client.hs b/libs/wire-api/src/Wire/API/User/Client.hs index 761d8b7337e..198167cde03 100644 --- a/libs/wire-api/src/Wire/API/User/Client.hs +++ b/libs/wire-api/src/Wire/API/User/Client.hs @@ -83,11 +83,11 @@ import Data.Id import Data.Json.Util import Data.Map.Strict qualified as Map import Data.Misc (Latitude (..), Location, Longitude (..), PlainTextPassword6, latitude, location, longitude) +import Data.OpenApi hiding (Schema, ToSchema, nullable, schema) +import Data.OpenApi qualified as Swagger hiding (nullable) import Data.Qualified import Data.Schema import Data.Set qualified as Set -import Data.Swagger hiding (Schema, ToSchema, schema) -import Data.Swagger qualified as Swagger import Data.Text.Encoding qualified as Text.E import Data.Time.Clock import Data.UUID (toASCIIBytes) @@ -368,7 +368,7 @@ instance Swagger.ToSchema UserClientsFull where pure $ NamedSchema (Just "UserClientsFull") $ mempty - & type_ ?~ SwaggerObject + & type_ ?~ OpenApiObject & description ?~ "Dictionary object of `Client` objects indexed by `UserId`." & example ?~ "{\"1355c55a-0ac8-11ee-97ee-db1a6351f093\": , ...}" diff --git a/libs/wire-api/src/Wire/API/User/Client/DPoPAccessToken.hs b/libs/wire-api/src/Wire/API/User/Client/DPoPAccessToken.hs index df719886f37..99ed6e13d92 100644 --- a/libs/wire-api/src/Wire/API/User/Client/DPoPAccessToken.hs +++ b/libs/wire-api/src/Wire/API/User/Client/DPoPAccessToken.hs @@ -22,10 +22,10 @@ module Wire.API.User.Client.DPoPAccessToken where import Data.Aeson (FromJSON, ToJSON) import Data.ByteString.Conversion (FromByteString (..), ToByteString (..), fromByteString', toByteString') +import Data.OpenApi qualified as S +import Data.OpenApi.ParamSchema (ToParamSchema (..)) import Data.SOP import Data.Schema -import Data.Swagger qualified as S -import Data.Swagger.ParamSchema (ToParamSchema (..)) import Data.Text as T import Data.Text.Encoding (decodeUtf8, encodeUtf8) import Imports diff --git a/libs/wire-api/src/Wire/API/User/Client/Prekey.hs b/libs/wire-api/src/Wire/API/User/Client/Prekey.hs index 4f03328465a..f58eaa000ed 100644 --- a/libs/wire-api/src/Wire/API/User/Client/Prekey.hs +++ b/libs/wire-api/src/Wire/API/User/Client/Prekey.hs @@ -35,8 +35,8 @@ where import Data.Aeson (FromJSON (..), ToJSON (..)) import Data.Hashable (hash) import Data.Id +import Data.OpenApi qualified as S import Data.Schema -import Data.Swagger qualified as S import Imports import Wire.Arbitrary (Arbitrary (arbitrary), GenericUniform (..)) diff --git a/libs/wire-api/src/Wire/API/User/Handle.hs b/libs/wire-api/src/Wire/API/User/Handle.hs index 08242a6dfe9..3db27ef8c12 100644 --- a/libs/wire-api/src/Wire/API/User/Handle.hs +++ b/libs/wire-api/src/Wire/API/User/Handle.hs @@ -28,10 +28,10 @@ import Control.Applicative import Data.Aeson (FromJSON (..), ToJSON (..)) import Data.Aeson qualified as A import Data.Id (UserId) +import Data.OpenApi qualified as S import Data.Qualified (Qualified (..), deprecatedSchema) import Data.Range import Data.Schema -import Data.Swagger qualified as S import Imports import Wire.Arbitrary (Arbitrary, GenericUniform (..)) diff --git a/libs/wire-api/src/Wire/API/User/Identity.hs b/libs/wire-api/src/Wire/API/User/Identity.hs index c71bec44864..2b88c1d3bd8 100644 --- a/libs/wire-api/src/Wire/API/User/Identity.hs +++ b/libs/wire-api/src/Wire/API/User/Identity.hs @@ -61,9 +61,9 @@ import Data.Attoparsec.Text import Data.Bifunctor (first) import Data.ByteString.Conversion import Data.CaseInsensitive qualified as CI +import Data.OpenApi (ToParamSchema (..)) +import Data.OpenApi qualified as S import Data.Schema -import Data.Swagger (ToParamSchema (..)) -import Data.Swagger qualified as S import Data.Text qualified as Text import Data.Text.Encoding (decodeUtf8', encodeUtf8) import Data.Time.Clock @@ -326,7 +326,7 @@ instance S.ToSchema UserSSOId where pure $ S.NamedSchema (Just "UserSSOId") $ mempty - & S.type_ ?~ S.SwaggerObject + & S.type_ ?~ S.OpenApiObject & S.properties .~ [ ("tenant", tenantSchema), ("subject", subjectSchema), diff --git a/libs/wire-api/src/Wire/API/User/IdentityProvider.hs b/libs/wire-api/src/Wire/API/User/IdentityProvider.hs index f45a6f991ea..e954f15c2e6 100644 --- a/libs/wire-api/src/Wire/API/User/IdentityProvider.hs +++ b/libs/wire-api/src/Wire/API/User/IdentityProvider.hs @@ -31,8 +31,8 @@ import Data.ByteString.Conversion qualified as BSC import Data.HashMap.Strict.InsOrd (InsOrdHashMap) import Data.HashMap.Strict.InsOrd qualified as InsOrdHashMap import Data.Id (TeamId) +import Data.OpenApi import Data.Proxy (Proxy (Proxy)) -import Data.Swagger import Imports import Network.HTTP.Media ((//)) import SAML2.WebSSO (IdPConfig) @@ -108,9 +108,9 @@ instance ToHttpApiData WireIdPAPIVersion where instance ToParamSchema WireIdPAPIVersion where toParamSchema Proxy = mempty - { _paramSchemaDefault = Just "v2", - _paramSchemaType = Just SwaggerString, - _paramSchemaEnum = Just (String . toQueryParam <$> [(minBound :: WireIdPAPIVersion) ..]) + { _schemaDefault = Just "v2", + _schemaType = Just OpenApiString, + _schemaEnum = Just (String . toQueryParam <$> [(minBound :: WireIdPAPIVersion) ..]) } instance Cql.Cql WireIdPAPIVersion where @@ -205,7 +205,7 @@ instance ToSchema IdPMetadataInfo where & properties .~ properties_ & minProperties ?~ 1 & maxProperties ?~ 1 - & type_ ?~ SwaggerObject + & type_ ?~ OpenApiObject where properties_ :: InsOrdHashMap Text (Referenced Schema) properties_ = diff --git a/libs/wire-api/src/Wire/API/User/Orphans.hs b/libs/wire-api/src/Wire/API/User/Orphans.hs index 05f49534e4f..10ec177a3fe 100644 --- a/libs/wire-api/src/Wire/API/User/Orphans.hs +++ b/libs/wire-api/src/Wire/API/User/Orphans.hs @@ -26,8 +26,8 @@ import Data.Char import Data.Currency qualified as Currency import Data.ISO3166_CountryCodes import Data.LanguageCodes +import Data.OpenApi import Data.Proxy -import Data.Swagger import Data.UUID import Data.X509 as X509 import Imports @@ -35,7 +35,7 @@ import SAML2.WebSSO qualified as SAML import SAML2.WebSSO.Types.TH (deriveJSONOptions) import Servant.API ((:>)) import Servant.Multipart qualified as SM -import Servant.Swagger +import Servant.OpenApi import URI.ByteString deriving instance Generic ISO639_1 @@ -94,7 +94,7 @@ instance ToSchema (SAML.FormRedirect SAML.AuthnRequest) where pure $ NamedSchema (Just "FormRedirect") $ mempty - & type_ ?~ SwaggerObject + & type_ ?~ OpenApiObject & properties . at "uri" ?~ Inline (toSchema (Proxy @Text)) & properties . at "xml" ?~ authnReqSchema @@ -110,8 +110,8 @@ instance ToSchema SAML.SPMetadata where instance ToSchema Void where declareNamedSchema _ = declareNamedSchema (Proxy @String) -instance HasSwagger route => HasSwagger (SM.MultipartForm SM.Mem resp :> route) where - toSwagger _proxy = toSwagger (Proxy @route) +instance HasOpenApi route => HasOpenApi (SM.MultipartForm SM.Mem resp :> route) where + toOpenApi _proxy = toOpenApi (Proxy @route) instance ToSchema SAML.IdPId where declareNamedSchema _ = declareNamedSchema (Proxy @UUID) diff --git a/libs/wire-api/src/Wire/API/User/Password.hs b/libs/wire-api/src/Wire/API/User/Password.hs index 2a6b9bf20ed..4f14e4ca7c6 100644 --- a/libs/wire-api/src/Wire/API/User/Password.hs +++ b/libs/wire-api/src/Wire/API/User/Password.hs @@ -36,11 +36,11 @@ import Data.Aeson qualified as A import Data.Aeson.Types (Parser) import Data.ByteString.Conversion import Data.Misc (PlainTextPassword8) +import Data.OpenApi qualified as S +import Data.OpenApi.ParamSchema import Data.Proxy (Proxy (Proxy)) import Data.Range (Ranged (..)) import Data.Schema as Schema -import Data.Swagger qualified as S -import Data.Swagger.ParamSchema import Data.Text.Ascii import Data.Tuple.Extra (fst3, snd3, thd3) import Imports diff --git a/libs/wire-api/src/Wire/API/User/Profile.hs b/libs/wire-api/src/Wire/API/User/Profile.hs index 8f03b39b375..ae018f20b75 100644 --- a/libs/wire-api/src/Wire/API/User/Profile.hs +++ b/libs/wire-api/src/Wire/API/User/Profile.hs @@ -58,9 +58,9 @@ import Data.Attoparsec.Text import Data.ByteString.Conversion import Data.ISO3166_CountryCodes import Data.LanguageCodes +import Data.OpenApi qualified as S import Data.Range import Data.Schema -import Data.Swagger qualified as S import Data.Text qualified as Text import Imports import Wire.API.Asset (AssetKey (..)) diff --git a/libs/wire-api/src/Wire/API/User/RichInfo.hs b/libs/wire-api/src/Wire/API/User/RichInfo.hs index ef0eac713e8..32a3db8fa19 100644 --- a/libs/wire-api/src/Wire/API/User/RichInfo.hs +++ b/libs/wire-api/src/Wire/API/User/RichInfo.hs @@ -52,8 +52,8 @@ import Data.CaseInsensitive (CI) import Data.CaseInsensitive qualified as CI import Data.List.Extra (nubOrdOn) import Data.Map qualified as Map +import Data.OpenApi qualified as S import Data.Schema -import Data.Swagger qualified as S import Data.Text qualified as Text import Imports import Test.QuickCheck qualified as QC diff --git a/libs/wire-api/src/Wire/API/User/Saml.hs b/libs/wire-api/src/Wire/API/User/Saml.hs index 8ff2e27c954..09ad0d24367 100644 --- a/libs/wire-api/src/Wire/API/User/Saml.hs +++ b/libs/wire-api/src/Wire/API/User/Saml.hs @@ -30,8 +30,8 @@ import Data.Aeson hiding (fieldLabelModifier) import Data.Aeson.TH hiding (fieldLabelModifier) import Data.ByteString.Builder qualified as Builder import Data.Id (UserId) +import Data.OpenApi import Data.Proxy (Proxy (Proxy)) -import Data.Swagger import Data.Text qualified as T import Data.Time import GHC.TypeLits (KnownSymbol, symbolVal) diff --git a/libs/wire-api/src/Wire/API/User/Scim.hs b/libs/wire-api/src/Wire/API/User/Scim.hs index f3440beea7b..752c608bd85 100644 --- a/libs/wire-api/src/Wire/API/User/Scim.hs +++ b/libs/wire-api/src/Wire/API/User/Scim.hs @@ -59,8 +59,8 @@ import Data.Id (ScimTokenId, TeamId, UserId) import Data.Json.Util ((#)) import Data.Map qualified as Map import Data.Misc (PlainTextPassword6) +import Data.OpenApi hiding (Operation) import Data.Proxy -import Data.Swagger hiding (Operation) import Data.Text.Encoding (encodeUtf8) import Data.Time.Clock (UTCTime) import Imports @@ -462,7 +462,7 @@ instance ToSchema ScimTokenInfo where pure $ NamedSchema (Just "ScimTokenInfo") $ mempty - & type_ ?~ SwaggerObject + & type_ ?~ OpenApiObject & properties .~ [ ("team", teamSchema), ("id", idSchema), @@ -478,7 +478,7 @@ instance ToSchema CreateScimToken where pure $ NamedSchema (Just "CreateScimToken") $ mempty - & type_ ?~ SwaggerObject + & type_ ?~ OpenApiObject & properties .~ [ ("description", textSchema), ("password", textSchema), @@ -493,7 +493,7 @@ instance ToSchema CreateScimTokenResponse where pure $ NamedSchema (Just "CreateScimTokenResponse") $ mempty - & type_ ?~ SwaggerObject + & type_ ?~ OpenApiObject & properties .~ [ ("token", tokenSchema), ("info", infoSchema) @@ -506,7 +506,7 @@ instance ToSchema ScimTokenList where pure $ NamedSchema (Just "ScimTokenList") $ mempty - & type_ ?~ SwaggerObject + & type_ ?~ OpenApiObject & properties .~ [ ("tokens", infoListSchema) ] diff --git a/libs/wire-api/src/Wire/API/User/Search.hs b/libs/wire-api/src/Wire/API/User/Search.hs index 819f0111ab0..deaf7c08f4d 100644 --- a/libs/wire-api/src/Wire/API/User/Search.hs +++ b/libs/wire-api/src/Wire/API/User/Search.hs @@ -43,11 +43,11 @@ import Data.ByteString.Conversion (FromByteString (..), ToByteString (..)) import Data.Either.Combinators (mapLeft) import Data.Id (TeamId, UserId) import Data.Json.Util (UTCTimeMillis) +import Data.OpenApi (ToParamSchema (..)) +import Data.OpenApi qualified as S import Data.Proxy import Data.Qualified import Data.Schema -import Data.Swagger (ToParamSchema (..)) -import Data.Swagger qualified as S import Data.Text qualified as T import Data.Text.Ascii (AsciiBase64Url, toText, validateBase64Url) import Imports @@ -228,7 +228,7 @@ data TeamUserSearchSortBy instance S.ToParamSchema TeamUserSearchSortBy where toParamSchema _ = mempty - & S.type_ ?~ S.SwaggerString + & S.type_ ?~ S.OpenApiString & S.enum_ ?~ fmap teamUserSearchSortByName [minBound .. maxBound] instance ToByteString TeamUserSearchSortBy where @@ -264,7 +264,7 @@ data TeamUserSearchSortOrder instance S.ToParamSchema TeamUserSearchSortOrder where toParamSchema _ = mempty - & S.type_ ?~ S.SwaggerString + & S.type_ ?~ S.OpenApiString & S.enum_ ?~ fmap teamUserSearchSortOrderName [minBound .. maxBound] instance ToByteString TeamUserSearchSortOrder where diff --git a/libs/wire-api/src/Wire/API/UserMap.hs b/libs/wire-api/src/Wire/API/UserMap.hs index bcf41da1559..31f81392195 100644 --- a/libs/wire-api/src/Wire/API/UserMap.hs +++ b/libs/wire-api/src/Wire/API/UserMap.hs @@ -24,9 +24,9 @@ import Data.Aeson (FromJSON, ToJSON (toJSON)) import Data.Domain (Domain) import Data.Id (UserId) import Data.Map qualified as Map +import Data.OpenApi (HasDescription (description), HasExample (example), NamedSchema (..), ToSchema (..), declareSchema, toSchema) import Data.Proxy (Proxy (..)) import Data.Set qualified as Set -import Data.Swagger (HasDescription (description), HasExample (example), NamedSchema (..), ToSchema (..), declareSchema, toSchema) import Data.Text qualified as Text import Data.Typeable (typeRep) import Imports @@ -56,7 +56,7 @@ instance Functor QualifiedUserMap where instance Arbitrary a => Arbitrary (QualifiedUserMap a) where arbitrary = QualifiedUserMap <$> mapOf' arbitrary arbitrary -instance (Typeable a, ToSchema a, ToJSON a, Arbitrary a) => ToSchema (UserMap (Set a)) where +instance (ToSchema a, ToJSON a, Arbitrary a) => ToSchema (UserMap (Set a)) where declareNamedSchema _ = do mapSch <- declareSchema (Proxy @(Map UserId (Set a))) let valueTypeName = Text.pack $ show $ typeRep $ Proxy @a diff --git a/libs/wire-api/src/Wire/API/Wrapped.hs b/libs/wire-api/src/Wire/API/Wrapped.hs index f6d71142a5f..44c41bbddc2 100644 --- a/libs/wire-api/src/Wire/API/Wrapped.hs +++ b/libs/wire-api/src/Wire/API/Wrapped.hs @@ -21,8 +21,8 @@ import Control.Lens ((.~), (?~)) import Data.Aeson import Data.Aeson.Key qualified as Key import Data.HashMap.Strict.InsOrd qualified as InsOrdHashMap +import Data.OpenApi import Data.Proxy (Proxy (..)) -import Data.Swagger import Data.Text qualified as Text import GHC.TypeLits (KnownSymbol, Symbol, symbolVal) import Imports @@ -48,7 +48,7 @@ instance (ToSchema a, KnownSymbol name) => ToSchema (Wrapped name a) where pure $ NamedSchema Nothing $ mempty - & type_ ?~ SwaggerObject + & type_ ?~ OpenApiObject & properties .~ InsOrdHashMap.singleton (Text.pack (symbolVal (Proxy @name))) wrappedSchema instance (Arbitrary a, KnownSymbol name) => Arbitrary (Wrapped name a) where diff --git a/libs/wire-api/test/unit/Test/Wire/API/Roundtrip/Aeson.hs b/libs/wire-api/test/unit/Test/Wire/API/Roundtrip/Aeson.hs index 79433dbd2d7..aefaa6cb8cd 100644 --- a/libs/wire-api/test/unit/Test/Wire/API/Roundtrip/Aeson.hs +++ b/libs/wire-api/test/unit/Test/Wire/API/Roundtrip/Aeson.hs @@ -20,7 +20,7 @@ module Test.Wire.API.Roundtrip.Aeson (tests) where import Data.Aeson (FromJSON, ToJSON, parseJSON, toJSON) import Data.Aeson.Types (parseEither) import Data.Id (ConvId) -import Data.Swagger (ToSchema, validatePrettyToJSON) +import Data.OpenApi (ToSchema, validatePrettyToJSON) import Imports import Test.Tasty qualified as T import Test.Tasty.QuickCheck (Arbitrary, counterexample, testProperty, (.&&.), (===)) @@ -355,7 +355,7 @@ testRoundTrip = testProperty msg trip testRoundTripWithSwagger :: forall a. - (Arbitrary a, Typeable a, ToJSON a, FromJSON a, ToSchema a, Eq a, Show a) => + (Arbitrary a, ToJSON a, FromJSON a, ToSchema a, Eq a, Show a) => T.TestTree testRoundTripWithSwagger = testProperty msg (trip .&&. scm) where diff --git a/libs/wire-api/test/unit/Test/Wire/API/Swagger.hs b/libs/wire-api/test/unit/Test/Wire/API/Swagger.hs index 8de2cb6ad16..bbb37e6e2a4 100644 --- a/libs/wire-api/test/unit/Test/Wire/API/Swagger.hs +++ b/libs/wire-api/test/unit/Test/Wire/API/Swagger.hs @@ -18,7 +18,7 @@ module Test.Wire.API.Swagger (tests) where import Data.Aeson (ToJSON) -import Data.Swagger (ToSchema, validatePrettyToJSON) +import Data.OpenApi (ToSchema, validatePrettyToJSON) import Imports import Test.Tasty qualified as T import Test.Tasty.QuickCheck (Arbitrary, counterexample, testProperty) @@ -56,7 +56,7 @@ tests = testToJSON @(Wrapped.Wrapped "some_user" User.User) ] -testToJSON :: forall a. (Arbitrary a, Typeable a, ToJSON a, ToSchema a, Show a) => T.TestTree +testToJSON :: forall a. (Arbitrary a, ToJSON a, ToSchema a, Show a) => T.TestTree testToJSON = testProperty msg trip where msg = show (typeRep @a) diff --git a/libs/wire-api/wire-api.cabal b/libs/wire-api/wire-api.cabal index e488149d735..b7d3af68205 100644 --- a/libs/wire-api/wire-api.cabal +++ b/libs/wire-api/wire-api.cabal @@ -28,6 +28,7 @@ library Wire.API.Conversation.Role Wire.API.Conversation.Typing Wire.API.CustomBackend + Wire.API.Deprecated Wire.API.Error Wire.API.Error.Brig Wire.API.Error.Cannon @@ -270,6 +271,7 @@ library , metrics-wai , mime >=0.4 , mtl + , openapi3 , pem >=0.2 , polysemy , proto-lens @@ -288,13 +290,12 @@ library , servant-client-core , servant-conduit , servant-multipart + , servant-openapi3 , servant-server - , servant-swagger , singletons , singletons-base , singletons-th , sop-core - , swagger2 , tagged , text >=0.11 , time >=1.4 @@ -748,12 +749,12 @@ test-suite wire-api-tests , imports , memory , metrics-wai + , openapi3 , process , QuickCheck , schema-profunctor , servant , servant-server - , swagger2 , tasty , tasty-hspec , tasty-hunit diff --git a/nix/haskell-pins.nix b/nix/haskell-pins.nix index 291ea06526f..cd56258ac99 100644 --- a/nix/haskell-pins.nix +++ b/nix/haskell-pins.nix @@ -133,13 +133,6 @@ let sha256 = "sha256-g2lbKt3+hToVFQvaHOa9dg4HqAL7YgReo8fy7wQavmY="; }; }; - swagger2 = { - src = fetchgit { - url = "https://github.com/GetShopTV/swagger2"; - rev = "d79deca03b714cdd4531217831a8305068b2e8f9"; - sha256 = "sha256-R3p0L0TgM0Bspe5z6vauwdPq9TmEWpMC53DBkMtCEoE="; - }; - }; # MR: https://gitlab.com/twittner/cql-io/-/merge_requests/20 cql-io = { src = fetchgit { @@ -180,6 +173,15 @@ let sha256 = "sha256-SKEE9ZqhjBxHYUKQaoB4IpN4/Ui3tS4S98FgZqj7WlY="; }; }; + servant-openapi3 = { + src = fetchgit { + # This is a patched version of the library that sets the required flag for HTTP request bodies. + # A PR for these changes has been made for the upstream library. biocad/servant-openapi3#49 + url = "https://github.com/lepsa/servant-openapi3"; + rev = "5cdb2783f15058f753c41b800415d4ba1149a78b"; + sha256 = "sha256-8FM3IAA3ewCuv9Mar8aWmzbyfKK9eLXIJPMHzmYb1zE="; + }; + }; # This can be removed once postie 0.6.0.3 (or later) is in nixpkgs postie = { src = fetchgit { diff --git a/nix/manual-overrides.nix b/nix/manual-overrides.nix index 31b6b7ce91a..4e4f70ab116 100644 --- a/nix/manual-overrides.nix +++ b/nix/manual-overrides.nix @@ -5,6 +5,7 @@ hself: hsuper: { aeson = (hlib.doJailbreak hsuper.aeson_2_1_2_1); binary-parsers = hlib.markUnbroken (hlib.doJailbreak hsuper.binary-parsers); bytestring-arbitrary = hlib.markUnbroken (hlib.doJailbreak hsuper.bytestring-arbitrary); + openapi3 = hlib.markUnbroken (hlib.dontCheck hsuper.openapi3); cql = hlib.appendPatch (hlib.markUnbroken hsuper.cql) (fetchpatch { url = "https://gitlab.com/twittner/cql/-/merge_requests/11.patch"; sha256 = "sha256-qfcCRkKjSS1TEqPRVBU9Ox2DjsdGsYG/F3DrZ5JGoEI="; @@ -23,7 +24,6 @@ hself: hsuper: { servant-swagger-ui = hlib.doJailbreak hsuper.servant-swagger-ui; servant-swagger-ui-core = hlib.doJailbreak hsuper.servant-swagger-ui-core; sodium-crypto-sign = hlib.addPkgconfigDepend hsuper.sodium-crypto-sign libsodium.dev; - swagger2 = hlib.doJailbreak hsuper.swagger2; text-icu-translit = hlib.markUnbroken (hlib.dontCheck hsuper.text-icu-translit); text-short = hlib.dontCheck hsuper.text-short; type-errors = hlib.dontCheck hsuper.type-errors; diff --git a/services/brig/brig.cabal b/services/brig/brig.cabal index c7d3826c41b..1a1b9814a16 100644 --- a/services/brig/brig.cabal +++ b/services/brig/brig.cabal @@ -260,6 +260,7 @@ library , mwc-random , network >=2.4 , network-conduit-tls + , openapi3 , optparse-applicative >=0.11 , polysemy , polysemy-plugin @@ -276,15 +277,14 @@ library , schema-profunctor , scientific >=0.3.4 , servant + , servant-openapi3 , servant-server - , servant-swagger , servant-swagger-ui , sodium-crypto-sign >=0.1 , split >=0.2 , ssl-util , statistics >=0.13 , stomp-queue >=0.3 - , swagger2 , template >=0.2 , template-haskell , text >=0.11 diff --git a/services/brig/default.nix b/services/brig/default.nix index c6b3867fcaa..6e7fa95539e 100644 --- a/services/brig/default.nix +++ b/services/brig/default.nix @@ -85,6 +85,7 @@ , network , network-conduit-tls , network-uri +, openapi3 , optparse-applicative , pem , pipes @@ -110,8 +111,8 @@ , servant , servant-client , servant-client-core +, servant-openapi3 , servant-server -, servant-swagger , servant-swagger-ui , sodium-crypto-sign , spar @@ -120,7 +121,6 @@ , statistics , stomp-queue , streaming-commons -, swagger2 , tasty , tasty-cannon , tasty-hunit @@ -235,6 +235,7 @@ mkDerivation { mwc-random network network-conduit-tls + openapi3 optparse-applicative polysemy polysemy-plugin @@ -251,15 +252,14 @@ mkDerivation { schema-profunctor scientific servant + servant-openapi3 servant-server - servant-swagger servant-swagger-ui sodium-crypto-sign split ssl-util statistics stomp-queue - swagger2 template template-haskell text diff --git a/services/brig/src/Brig/API/Internal.hs b/services/brig/src/Brig/API/Internal.hs index cf13f9d3386..75f90bd606c 100644 --- a/services/brig/src/Brig/API/Internal.hs +++ b/services/brig/src/Brig/API/Internal.hs @@ -76,7 +76,7 @@ import Network.Wai.Routing hiding (toList) import Network.Wai.Utilities as Utilities import Polysemy import Servant hiding (Handler, JSON, addHeader, respond) -import Servant.Swagger.Internal.Orphans () +import Servant.OpenApi.Internal.Orphans () import System.Logger.Class qualified as Log import System.Random (randomRIO) import UnliftIO.Async diff --git a/services/brig/src/Brig/API/Public.hs b/services/brig/src/Brig/API/Public.hs index 3343b551185..24996bde99a 100644 --- a/services/brig/src/Brig/API/Public.hs +++ b/services/brig/src/Brig/API/Public.hs @@ -87,9 +87,10 @@ import Data.List.NonEmpty (nonEmpty) import Data.Map.Strict qualified as Map import Data.Misc (IpAddr (..)) import Data.Nonce (Nonce, randomNonce) +import Data.OpenApi qualified as S import Data.Qualified import Data.Range -import Data.Swagger qualified as S +import Data.Schema () import Data.Text qualified as Text import Data.Text.Ascii qualified as Ascii import Data.Text.Lazy (pack) @@ -103,7 +104,7 @@ import Network.Wai.Utilities as Utilities import Polysemy import Servant hiding (Handler, JSON, addHeader, respond) import Servant qualified -import Servant.Swagger.Internal.Orphans () +import Servant.OpenApi.Internal.Orphans () import Servant.Swagger.UI import System.Logger.Class qualified as Log import Util.Logging (logFunction, logHandle, logTeam, logUser) @@ -220,7 +221,7 @@ versionedSwaggerDocsAPI Nothing = allroutes (throwError listAllVersionsResp) internalEndpointsSwaggerDocsAPI :: String -> PortNumber -> - S.Swagger -> + S.OpenApi -> Servant.Server (VersionedSwaggerDocsAPIBase service) internalEndpointsSwaggerDocsAPI service examplePort swagger (Just (VersionNumber V5)) = swaggerSchemaUIServer $ diff --git a/services/brig/src/Brig/API/Public/Swagger.hs b/services/brig/src/Brig/API/Public/Swagger.hs index 0f81a74a4d4..e17607cf8f7 100644 --- a/services/brig/src/Brig/API/Public/Swagger.hs +++ b/services/brig/src/Brig/API/Public/Swagger.hs @@ -18,8 +18,8 @@ import Data.Aeson qualified as A import Data.FileEmbed import Data.HashMap.Strict.InsOrd qualified as HM import Data.HashSet.InsOrd qualified as InsOrdSet -import Data.Swagger qualified as S -import Data.Swagger.Declare qualified as S +import Data.OpenApi qualified as S +import Data.OpenApi.Declare qualified as S import Data.Text qualified as T import FileEmbedLzma import GHC.TypeLits @@ -27,7 +27,7 @@ import Imports hiding (head) import Language.Haskell.TH import Network.Socket import Servant -import Servant.Swagger.Internal.Orphans () +import Servant.OpenApi.Internal.Orphans () import Servant.Swagger.UI import Wire.API.Event.Conversation qualified import Wire.API.Event.FeatureConfig qualified @@ -68,16 +68,14 @@ swaggerPregenUIServer = . fromMaybe A.Null . A.decode -adjustSwaggerForInternalEndpoint :: String -> PortNumber -> S.Swagger -> S.Swagger +adjustSwaggerForInternalEndpoint :: String -> PortNumber -> S.OpenApi -> S.OpenApi adjustSwaggerForInternalEndpoint service examplePort swagger = swagger & S.info . S.title .~ T.pack ("Wire-Server internal API (" ++ service ++ ")") & S.info . S.description ?~ renderedDescription - & S.host ?~ S.Host "localhost" (Just examplePort) & S.allOperations . S.tags <>~ tag -- Enforce HTTP as the services themselves don't understand HTTPS - & S.schemes ?~ [S.Http] - & S.allOperations . S.schemes ?~ [S.Http] + & S.servers .~ [S.Server ("http://localhost:" <> T.pack (show examplePort)) Nothing mempty] where tag :: InsOrdSet.InsOrdHashSet S.TagName tag = InsOrdSet.singleton @S.TagName (T.pack service) @@ -102,7 +100,7 @@ adjustSwaggerForInternalEndpoint service examplePort swagger = emptySwagger :: Servant.Server (ServiceSwaggerDocsAPIBase a) emptySwagger = swaggerSchemaUIServer $ - mempty @S.Swagger + mempty @S.OpenApi & S.info . S.description ?~ "There is no Swagger documentation for this version. Please refer to v3 or later." diff --git a/services/brig/src/Brig/User/EJPD.hs b/services/brig/src/Brig/User/EJPD.hs index 07116e81207..fea8e51a37a 100644 --- a/services/brig/src/Brig/User/EJPD.hs +++ b/services/brig/src/Brig/User/EJPD.hs @@ -36,7 +36,7 @@ import Data.Id (UserId) import Data.Set qualified as Set import Imports hiding (head) import Polysemy (Member) -import Servant.Swagger.Internal.Orphans () +import Servant.OpenApi.Internal.Orphans () import Wire.API.Connection (Relation, RelationWithHistory (..), relationDropHistory) import Wire.API.Push.Token qualified as PushTok import Wire.API.Routes.Internal.Brig.EJPD (EJPDRequestBody (EJPDRequestBody), EJPDResponseBody (EJPDResponseBody), EJPDResponseItem (EJPDResponseItem)) diff --git a/services/spar/default.nix b/services/spar/default.nix index 4bd791dfcfe..e5b1d9bd105 100644 --- a/services/spar/default.nix +++ b/services/spar/default.nix @@ -41,6 +41,7 @@ , MonadRandom , mtl , network-uri +, openapi3 , optparse-applicative , polysemy , polysemy-check @@ -53,10 +54,9 @@ , saml2-web-sso , servant , servant-multipart +, servant-openapi3 , servant-server -, servant-swagger , silently -, swagger2 , tasty-hunit , text , text-latin1 @@ -211,14 +211,14 @@ mkDerivation { metrics-wai mtl network-uri + openapi3 polysemy polysemy-plugin polysemy-wire-zoo QuickCheck saml2-web-sso servant - servant-swagger - swagger2 + servant-openapi3 time tinylog types-common diff --git a/services/spar/spar.cabal b/services/spar/spar.cabal index 568034cedde..124cbb93a99 100644 --- a/services/spar/spar.cabal +++ b/services/spar/spar.cabal @@ -618,15 +618,15 @@ test-suite spec , metrics-wai , mtl , network-uri + , openapi3 , polysemy , polysemy-plugin , polysemy-wire-zoo , QuickCheck , saml2-web-sso >=0.19 , servant - , servant-swagger + , servant-openapi3 , spar - , swagger2 , time , tinylog , types-common diff --git a/services/spar/test/Arbitrary.hs b/services/spar/test/Arbitrary.hs index d8b2daf6839..44d8f38ddac 100644 --- a/services/spar/test/Arbitrary.hs +++ b/services/spar/test/Arbitrary.hs @@ -24,8 +24,8 @@ module Arbitrary where import Data.Aeson import Data.Id (TeamId, UserId) +import Data.OpenApi hiding (Header (..)) import Data.Proxy -import Data.Swagger hiding (Header (..)) import Imports import SAML2.WebSSO.Test.Arbitrary () import SAML2.WebSSO.Types diff --git a/services/spar/test/Test/Spar/APISpec.hs b/services/spar/test/Test/Spar/APISpec.hs index 07bfdb8fde3..a82d00c40f6 100644 --- a/services/spar/test/Test/Spar/APISpec.hs +++ b/services/spar/test/Test/Spar/APISpec.hs @@ -27,7 +27,7 @@ import Data.Metrics.Servant (routesToPaths) import Data.Metrics.Test (pathsConsistencyCheck) import Data.Proxy (Proxy (Proxy)) import Imports -import Servant.Swagger (validateEveryToJSON) +import Servant.OpenApi (validateEveryToJSON) import Spar.API as API import Test.Hspec (Spec, describe, it, shouldBe, shouldSatisfy) import Test.QuickCheck (property) diff --git a/tools/fedcalls/default.nix b/tools/fedcalls/default.nix index 133e6e886bd..2d9d10e326d 100644 --- a/tools/fedcalls/default.nix +++ b/tools/fedcalls/default.nix @@ -3,15 +3,16 @@ # must be regenerated whenever local packages are added or removed, or # dependencies are added or removed. { mkDerivation -, aeson , base , containers , gitignoreSource , imports , insert-ordered-containers , language-dot +, lens , lib -, swagger2 +, openapi3 +, text , wire-api }: mkDerivation { @@ -21,13 +22,14 @@ mkDerivation { isLibrary = false; isExecutable = true; executableHaskellDepends = [ - aeson base containers imports insert-ordered-containers language-dot - swagger2 + lens + openapi3 + text wire-api ]; description = "Generate a dot file from swagger docs representing calls to federated instances"; diff --git a/tools/fedcalls/fedcalls.cabal b/tools/fedcalls/fedcalls.cabal index a7bf9ac1981..615a8bbd151 100644 --- a/tools/fedcalls/fedcalls.cabal +++ b/tools/fedcalls/fedcalls.cabal @@ -63,13 +63,14 @@ executable fedcalls -rtsopts -Wredundant-constraints -Wunused-packages build-depends: - aeson - , base + base , containers , imports , insert-ordered-containers , language-dot - , swagger2 + , lens + , openapi3 + , text , wire-api default-language: GHC2021 diff --git a/tools/fedcalls/src/Main.hs b/tools/fedcalls/src/Main.hs index c1b4471da9f..387424fde9c 100644 --- a/tools/fedcalls/src/Main.hs +++ b/tools/fedcalls/src/Main.hs @@ -23,23 +23,13 @@ module Main where import Control.Exception (assert) -import Data.Aeson as A -import Data.Aeson.Types qualified as A +import Control.Lens import Data.HashMap.Strict.InsOrd qualified as HM +import Data.HashSet.InsOrd (InsOrdHashSet) import Data.Map qualified as M -import Data.Swagger - ( PathItem, - Swagger, - _operationExtensions, - _pathItemDelete, - _pathItemGet, - _pathItemHead, - _pathItemOptions, - _pathItemPatch, - _pathItemPost, - _pathItemPut, - _swaggerPaths, - ) +import Data.OpenApi +import Data.OpenApi.Lens qualified as S +import Data.Text qualified as T import Imports import Language.Dot as D import Wire.API.Routes.API @@ -65,7 +55,7 @@ calls = assert (calls' == nub calls') calls' where calls' = mconcat $ parse <$> swaggers -swaggers :: [Swagger] +swaggers :: [OpenApi] swaggers = [ -- TODO: introduce allSwaggerDocs in wire-api that collects these for all -- services, use that in /services/brig/src/Brig/API/Public.hs instead of @@ -101,37 +91,63 @@ data MakesCallTo = MakesCallTo ------------------------------ -parse :: Swagger -> [MakesCallTo] -parse = +parse :: OpenApi -> [MakesCallTo] +parse oapi = mconcat - . fmap parseOperationExtensions + . fmap (parseOperationExtensions allTags) . mconcat . fmap flattenPathItems . HM.toList - . _swaggerPaths + $ oapi ^. S.paths + where + allTags = oapi ^. S.tags + +-- Simple aliases to help track which field is what +type RPC = String + +type Component = String -- | extract path, method, and operation extensions -flattenPathItems :: (FilePath, PathItem) -> [((FilePath, String), HM.InsOrdHashMap Text Value)] +flattenPathItems :: (FilePath, PathItem) -> [((FilePath, String), InsOrdHashSet TagName)] flattenPathItems (path, item) = filter ((/= mempty) . snd) $ catMaybes - [ ((path, "get"),) . _operationExtensions <$> _pathItemGet item, - ((path, "put"),) . _operationExtensions <$> _pathItemPut item, - ((path, "post"),) . _operationExtensions <$> _pathItemPost item, - ((path, "delete"),) . _operationExtensions <$> _pathItemDelete item, - ((path, "options"),) . _operationExtensions <$> _pathItemOptions item, - ((path, "head"),) . _operationExtensions <$> _pathItemHead item, - ((path, "patch"),) . _operationExtensions <$> _pathItemPatch item + [ ((path, "get"),) . view S.tags <$> _pathItemGet item, + ((path, "put"),) . view S.tags <$> _pathItemPut item, + ((path, "post"),) . view S.tags <$> _pathItemPost item, + ((path, "delete"),) . view S.tags <$> _pathItemDelete item, + ((path, "options"),) . view S.tags <$> _pathItemOptions item, + ((path, "head"),) . view S.tags <$> _pathItemHead item, + ((path, "patch"),) . view S.tags <$> _pathItemPatch item ] -parseOperationExtensions :: ((FilePath, String), HM.InsOrdHashMap Text Value) -> [MakesCallTo] -parseOperationExtensions ((path, method), hm) = uncurry (MakesCallTo path method) <$> findCallsFedInfo hm +parseOperationExtensions :: InsOrdHashSet Tag -> ((FilePath, String), InsOrdHashSet TagName) -> [MakesCallTo] +parseOperationExtensions allTags ((path, method), hm) = + uncurry (MakesCallTo path method) <$> findCallsFedInfo allTags hm -findCallsFedInfo :: HM.InsOrdHashMap Text Value -> [(String, String)] -findCallsFedInfo hm = case A.parse parseJSON <$> HM.lookup "wire-makes-federated-call-to" hm of - Just (A.Success (fedcalls :: [(String, String)])) -> fedcalls - Just bad -> error $ "invalid extension `wire-makes-federated-call-to`: expected `[(comp, name), ...]`, got " <> show bad - Nothing -> [] +-- Given a set of tags, and a set of tag names for an operation, +-- parse out the RPC calls and their components +findCallsFedInfo :: InsOrdHashSet Tag -> InsOrdHashSet TagName -> [(Component, RPC)] +findCallsFedInfo allTags = mapMaybe extractStrings . toList + where + magicPrefix :: Text + magicPrefix = "wire-makes-federated-call-to-" + extractStrings :: TagName -> Maybe (Component, RPC) + extractStrings tagName = + tag >>= \t -> + (,) + -- Extract the name and description, and drop everything that is empty + -- This gives us the component name, and as a route may call the same component + -- multiple times, it has to go into the description so it isn't dropped by the set. + <$> fmap T.unpack t._tagDescription + -- Strip off the magic string from the tag names, and drop empty results + -- This also implicitly filters for results that start with the prefix. + -- This gives us the RPC name, as that will be unique for each route, and it + -- doesn't matter if it is set multiple times and dropped in the set, as it + -- still describes that Fed call is made. + <*> fmap T.unpack (T.stripPrefix magicPrefix t._tagName) + where + tag = find (\t -> t._tagName == tagName) allTags ------------------------------ @@ -158,7 +174,7 @@ mkDotGraph inbound = Graph StrictGraph DirectedGraph Nothing (mods <> nodes <> e itemSourceNode (MakesCallTo path method _ _) = method <> " " <> path itemTargetNode :: MakesCallTo -> String - itemTargetNode (MakesCallTo _ _ comp name) = "[" <> comp <> "]:" <> name + itemTargetNode (MakesCallTo _ _ comp rpcName) = "[" <> comp <> "]:" <> rpcName callingNodes :: Map String Integer callingNodes = diff --git a/tools/stern/default.nix b/tools/stern/default.nix index c8c64c0d784..3a5afeaa844 100644 --- a/tools/stern/default.nix +++ b/tools/stern/default.nix @@ -28,16 +28,16 @@ , lib , metrics-wai , mtl +, openapi3 , optparse-applicative , random , retry , schema-profunctor , servant +, servant-openapi3 , servant-server -, servant-swagger , servant-swagger-ui , split -, swagger2 , tagged , tasty , tasty-hunit @@ -78,13 +78,13 @@ mkDerivation { lens metrics-wai mtl + openapi3 schema-profunctor servant + servant-openapi3 servant-server - servant-swagger servant-swagger-ui split - swagger2 text tinylog transformers diff --git a/tools/stern/src/Stern/API/Routes.hs b/tools/stern/src/Stern/API/Routes.hs index f3e7116d514..aae30de2805 100644 --- a/tools/stern/src/Stern/API/Routes.hs +++ b/tools/stern/src/Stern/API/Routes.hs @@ -32,14 +32,14 @@ import Data.Aeson qualified as A import Data.Handle import Data.Id import Data.Kind +import Data.OpenApi qualified as S import Data.Schema qualified as Schema -import Data.Swagger qualified as S import Imports hiding (head) import Network.HTTP.Types.Status import Network.Wai.Utilities import Servant hiding (Handler, WithStatus (..), addHeader, respond) -import Servant.Swagger (HasSwagger (toSwagger)) -import Servant.Swagger.Internal.Orphans () +import Servant.OpenApi (HasOpenApi (toOpenApi)) +import Servant.OpenApi.Internal.Orphans () import Servant.Swagger.UI import Stern.Types import Wire.API.CustomBackend @@ -455,7 +455,7 @@ type SwaggerDocsAPI = SwaggerSchemaUI "swagger-ui" "swagger.json" swaggerDocs :: Servant.Server SwaggerDocsAPI swaggerDocs = swaggerSchemaUIServer $ - toSwagger (Proxy @SternAPI) + toOpenApi (Proxy @SternAPI) & S.info . S.title .~ "Stern API" & cleanupSwagger diff --git a/tools/stern/src/Stern/Types.hs b/tools/stern/src/Stern/Types.hs index b62994c943d..f8ed807492a 100644 --- a/tools/stern/src/Stern/Types.hs +++ b/tools/stern/src/Stern/Types.hs @@ -30,10 +30,10 @@ import Data.Aeson import Data.Aeson.TH import Data.ByteString.Conversion import Data.Json.Util +import Data.OpenApi qualified as Swagger import Data.Proxy import Data.Range import Data.Schema qualified as S -import Data.Swagger qualified as Swagger import Galley.Types.Teams import Imports import Servant.API @@ -127,7 +127,7 @@ instance Swagger.ToSchema ConsentLog where declareNamedSchema _ = pure . Swagger.NamedSchema (Just "ConsentLog") $ mempty - & Swagger.type_ ?~ Swagger.SwaggerObject + & Swagger.type_ ?~ Swagger.OpenApiObject & Swagger.description ?~ "(object structure is not specified in this schema)" newtype ConsentValue = ConsentValue @@ -152,7 +152,7 @@ instance Swagger.ToSchema ConsentLogAndMarketo where declareNamedSchema _ = pure . Swagger.NamedSchema (Just "ConsentLogAndMarketo") $ mempty - & Swagger.type_ ?~ Swagger.SwaggerObject + & Swagger.type_ ?~ Swagger.OpenApiObject & Swagger.description ?~ "(object structure is not specified in this schema)" newtype UserMetaInfo = UserMetaInfo @@ -164,7 +164,7 @@ instance Swagger.ToSchema UserMetaInfo where declareNamedSchema _ = pure . Swagger.NamedSchema (Just "UserMetaInfo") $ mempty - & Swagger.type_ ?~ Swagger.SwaggerObject + & Swagger.type_ ?~ Swagger.OpenApiObject & Swagger.description ?~ "(object structure is not specified in this schema)" newtype InvoiceId = InvoiceId {unInvoiceId :: Text} diff --git a/tools/stern/stern.cabal b/tools/stern/stern.cabal index 0a4be042c59..4cb3eff82ea 100644 --- a/tools/stern/stern.cabal +++ b/tools/stern/stern.cabal @@ -91,13 +91,13 @@ library , lens >=4.4 , metrics-wai >=0.3 , mtl >=2.1 + , openapi3 , schema-profunctor , servant + , servant-openapi3 , servant-server - , servant-swagger , servant-swagger-ui , split >=0.2 - , swagger2 , text >=1.1 , tinylog >=0.10 , transformers >=0.3