import { BlobService , common , createBlobService , ErrorOrResponse , ErrorOrResult } from "azure-storage" ; import * as fs from "fs" ; import * as https from "https" ; import { streamDone , streamOfString , stringOfStream } from "../util/io" ; import { gzip , unGzip } from "../util/tgz" ; import { parseJson } from "../util/util" ; import { getSecret , Secret } from "./secrets" ; import { azureContainer , azureStorageAccount } from "./settings" ; export default class BlobWriter { static async create ( ) { return new BlobWriter ( createBlobService ( azureStorageAccount , await getSecret ( Secret . AZURE_STORAGE_ACCESS_KEY ) ) ) ; } private constructor ( private readonly service ) { } setCorsProperties ( ) { const properties = { Cors : { CorsRule : [ { AllowedOrigins : [ "*" ] , AllowedMethods : [ "GET" ] , AllowedHeaders : [ ] , ExposedHeaders : [ ] , MaxAgeInSeconds : 60 * 60 * 24 , } , ] , } , } ; return promisifyErrorOrResponse ( cb => { this . service . setServiceProperties ( properties , cb ) ; } ) ; } ensureCreated ( options ) { return promisifyErrorOrResult < BlobService . ContainerResult > ( cb => { this . service . createContainerIfNotExists ( azureContainer , options , cb ) ; } ) as unknown as Promise < void > ; } createBlobFromFile ( blobName , fileName ) { return this . createBlobFromStream ( blobName , fs . createReadStream ( fileName ) ) ; } createBlobFromText ( blobName , text ) { return this . createBlobFromStream ( blobName , streamOfString ( text ) ) ; } async listBlobs ( prefix ) { const once = ( tkn ) => promisifyErrorOrResult < BlobService . ListBlobsResult > ( cb => { this . service . listBlobsSegmentedWithPrefix ( azureContainer , prefix , tkn ! , cb ) ; } ) ; const out = [ ] ; let token ; do { const { entries , continuationToken } = await once ( token ) ; out . push ( ... entries ) ; token = continuationToken ; } while ( token ) ; return out ; } deleteBlob ( blobName ) { return promisifyErrorOrResponse ( cb => { this . service . deleteBlob ( azureContainer , blobName , cb ) ; } ) ; } private createBlobFromStream ( blobName , stream ) { const options = { contentSettings : { contentEncoding : "gzip" , contentType : "application/json; charset=utf-8" , } , } ; return streamDone ( gzip ( stream ) . pipe ( this . service . createWriteStreamToBlockBlob ( azureContainer , blobName , options , undefined ! ) ) ) ; } } export async function readBlob ( blobName ) { return new Promise < string > ( ( resolve , reject ) => { const url = urlOfBlob ( blobName ) ; const req = https . get ( url , res => { switch ( res . statusCode ) { case 200 : if ( res . headers [ "content-encoding" ] !== "gzip" ) { reject ( new Error ( `${ url } is not gzipped` ) ) ; } else { resolve ( stringOfStream ( unGzip ( res ) , blobName ) ) ; } break ; default : reject ( new Error ( `Can't get ${ url }: ${ res . statusCode } error` ) ) ; } } ) ; req . on ( "error" , reject ) ; } ) ; } export async function readJsonBlob ( blobName ) { return parseJson ( await readBlob ( blobName ) ) ; } export function urlOfBlob ( blobName ) { return `https://${ azureContainer }.blob.core.windows.net/${ azureContainer }/${ blobName }` ; } function promisifyErrorOrResult < A > ( callsBack ) { return new Promise < A > ( ( resolve , reject ) => { callsBack ( ( err , result ) => { if ( err ) { reject ( err ) ; } else { resolve ( result ) ; } } ) ; } ) ; } function promisifyErrorOrResponse ( callsBack ) { return new Promise < void > ( ( resolve , reject ) => { callsBack ( err => { if ( err ) { reject ( err ) ; } else { resolve ( ) ; } } ) ; } ) ; }