import { assert , gitHashFile , IsoBuffer , Uint8ArrayToString , unreachableCase } from "@fluidframework/common-utils" ; import { ICreateTreeEntry } from "@fluidframework/gitresources" ; import { getGitMode , getGitType } from "@fluidframework/protocol-base" ; import { ISnapshotTreeEx , ISummaryTree , SummaryObject , SummaryType , } from "@fluidframework/protocol-definitions" ; import { ISummaryUploadManager , IGitManager } from "./storage" ; import { IWholeSummaryPayloadType } from "./storageContracts" ; /**
 * Recursively writes summary tree as individual summary blobs.
 */ export class SummaryTreeUploadManager implements ISummaryUploadManager { constructor ( private readonly manager , private readonly blobsShaCache , private readonly getPreviousFullSnapshot ) { } public async writeSummaryTree ( summaryTree , parentHandle , summaryType , sequenceNumber ? ) { const previousFullSnapshot = await this . getPreviousFullSnapshot ( parentHandle ) ; return this . writeSummaryTreeCore ( summaryTree , previousFullSnapshot ?? undefined ) ; } private async writeSummaryTreeCore ( summaryTree , previousFullSnapshot ) { const entries = await Promise . all ( Object . keys ( summaryTree . tree ) . map ( async ( key ) => { const entry = summaryTree . tree [ key ] ; const pathHandle = await this . writeSummaryTreeObject ( entry , previousFullSnapshot ) ; const treeEntry = { mode : getGitMode ( entry ) , path : encodeURIComponent ( key ) , sha : pathHandle , type : getGitType ( entry ) } ; return treeEntry ; } ) ) ; const treeHandle = await this . manager . createGitTree ( { tree : entries } ) ; return treeHandle . sha ; } private async writeSummaryTreeObject ( object , previousFullSnapshot ) { switch ( object . type ) { case SummaryType . Blob : { return this . writeSummaryBlob ( object . content ) ; } case SummaryType . Handle : { if ( previousFullSnapshot === undefined ) { throw Error ( "Parent summary does not exist to reference by handle." ) ; } return this . getIdFromPath ( object . handleType , object . handle , previousFullSnapshot ) ; } case SummaryType . Tree : { return this . writeSummaryTreeCore ( object , previousFullSnapshot ) ; } case SummaryType . Attachment : { return object . id ; } default : unreachableCase ( object , `Unknown type: ${ ( object as any ) . type }` ) ; } } private async writeSummaryBlob ( content ) { const { parsedContent , encoding } = typeof content === "string" ? { parsedContent : content , encoding : "utf-8" } : { parsedContent : Uint8ArrayToString ( content , "base64" ) , encoding : "base64" } ; const hash = await gitHashFile ( IsoBuffer . from ( parsedContent , encoding ) ) ; if ( ! this . blobsShaCache . has ( hash ) ) { this . blobsShaCache . set ( hash , "" ) ; const blob = await this . manager . createBlob ( parsedContent , encoding ) ; assert ( hash === blob . sha , 0x0b6 ) ; } return hash ; } private getIdFromPath ( handleType , handlePath , previousFullSnapshot ) { const path = handlePath . split ( "/" ) . map ( ( part ) => decodeURIComponent ( part ) ) ; if ( path [ 0 ] === "" ) { path . shift ( ) ; } if ( path . length === 0 ) { return previousFullSnapshot . id ; } return this . getIdFromPathCore ( handleType , path , previousFullSnapshot ) ; } private getIdFromPathCore ( handleType , path , /** Previous snapshot, subtree relative to this path part */ previousSnapshot ) { assert ( path . length > 0 , 0x0b3 ) ; const key = path [ 0 ] ; if ( path . length === 1 ) { switch ( handleType ) { case SummaryType . Blob : { const tryId = previousSnapshot . blobs [ key ] ; assert ( ! ! tryId , 0x0b4 ) ; return tryId ; } case SummaryType . Tree : { const tryId = previousSnapshot . trees [ key ] ?. id ; assert ( ! ! tryId , 0x0b5 ) ; return tryId ; } default : throw Error ( `Unexpected handle summary object type: "${ handleType }".` ) ; } } return this . getIdFromPathCore ( handleType , path . slice ( 1 ) , previousSnapshot . trees [ key ] ) ; } }