packages feed

ms-azure-api 0.1.0.0 → 0.2.0.0

raw patch · 5 files changed

+147/−16 lines, 5 filesdep +xenodep +xmlbfdep +xmlbf-xenoPVP ok

version bump matches the API change (PVP)

Dependencies added: xeno, xmlbf, xmlbf-xeno

API changes (from Hackage documentation)

+ MSAzureAPI.Internal.Common: (==:) :: Text -> Text -> Option 'Https
+ MSAzureAPI.Internal.Common: getBs :: APIPlane -> [Text] -> Option 'Https -> AccessToken -> Req ByteString
+ MSAzureAPI.StorageServices.FileService: DIDirectory :: Text -> Text -> DirItem
+ MSAzureAPI.StorageServices.FileService: DIFile :: Text -> Text -> DirItem
+ MSAzureAPI.StorageServices.FileService: [diId] :: DirItem -> Text
+ MSAzureAPI.StorageServices.FileService: [diName] :: DirItem -> Text
+ MSAzureAPI.StorageServices.FileService: data DirItem
+ MSAzureAPI.StorageServices.FileService: instance GHC.Show.Show MSAzureAPI.StorageServices.FileService.DirItem
+ MSAzureAPI.StorageServices.FileService: listDirectoriesAndFiles :: Text -> Text -> Text -> AccessToken -> Req (Either String [DirItem])

Files

