diff --git a/.gitignore b/.gitignore index cf68426..0757c59 100644 --- a/.gitignore +++ b/.gitignore @@ -178,3 +178,6 @@ nuget/*.nupkg .paket/paket.exe paket-files/ + +#Fake build files +.fake diff --git a/src/FSharp.Azure.StorageTypeProvider/Blob/BlobMemberFactory.fs b/src/FSharp.Azure.StorageTypeProvider/Blob/BlobMemberFactory.fs index 21c025c..da61fd5 100644 --- a/src/FSharp.Azure.StorageTypeProvider/Blob/BlobMemberFactory.fs +++ b/src/FSharp.Azure.StorageTypeProvider/Blob/BlobMemberFactory.fs @@ -20,14 +20,14 @@ let rec private createBlobItem (domainType : ProvidedTypeDefinition) connectionS let fileTypeDefinition = match properties.BlobType, path with | BlobType.PageBlob, _ -> "PageBlob" - | _, ContainerBuilder.XML -> "XmlBlob" - | _, ContainerBuilder.Binary | _, ContainerBuilder.Text -> "BlockBlob" + | _, BlobBuilder.XML -> "XmlBlob" + | _, BlobBuilder.Binary | _, BlobBuilder.Text -> "BlockBlob" |> fun typeName -> domainType.GetMember(typeName).[0] :?> ProvidedTypeDefinition match properties.BlobType, properties.Length with | _, 0L -> None - | BlobType.PageBlob, _ -> Some <| ProvidedProperty(name, fileTypeDefinition, GetterCode = fun _ -> <@@ ContainerBuilder.createPageBlobFile connectionString containerName path @@>) - | BlobType.BlockBlob, _ -> Some <| ProvidedProperty(name, fileTypeDefinition, GetterCode = fun _ -> <@@ ContainerBuilder.createBlockBlobFile connectionString containerName path @@>) + | BlobType.PageBlob, _ -> Some <| ProvidedProperty(name, fileTypeDefinition, GetterCode = fun _ -> <@@ BlobBuilder.createPageBlobFile connectionString containerName path @@>) + | BlobType.BlockBlob, _ -> Some <| ProvidedProperty(name, fileTypeDefinition, GetterCode = fun _ -> <@@ BlobBuilder.createBlockBlobFile connectionString containerName path @@>) | _ -> None let private createContainerType (domainType : ProvidedTypeDefinition) connectionString (container : LightweightContainer) = diff --git a/src/FSharp.Azure.StorageTypeProvider/Blob/BlobRepository.fs b/src/FSharp.Azure.StorageTypeProvider/Blob/BlobRepository.fs index 469dc7e..7f92637 100644 --- a/src/FSharp.Azure.StorageTypeProvider/Blob/BlobRepository.fs +++ b/src/FSharp.Azure.StorageTypeProvider/Blob/BlobRepository.fs @@ -37,6 +37,17 @@ let rec private getContainerStructure wildcard (container : CloudBlobContainer) | _ -> None) |> Seq.toArray +let listBlobs incSubDirs (container:CloudBlobContainer) prefix = + container.ListBlobs(prefix, incSubDirs) + |> Seq.map(fun b -> + match b with + | :? ICloudBlob as blob -> + let path, name = getItemName blob.Name blob.Parent + Some(Blob(path, name, blob.Properties)) + | _ -> None) //can safely ignore folder types as we have a flat structure if & only if we want to include items from sub directories + |> Seq.filter(fun b -> b.IsSome) + |> Seq.map(fun b -> b.Value) + let getBlobStorageAccountManifest connection = (getBlobClient connection).ListContainers() |> Seq.toList diff --git a/src/FSharp.Azure.StorageTypeProvider/Blob/ProvidedBlobTypes.fs b/src/FSharp.Azure.StorageTypeProvider/Blob/ProvidedBlobTypes.fs index 532f8f8..50e7f7e 100644 --- a/src/FSharp.Azure.StorageTypeProvider/Blob/ProvidedBlobTypes.fs +++ b/src/FSharp.Azure.StorageTypeProvider/Blob/ProvidedBlobTypes.fs @@ -94,6 +94,25 @@ type XmlFile internal (defaultConnectionString, container, file) = return XDocument.Parse text } +module BlobBuilder = + let internal (|Text|Binary|XML|) (name : string) = + let endsWith extension = name.EndsWith(extension, StringComparison.InvariantCultureIgnoreCase) + match name with + | _ when [ ".txt"; ".csv" ] |> Seq.exists endsWith -> Text + | _ when endsWith ".xml" -> XML + | _ -> Binary + + /// Creates a block blob file object. + let createBlockBlobFile connectionString containerName path = + let details = connectionString, containerName, path + match path with + | XML -> XmlFile(details) :> BlockBlobFile + | Text | Binary -> BlockBlobFile(details) + + /// Creates a page blob file object. + let createPageBlobFile connectionString containerName path = + PageBlobFile(connectionString, containerName, path) + /// Represents a pseudo-folder in blob storage. type BlobFolder internal (defaultConnectionString, container, file) = /// Downloads the entire folder contents to the local file system asynchronously. @@ -101,6 +120,29 @@ type BlobFolder internal (defaultConnectionString, container, file) = let connectionDetails = defaultArg connectionString defaultConnectionString, container, file downloadFolder (connectionDetails, path) + /// The Path of the current folder + member __.Path = + file + + /// Lists all blobs contained in this folder + member __.ListBlobs(?includeBlobsInSubFolders) = + let incSubFolders = defaultArg includeBlobsInSubFolders false + let container = getContainerRef (defaultConnectionString,container) + listBlobs incSubFolders container file + |> Seq.map (fun b -> + match b with + | Blob(path, name, properties) -> + match properties.BlobType with + | BlobType.PageBlob -> + (BlobBuilder.createPageBlobFile defaultConnectionString container.Name path) :> BlobFile + | _ -> + (BlobBuilder.createBlockBlobFile defaultConnectionString container.Name path) :> BlobFile + |> Some + | _ -> None) + |> Seq.filter(fun x -> x.IsSome) + |> Seq.map(fun x -> x.Value) + + /// Represents a container in blob storage. type BlobContainer internal (defaultConnectionString, container) = /// Gets a handle to the Azure SDK client for this container. @@ -127,23 +169,7 @@ module internal ProvidedTypeGenerator = /// Builder methods to construct blobs etc.. /// [omit] module ContainerBuilder = - let internal (|Text|Binary|XML|) (name : string) = - let endsWith extension = name.EndsWith(extension, StringComparison.InvariantCultureIgnoreCase) - match name with - | _ when [ ".txt"; ".csv" ] |> Seq.exists endsWith -> Text - | _ when endsWith ".xml" -> XML - | _ -> Binary - - /// Creates a block blob file object. - let createBlockBlobFile connectionString containerName path = - let details = connectionString, containerName, path - match path with - | XML -> XmlFile(details) :> BlockBlobFile - | Text | Binary -> BlockBlobFile(details) - /// Creates a page blob file object. - let createPageBlobFile connectionString containerName path = - PageBlobFile(connectionString, containerName, path) /// Creates a blob container object. let createContainer connectionString containerName = BlobContainer(connectionString, containerName) diff --git a/tests/IntegrationTests/BlobUnitTests.fs b/tests/IntegrationTests/BlobUnitTests.fs index b43f6f4..962c237 100644 --- a/tests/IntegrationTests/BlobUnitTests.fs +++ b/tests/IntegrationTests/BlobUnitTests.fs @@ -121,4 +121,23 @@ let testFolderDownload download expectedFiles expectedFolders = let ``Can correctly download a folder``() = testFolderDownload container.``folder/``.Download 2 0 [] -let ``Can correctly download a container``() = testFolderDownload container.Download 8 1 \ No newline at end of file +let ``Can correctly download a container``() = testFolderDownload container.Download 12 5 + +[] +let ``Can access Path property on a folder`` = + let childFolder = Local.Containers.samples.``folder2/``.``child/`` + Assert.Equal("folder2/child/", childFolder.Path) + +[] +let ``ListBlobs method returns correct number of blobs`` = + let childFolder = Local.Containers.samples.``folder2/``.``child/`` + let allBlobs = childFolder.ListBlobs() + let count = allBlobs |> Seq.length + Assert.Equal(1,count) + +[] +let ``Can access List blobs method on a folder`` = + let childFolder = Local.Containers.samples.``folder2/``.``child/`` + let allBlobs = childFolder.ListBlobs(true) + let count = allBlobs |> Seq.length + Assert.Equal(4,count) \ No newline at end of file diff --git a/tests/IntegrationTests/ResetTestData.fsx b/tests/IntegrationTests/ResetTestData.fsx index fdf7c48..ff4f07b 100644 --- a/tests/IntegrationTests/ResetTestData.fsx +++ b/tests/IntegrationTests/ResetTestData.fsx @@ -36,8 +36,13 @@ let createData _ = createBlockBlob "folder/childFile.txt" "child file stuff" createBlockBlob "sample.txt" "the quick brown fox jumps over the lazy dog\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Cras malesuada.\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla porttitor." createBlockBlob "data.xml" "thing" + createBlockBlob "folder2/child/grandchild1/descedant1.txt" "not important" + createBlockBlob "folder2/child/grandchild1/descedant2.txt" "not important" + createBlockBlob "folder2/child/grandchild2/descedant3.txt" "not important" + createBlockBlob "folder2/child/descedant4.txt" "not important" + createBlockBlob "folder/pageDataChild.txt" "hello from child page blob" createPageBlob "pageData.bin" "hello from page blob" - createPageBlob "folder/pageDataChild.txt" "hello from child page blob" + createData |> logWith "blob"