Skip to content

Commit

Permalink
Merge pull request #240 from ardriveapp/PE-6232-improve-gql-queries
Browse files Browse the repository at this point in the history
feat(gql): utilize drive ID in conflict resolution and list folder qu…
  • Loading branch information
fedellen authored Jun 6, 2024
2 parents bd6f0d1 + 7a50cfb commit 8907470
Show file tree
Hide file tree
Showing 8 changed files with 115 additions and 53 deletions.
75 changes: 54 additions & 21 deletions src/ardrive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,11 @@ export class ArDrive extends ArDriveAnonymous {
}

// Assert that there are no duplicate names in the destination folder
const entityNamesInParentFolder = await this.arFsDao.getPublicEntityNamesInFolder(newParentFolderId, owner);
const entityNamesInParentFolder = await this.arFsDao.getPublicEntityNamesInFolder(
newParentFolderId,
owner,
destFolderDriveId
);
if (entityNamesInParentFolder.includes(originalFileMetaData.name)) {
// TODO: Add optional interactive prompt to resolve name conflicts in ticket PE-599
throw new Error(errorMessage.entityNameExists);
Expand Down Expand Up @@ -269,7 +273,8 @@ export class ArDrive extends ArDriveAnonymous {
const entityNamesInParentFolder = await this.arFsDao.getPrivateEntityNamesInFolder(
newParentFolderId,
owner,
driveKey
driveKey,
destFolderDriveId
);
if (entityNamesInParentFolder.includes(originalFileMetaData.name)) {
// TODO: Add optional interactive prompt to resolve name conflicts in ticket PE-599
Expand Down Expand Up @@ -353,7 +358,11 @@ export class ArDrive extends ArDriveAnonymous {
}

// Assert that there are no duplicate names in the destination folder
const entityNamesInParentFolder = await this.arFsDao.getPublicEntityNamesInFolder(newParentFolderId, owner);
const entityNamesInParentFolder = await this.arFsDao.getPublicEntityNamesInFolder(
newParentFolderId,
owner,
destFolderDriveId
);
if (entityNamesInParentFolder.includes(originalFolderMetaData.name)) {
// TODO: Add optional interactive prompt to resolve name conflicts in ticket PE-599
throw new Error(errorMessage.entityNameExists);
Expand Down Expand Up @@ -439,7 +448,8 @@ export class ArDrive extends ArDriveAnonymous {
const entityNamesInParentFolder = await this.arFsDao.getPrivateEntityNamesInFolder(
newParentFolderId,
owner,
driveKey
driveKey,
destFolderDriveId
);
if (entityNamesInParentFolder.includes(originalFolderMetaData.name)) {
// TODO: Add optional interactive prompt to resolve name conflicts in ticket PE-599
Expand Down Expand Up @@ -523,14 +533,14 @@ export class ArDrive extends ArDriveAnonymous {
const resolvedEntitiesToUpload: UploadStats[] = [];

for (const entity of entitiesToUpload) {
const { destFolderId, wrappedEntity, driveKey, owner, destName } = entity;
const { destFolderId, wrappedEntity, driveKey, owner, destName, destDriveId } = entity;

const resolveConflictParams = {
conflictResolution,
getConflictInfoFn: (folderId: FolderID) =>
driveKey
? this.arFsDao.getPrivateNameConflictInfoInFolder(folderId, owner, driveKey)
: this.arFsDao.getPublicNameConflictInfoInFolder(folderId, owner),
? this.arFsDao.getPrivateNameConflictInfoInFolder(folderId, owner, driveKey, destDriveId)
: this.arFsDao.getPublicNameConflictInfoInFolder(folderId, owner, destDriveId),
prompts,
destFolderId
};
Expand Down Expand Up @@ -729,6 +739,7 @@ export class ArDrive extends ArDriveAnonymous {
destinationFolderId
}: RetryPublicArFSFileByDestFolderIdParams): Promise<ArFSResult> {
const metaDataTx = await this.deriveMetaDataTxFromPublicFolder(destinationFolderId, dataTxId);
const driveId = await this.arFsDao.getDriveIdForFolderId(destinationFolderId);

let metaDataTxId: undefined | TransactionID = undefined;
let createMetaDataPlan: undefined | ArFSCreateFileMetaDataV2Plan = undefined;
Expand All @@ -743,7 +754,8 @@ export class ArDrive extends ArDriveAnonymous {
const isValidUpload = await this.assertWriteFileMetaData({
wrappedFile,
conflictResolution,
destinationFolderId
destinationFolderId,
driveId
});

if (!isValidUpload) {
Expand Down Expand Up @@ -827,11 +839,13 @@ export class ArDrive extends ArDriveAnonymous {
dataTxId: TransactionID
): Promise<ArFSPublicFile | undefined> {
const owner = await this.wallet.getAddress();
const driveId = await this.arFsDao.getDriveIdForFolderId(destinationFolderId);
await this.assertFolderExists(destinationFolderId, owner);

const allFileMetaDataTxInFolder = await this.arFsDao.getPublicFilesWithParentFolderIds(
[destinationFolderId],
owner
owner,
driveId
);
const metaDataTxsForThisTx = allFileMetaDataTxInFolder.filter((f) => `${f.dataTxId}` === `${dataTxId}`);

Expand All @@ -853,19 +867,22 @@ export class ArDrive extends ArDriveAnonymous {
private async assertWriteFileMetaData({
wrappedFile,
destinationFolderId,
conflictResolution
conflictResolution,
driveId
}: {
wrappedFile: ArFSFileToUpload;
destinationFolderId: FolderID;
conflictResolution: FileNameConflictResolution;
driveId: DriveID;
}): Promise<boolean> {
const owner = await this.wallet.getAddress();
await resolveFileNameConflicts({
wrappedFile,
conflictResolution,
destFolderId: destinationFolderId,
destinationFileName: wrappedFile.destinationBaseName,
getConflictInfoFn: (folderId: FolderID) => this.arFsDao.getPublicNameConflictInfoInFolder(folderId, owner)
getConflictInfoFn: (folderId: FolderID) =>
this.arFsDao.getPublicNameConflictInfoInFolder(folderId, owner, driveId)
});

if (wrappedFile.conflictResolution) {
Expand Down Expand Up @@ -1019,7 +1036,11 @@ export class ArDrive extends ArDriveAnonymous {
const owner = await this.wallet.getAddress();

// Assert that there are no duplicate names in the destination folder
const entityNamesInParentFolder = await this.arFsDao.getPublicEntityNamesInFolder(parentFolderId, owner);
const entityNamesInParentFolder = await this.arFsDao.getPublicEntityNamesInFolder(
parentFolderId,
owner,
driveId
);
if (entityNamesInParentFolder.includes(folderName)) {
// TODO: Add optional interactive prompt to resolve name conflicts in ticket PE-599
throw new Error(errorMessage.entityNameExists);
Expand Down Expand Up @@ -1083,7 +1104,8 @@ export class ArDrive extends ArDriveAnonymous {
const entityNamesInParentFolder = await this.arFsDao.getPrivateEntityNamesInFolder(
parentFolderId,
owner,
driveKey
driveKey,
driveId
);
if (entityNamesInParentFolder.includes(folderName)) {
// TODO: Add optional interactive prompt to resolve name conflicts in ticket PE-599
Expand Down Expand Up @@ -1438,10 +1460,11 @@ export class ArDrive extends ArDriveAnonymous {
await fileToDownload.write();
}

async assertUniqueNameWithinPublicFolder(name: string, folderId: FolderID): Promise<void> {
async assertUniqueNameWithinPublicFolder(name: string, folderId: FolderID, driveId: DriveID): Promise<void> {
const allSiblingNames = await this.arFsDao.getPublicEntityNamesInFolder(
folderId,
await this.wallet.getAddress()
await this.wallet.getAddress(),
driveId
);
const collidesWithExistingSiblingName = allSiblingNames.reduce((accumulator, siblingName) => {
return accumulator || siblingName === name;
Expand All @@ -1451,11 +1474,17 @@ export class ArDrive extends ArDriveAnonymous {
}
}

async assertUniqueNameWithinPrivateFolder(name: string, folderId: FolderID, driveKey: DriveKey): Promise<void> {
async assertUniqueNameWithinPrivateFolder(
name: string,
folderId: FolderID,
driveKey: DriveKey,
driveId: DriveID
): Promise<void> {
const allSiblingNames = await this.arFsDao.getPrivateEntityNamesInFolder(
folderId,
await this.wallet.getAddress(),
driveKey
driveKey,
driveId
);
const collidesWithExistingSiblingName = allSiblingNames.reduce((accumulator, siblingName) => {
return accumulator || siblingName === name;
Expand All @@ -1467,13 +1496,14 @@ export class ArDrive extends ArDriveAnonymous {

async renamePublicFile({ fileId, newName }: RenamePublicFileParams): Promise<ArFSResult> {
const owner = await this.wallet.getAddress();
const driveId = await this.getDriveIdForFileId(fileId);

const file = await this.getPublicFile({ fileId, owner });
if (file.name === newName) {
throw new Error(`To rename a file, the new name must be different`);
}
assertValidArFSFileName(newName);
await this.assertUniqueNameWithinPublicFolder(newName, file.parentFolderId);
await this.assertUniqueNameWithinPublicFolder(newName, file.parentFolderId, driveId);
const fileMetadataTxDataStub = new ArFSPublicFileMetadataTransactionData(
newName,
file.size,
Expand Down Expand Up @@ -1527,11 +1557,12 @@ export class ArDrive extends ArDriveAnonymous {
async renamePrivateFile({ fileId, newName, driveKey }: RenamePrivateFileParams): Promise<ArFSResult> {
const owner = await this.wallet.getAddress();
const file = await this.getPrivateFile({ fileId, driveKey, owner });
const driveId = await this.getDriveIdForFileId(fileId);
if (file.name === newName) {
throw new Error(`To rename a file, the new name must be different`);
}
assertValidArFSFileName(newName);
await this.assertUniqueNameWithinPrivateFolder(newName, file.parentFolderId, driveKey);
await this.assertUniqueNameWithinPrivateFolder(newName, file.parentFolderId, driveKey, driveId);
const fileMetadataTxDataStub = await ArFSPrivateFileMetadataTransactionData.from(
newName,
file.size,
Expand Down Expand Up @@ -1590,6 +1621,7 @@ export class ArDrive extends ArDriveAnonymous {
async renamePublicFolder({ folderId, newName }: RenamePublicFolderParams): Promise<ArFSResult> {
const owner = await this.wallet.getAddress();
const folder = await this.getPublicFolder({ folderId, owner });
const driveId = await this.getDriveIdForFolderId(folderId);
if (`${folder.parentFolderId}` === ROOT_FOLDER_ID_PLACEHOLDER) {
throw new Error(
`The root folder with ID '${folderId}' cannot be renamed as it shares its name with its parent drive. Consider renaming the drive instead.`
Expand All @@ -1599,7 +1631,7 @@ export class ArDrive extends ArDriveAnonymous {
throw new Error(`New folder name '${newName}' must be different from the current folder name!`);
}
assertValidArFSFolderName(newName);
await this.assertUniqueNameWithinPublicFolder(newName, folder.parentFolderId);
await this.assertUniqueNameWithinPublicFolder(newName, folder.parentFolderId, driveId);
const folderMetadataTxDataStub = new ArFSPublicFolderTransactionData(newName, folder.customMetaDataJson);

const metadataRewardSettings = this.uploadPlanner.isTurboUpload()
Expand Down Expand Up @@ -1647,6 +1679,7 @@ export class ArDrive extends ArDriveAnonymous {
async renamePrivateFolder({ folderId, newName, driveKey }: RenamePrivateFolderParams): Promise<ArFSResult> {
const owner = await this.wallet.getAddress();
const folder = await this.getPrivateFolder({ folderId, driveKey, owner });
const driveId = await this.getDriveIdForFolderId(folderId);
if (`${folder.parentFolderId}` === ROOT_FOLDER_ID_PLACEHOLDER) {
throw new Error(
`The root folder with ID '${folderId}' cannot be renamed as it shares its name with its parent drive. Consider renaming the drive instead.`
Expand All @@ -1656,7 +1689,7 @@ export class ArDrive extends ArDriveAnonymous {
throw new Error(`New folder name '${newName}' must be different from the current folder name!`);
}
assertValidArFSFolderName(newName);
await this.assertUniqueNameWithinPrivateFolder(newName, folder.parentFolderId, driveKey);
await this.assertUniqueNameWithinPrivateFolder(newName, folder.parentFolderId, driveKey, driveId);
const folderMetadataTxDataStub = await ArFSPrivateFolderTransactionData.from(
newName,
driveKey,
Expand Down
4 changes: 2 additions & 2 deletions src/arfs/arfs_builders/arfs_builders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,9 +193,9 @@ export abstract class ArFSFileOrFolderBuilder<
> extends ArFSMetadataEntityBuilder<T> {
parentFolderId?: FolderID;

protected async parseFromArweaveNode(node?: GQLNodeInterface): Promise<GQLTagInterface[]> {
protected async parseFromArweaveNode(node?: GQLNodeInterface, owner?: ArweaveAddress): Promise<GQLTagInterface[]> {
const unparsedTags: GQLTagInterface[] = [];
const tags = await super.parseFromArweaveNode(node);
const tags = await super.parseFromArweaveNode(node, owner);
tags.forEach((tag: GQLTagInterface) => {
const key = tag.name;
const { value } = tag;
Expand Down
14 changes: 7 additions & 7 deletions src/arfs/arfs_builders/arfs_drive_builders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
ArFSMetadataEntityBuilderParams,
ArFSPrivateMetadataEntityBuilderParams
} from './arfs_builders';
import { ArFSPrivateDriveKeyless } from '../../exports';
import { ArFSPrivateDriveKeyless, ArweaveAddress } from '../../exports';
import { GatewayAPI } from '../../utils/gateway_api';

export interface DriveMetaDataTransactionData extends EntityMetaDataTransactionData {
Expand Down Expand Up @@ -54,9 +54,9 @@ export class ArFSPublicDriveBuilder extends ArFSDriveBuilder<ArFSPublicDrive> {
];
}

protected async parseFromArweaveNode(node?: GQLNodeInterface): Promise<GQLTagInterface[]> {
protected async parseFromArweaveNode(node?: GQLNodeInterface, owner?: ArweaveAddress): Promise<GQLTagInterface[]> {
const unparsedTags: GQLTagInterface[] = [];
const tags = await super.parseFromArweaveNode(node);
const tags = await super.parseFromArweaveNode(node, owner);
tags.forEach((tag: GQLTagInterface) => {
const key = tag.name;
const { value } = tag;
Expand Down Expand Up @@ -155,9 +155,9 @@ export class ArFSPrivateDriveBuilder extends ArFSDriveBuilder<ArFSPrivateDrive>
return fileBuilder;
}

protected async parseFromArweaveNode(node?: GQLNodeInterface): Promise<GQLTagInterface[]> {
protected async parseFromArweaveNode(node?: GQLNodeInterface, owner?: ArweaveAddress): Promise<GQLTagInterface[]> {
const unparsedTags: GQLTagInterface[] = [];
const tags = await super.parseFromArweaveNode(node);
const tags = await super.parseFromArweaveNode(node, owner);
tags.forEach((tag: GQLTagInterface) => {
const key = tag.name;
const { value } = tag;
Expand Down Expand Up @@ -286,9 +286,9 @@ export class SafeArFSDriveBuilder extends ArFSDriveBuilder<ArFSDriveEntity> {
return driveBuilder;
}

protected async parseFromArweaveNode(node?: GQLNodeInterface): Promise<GQLTagInterface[]> {
protected async parseFromArweaveNode(node?: GQLNodeInterface, owner?: ArweaveAddress): Promise<GQLTagInterface[]> {
const unparsedTags: GQLTagInterface[] = [];
const tags = await super.parseFromArweaveNode(node);
const tags = await super.parseFromArweaveNode(node, owner);
tags.forEach((tag: GQLTagInterface) => {
const key = tag.name;
const { value } = tag;
Expand Down
8 changes: 4 additions & 4 deletions src/arfs/arfs_builders/arfs_file_builders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ export abstract class ArFSFileBuilder<T extends ArFSPublicFile | ArFSPrivateFile
];
}

protected async parseFromArweaveNode(node?: GQLNodeInterface): Promise<GQLTagInterface[]> {
const tags = await super.parseFromArweaveNode(node);
protected async parseFromArweaveNode(node?: GQLNodeInterface, owner?: ArweaveAddress): Promise<GQLTagInterface[]> {
const tags = await super.parseFromArweaveNode(node, owner);
return tags.filter((tag) => tag.name !== 'File-Id');
}

Expand Down Expand Up @@ -148,9 +148,9 @@ export class ArFSPrivateFileBuilder extends ArFSFileBuilder<ArFSPrivateFile> {
return fileBuilder;
}

protected async parseFromArweaveNode(node?: GQLNodeInterface): Promise<GQLTagInterface[]> {
protected async parseFromArweaveNode(node?: GQLNodeInterface, owner?: ArweaveAddress): Promise<GQLTagInterface[]> {
const unparsedTags: GQLTagInterface[] = [];
const tags = await super.parseFromArweaveNode(node);
const tags = await super.parseFromArweaveNode(node, owner);
tags.forEach((tag: GQLTagInterface) => {
const key = tag.name;
const { value } = tag;
Expand Down
8 changes: 4 additions & 4 deletions src/arfs/arfs_builders/arfs_folder_builders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ export abstract class ArFSFolderBuilder<T extends ArFSPublicFolder | ArFSPrivate
'folder',
T
> {
protected async parseFromArweaveNode(node?: GQLNodeInterface): Promise<GQLTagInterface[]> {
const tags = await super.parseFromArweaveNode(node);
protected async parseFromArweaveNode(node?: GQLNodeInterface, owner?: ArweaveAddress): Promise<GQLTagInterface[]> {
const tags = await super.parseFromArweaveNode(node, owner);
return tags.filter((tag) => tag.name !== 'Folder-Id');
}

Expand Down Expand Up @@ -135,9 +135,9 @@ export class ArFSPrivateFolderBuilder extends ArFSFolderBuilder<ArFSPrivateFolde
return folderBuilder;
}

protected async parseFromArweaveNode(node?: GQLNodeInterface): Promise<GQLTagInterface[]> {
protected async parseFromArweaveNode(node?: GQLNodeInterface, owner?: ArweaveAddress): Promise<GQLTagInterface[]> {
const unparsedTags: GQLTagInterface[] = [];
const tags = await super.parseFromArweaveNode(node);
const tags = await super.parseFromArweaveNode(node, owner);
tags.forEach((tag: GQLTagInterface) => {
const key = tag.name;
const { value } = tag;
Expand Down
4 changes: 2 additions & 2 deletions src/arfs/arfs_entities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ export class ArFSEntity {
readonly contentType: ContentType; // the mime type of the file uploaded. in the case of drives and folders, it is always a JSON file. Public drive/folders must use "application/json" and private drives use "application/octet-stream" since this data is encrypted.
readonly driveId: DriveID; // the unique drive identifier, created with uuidv4 https://www.npmjs.com/package/uuidv4 eg. 41800747-a852-4dc9-9078-6c20f85c0f3a
readonly entityType: EntityType; // the type of ArFS entity this is. this can only be set to "drive", "folder", "file"
readonly name: string; // user defined entity name, cannot be longer than 64 characters. This is stored in the JSON file that is uploaded along with the drive/folder/file metadata transaction
readonly txId: TransactionID; // the arweave transaction id for this entity. 43 numbers/letters eg. 1xRhN90Mu5mEgyyrmnzKgZP0y3aK8AwSucwlCOAwsaI
readonly name: string; // user defined entity name, cannot be longer than 64 characters. This is stored in the JSON file that is uploaded along with the drive/folder/file metadata transaction cspell:disable
readonly txId: TransactionID; // the arweave transaction id for this entity. 43 numbers/letters eg. 1xRhN90Mu5mEgyyrmnzKgZP0y3aK8AwSucwlCOAwsaI cspell:enable
readonly unixTime: UnixTime; // seconds since unix epoch, taken at the time of upload, 10 numbers eg. 1620068042

readonly boost?: FeeMultiple;
Expand Down
Loading

0 comments on commit 8907470

Please sign in to comment.