CHANGELOG.md view
@@ -8,6 +8,12 @@  ## Unreleased +## 0.2++MSAzureAPI.StorageServices.FileService. listDirectoriesAndFiles++Add XML support via `xeno` and `xmlbf` to parse `listDirectoriesAndFiles` response bodies+ ## 0.1.0.0  First release
README.md view
@@ -11,7 +11,7 @@  This library provides the client interface (under the `MSAzureAPI` namespace). -Authentication can be implemented with the @ms-auth@ library.+Authentication can be implemented with the `ms-auth` library.  ## Status 
ms-azure-api.cabal view
@@ -1,8 +1,8 @@ name:                ms-azure-api-version:             0.1.0.0+version:             0.2.0.0 synopsis:            Microsoft Azure API description:         Bindings to the Microsoft Azure API-homepage:            https://github.com/unfoldml/ms-api+homepage:            https://github.com/unfoldml/ms-graph-api license:             BSD3 license-file:        LICENSE author:              Marco Zocca@@ -34,6 +34,9 @@                      , time >= 1.8                      , transformers >= 0.5                      , unliftio+                     , xeno+                     , xmlbf+                     , xmlbf-xeno   ghc-options:         -Wall                        -Wcompat                        -Wno-unused-imports@@ -47,4 +50,4 @@  source-repository head   type:     git-  location: https://github.com/unfoldml/ms-azure-api+  location: https://github.com/unfoldml/ms-graph-api
src/MSAzureAPI/Internal/Common.hs view
@@ -5,8 +5,11 @@ module MSAzureAPI.Internal.Common (   APIPlane(..)   , get+  , getBs   , getLbs   , post+  -- ** URL parameters+  , (==:)   -- ** Helpers   , tryReq   -- ** JSON@@ -43,12 +46,26 @@ import UnliftIO (MonadUnliftIO(..)) import UnliftIO.Exception (try) +-- | URL parameters+(==:) :: Text -- ^ key+      -> Text -- ^ value+      -> Option 'Https+(==:) = (=:)  -- | @GET@ a 'LBS.ByteString' e.g. a file getLbs :: APIPlane        -> [Text] -- ^ URI path segments        -> Option 'Https -> AccessToken -> Req LBS.ByteString getLbs apiplane paths params tok = responseBody <$> req GET url NoReqBody lbsResponse opts+  where+    opts = auth <> params+    (url, auth) = msAzureReqConfig apiplane paths tok++-- | @GET@ a 'BS.ByteString' e.g. a file+getBs :: APIPlane+      -> [Text] -- ^ URI path segments+      -> Option 'Https -> AccessToken -> Req BS.ByteString+getBs apiplane paths params tok = responseBody <$> req GET url NoReqBody bsResponse opts   where     opts = auth <> params     (url, auth) = msAzureReqConfig apiplane paths tok
src/MSAzureAPI/StorageServices/FileService.hs view
@@ -3,27 +3,50 @@ -- authorize with AD : https://learn.microsoft.com/en-us/rest/api/storageservices/authorize-with-azure-active-directory -- -- permissions for calling data operations : https://learn.microsoft.com/en-us/rest/api/storageservices/authorize-with-azure-active-directory#permissions-for-calling-data-operations-module MSAzureAPI.StorageServices.FileService (getFile) where+module MSAzureAPI.StorageServices.FileService (+  -- * Files+  getFile+  -- * Directories+  , listDirectoriesAndFiles+  , DirItem(..)+  ) where +import Control.Applicative (Alternative(..)) import Control.Monad.IO.Class (MonadIO(..))+import Data.Foldable (asum)+import Data.Functor (void)+import Data.Maybe (listToMaybe)+import qualified Text.ParserCombinators.ReadP as RP (ReadP, readP_to_S, choice, many, between, char, string, satisfy)  -- bytestring+import qualified Data.ByteString as BS (ByteString) import qualified Data.ByteString.Char8 as BS8 (pack, unpack) import qualified Data.ByteString.Lazy as LBS (ByteString) -- hoauth2 -- import Network.OAuth.OAuth2 (OAuth2Token(..)) import Network.OAuth.OAuth2.Internal (AccessToken(..)) -- req-import Network.HTTP.Req (Req, Url, Option, Scheme(..), header)+import Network.HTTP.Req (Req, Url, Option, Scheme(..), header, (=:)) -- text import Data.Text (Text, pack, unpack)+import qualified Data.Text.Lazy as TL (Text, pack, unpack, toStrict) -- time import Data.Time (UTCTime, getCurrentTime) import Data.Time.Format (FormatTime, formatTime, defaultTimeLocale) import Data.Time.LocalTime (getZonedTime)+-- xeno+import qualified Xeno.DOM.Robust as X (Node, Content(..), name, contents, children)+-- xmlbf-xeno+import qualified Xmlbf.Xeno as XB (fromRawXml)+-- xmlbf+import qualified Xmlbf as XB (Parser, runParser, pElement, pText) -import MSAzureAPI.Internal.Common (APIPlane(..), get, post, getLbs)+import MSAzureAPI.Internal.Common (APIPlane(..), (==:), get, getBs, post, getLbs) ++++ {- | Headers:  https://learn.microsoft.com/en-us/rest/api/storageservices/authorize-with-azure-active-directory#call-storage-operations-with-oauth-tokens@@ -92,16 +115,98 @@  -- | list directories and files  https://learn.microsoft.com/en-us/rest/api/storageservices/list-directories-and-files#request  ----- GET https://myaccount.file.core.windows.net/myshare/mydirectorypath?restype=directory&comp=list--- listDirectoryAndFiles+-- @GET https:\/\/myaccount.file.core.windows.net\/myshare\/mydirectorypath?restype=directory&comp=list@+listDirectoriesAndFiles :: Text -- ^ storage account+                        -> Text -- ^ file share+                        -> Text -- ^ directory path, including directories+                        -> AccessToken+                        -> Req (Either String [DirItem])+listDirectoriesAndFiles acct fshare fpath atok = do+  os <- msStorageReqHeaders+  bs <- getBs (APData domain) pth (os <> "restype" ==: "directory" <> "comp" ==: "list") atok+  pure $ parseXML listDirectoriesP bs+  where+    domain = acct <> ".file.core.windows.net"+    pth = [fshare, fpath] ------ Path component 	Description------ myaccount 	The name of your storage account.--- myshare 	The name of your file share.--- mydirectorypath 	Optional. The path to the directory.--- myfile 	The name of the file.+-- | Directory item, as returned by 'listDirectoriesAndFiles'+data DirItem = DIFile {diId :: Text, diName :: Text}+             | DIDirectory {diId :: Text, diName :: Text}+             deriving (Show) +-- | XML parser for the response body format shown here: https://learn.microsoft.com/en-us/rest/api/storageservices/list-directories-and-files#response-body+listDirectoriesP :: XB.Parser [DirItem]+listDirectoriesP = do+  tag "EnumerationResults" $ do+    enumResultsIgnore+    es <- entries+    selfClosing "NextMarker"+    pure es +enumResultsIgnore :: XB.Parser ()+enumResultsIgnore = ignoreList ["Marker", "Prefix", "MaxResults", "DirectoryId"] +entries :: XB.Parser [DirItem]+entries = tag "Entries" $ many (file <|> directory)++file :: XB.Parser DirItem+file = tag "File" $ do+  fid <- fileId+  fname <- fileName+  properties+  entryFooter+  pure $ DIFile fid fname++directory :: XB.Parser DirItem+directory = tag "Directory" $ do+  fid <- fileId+  fname <- fileName+  properties+  entryFooter+  pure $ DIDirectory fid fname++++entryFooter :: XB.Parser ()+entryFooter = ignoreList ["Attributes", "PermissionKey"]++fileId :: XB.Parser Text+fileId = TL.toStrict <$> tag "FileId" anystring++fileName :: XB.Parser Text+fileName = TL.toStrict <$> tag "Name" anystring++properties :: XB.Parser ()+properties = tag "Properties" $+  ignoreList ["Content-Length", "CreationTime", "LastAccessTime", "LastWriteTime", "ChangeTime", "Last-Modified", "Etag"]++ignoreList :: [Text] -> XB.Parser ()+ignoreList ns = void $ many (asum (map (`XB.pElement` XB.pText) ns))++selfClosing :: Text -> XB.Parser ()+selfClosing t = tag t (pure ())+++anystring :: XB.Parser TL.Text+anystring = XB.pText+tag :: Text -> XB.Parser a -> XB.Parser a+tag = XB.pElement++parseXML :: XB.Parser b -> BS.ByteString -> Either String b+parseXML p bs = XB.fromRawXml bs >>= XB.runParser p++++-- -- t0, t1, tdir, tfile, tentries :: String+-- t0, t1, t1', tfile :: BS.ByteString+-- t0 = "<Properties><CreationTime>datetime</CreationTime><LastAccessTime>datetime</LastAccessTime><LastWriteTime>datetime</LastWriteTime><ChangeTime>datetime</ChangeTime><Last-Modified>datetime</Last-Modified><Etag>etag</Etag></Properties>"++-- t1' = "<?xml version=\"1.0\" encoding=\"utf-8\"?> <EnumerationResults ServiceEndpoint=\"https://myaccount.file.core.windows.net/\" ShareName=\"myshare\" ShareSnapshot=\"date-time\" DirectoryPath=\"directory-path\"> <Marker>string-value</Marker> <Prefix>string-value</Prefix> <MaxResults>int-value</MaxResults> <DirectoryId>directory-id</DirectoryId> <Entries> <File> <FileId>file-id</FileId> <Name>file-name</Name> <Properties> <Content-Length>size-in-bytes</Content-Length> <CreationTime>datetime</CreationTime> <LastAccessTime>datetime</LastAccessTime> <LastWriteTime>datetime</LastWriteTime> <ChangeTime>datetime</ChangeTime> <Last-Modified>datetime</Last-Modified> <Etag>etag</Etag> </Properties> <Attributes>Archive|Hidden|Offline|ReadOnly</Attributes> <PermissionKey>4066528134148476695*1</PermissionKey> </File> <Directory> <FileId>file-id</FileId> <Name>directory-name</Name> <Properties> <CreationTime>datetime</CreationTime> <LastAccessTime>datetime</LastAccessTime> <LastWriteTime>datetime</LastWriteTime> <ChangeTime>datetime</ChangeTime> <Last-Modified>datetime</Last-Modified> <Etag>etag</Etag> </Properties> <Attributes>Archive|Hidden|Offline|ReadOnly</Attributes> <PermissionKey>4066528134148476695*1</PermissionKey> </Directory> </Entries> <NextMarker /> </EnumerationResults>"++-- t1 = "<?xml version=\"1.0\" encoding=\"utf-8\"?><EnumerationResults ServiceEndpoint=\"https://myaccount.file.core.windows.net/\" ShareName=\"myshare\" ShareSnapshot=\"date-time\" DirectoryPath=\"directory-path\"><Marker>string-value</Marker><Prefix>string-value</Prefix><MaxResults>int-value</MaxResults><DirectoryId>directory-id</DirectoryId><Entries><File><FileId>file-id</FileId><Name>file-name</Name><Properties><Content-Length>size-in-bytes</Content-Length><CreationTime>datetime</CreationTime><LastAccessTime>datetime</LastAccessTime><LastWriteTime>datetime</LastWriteTime><ChangeTime>datetime</ChangeTime><Last-Modified>datetime</Last-Modified><Etag>etag</Etag></Properties><Attributes>Archive|Hidden|Offline|ReadOnly</Attributes><PermissionKey>4066528134148476695*1</PermissionKey></File><Directory><FileId>file-id</FileId><Name>directory-name</Name><Properties><CreationTime>datetime</CreationTime><LastAccessTime>datetime</LastAccessTime><LastWriteTime>datetime</LastWriteTime><ChangeTime>datetime</ChangeTime><Last-Modified>datetime</Last-Modified><Etag>etag</Etag></Properties><Attributes>Archive|Hidden|Offline|ReadOnly</Attributes><PermissionKey>4066528134148476695*1</PermissionKey></Directory></Entries><NextMarker /></EnumerationResults>"++-- -- tdir = "<Directory><FileId>file-id</FileId><Name>directory-name</Name><Properties><CreationTime>datetime</CreationTime><LastAccessTime>datetime</LastAccessTime><LastWriteTime>datetime</LastWriteTime><ChangeTime>datetime</ChangeTime><Last-Modified>datetime</Last-Modified><Etag>etag</Etag></Properties><Attributes>Archive|Hidden|Offline|ReadOnly</Attributes><PermissionKey>4066528134148476695*1</PermissionKey></Directory>"++-- tfile = "<File><FileId>file-id</FileId><Name>file-name</Name><Properties><Content-Length>size-in-bytes</Content-Length><CreationTime>datetime</CreationTime><LastAccessTime>datetime</LastAccessTime><LastWriteTime>datetime</LastWriteTime><ChangeTime>datetime</ChangeTime><Last-Modified>datetime</Last-Modified><Etag>etag</Etag></Properties><Attributes>Archive|Hidden|Offline|ReadOnly</Attributes><PermissionKey>4066528134148476695*1</PermissionKey></File>"++-- -- tentries = "<Entries><File><FileId>file-id</FileId><Name>file-name</Name><Properties><Content-Length>size-in-bytes</Content-Length><CreationTime>datetime</CreationTime><LastAccessTime>datetime</LastAccessTime><LastWriteTime>datetime</LastWriteTime><ChangeTime>datetime</ChangeTime><Last-Modified>datetime</Last-Modified><Etag>etag</Etag></Properties><Attributes>Archive|Hidden|Offline|ReadOnly</Attributes><PermissionKey>4066528134148476695*1</PermissionKey></File><Directory><FileId>file-id</FileId><Name>directory-name</Name><Properties><CreationTime>datetime</CreationTime><LastAccessTime>datetime</LastAccessTime><LastWriteTime>datetime</LastWriteTime><ChangeTime>datetime</ChangeTime><Last-Modified>datetime</Last-Modified><Etag>etag</Etag></Properties><Attributes>Archive|Hidden|Offline|ReadOnly</Attributes><PermissionKey>4066528134148476695*1</PermissionKey></Directory></Entries>"