Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PE-7146: skip files missing data content type while listing folders and drives #245

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ardrive-core-js",
"version": "2.0.6",
"version": "2.0.7",
"description": "ArDrive Core contains the essential back end application features to support the ArDrive CLI and Desktop apps, such as file management, Permaweb upload/download, wallet management and other common functions.",
"main": "./lib/exports.js",
"types": "./lib/exports.d.ts",
Expand Down
7 changes: 5 additions & 2 deletions src/arfs/arfs_builders/arfs_file_builders.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { expect } from 'chai';
import { stub } from 'sinon';
import { fakeArweave, stubTxID } from '../../../tests/stubs';
import { fakeArweave, stubTxID, stubTxIDAlt } from '../../../tests/stubs';
import { expectAsyncErrorThrow } from '../../../tests/test_helpers';
import { EntityKey, GQLNodeInterface } from '../../types';
import { EID, EntityKey, GQLNodeInterface } from '../../types';
import { gatewayUrlForArweave } from '../../utils/common';
import { GatewayAPI } from '../../utils/gateway_api';
import { ArFSPrivateFileBuilder, ArFSPublicFileBuilder } from './arfs_file_builders';
import { ArFSDAOAnonymous } from '../arfsdao_anonymous';
import { ADDR, DriveID, FolderID } from '../../types';
import { stub, SinonStub } from 'sinon';

const gatewayApi = new GatewayAPI({
gatewayUrl: gatewayUrlForArweave(fakeArweave),
Expand Down
34 changes: 12 additions & 22 deletions src/arfs/arfs_builders/arfs_file_builders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { Utf8ArrayToStr, extToMime } from '../../utils/common';
import { ArFSPublicFile, ArFSPrivateFile } from '../arfs_entities';
import { ArFSFileOrFolderBuilder } from './arfs_builders';
import { GatewayAPI } from '../../utils/gateway_api';

import { FileBuilderValidation, InvalidFileStateException } from '../../types/exceptions';
export interface FileMetaDataTransactionData extends EntityMetaDataTransactionData {
// FIXME: do we need our safe types here? This interface refers to a JSON with primitive types
name: string;
Expand Down Expand Up @@ -86,16 +86,10 @@ export class ArFSPublicFileBuilder extends ArFSFileBuilder<ArFSPublicFile> {
this.dataTxId = new TransactionID(dataJSON.dataTxId);
this.dataContentType = dataJSON.dataContentType ?? extToMime(this.name);

if (
!this.name ||
this.size === undefined ||
!this.lastModifiedDate ||
!this.dataTxId ||
!this.dataContentType ||
!(this.entityType === 'file')
) {
throw new Error('Invalid file state');
}
const fileBuilderValidation = new FileBuilderValidation();
fileBuilderValidation.validateFileProperties(this);
fileBuilderValidation.throwIfMissingProperties();

this.parseCustomMetaDataFromDataJson(dataJSON);

return Promise.resolve(
Expand Down Expand Up @@ -188,6 +182,10 @@ export class ArFSPrivateFileBuilder extends ArFSFileBuilder<ArFSPrivateFile> {
const dataBuffer = Buffer.from(txData);
const fileKey = this.fileKey ?? (await deriveFileKey(`${this.fileId}`, this.driveKey));

if (!fileKey) {
throw new InvalidFileStateException(['fileKey']);
}

const decryptedFileBuffer: Buffer = await fileDecrypt(this.cipherIV, fileKey, dataBuffer);
const decryptedFileString: string = await Utf8ArrayToStr(decryptedFileBuffer);
const decryptedFileJSON: FileMetaDataTransactionData = await JSON.parse(decryptedFileString);
Expand All @@ -199,17 +197,9 @@ export class ArFSPrivateFileBuilder extends ArFSFileBuilder<ArFSPrivateFile> {
this.dataTxId = new TransactionID(decryptedFileJSON.dataTxId);
this.dataContentType = decryptedFileJSON.dataContentType ?? extToMime(this.name);

if (
!this.name ||
this.size === undefined ||
!this.lastModifiedDate ||
!this.dataTxId ||
!this.dataContentType ||
!fileKey ||
!(this.entityType === 'file')
) {
throw new Error('Invalid file state');
}
const fileBuilderValidation = new FileBuilderValidation();
fileBuilderValidation.validateFileProperties(this);
fileBuilderValidation.throwIfMissingProperties();

this.parseCustomMetaDataFromDataJson(decryptedFileJSON);

Expand Down
39 changes: 25 additions & 14 deletions src/arfs/arfsdao.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ import { assertDataRootsMatch, rePrepareV2Tx } from '../utils/arfsdao_utils';
import { ArFSDataToUpload, ArFSFolderToUpload, DrivePrivacy, errorMessage } from '../exports';
import { Turbo, TurboCachesResponse } from './turbo';
import { ArweaveSigner } from 'arbundles/src/signing';
import { InvalidFileStateException } from '../types/exceptions';

/** Utility class for holding the driveId and driveKey of a new drive */
export class PrivateDriveKeyData {
Expand Down Expand Up @@ -1583,21 +1584,31 @@ export class ArFSDAO extends ArFSDAOAnonymous {
const transactions = await this.gatewayApi.gqlRequest(gqlQuery);
const { edges } = transactions;
hasNextPage = transactions.pageInfo.hasNextPage;
const files: Promise<ArFSPrivateFile>[] = edges.map(async (edge: GQLEdgeInterface) => {
const { node } = edge;
cursor = edge.cursor;
const fileBuilder = ArFSPrivateFileBuilder.fromArweaveNode(node, this.gatewayApi, driveKey);
// Build the file so that we don't add something invalid to the cache
const file = await fileBuilder.build(node);
const fileKey: FileKey = await deriveFileKey(`${file.fileId}`, driveKey);
const cacheKey = {
fileId: file.fileId,
owner,
fileKey
};
return this.caches.privateFileCache.put(cacheKey, Promise.resolve(file));
const files: Promise<ArFSPrivateFile | null>[] = edges.map(async (edge: GQLEdgeInterface) => {
try {
const { node } = edge;
cursor = edge.cursor;
const fileBuilder = ArFSPrivateFileBuilder.fromArweaveNode(node, this.gatewayApi, driveKey);
// Build the file so that we don't add something invalid to the cache
const file = await fileBuilder.build(node);
const fileKey: FileKey = await deriveFileKey(`${file.fileId}`, driveKey);
const cacheKey = {
fileId: file.fileId,
owner,
fileKey
};
return this.caches.privateFileCache.put(cacheKey, Promise.resolve(file));
} catch (e) {
if (e instanceof InvalidFileStateException) {
console.error(`Error building file. Skipping... Error: ${e}`);
return null;
}

throw e;
}
});
allFiles.push(...(await Promise.all(files)));
const validFiles = (await Promise.all(files)).filter((f) => f !== null) as ArFSPrivateFile[];
allFiles.push(...validFiles);
}
return latestRevisionsOnly ? allFiles.filter(latestRevisionFilter) : allFiles;
}
Expand Down
Loading
Loading