Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WPB-11973] Freeze API version 7, create dev API version 8. #4356

Merged
merged 6 commits into from
Dec 6, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/3-bug-fixes/WPB-11973-bump-api-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Freeze API version 7, create new dev version 8. Also update checklist.
3 changes: 1 addition & 2 deletions docs/src/developer/developer/api-versioning.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,7 @@ the version. In these example we assume that version `V6` should be finalized an
- In the same `Version` module update the `developmentVersions` value to list
only the new version.
- In `services/brig/src/Brig/API/Public.hs`
- update `versionedSwaggerDocsAPI` so that the finalized version points to the pregenerated swagger
- and `internalEndpointsSwaggerDocsAPI` so that the finalized version `V6`, the new version `V7`, as well as the unversioned path point to the swagger of the internal API, and the previous latest stable version V5 points to an empty swagger.
- update `versionedSwaggerDocsAPI` so that the finalized version points to the pregenerated swagger, and the dynamically generated swagger spits out swagger for the new `V7`.
- Set the version for `gDefaultAPIVersion` in `integration/test/Testlib/Env.hs` to 7.
- Consider updating the `backendApiVersion` value in Stern, which is
unit-tested by checking if it is listed as supported in the response to `GET
Expand Down
8 changes: 4 additions & 4 deletions integration/test/Test/Client.hs
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,8 @@ testUpdateClientWithConsumableNotificationsCapability = do
resp.status `shouldMatchInt` 200
resp.json %. "0.capabilities" `shouldMatch` [consumeCapability]

testGetClientCapabilitiesV6 :: App ()
testGetClientCapabilitiesV6 = do
testGetClientCapabilitiesV7 :: App ()
testGetClientCapabilitiesV7 = do
let allCapabilities = ["legalhold-implicit-consent", "consumable-notifications"]
alice <- randomUser OwnDomain def
addClient alice def {acapabilities = Just allCapabilities} `bindResponse` \resp -> do
Expand All @@ -116,8 +116,8 @@ testGetClientCapabilitiesV6 = do
resp.status `shouldMatchInt` 200
resp.json %. "0.capabilities" `shouldMatchSet` allCapabilities

-- In API v6 and below, the "capabilities" field is an enum, so having a new
-- In API v7 and below, the "capabilities" field is an enum, so having a new
-- value for this enum is a breaking change.
withAPIVersion 6 $ getSelfClients alice `bindResponse` \resp -> do
withAPIVersion 7 $ getSelfClients alice `bindResponse` \resp -> do
resp.status `shouldMatchInt` 200
resp.json %. "0.capabilities.capabilities" `shouldMatchSet` ["legalhold-implicit-consent"]
8 changes: 2 additions & 6 deletions integration/test/Test/Swagger.hs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import Testlib.Prelude
import UnliftIO.Temporary

existingVersions :: Set Int
existingVersions = Set.fromList [0, 1, 2, 3, 4, 5, 6, 7]
existingVersions = Set.fromList [0, 1, 2, 3, 4, 5, 6, 7, 8]

internalApis :: Set String
internalApis = Set.fromList ["brig", "cannon", "cargohold", "cannon", "spar"]
Expand Down Expand Up @@ -76,16 +76,12 @@ testSwaggerToc = do
bindResponse (get path) $ \resp -> do
resp.status `shouldMatchInt` 200
let body = cs @_ @String resp.body
body `shouldMatch` html
forM_ existingVersions $ \v ->
body `shouldContainString` ("v" <> show v)
body `shouldContainString` ("\nv" <> show v <> ":")
where
get :: String -> App Response
get path = rawBaseRequest OwnDomain Brig Unversioned path >>= submit "GET"

html :: String
html = "<html><head></head><body><h1>OpenAPI 3.0 docs for all Wire APIs</h1>\n<p>This wire-server system provides <a href=\"https://swagger.io/resources/open-api/\">OpenAPI 3.0</a> documentation of our HTTP REST API.</p> <p>The openapi docs are correct by construction (compiled from the server code), and more or less complete.</p> <p>Some endpoints are version-controlled. </a href=\"/api-version\">Show all supported versions.</a> <a href=\"https://docs.wire.com/developer/developer/api-versioning.html\">find out more.</a>\n<h2>Public (all available versions)</h2>\nv0: \n<a href=\"/v0/api/swagger-ui\">swagger-ui</a>; \n<a href=\"/v0/api/swagger.json\">swagger.json</a>\n<br>\nv1: \n<a href=\"/v1/api/swagger-ui\">swagger-ui</a>; \n<a href=\"/v1/api/swagger.json\">swagger.json</a>\n<br>\nv2: \n<a href=\"/v2/api/swagger-ui\">swagger-ui</a>; \n<a href=\"/v2/api/swagger.json\">swagger.json</a>\n<br>\nv3: \n<a href=\"/v3/api/swagger-ui\">swagger-ui</a>; \n<a href=\"/v3/api/swagger.json\">swagger.json</a>\n<br>\nv4: \n<a href=\"/v4/api/swagger-ui\">swagger-ui</a>; \n<a href=\"/v4/api/swagger.json\">swagger.json</a>\n<br>\nv5: \n<a href=\"/v5/api/swagger-ui\">swagger-ui</a>; \n<a href=\"/v5/api/swagger.json\">swagger.json</a>\n<br>\nv6: \n<a href=\"/v6/api/swagger-ui\">swagger-ui</a>; \n<a href=\"/v6/api/swagger.json\">swagger.json</a>\n<br>\nv7: \n<a href=\"/v7/api/swagger-ui\">swagger-ui</a>; \n<a href=\"/v7/api/swagger.json\">swagger.json</a>\n<br>\n\n<h2>Internal (not versioned)</h2>\n<p>Openapi docs for internal endpoints are served per service. I.e. there's one for `brig`, one for `cannon`, etc.. This is because Openapi doesn't play well with multiple actions having the same combination of HTTP method and URL path.</p>\nbrig:<br>\n<a href=\"/api-internal/swagger-ui/brig\">swagger-ui</a>; \n<a href=\"/api-internal/swagger-ui/brig-swagger.json\">swagger.json</a>\n<br>\ngalley:<br>\n<a href=\"/api-internal/swagger-ui/galley\">swagger-ui</a>; \n<a href=\"/api-internal/swagger-ui/galley-swagger.json\">swagger.json</a>\n<br>\nspar:<br>\n<a href=\"/api-internal/swagger-ui/spar\">swagger-ui</a>; \n<a href=\"/api-internal/swagger-ui/spar-swagger.json\">swagger.json</a>\n<br>\ncargohold:<br>\n<a href=\"/api-internal/swagger-ui/cargohold\">swagger-ui</a>; \n<a href=\"/api-internal/swagger-ui/cargohold-swagger.json\">swagger.json</a>\n<br>\ngundeck:<br>\n<a href=\"/api-internal/swagger-ui/gundeck\">swagger-ui</a>; \n<a href=\"/api-internal/swagger-ui/gundeck-swagger.json\">swagger.json</a>\n<br>\ncannon:<br>\n<a href=\"/api-internal/swagger-ui/cannon\">swagger-ui</a>; \n<a href=\"/api-internal/swagger-ui/cannon-swagger.json\">swagger.json</a>\n<br>\nproxy:<br>\n<a href=\"/api-internal/swagger-ui/proxy\">swagger-ui</a>; \n<a href=\"/api-internal/swagger-ui/proxy-swagger.json\">swagger.json</a>\n<br>\n\n<h2>Federated API (backend-to-backend)</h2>\nbrig (v0):<br><a href=\"/v0/api-federation/swagger-ui/brig\">swagger-ui</a>; <a href=\"/v0/api-federation/swagger-ui/brig-swagger.json\">swagger.json</a><br>brig (v1):<br><a href=\"/v1/api-federation/swagger-ui/brig\">swagger-ui</a>; <a href=\"/v1/api-federation/swagger-ui/brig-swagger.json\">swagger.json</a><br>brig (v2):<br><a href=\"/v2/api-federation/swagger-ui/brig\">swagger-ui</a>; <a href=\"/v2/api-federation/swagger-ui/brig-swagger.json\">swagger.json</a><br><br>\ngalley (v0):<br><a href=\"/v0/api-federation/swagger-ui/galley\">swagger-ui</a>; <a href=\"/v0/api-federation/swagger-ui/galley-swagger.json\">swagger.json</a><br>galley (v1):<br><a href=\"/v1/api-federation/swagger-ui/galley\">swagger-ui</a>; <a href=\"/v1/api-federation/swagger-ui/galley-swagger.json\">swagger.json</a><br>galley (v2):<br><a href=\"/v2/api-federation/swagger-ui/galley\">swagger-ui</a>; <a href=\"/v2/api-federation/swagger-ui/galley-swagger.json\">swagger.json</a><br><br>\ncargohold (v0):<br><a href=\"/v0/api-federation/swagger-ui/cargohold\">swagger-ui</a>; <a href=\"/v0/api-federation/swagger-ui/cargohold-swagger.json\">swagger.json</a><br>cargohold (v1):<br><a href=\"/v1/api-federation/swagger-ui/cargohold\">swagger-ui</a>; <a href=\"/v1/api-federation/swagger-ui/cargohold-swagger.json\">swagger.json</a><br>cargohold (v2):<br><a href=\"/v2/api-federation/swagger-ui/cargohold\">swagger-ui</a>; <a href=\"/v2/api-federation/swagger-ui/cargohold-swagger.json\">swagger.json</a><br><br>\n\n</body></html>\n"

data Swagger = SwaggerPublic | SwaggerInternal Service

instance TestCases Swagger where
Expand Down
2 changes: 1 addition & 1 deletion integration/test/Testlib/Env.hs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ mkGlobalEnv cfgFile = do
gFederationV0Domain = intConfig.federationV0.originDomain,
gFederationV1Domain = intConfig.federationV1.originDomain,
gDynamicDomains = (.domain) <$> Map.elems intConfig.dynamicBackends,
gDefaultAPIVersion = 7,
gDefaultAPIVersion = 8,
gManager = manager,
gServicesCwdBase = devEnvProjectRoot <&> (</> "services"),
gBackendResourcePool = resourcePool,
Expand Down
52 changes: 38 additions & 14 deletions libs/wire-api/src/Wire/API/Routes/Public/Brig.hs
Original file line number Diff line number Diff line change
Expand Up @@ -750,9 +750,9 @@ type UserClientAPI =
-- - ClientAdded event to self
-- - ClientRemoved event to self, if removing old clients due to max number
Named
"add-client-v6"
"add-client@v7"
( Summary "Register a new client"
:> Until 'V7
:> Until 'V8
:> CanThrow 'TooManyClients
:> CanThrow 'MissingAuth
:> CanThrow 'MalformedPrekeys
Expand All @@ -761,20 +761,20 @@ type UserClientAPI =
:> ZLocalUser
:> ZConn
:> "clients"
:> ReqBody '[JSON] NewClient
:> VersionedReqBody 'V7 '[JSON] NewClient
:> MultiVerb1
'POST
'[JSON]
( WithHeaders
ClientHeaders
Client
(VersionedRespond 'V6 201 "Client registered" Client)
(VersionedRespond 'V7 201 "Client registered" Client)
)
)
:<|> Named
"add-client"
( Summary "Register a new client"
:> From 'V6
:> From 'V8
:> CanThrow 'TooManyClients
:> CanThrow 'MissingAuth
:> CanThrow 'MalformedPrekeys
Expand All @@ -793,6 +793,17 @@ type UserClientAPI =
(Respond 201 "Client registered" Client)
)
)
:<|> Named
"update-client@v7"
( Summary "Update a registered client"
:> Until 'V8
:> CanThrow 'MalformedPrekeys
:> ZUser
:> "clients"
:> CaptureClientId "client"
:> VersionedReqBody 'V7 '[JSON] UpdateClient
:> MultiVerb1 'PUT '[JSON] (RespondEmpty 200 "Client updated")
)
:<|> Named
"update-client"
( Summary "Update a registered client"
Expand All @@ -801,7 +812,7 @@ type UserClientAPI =
:> "clients"
:> CaptureClientId "client"
:> ReqBody '[JSON] UpdateClient
:> MultiVerb 'PUT '[JSON] '[RespondEmpty 200 "Client updated"] ()
:> MultiVerb1 'PUT '[JSON] (RespondEmpty 200 "Client updated")
)
:<|>
-- This endpoint can lead to the following events being sent:
Expand All @@ -817,21 +828,21 @@ type UserClientAPI =
:> MultiVerb 'DELETE '[JSON] '[RespondEmpty 200 "Client deleted"] ()
)
:<|> Named
"list-clients-v6"
"list-clients@v7"
( Summary "List the registered clients"
:> Until 'V7
:> Until 'V8
:> ZUser
:> "clients"
:> MultiVerb1
'GET
'[JSON]
( VersionedRespond 'V6 200 "List of clients" [Client]
( VersionedRespond 'V7 200 "List of clients" [Client]
)
)
:<|> Named
"list-clients"
( Summary "List the registered clients"
:> From 'V7
:> From 'V8
:> ZUser
:> "clients"
:> MultiVerb1
Expand All @@ -841,24 +852,24 @@ type UserClientAPI =
)
)
:<|> Named
"get-client-v6"
"get-client@v7"
( Summary "Get a registered client by ID"
:> Until 'V7
:> Until 'V8
:> ZUser
:> "clients"
:> CaptureClientId "client"
:> MultiVerb
'GET
'[JSON]
'[ EmptyErrorForLegacyReasons 404 "Client not found",
VersionedRespond 'V6 200 "Client found" Client
VersionedRespond 'V7 200 "Client found" Client
]
(Maybe Client)
)
:<|> Named
"get-client"
( Summary "Get a registered client by ID"
:> From 'V7
:> From 'V8
:> ZUser
:> "clients"
:> CaptureClientId "client"
Expand All @@ -870,6 +881,19 @@ type UserClientAPI =
]
(Maybe Client)
)
:<|> Named
"get-client-capabilities@v7"
( Summary "Read back what the client has been posting about itself"
:> Until 'V8
:> ZUser
:> "clients"
:> CaptureClientId "client"
:> "capabilities"
:> MultiVerb1
'GET
'[JSON]
(VersionedRespond 'V7 200 "capabilities" ClientCapabilityList)
)
:<|> Named
"get-client-capabilities"
( Summary "Read back what the client has been posting about itself"
Expand Down
8 changes: 4 additions & 4 deletions libs/wire-api/src/Wire/API/Routes/Public/Brig/Bot.hs
Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,9 @@ type BotAPI =
:> MultiVerb1 'POST '[JSON] (RespondEmpty 200 "")
)
:<|> Named
"bot-get-client-v6"
"bot-get-client@v7"
( Summary "Get client for bot"
:> Until 'V7
:> Until 'V8
:> CanThrow 'AccessDenied
:> CanThrow 'ClientNotFound
:> ZBot
Expand All @@ -158,14 +158,14 @@ type BotAPI =
'GET
'[JSON]
'[ ErrorResponse 'ClientNotFound,
VersionedRespond 'V6 200 "Client found" Client
VersionedRespond 'V7 200 "Client found" Client
]
(Maybe Client)
)
:<|> Named
"bot-get-client"
( Summary "Get client for bot"
:> From 'V7
:> From 'V8
:> CanThrow 'AccessDenied
:> CanThrow 'ClientNotFound
:> ZBot
Expand Down
3 changes: 3 additions & 0 deletions libs/wire-api/src/Wire/API/Routes/Public/Cannon.hs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ type CannonAPI =
Named
"await-notifications"
( Summary "Establish websocket connection"
-- Description "This is the legacy variant of \"consume-events\""
:> "await"
:> ZUser
:> ZConn
Expand All @@ -44,6 +45,8 @@ type CannonAPI =
:<|> Named
"consume-events"
( Summary "Consume events over a websocket connection"
:> Description "This is the rabbitMQ-based variant of \"await-notifications\""
-- :> From 'V8 -- cannon is not versioned yet
:> "events"
:> ZUser
:> QueryParam'
Expand Down
6 changes: 4 additions & 2 deletions libs/wire-api/src/Wire/API/Routes/Version.hs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ import Wire.Arbitrary (Arbitrary, GenericUniform (GenericUniform))
-- and 'developmentVersions' stay in sync; everything else here should keep working without
-- change. See also documentation in the *docs* directory.
-- https://docs.wire.com/developer/developer/api-versioning.html#version-bump-checklist
data Version = V0 | V1 | V2 | V3 | V4 | V5 | V6 | V7
data Version = V0 | V1 | V2 | V3 | V4 | V5 | V6 | V7 | V8
deriving stock (Eq, Ord, Bounded, Enum, Show, Generic)
deriving (FromJSON, ToJSON) via (Schema Version)
deriving (Arbitrary) via (GenericUniform Version)
Expand All @@ -100,6 +100,7 @@ versionInt V4 = 4
versionInt V5 = 5
versionInt V6 = 6
versionInt V7 = 7
versionInt V8 = 8

supportedVersions :: [Version]
supportedVersions = [minBound .. maxBound]
Expand Down Expand Up @@ -210,7 +211,8 @@ isDevelopmentVersion V3 = False
isDevelopmentVersion V4 = False
isDevelopmentVersion V5 = False
isDevelopmentVersion V6 = False
isDevelopmentVersion _ = True
isDevelopmentVersion V7 = False
isDevelopmentVersion V8 = True

developmentVersions :: [Version]
developmentVersions = filter isDevelopmentVersion supportedVersions
Expand Down
Loading
Loading