diff --git a/changelog.d/5-internal/pr-2008 b/changelog.d/5-internal/pr-2008 index a5c36a513b..a378dfd9c7 100644 --- a/changelog.d/5-internal/pr-2008 +++ b/changelog.d/5-internal/pr-2008 @@ -1 +1 @@ -Servantify Galley Teams API. (#2008) +Servantify Galley Teams API. (#2008, #2010) diff --git a/libs/wire-api/src/Wire/API/ErrorDescription.hs b/libs/wire-api/src/Wire/API/ErrorDescription.hs index 30f7ebf71b..f6312d19f9 100644 --- a/libs/wire-api/src/Wire/API/ErrorDescription.hs +++ b/libs/wire-api/src/Wire/API/ErrorDescription.hs @@ -20,6 +20,7 @@ import Servant.API.Status (KnownStatus, statusVal) import Servant.Client.Core import Servant.Swagger.Internal import Wire.API.Routes.MultiVerb +import Wire.API.Team.Permission -- This can be added to an endpoint to document a possible failure -- case outside its return type (usually through an exception). @@ -234,6 +235,11 @@ noIdentity n = ErrorDescription (Text.pack (symbolVal (Proxy @desc)) <> " (code type OperationDenied = ErrorDescription 403 "operation-denied" "Insufficient permissions" +-- FUTUREWORK(leif): We need this to document possible (operation denied) errors in the servant routes. +-- Be aware that this is redundant and should be replaced by a more type safe solution in the future. +type family OperationDeniedError (a :: Perm) :: * where + OperationDeniedError 'SetTeamData = ErrorDescription 403 "operation-denied" "Insufficient permissions (missing SetTeamData)" + operationDeniedSpecialized :: String -> OperationDenied operationDeniedSpecialized p = ErrorDescription $ 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 fe3ec2ec6e..22ed3a43c2 100644 --- a/libs/wire-api/src/Wire/API/Routes/Public/Galley.hs +++ b/libs/wire-api/src/Wire/API/Routes/Public/Galley.hs @@ -47,6 +47,7 @@ import Wire.API.ServantProto (Proto, RawProto) import Wire.API.Team import Wire.API.Team.Conversation import Wire.API.Team.Feature +import Wire.API.Team.Permission (Perm (..)) instance AsHeaders '[ConvId] Conversation Conversation where toHeaders c = (I (qUnqualified (cnvQualifiedId c)) :* Nil, c) @@ -725,7 +726,22 @@ data Api routes = Api TeamId (RespondEmpty 201 "Team ID as `Location` header value") ] - TeamId + TeamId, + updateTeam :: + routes + :- Summary "Update team properties" + :> ZUser + :> ZConn + :> CanThrow NotATeamMember + :> CanThrow (OperationDeniedError 'SetTeamData) + :> "teams" + :> Capture "tid" TeamId + :> ReqBody '[JSON] TeamUpdateData + :> MultiVerb + 'PUT + '[JSON] + '[RespondEmpty 200 "Team updated"] + () } deriving (Generic) diff --git a/libs/wire-api/src/Wire/API/Team/Member.hs b/libs/wire-api/src/Wire/API/Team/Member.hs index 9a7cfda570..600978c6b1 100644 --- a/libs/wire-api/src/Wire/API/Team/Member.hs +++ b/libs/wire-api/src/Wire/API/Team/Member.hs @@ -336,7 +336,7 @@ invitedSchema' :: ObjectSchema SwaggerDoc (Maybe (UserId, UTCTimeMillis)) invitedSchema' = withParser invitedSchema $ \(invby, invat) -> case (invby, invat) of (Just b, Just a) -> pure $ Just (b, a) - (Nothing, Nothing) -> pure $ Nothing + (Nothing, Nothing) -> pure Nothing _ -> fail "created_by, created_at" instance ToSchema NewTeamMember where diff --git a/services/galley/src/Galley/API/Public.hs b/services/galley/src/Galley/API/Public.hs index 42db3104f9..4ee1c27b97 100644 --- a/services/galley/src/Galley/API/Public.hs +++ b/services/galley/src/Galley/API/Public.hs @@ -188,28 +188,14 @@ servantSitemap = GalleyAPI.featureConfigConferenceCallingGet = Features.getFeatureConfig @'Public.WithoutLockStatus @'Public.TeamFeatureConferenceCalling Features.getConferenceCallingInternal, GalleyAPI.featureConfigSelfDeletingMessagesGet = Features.getFeatureConfig @'Public.WithLockStatus @'Public.TeamFeatureSelfDeletingMessages Features.getSelfDeletingMessagesInternal, GalleyAPI.featureConfigGuestLinksGet = Features.getFeatureConfig @'Public.WithLockStatus @'Public.TeamFeatureGuestLinks Features.getGuestLinkInternal, - GalleyAPI.createNonBindingTeam = Teams.createNonBindingTeamH + GalleyAPI.createNonBindingTeam = Teams.createNonBindingTeamH, + GalleyAPI.updateTeam = Teams.updateTeamH } sitemap :: Routes ApiBuilder (Sem GalleyEffects) () sitemap = do -- Team API ----------------------------------------------------------- - put "/teams/:tid" (continue Teams.updateTeamH) $ - zauthUserId - .&. zauthConnId - .&. capture "tid" - .&. jsonRequest @Public.TeamUpdateData - .&. accept "application" "json" - document "PUT" "updateTeam" $ do - summary "Update team properties" - parameter Path "tid" bytes' $ - description "Team ID" - body (ref Public.modelUpdateData) $ - description "JSON body" - errorResponse (Error.errorDescriptionTypeToWai @Error.NotATeamMember) - errorResponse (Error.errorDescriptionToWai (Error.operationDenied Public.SetTeamData)) - get "/teams" (continue Teams.getManyTeamsH) $ zauthUserId .&. opt (query "ids" ||| query "start") diff --git a/services/galley/src/Galley/API/Teams.hs b/services/galley/src/Galley/API/Teams.hs index 34e27ae7fc..5883ebdb88 100644 --- a/services/galley/src/Galley/API/Teams.hs +++ b/services/galley/src/Galley/API/Teams.hs @@ -326,23 +326,6 @@ updateTeamStatus tid (TeamStatusUpdate newStatus cur) = do (_, _) -> throw InvalidTeamStatusUpdate updateTeamH :: - Members - '[ Error ActionError, - Error NotATeamMember, - GundeckAccess, - Input UTCTime, - TeamStore, - WaiRoutes - ] - r => - UserId ::: ConnId ::: TeamId ::: JsonRequest Public.TeamUpdateData ::: JSON -> - Sem r Response -updateTeamH (zusr ::: zcon ::: tid ::: req ::: _) = do - updateData <- fromJsonBody req - updateTeam zusr zcon tid updateData - pure empty - -updateTeam :: Members '[ Error ActionError, Error NotATeamMember, @@ -356,7 +339,7 @@ updateTeam :: TeamId -> Public.TeamUpdateData -> Sem r () -updateTeam zusr zcon tid updateData = do +updateTeamH zusr zcon tid updateData = do zusrMembership <- E.getTeamMember tid zusr -- let zothers = map (view userId) membs -- Log.debug $