'use strict' ; import { ListItemObject , ListItemPrefix , ListResults , PutObjectOptions , StorageProvider } from '@smcloudstore/core/dist/StorageProvider' ; import * as Azure from 'azure-storage' ; import { Stream , Transform } from 'stream' ; /**
 * Connection options for an Azure Blob Storage provider.
 */ interface AzureStorageConnectionObject { /** Name of the storage account */ storageAccount : string ; /** Access key (secret key) for the storage account */ storageAccessKey : string ; /** Endpoint to use. Default is `core.windows.net` */ host ? : string ; } type AzureStorageConnectionOptions = string | AzureStorageConnectionObject ; /**
 * Options passed when creating a container
 */ interface AzureStorageCreateContainerOptions { /**
     * Determine access level for all files in the container. Defaults to 'none' if not specified.
     * 'public' and 'private' are not standard in the Azure APIs, and are aliased to 'container' and 'none' respectively.
     */ access ? : 'blob' | 'container' | 'none' | 'public' | 'private' ; } param options returns function PutObjectRequestOptions ( options ) { const requestOptions = { contentSettings : { } , metadata : { } } as Azure . BlobService . CreateBlockBlobRequestOptions ; if ( ! options ) { return requestOptions ; } if ( options . metadata ) { requestOptions . metadata = { } ; for ( const key in options . metadata ) { if ( ! options . metadata . hasOwnProperty ( key ) ) { continue ; } const keyLowerCase = key . toLowerCase ( ) ; switch ( keyLowerCase ) { case 'cache-control' : requestOptions . contentSettings . cacheControl = options . metadata [ key ] ; break ; case 'content-disposition' : requestOptions . contentSettings . contentDisposition = options . metadata [ key ] ; break ; case 'content-encoding' : requestOptions . contentSettings . contentEncoding = options . metadata [ key ] ; break ; case 'content-language' : requestOptions . contentSettings . contentLanguage = options . metadata [ key ] ; break ; case 'content-md5' : requestOptions . contentSettings . contentMD5 = options . metadata [ key ] ; break ; case 'content-type' : requestOptions . contentSettings . contentType = options . metadata [ key ] ; break ; default : requestOptions . metadata [ key ] = options . metadata [ key ] ; break ; } } } return requestOptions ; } /**
 * Client to interact with Azure Blob Storage.
 */ class AzureStorageProvider extends StorageProvider { protected _client : Azure . BlobService ; param connection constructor ( connection ) { super ( connection ) ; this . _provider = 'azure-storage' ; if ( typeof connection == 'string' ) { this . _client = Azure . createBlobService ( connection as string ) ; } else { this . _client = Azure . createBlobService ( connection . storageAccount , connection . storageAccessKey , connection . host || undefined ) ; } } param container param options returns async createContainer ( container , options ? ) { return this . _createContainerInternal ( container , false , options ) . then ( ( ) => { return ; } ) ; } param container returns async isContainer ( container ) { return new Promise ( ( resolve , reject ) => { this . _client . getContainerProperties ( container , ( err , response ) => { if ( err ) { return err . toString ( ) . match ( /NotFound/ ) ? resolve ( false ) : reject ( err ) ; } else if ( response && response . name ) { return resolve ( true ) ; } else { throw Error ( 'Response does not contain storage account name' ) ; } } ) ; } ) ; } param container param options returns async ensureContainer ( container , options ? ) { return this . _createContainerInternal ( container , true , options ) . then ( ( ) => { return ; } ) ; } returns async listContainers ( ) { const resultList = [ ] as string [ ] ; const requestPromise = ( continuationToken ) : Promise < string [ ] > => { return new Promise ( ( resolve , reject ) => { this . _client . listContainersSegmented ( continuationToken , ( err , response ) => { if ( err ) { return reject ( err ) ; } if ( ! response . entries || ! Array . isArray ( response . entries ) ) { throw Error ( 'Response does not contain an entries array' ) ; } for ( const i in response . entries ) { if ( response . entries . hasOwnProperty ( i ) ) { const e = response . entries [ i ] ; if ( ! e || ! e . name ) { throw Error ( 'Invalid entry' ) ; } resultList . push ( e . name ) ; } } if ( response . continuationToken ) { resolve ( requestPromise ( response . continuationToken ) ) ; } else { resolve ( resultList ) ; } } ) ; } ) ; } ; return requestPromise ( null ) ; } param container returns async deleteContainer ( container ) { return new Promise ( ( resolve , reject ) => { this . _client . deleteContainer ( container , ( err , response ) => { if ( err ) { return reject ( err ) ; } else if ( ! response || ! response . isSuccessful ) { throw Error ( 'Response was empty or not successful' ) ; } else { return resolve ( ) ; } } ) ; } ) ; } param container param path param data param options returns async putObject ( container , path , data , options ? ) { if ( ! data ) { throw Error ( 'Argument data is empty' ) ; } const requestOptions = PutObjectRequestOptions ( options ) ; return new Promise ( ( resolve , reject ) => { const callback = ( err , response ) => { if ( err ) { return reject ( err ) ; } if ( ! response || ( ! response . name && ! response . commmittedBlocks ) ) { throw Error ( 'Response was empty or not successful' ) ; } else { return resolve ( ) ; } } ; if ( typeof data == 'object' && typeof ( data as any ) . pipe == 'function' ) { ( data as Stream ) . pipe ( this . _client . createWriteStreamToBlockBlob ( container , path , requestOptions , callback ) ) ; } else if ( typeof data == 'string' || ( typeof data == 'object' && Buffer . isBuffer ( data ) ) ) { this . _client . createBlockBlobFromText ( container , path , data , requestOptions , callback ) ; } else { throw Error ( 'Argument data must be a Stream, a String or a Buffer' ) ; } } ) ; } param container param path returns async getObject ( container , path ) { const duplexStream = new Transform ( { transform : ( chunk , encoding , done ) => { done ( null , chunk ) ; } } ) ; this . _client . getBlobToStream ( container , path , duplexStream , ( err , response ) => { if ( err ) { duplexStream . destroy ( ( typeof err == 'object' && err instanceof Error ) ? err : Error ( err ) ) ; } } ) ; return Promise . resolve ( duplexStream ) ; } param container param prefix returns async listObjects ( container , prefix ? ) { const resultList = [ ] ; const requestPromise = ( type , continuationToken ) : Promise < ListResults > => { return new Promise ( ( resolve , reject ) => { const blobTypeConstants = ( Azure . Constants . BlobConstants as any ) . ListBlobTypes ; const listBlobType = ( type == 'prefix' ) ? blobTypeConstants . Directory : blobTypeConstants . Blob ; const clientAny = this . _client as any ; clientAny . _listBlobsOrDircotriesSegmentedWithPrefix ( container , prefix || null , continuationToken , listBlobType , { delimiter : '/' } , ( err , response ) => { if ( err ) { return reject ( err ) ; } for ( const i in response . entries ) { if ( response . entries . hasOwnProperty ( i ) ) { const e = response . entries [ i ] ; if ( e . etag ) { const res = { creationTime : e . creationTime ? new Date ( e . creationTime ) : undefined , lastModified : e . lastModified ? new Date ( e . lastModified ) : undefined , path : e . name , size : parseInt ( e . contentLength , 10 ) } as ListItemObject ; if ( e . contentSettings && e . contentSettings . contentMD5 ) { res . contentMD5 = Buffer . from ( e . contentSettings . contentMD5 , 'base64' ) . toString ( 'hex' ) ; } if ( e . contentSettings && e . contentSettings . contentType ) { res . contentType = e . contentSettings . contentType ; } resultList . push ( res ) ; } else { resultList . push ( { prefix : e . name } as ListItemPrefix ) ; } } } if ( response . continuationToken ) { resolve ( requestPromise ( type , response . continuationToken ) ) ; } else { resolve ( resultList ) ; } } ) ; } ) ; } ; return Promise . all ( [ requestPromise ( 'blob' , null ) , requestPromise ( 'prefix' , null ) ] ) . then ( ( ) => { return resultList ; } ) ; } param container param path returns async deleteObject ( container , path ) { return new Promise ( ( resolve , reject ) => { this . _client . deleteBlob ( container , path , ( err , response ) => { if ( err ) { return reject ( err ) ; } else if ( ! response || ! response . isSuccessful ) { throw Error ( 'Response was empty or not successful' ) ; } else { return resolve ( ) ; } } ) ; } ) ; } param container param path param ttl returns async presignedGetUrl ( container , path , ttl ? ) { return this . presignedUrl ( 'getObject' , container , path , ttl ) ; } param container param path param options param ttl returns async presignedPutUrl ( container , path , options ? , ttl ? ) { const requestOptions = PutObjectRequestOptions ( options ) ; return this . presignedUrl ( 'putObject' , container , path , requestOptions . contentSettings , ttl ) ; } param operation param container param path param contentSettings param ttl returns async private presignedUrl ( operation , container , path , contentSettings ? , ttl ? ) { if ( ! ttl || ttl < 1 ) { ttl = 86400 ; } if ( contentSettings && contentSettings . contentMD5 ) { delete contentSettings . contentMD5 ; } const policy = { AccessPolicy : { Expiry : new Date ( Date . now ( ) + ttl * 1000 ) , Permissions : ( operation == 'getObject' ) ? Azure . BlobUtilities . SharedAccessPermissions . READ : Azure . BlobUtilities . SharedAccessPermissions . WRITE } } as Azure . common . SharedAccessPolicy ; const signature = this . _client . generateSharedAccessSignature ( container , path , policy , contentSettings ) ; const url = this . _client . getUrl ( container , path , signature ) ; return Promise . resolve ( url ) ; } param container param ifNotExists param options returns async private _createContainerInternal ( container , ifNotExists , options ? ) { return new Promise ( ( resolve , reject ) => { const containerOpts = { publicAccessLevel : null } as Azure . BlobService . CreateContainerOptions ; if ( options && options . access ) { if ( options . access == 'blob' ) { containerOpts . publicAccessLevel = 'blob' ; } else if ( options . access == 'container' || options . access == 'public' ) { containerOpts . publicAccessLevel = 'container' ; } } const callback = ( err , response ) => { if ( err ) { return reject ( err ) ; } else if ( response && response . name ) { return resolve ( ) ; } else { throw Error ( 'Response does not contain storage account name' ) ; } } ; if ( ifNotExists ) { this . _client . createContainerIfNotExists ( container , containerOpts , callback ) ; } else { this . _client . createContainer ( container , containerOpts , callback ) ; } } ) ; } } export = AzureStorageProvider ;