Skip to content

Commit

Permalink
Move LocalFileGzip to collab server package
Browse files Browse the repository at this point in the history
  • Loading branch information
garrettjstevens committed Sep 20, 2024
1 parent 9d149dc commit db455a3
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 89 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,12 @@ import { InjectModel } from '@nestjs/mongoose'
import { Model } from 'mongoose'

import { CreateFileDto } from './dto/create-file.dto'
import { writeFileAndCalculateHash, FileRequest } from './filesUtil'
import {
writeFileAndCalculateHash,
FileRequest,
LocalFileGzip,
} from './filesUtil'
import { GenericFilehandle, LocalFile } from 'generic-filehandle'
import { LocalFileGzip } from '@apollo-annotation/shared'

@Injectable()
export class FilesService {
Expand Down
90 changes: 87 additions & 3 deletions packages/apollo-collaboration-server/src/files/filesUtil.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
import { createHash } from 'node:crypto'
import { createWriteStream } from 'node:fs'
import { mkdir, mkdtemp, rename, rmdir } from 'node:fs/promises'
import { createWriteStream, Stats } from 'node:fs'
import {
FileHandle,
mkdir,
mkdtemp,
open,
rename,
rmdir,
} from 'node:fs/promises'
import { join } from 'node:path'
import { Readable } from 'node:stream'
import { pipeline } from 'node:stream/promises'
import { createGzip } from 'node:zlib'
import { promisify } from 'node:util'
import { createGzip, gunzip } from 'node:zlib'

import { Logger } from '@nestjs/common'
import { Request } from 'express'
import { GenericFilehandle, FilehandleOptions } from 'generic-filehandle'

interface FileUpload {
originalname: string
Expand Down Expand Up @@ -81,3 +90,78 @@ export async function writeFileAndCalculateHash(
logger.log('File upload finished')
return fileChecksum
}

async function unzip(input: FileHandle): Promise<Buffer> {
const gunzipP = promisify(gunzip)
const fileContents = await input.readFile()
const unzippedContents = await gunzipP(fileContents)
return unzippedContents
}

export class LocalFileGzip implements GenericFilehandle {
private fileHandle: Promise<FileHandle> | undefined
private filename: string
private opts: FilehandleOptions

public constructor(source: string, opts: FilehandleOptions = {}) {
this.filename = source
this.opts = opts
}

private getFileHandle(): Promise<FileHandle> {
if (!this.fileHandle) {
this.fileHandle = open(this.filename)
}
return this.fileHandle
}

public async read(
buffer: Buffer,
offset = 0,
length: number,
position = 0,
): Promise<{ bytesRead: number; buffer: Buffer }> {
const fileHandle = await this.getFileHandle()
const unzippedContents = await unzip(fileHandle)
const bytesRead = unzippedContents.copy(
buffer,
position,
offset,
offset + length,
)
return { bytesRead, buffer }
}

public async readFile(): Promise<Buffer>
public async readFile(options: BufferEncoding): Promise<string>
public async readFile<T extends undefined>(
options:
| Omit<FilehandleOptions, 'encoding'>
| (Omit<FilehandleOptions, 'encoding'> & { encoding: T }),
): Promise<Buffer>
public async readFile<T extends BufferEncoding>(
options: Omit<FilehandleOptions, 'encoding'> & { encoding: T },
): Promise<string>

public async readFile(
_options?: FilehandleOptions | BufferEncoding,
): Promise<Buffer | string> {
const fileHandle = await this.getFileHandle()
const unzippedContents = await unzip(fileHandle)
if (this.opts.encoding) {
return unzippedContents.toString(this.opts.encoding)
}
return unzippedContents
}

// todo memoize
public async stat(): Promise<Stats> {
const fh = await this.getFileHandle()
return fh.stat()
}

public async close(): Promise<void> {
const fh = await this.getFileHandle()
return fh.close()
}
}
83 changes: 0 additions & 83 deletions packages/apollo-shared/src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,89 +3,6 @@
import { AnnotationFeatureSnapshot } from '@apollo-annotation/mst'
import { GFF3Feature } from '@gmod/gff'

import fs from 'node:fs'

import { Buffer } from 'node:buffer'
import { promisify } from 'node:util'
import { GenericFilehandle, FilehandleOptions, Stats } from 'generic-filehandle'
import { FileHandle } from 'node:fs/promises'
import { gunzip } from 'node:zlib'

export class LocalFileGzip implements GenericFilehandle {
private fileHandle: Promise<FileHandle> | undefined
private filename: string
private opts: FilehandleOptions

public constructor(source: string, opts: FilehandleOptions = {}) {
this.filename = source
this.opts = opts
}

private getFileHandle(): Promise<FileHandle> {
if (!this.fileHandle) {
this.fileHandle = fs.promises.open(this.filename)
}
return this.fileHandle
}

public async read(
buffer: Buffer,
offset = 0,
length: number,
position = 0,
): Promise<{ bytesRead: number; buffer: Buffer }> {
const fileHandle = await this.getFileHandle()
const unzippedContents = await unzip(fileHandle)
const bytesRead = unzippedContents.copy(
buffer,
position,
offset,
offset + length,
)
return { bytesRead, buffer }
}

public async readFile(): Promise<Buffer>
public async readFile(options: BufferEncoding): Promise<string>
public async readFile<T extends undefined>(
options:
| Omit<FilehandleOptions, 'encoding'>
| (Omit<FilehandleOptions, 'encoding'> & { encoding: T }),
): Promise<Buffer>
public async readFile<T extends BufferEncoding>(
options: Omit<FilehandleOptions, 'encoding'> & { encoding: T },
): Promise<string>

public async readFile(
_options?: FilehandleOptions | BufferEncoding,
): Promise<Buffer | string> {
const fileHandle = await this.getFileHandle()
const unzippedContents = await unzip(fileHandle)
if (this.opts.encoding) {
return unzippedContents.toString(this.opts.encoding)
}
return unzippedContents
}

// todo memoize
public async stat(): Promise<Stats> {
const fh = await this.getFileHandle()
return fh.stat()
}

public async close(): Promise<void> {
const fh = await this.getFileHandle()
return fh.close()
}
}

async function unzip(input: FileHandle): Promise<Buffer> {
const gunzipP = promisify(gunzip)
const fileContents = await input.readFile()
const unzippedContents = await gunzipP(fileContents)
return unzippedContents
}

export function makeGFF3Feature(
feature: AnnotationFeatureSnapshot,
parentId?: string,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ export function AddAssembly({
const fileUploadChangeBase = {
assembly: new ObjectID().toHexString(),
assemblyName,
fileId,
fileIds: { fa: fileId },
}
change =
fileType === FileType.GFF3 && importFeatures
Expand Down

0 comments on commit db455a3

Please sign in to comment.