import { fromUtf8ToBase64 } from "@fluidframework/common-utils" ; import * as git from "@fluidframework/gitresources" ; import { RestWrapper , BasicRestWrapper } from "./restWrapper" ; import { IHistorian } from "./storage" ; import { IWholeFlatSummary , IWholeSummaryPayload , IWriteSummaryResponse } from "./storageContracts" ; function endsWith ( value , endings ) { for ( const ending of endings ) { if ( value . endsWith ( ending ) ) { return true ; } } return false ; } export interface ICredentials { user : string ; password : string ; } export const getAuthorizationTokenFromCredentials = ( credentials ) : string => `Basic ${ fromUtf8ToBase64 ( `${ credentials . user }:${ credentials . password }` ) }` ; /**
 * Implementation of the IHistorian interface that calls out to a REST interface
 */ export class Historian implements IHistorian { private readonly defaultQueryString : Record < string , unknown > = { } ; private readonly cacheBust : boolean ; constructor ( public endpoint , private readonly historianApi , disableCache , private readonly restWrapper ? ) { if ( disableCache && this . historianApi ) { this . defaultQueryString . disableCache = disableCache ; this . cacheBust = false ; } else { this . cacheBust = disableCache ; } if ( this . restWrapper === undefined ) { this . restWrapper = new BasicRestWrapper ( this . endpoint ) ; } } public getHeader ( sha ) { if ( this . historianApi ) { return this . restWrapper . get ( `/headers/${ encodeURIComponent ( sha ) }` , this . getQueryString ( ) ) ; } else { return this . getHeaderDirect ( sha ) ; } } public getFullTree ( sha ) { return this . restWrapper . get ( `/tree/${ encodeURIComponent ( sha ) }` , this . getQueryString ( ) ) ; } public getBlob ( sha ) { return this . restWrapper . get < git . IBlob > ( `/git/blobs/${ encodeURIComponent ( sha ) }` , this . getQueryString ( ) ) ; } public createBlob ( blob ) { return this . restWrapper . post < git . ICreateBlobResponse > ( `/git/blobs` , blob , this . getQueryString ( ) ) ; } public getContent ( path , ref ) { return this . restWrapper . get ( `/contents/${ path }` , this . getQueryString ( { ref } ) ) ; } public getCommits ( sha , count ) { return this . restWrapper . get < git . ICommitDetails [ ] > ( `/commits` , this . getQueryString ( { count , sha } ) ) . catch ( ( error ) => ( error === 400 || error === 404 ) ? [ ] as git . ICommitDetails [ ] : Promise . reject < git . ICommitDetails [ ] > ( error ) ) ; } public getCommit ( sha ) { return this . restWrapper . get < git . ICommit > ( `/git/commits/${ encodeURIComponent ( sha ) }` , this . getQueryString ( ) ) ; } public createCommit ( commit ) { return this . restWrapper . post < git . ICommit > ( `/git/commits` , commit , this . getQueryString ( ) ) ; } public getRefs ( ) { return this . restWrapper . get ( `/git/refs` , this . getQueryString ( ) ) ; } public getRef ( ref ) { return this . restWrapper . get ( `/git/refs/${ ref }` , this . getQueryString ( ) ) ; } public createRef ( params ) { return this . restWrapper . post ( `/git/refs` , params , this . getQueryString ( ) ) ; } public updateRef ( ref , params ) { return this . restWrapper . patch ( `/git/refs/${ ref }` , params , this . getQueryString ( ) ) ; } public async deleteRef ( ref ) { await this . restWrapper . delete ( `/git/refs/${ ref }` , this . getQueryString ( ) ) ; } public createTag ( tag ) { return this . restWrapper . post ( `/git/tags` , tag , this . getQueryString ( ) ) ; } public getTag ( tag ) { return this . restWrapper . get ( `/git/tags/${ tag }` , this . getQueryString ( ) ) ; } public createTree ( tree ) { return this . restWrapper . post < git . ITree > ( `/git/trees` , tree , this . getQueryString ( ) ) ; } public getTree ( sha , recursive ) { return this . restWrapper . get < git . ITree > ( `/git/trees/${ encodeURIComponent ( sha ) }` , this . getQueryString ( { recursive : recursive ? 1 : 0 } ) ) ; } public async createSummary ( summary ) { return this . restWrapper . post < IWriteSummaryResponse > ( `/git/summaries` , summary , this . getQueryString ( ) ) ; } public async deleteSummary ( softDelete ) { const headers = { "Soft-Delete" : softDelete } ; return this . restWrapper . delete ( `/git/summaries` , this . getQueryString ( ) , headers ) ; } public async getSummary ( sha ) { return this . restWrapper . get < IWholeFlatSummary > ( `/git/summaries/${ sha }` , this . getQueryString ( ) ) ; } private async getHeaderDirect ( sha ) { const tree = await this . getTree ( sha , true ) ; const includeBlobs = [ ".attributes" , ".blobs" , ".messages" , "header" ] ; const blobsP = [ ] ; for ( const entry of tree . tree ) { if ( entry . type === "blob" && endsWith ( entry . path , includeBlobs ) ) { const blobP = this . getBlob ( entry . sha ) ; blobsP . push ( blobP ) ; } } const blobs = await Promise . all ( blobsP ) ; return { blobs , tree } ; } private getQueryString ( queryString ? ) { if ( this . cacheBust ) { return { cacheBust : Date . now ( ) , ... this . defaultQueryString , ... queryString } ; } return { ... this . defaultQueryString , ... queryString } ; } }