From 46264eaa2d51f94622162a7e66a0ad0a30ea0dc9 Mon Sep 17 00:00:00 2001 From: Marco Zocca Date: Sun, 2 Jul 2023 15:06:00 +0200 Subject: [PATCH] Subscription fixes, v 0.10 --- ms-graph-api/CHANGELOG.md | 5 + ms-graph-api/ms-graph-api.cabal | 2 +- .../ChangeNotifications/Subscription.hs | 106 ++++++++++++++---- 3 files changed, 90 insertions(+), 23 deletions(-) diff --git a/ms-graph-api/CHANGELOG.md b/ms-graph-api/CHANGELOG.md index 75d38a6..238c49c 100644 --- a/ms-graph-api/CHANGELOG.md +++ b/ms-graph-api/CHANGELOG.md @@ -9,6 +9,11 @@ and this project adheres to the ## Unreleased +## 0.10.0.0 + +*Breaking changes* +- MSGraphAPI.ChangeNotifications.Subscription : Subscription has fewer fields (the ID and TLS version fields are optional) + ## 0.9.0.0 MSGraphAPI.Files.Drive diff --git a/ms-graph-api/ms-graph-api.cabal b/ms-graph-api/ms-graph-api.cabal index 4b97151..f7e6704 100644 --- a/ms-graph-api/ms-graph-api.cabal +++ b/ms-graph-api/ms-graph-api.cabal @@ -1,5 +1,5 @@ name: ms-graph-api -version: 0.9.0.0 +version: 0.10.0.0 synopsis: Microsoft Graph API description: Bindings to the Microsoft Graph API homepage: https://github.com/unfoldml/ms-graph-api diff --git a/ms-graph-api/src/MSGraphAPI/ChangeNotifications/Subscription.hs b/ms-graph-api/src/MSGraphAPI/ChangeNotifications/Subscription.hs index 9f3b925..056461b 100644 --- a/ms-graph-api/src/MSGraphAPI/ChangeNotifications/Subscription.hs +++ b/ms-graph-api/src/MSGraphAPI/ChangeNotifications/Subscription.hs @@ -1,15 +1,66 @@ -- | Subscription to change notifications through webhooks -- --- see for a list of resources that can produce change notifications +-- NB: Creating a subscription requires read scope to the resource. For example, to get change notifications on messages, your app needs the @Mail.Read@ permission. +-- +-- Currently, subscriptions are enabled for the following resources. Note: Subscriptions to resources marked with an asterisk (*) are supported on the @\/beta@ endpoint only. +-- +-- + -- * An alert from the Microsoft Graph Security API. +-- + -- * A baseTask (deprecated) of a user in Microsoft To-Do.* +-- + -- * A callRecord produced after a call or meeting in Microsoft Teams. +-- + -- * A channel in Microsoft Teams. +-- + -- * A chat in Microsoft Teams. +-- + -- * A chatMessage sent via teams or channels in Microsoft Teams. +-- + -- * A conversation in a Microsoft 365 group. +-- + -- * A conversationMember in a team, channel, or chat in Microsoft Teams. +-- + -- * Content in the hierarchy of a root folder 'DriveItem' in OneDrive for Business, or of a root folder or subfolder 'DriveItem' in a user's personal OneDrive. +-- + -- * A 'Group' in Azure Active Directory. +-- + -- * A list under a SharePoint site. +-- + -- * A message, event, or contact in Outlook. +-- + -- * An online meeting in Microsoft Teams.* +-- + -- * The presence of a user in Microsoft Teams. +-- + -- * A team in Microsoft Teams. +-- + -- * A printer (when a print job for the printer gets to JobFetchable state - ready to be fetched for printing) and a printTaskDefinition in Universal Print. For more information, see Subscribe to change notifications from cloud printing APIs. +-- + -- * A todoTask of a user in Microsoft To Do (webhooks are only available in the worldwide endpoint and no other national clouds). +-- + -- * A 'User' in Azure Active Directory. +-- +-- +-- See for an up to date list of resources that can produce change notifications -- -- see for protocol details -module MSGraphAPI.ChangeNotifications.Subscription where +module MSGraphAPI.ChangeNotifications.Subscription ( + -- * Sender + createSubscription + -- ** types + , Subscription(..) + , ChangeType(..) + -- * Receiver + -- ** types + , ChangeNotification(..) + ) where -import Data.List.NonEmpty (NonEmpty) +import Data.List.NonEmpty (NonEmpty(..)) import GHC.Generics (Generic(..)) -- aeson -import qualified Data.Aeson as A (ToJSON(..), FromJSON(..), eitherDecode, genericParseJSON, genericToEncoding, defaultOptions, Options(..), withObject, withText, (.:), (.:?), object, (.=)) +import qualified Data.Aeson as A (ToJSON(..), FromJSON(..), encode, eitherDecode, genericParseJSON, genericToEncoding, defaultOptions, Options(..), withObject, withText, (.:), (.:?), object, (.=)) import qualified Data.Aeson.Encoding as A (text) -- hoauth import Network.OAuth.OAuth2.Internal (AccessToken(..)) @@ -18,20 +69,22 @@ import Network.HTTP.Req (Req) -- text import Data.Text (Text, pack, unpack) -- time -import Data.Time (LocalTime) +import Data.Time (UTCTime) -- import Data.UUID.Types (UUID) import qualified MSGraphAPI.Internal.Common as MSG (Collection(..), get, post, aesonOptions) import MSGraphAPI.Files.DriveItem (DriveItem) +import MSGraphAPI.Users.Group (Group) +import MSGraphAPI.Users.User (User) -- | Represents the notification sent to the subscriber. https://learn.microsoft.com/en-us/graph/api/resources/changenotification?view=graph-rest-1.0 -- --- +-- data ChangeNotification a = ChangeNotification { - cnChangeType :: ChangeType - , cnClientState :: Text - , cnId :: Text - , cnResource :: Text + cnChangeType :: ChangeType -- ^ type of change that will raise the change notification. + , cnClientState :: Text -- ^ Value of the clientState property sent in the subscription request (if any). The maximum length is 255 characters. The client can check whether the change notification came from the service by comparing the values of the clientState property. + , cnId :: Text -- ^ Unique ID for the notification. + , cnResource :: Text -- ^ The URI of the resource that emitted the change notification relative to @https:\/\/graph.microsoft.com@ , cnResourceData :: Maybe a , cnSubscriptionId :: Text , cnTenantId :: Text @@ -43,21 +96,23 @@ instance A.FromJSON a => A.FromJSON (ChangeNotification a) where -- -- @ POST https:\/\/graph.microsoft.com\/v1.0\/subscriptions@ -- --- Creating a subscription requires read scope to the resource. For example, to get change notifications on messages, your app needs the @Mail.Read@ permission. -createSubscription :: (A.FromJSON b) => Subscription -> AccessToken -> Req b +-- NB: Creating a subscription requires read scope to the resource. For example, to get change notifications on messages, your app needs the @Mail.Read@ permission. +-- +-- NB2: When you create a subscription to receive change notifications through webhooks, MS Graph first validates the notification endpoint that's provided in the @notificationUrl@ property of the subscription request. MS Graph encodes a validation token and includes it in a @POST@ request to the notification URL, the client must properly decode the URL to get the plain text validation token, and respond within 10 seconds. See https://learn.microsoft.com/en-us/graph/change-notifications-delivery-webhooks?tabs=http#notificationurl-validation for full details +createSubscription :: (A.FromJSON b) => + Subscription -- ^ Configuration of the change webhook to be created by this request + -> AccessToken -> Req b createSubscription = MSG.post ["subscriptions"] mempty -- | A subscription allows a client app to receive change notifications about changes to data in Microsoft Graph. -- -- https://learn.microsoft.com/en-us/graph/api/resources/subscription?view=graph-rest-1.0 data Subscription = Subscription { - cnsId :: Text - , cnsChangeType :: NonEmpty ChangeType - , cnsClientState :: Text - , cnsExpirationDateTime :: LocalTime - , cnsNotificationUrl :: Text -- ^ The URL of the endpoint that will receive the change notifications. This URL must make use of the HTTPS protocol. Any query string parameter included in the notificationUrl property will be included in the HTTP POST request when Microsoft Graph sends the change notifications. - , cnsResource :: Text -- ^ Specifies the resource that will be monitored for changes. Do not include the base URL (@https:\/\/graph.microsoft.com\/v1.0\/@) - , cnsLatestSupportedTLSVersion :: LatestTLSVer + cnsChangeType :: NonEmpty ChangeType -- ^ Type of change in the subscribed resource that will raise a change notification. + , cnsClientState :: Text -- ^ Value of the clientState property sent by the service in each change notification. The maximum length is 128 characters. The client can check that the change notification came from the service by comparing the value of the clientState property sent with the subscription with the value of the clientState property received with each change notification. + , cnsExpirationDateTime :: UTCTime -- ^ date and time when the webhook subscription expires. The time is in UTC, and can be an amount of time from subscription creation that varies for the resource subscribed to. For the maximum supported subscription length of time, see https://learn.microsoft.com/en-us/graph/api/resources/subscription?view=graph-rest-1.0#maximum-length-of-subscription-per-resource-type + , cnsNotificationUrl :: Text -- ^ URL of the endpoint that will receive the change notifications. This URL must make use of the HTTPS protocol. Any query string parameter included in the notificationUrl property will be included in the HTTP POST request when Microsoft Graph sends the change notifications. + , cnsResource :: Text -- ^ The resource that will be monitored for changes, e.g. @"\/users\/{id}\/drive\/root"@. Do not include the base URL (@https:\/\/graph.microsoft.com\/v1.0\/@) } deriving (Eq, Show, Generic) instance A.FromJSON Subscription where parseJSON = A.genericParseJSON (MSG.aesonOptions "cns") @@ -80,9 +135,16 @@ instance A.ToJSON LatestTLSVer where LTV12 -> A.text "v1_2" LTV13 -> A.text "v1_3" -data ChangeType = CTCreated - | CTUpdated - | CTDeleted +-- | the type of change in the subscribed resource that will raise a change notification. +-- +-- Note: +-- +-- * Drive root item and list change notifications support only the updated changeType. +-- +-- * User and group change notifications support updated and deleted changeType. Use updated to receive notifications when user or group is created, updated or soft deleted. Use deleted to receive notifications when user or group is permanently deleted. +data ChangeType = CTCreated -- ^ created + | CTUpdated -- ^ updated + | CTDeleted -- ^ deleted deriving (Eq, Show, Generic) instance A.FromJSON ChangeType where parseJSON = A.withText "ChangeType" $ \t ->