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

115 - Create use case to get a file #119

Merged
merged 11 commits into from
Feb 5, 2024
2 changes: 1 addition & 1 deletion src/datasets/domain/useCases/GetDataset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export class GetDataset implements UseCase<Dataset> {
async execute(
datasetId: number | string,
datasetVersionId: string | DatasetNotNumberedVersion = DatasetNotNumberedVersion.LATEST,
includeDeaccessioned: boolean = false,
includeDeaccessioned = false,
): Promise<Dataset> {
return await this.datasetsRepository.getDataset(datasetId, datasetVersionId, includeDeaccessioned);
}
Expand Down
2 changes: 1 addition & 1 deletion src/datasets/domain/useCases/GetDatasetCitation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export class GetDatasetCitation implements UseCase<string> {
async execute(
datasetId: number,
datasetVersionId: string | DatasetNotNumberedVersion = DatasetNotNumberedVersion.LATEST,
includeDeaccessioned: boolean = false,
includeDeaccessioned = false,
): Promise<string> {
return await this.datasetsRepository.getDatasetCitation(datasetId, datasetVersionId, includeDeaccessioned);
}
Expand Down
6 changes: 3 additions & 3 deletions src/files/domain/models/FilesSubset.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { File } from "./File";
import { File } from './File';

export interface FilesSubset {
files: File[];
totalFilesCount: number;
files: File[];
totalFilesCount: number;
}
5 changes: 4 additions & 1 deletion src/files/domain/repositories/IFilesRepository.ts
ekraffmiller marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { FilesSubset } from "../models/FilesSubset";
import { FilesSubset } from '../models/FilesSubset';
import { FileDataTable } from '../models/FileDataTable';
import { FileUserPermissions } from '../models/FileUserPermissions';
import { FileSearchCriteria, FileOrderCriteria } from '../models/FileCriteria';
import { FileCounts } from '../models/FileCounts';
import { FileDownloadSizeMode } from '../models/FileDownloadSizeMode';
import { File } from '../models/File';

export interface IFilesRepository {
getDatasetFiles(
Expand Down Expand Up @@ -36,4 +37,6 @@ export interface IFilesRepository {
getFileUserPermissions(fileId: number | string): Promise<FileUserPermissions>;

getFileDataTables(fileId: number | string): Promise<FileDataTable[]>;

getFile(fileId: number | string, datasetVersionId: string): Promise<File>;
}
2 changes: 1 addition & 1 deletion src/files/domain/useCases/GetDatasetFileCounts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export class GetDatasetFileCounts implements UseCase<FileCounts> {
async execute(
datasetId: number | string,
datasetVersionId: string | DatasetNotNumberedVersion = DatasetNotNumberedVersion.LATEST,
includeDeaccessioned: boolean = false,
includeDeaccessioned = false,
fileSearchCriteria?: FileSearchCriteria,
): Promise<FileCounts> {
return await this.filesRepository.getDatasetFileCounts(
Expand Down
4 changes: 2 additions & 2 deletions src/files/domain/useCases/GetDatasetFiles.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { UseCase } from '../../../core/domain/useCases/UseCase';
import { IFilesRepository } from '../repositories/IFilesRepository';
import { FilesSubset } from "../models/FilesSubset";
import { FilesSubset } from '../models/FilesSubset';
import { FileSearchCriteria, FileOrderCriteria } from '../models/FileCriteria';
import { DatasetNotNumberedVersion } from '../../../datasets';

Expand All @@ -14,7 +14,7 @@ export class GetDatasetFiles implements UseCase<FilesSubset> {
async execute(
datasetId: number | string,
datasetVersionId: string | DatasetNotNumberedVersion = DatasetNotNumberedVersion.LATEST,
includeDeaccessioned: boolean = false,
includeDeaccessioned = false,
limit?: number,
offset?: number,
fileSearchCriteria?: FileSearchCriteria,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export class GetDatasetFilesTotalDownloadSize implements UseCase<number> {
datasetVersionId: string | DatasetNotNumberedVersion = DatasetNotNumberedVersion.LATEST,
fileDownloadSizeMode: FileDownloadSizeMode = FileDownloadSizeMode.ALL,
fileSearchCriteria?: FileSearchCriteria,
includeDeaccessioned: boolean = false,
includeDeaccessioned = false,
): Promise<number> {
return await this.filesRepository.getDatasetFilesTotalDownloadSize(
datasetId,
Expand Down
14 changes: 14 additions & 0 deletions src/files/domain/useCases/GetFile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { IFilesRepository } from '../repositories/IFilesRepository';
import { File } from '../models/File';
import { DatasetNotNumberedVersion } from '../../../datasets';

export class GetFile {
constructor(private readonly filesRepository: IFilesRepository) {}

async execute(
fileId: number | string,
datasetVersionId: string | DatasetNotNumberedVersion = DatasetNotNumberedVersion.LATEST,
): Promise<File> {
return await this.filesRepository.getFile(fileId, datasetVersionId);
}
}
3 changes: 3 additions & 0 deletions src/files/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { GetFileDownloadCount } from './domain/useCases/GetFileDownloadCount';
import { GetFileUserPermissions } from './domain/useCases/GetFileUserPermissions';
import { GetFileDataTables } from './domain/useCases/GetFileDataTables';
import { GetDatasetFilesTotalDownloadSize } from './domain/useCases/GetDatasetFilesTotalDownloadSize';
import { GetFile } from './domain/useCases/GetFile';

const filesRepository = new FilesRepository();

Expand All @@ -14,6 +15,7 @@ const getFileDownloadCount = new GetFileDownloadCount(filesRepository);
const getFileUserPermissions = new GetFileUserPermissions(filesRepository);
const getFileDataTables = new GetFileDataTables(filesRepository);
const getDatasetFilesTotalDownloadSize = new GetDatasetFilesTotalDownloadSize(filesRepository);
const getFile = new GetFile(filesRepository);

export {
getDatasetFiles,
Expand All @@ -22,6 +24,7 @@ export {
getFileDataTables,
getDatasetFileCounts,
getDatasetFilesTotalDownloadSize,
getFile,
};

export { File, FileEmbargo, FileChecksum } from './domain/models/File';
Expand Down
27 changes: 25 additions & 2 deletions src/files/infra/repositories/FilesRepository.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { ApiRepository } from '../../../core/infra/repositories/ApiRepository';
import { IFilesRepository } from '../../domain/repositories/IFilesRepository';
import {FilesSubset} from "../../domain/models/FilesSubset";
import { transformFilesResponseToFilesSubset } from './transformers/fileTransformers';
import { File } from '../../domain/models/File';
import { transformFileResponseToFile, transformFilesResponseToFilesSubset } from './transformers/fileTransformers';
import { FilesSubset } from '../../domain/models/FilesSubset';
import { FileDataTable } from '../../domain/models/FileDataTable';
import { transformDataTablesResponseToDataTables } from './transformers/fileDataTableTransformers';
import { FileUserPermissions } from '../../domain/models/FileUserPermissions';
Expand All @@ -10,6 +11,7 @@ import { FileSearchCriteria, FileOrderCriteria } from '../../domain/models/FileC
import { FileCounts } from '../../domain/models/FileCounts';
import { transformFileCountsResponseToFileCounts } from './transformers/fileCountsTransformers';
import { FileDownloadSizeMode } from '../../domain/models/FileDownloadSizeMode';
import { DatasetNotNumberedVersion } from '../../../datasets';

export interface GetFilesQueryParams {
includeDeaccessioned: boolean;
Expand Down Expand Up @@ -143,6 +145,27 @@ export class FilesRepository extends ApiRepository implements IFilesRepository {
});
}

public async getFile(fileId: number | string, datasetVersionId: string): Promise<File> {
return this.doGet(this.getFileEndpoint(fileId, datasetVersionId), true)
.then((response) => transformFileResponseToFile(response))
.catch((error) => {
throw error;
});
}

private getFileEndpoint(fileId: number | string, datasetVersionId: string): string {
if (datasetVersionId === DatasetNotNumberedVersion.DRAFT) {
return this.buildApiEndpoint(this.filesResourceName, 'draft', fileId);
}
if (datasetVersionId === DatasetNotNumberedVersion.LATEST) {
return this.buildApiEndpoint(this.filesResourceName, '', fileId);
}
// TODO: Implement once it is supported by the API https://github.com/IQSS/dataverse/issues/10280
throw new Error(
`Requesting a file by its dataset version is not yet supported. Requested version: ${datasetVersionId}. Please try using the :latest or :draft version instead.`,
);
}

private applyFileSearchCriteriaToQueryParams(
queryParams: GetFilesQueryParams | GetFilesTotalDownloadSizeQueryParams,
fileSearchCriteria: FileSearchCriteria,
Expand Down
11 changes: 7 additions & 4 deletions src/files/infra/repositories/transformers/fileTransformers.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import { File, FileEmbargo, FileChecksum } from '../../../domain/models/File';
import { AxiosResponse } from 'axios';
import { FilesSubset } from "../../../domain/models/FilesSubset";
import { FilesSubset } from '../../../domain/models/FilesSubset';

export const transformFilesResponseToFilesSubset = (
response: AxiosResponse,
): FilesSubset => {
export const transformFilesResponseToFilesSubset = (response: AxiosResponse): FilesSubset => {
const filesPayload = response.data.data;
const files: File[] = [];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
Expand All @@ -18,6 +16,11 @@ export const transformFilesResponseToFilesSubset = (
};
};

export const transformFileResponseToFile = (response: AxiosResponse): File => {
const filePayload = response.data.data;
return transformFilePayloadToFile(filePayload);
};

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const transformFilePayloadToFile = (filePayload: any): File => {
return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { MetadataBlock, MetadataFieldInfo } from '../../../domain/models/Metadat

export const transformMetadataBlockResponseToMetadataBlock = (response: AxiosResponse): MetadataBlock => {
const metadataBlockPayload = response.data.data;
let metadataFields: Record<string, MetadataFieldInfo> = {};
const metadataFields: Record<string, MetadataFieldInfo> = {};
const metadataBlockFieldsPayload = metadataBlockPayload.fields;
Object.keys(metadataBlockFieldsPayload).map((metadataFieldKey) => {
const metadataFieldInfoPayload = metadataBlockFieldsPayload[metadataFieldKey];
Expand All @@ -17,11 +17,8 @@ export const transformMetadataBlockResponseToMetadataBlock = (response: AxiosRes
};
};

const transformPayloadMetadataFieldInfo = (
metadataFieldInfoPayload: any,
isChild: boolean = false,
): MetadataFieldInfo => {
let metadataFieldInfo: MetadataFieldInfo = {
const transformPayloadMetadataFieldInfo = (metadataFieldInfoPayload: any, isChild = false): MetadataFieldInfo => {
const metadataFieldInfo: MetadataFieldInfo = {
name: metadataFieldInfoPayload.name,
displayName: metadataFieldInfoPayload.displayName,
title: metadataFieldInfoPayload.title,
Expand All @@ -34,7 +31,7 @@ const transformPayloadMetadataFieldInfo = (
};
if (!isChild && metadataFieldInfoPayload.hasOwnProperty('childFields')) {
const childMetadataFieldsPayload = metadataFieldInfoPayload.childFields;
let childMetadataFields: Record<string, MetadataFieldInfo> = {};
const childMetadataFields: Record<string, MetadataFieldInfo> = {};
Object.keys(childMetadataFieldsPayload).map((metadataFieldKey) => {
childMetadataFields[metadataFieldKey] = transformPayloadMetadataFieldInfo(
childMetadataFieldsPayload[metadataFieldKey],
Expand Down
92 changes: 90 additions & 2 deletions test/integration/files/FilesRepository.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { ApiConfig, DataverseApiAuthMechanism } from '../../../src/core/infra/re
import { assert } from 'sinon';
import { expect } from 'chai';
import { TestConstants } from '../../testHelpers/TestConstants';
import { uploadFileViaApi } from '../../testHelpers/files/filesHelper';
import {registerFileViaApi, uploadFileViaApi} from '../../testHelpers/files/filesHelper';
import { DatasetsRepository } from '../../../src/datasets/infra/repositories/DatasetsRepository';
import { ReadError } from '../../../src/core/domain/repositories/ReadError';
import { FileSearchCriteria, FileAccessStatus, FileOrderCriteria } from '../../../src/files/domain/models/FileCriteria';
Expand All @@ -27,10 +27,12 @@ describe('FilesRepository', () => {

const datasetRepository = new DatasetsRepository();

let testFileId: number;
let testFilePersistentId: string;
beforeAll(async () => {
ApiConfig.init(TestConstants.TEST_API_URL, DataverseApiAuthMechanism.API_KEY, process.env.TEST_API_KEY);
// Uploading test file 1 with some categories
await uploadFileViaApi(TestConstants.TEST_CREATED_DATASET_1_ID, testTextFile1Name, { categories: [testCategoryName] })
const uploadFileResponse = await uploadFileViaApi(TestConstants.TEST_CREATED_DATASET_1_ID, testTextFile1Name, { categories: [testCategoryName] })
.then()
.catch((e) => {
console.log(e);
Expand All @@ -57,6 +59,16 @@ describe('FilesRepository', () => {
console.log(e);
fail(`Tests beforeAll(): Error while uploading file ${testTabFile4Name}`);
});
// Registering test file 1
await registerFileViaApi(uploadFileResponse.data.data.files[0].dataFile.id);
const filesSubset = await sut.getDatasetFiles(
TestConstants.TEST_CREATED_DATASET_1_ID,
latestDatasetVersionId,
false,
FileOrderCriteria.NAME_AZ,
)
testFileId = filesSubset.files[0].id;
testFilePersistentId = filesSubset.files[0].persistentId;
});

describe('getDatasetFiles', () => {
Expand Down Expand Up @@ -431,4 +443,80 @@ describe('FilesRepository', () => {
);
});
});

describe('getFile', () => {
describe('by numeric id', () => {
test('should return file when providing a valid id', async () => {
const actual = await sut.getFile(testFileId, DatasetNotNumberedVersion.LATEST);

assert.match(actual.name, testTextFile1Name);
});

test('should return file draft when providing a valid id and version is draft', async () => {
const actual = await sut.getFile(testFileId, DatasetNotNumberedVersion.DRAFT);

assert.match(actual.name, testTextFile1Name);
});

test('should return Not Implemented Yet error when when providing a valid id and version is different than latest and draft', async () => {
// This tests can be removed once the API supports getting a file by version
let error: ReadError = undefined;

await sut.getFile(testFileId, '1.0').catch((e) => (error = e));

assert.match(
error.message,
`Requesting a file by its dataset version is not yet supported. Requested version: 1.0. Please try using the :latest or :draft version instead.`,
);
});

test('should return error when file does not exist', async () => {
let error: ReadError = undefined;

await sut.getFile(nonExistentFiledId, DatasetNotNumberedVersion.LATEST).catch((e) => (error = e));

assert.match(
error.message,
`There was an error when reading the resource. Reason was: [400] Error attempting get the requested data file.`,
);
});
});
describe('by persistent id', () => {
test('should return file when providing a valid persistent id', async () => {
const actual = await sut.getFile(testFilePersistentId, DatasetNotNumberedVersion.LATEST);

assert.match(actual.name, testTextFile1Name);
});

test('should return file draft when providing a valid persistent id and version is draft', async () => {
const actual = await sut.getFile(testFilePersistentId, DatasetNotNumberedVersion.DRAFT);

assert.match(actual.name, testTextFile1Name);
});

test('should return Not Implemented Yet error when when providing a valid persistent id and version is different than latest and draft', async () => {
// This tests can be removed once the API supports getting a file by version
let error: ReadError = undefined;

await sut.getFile(testFilePersistentId, '1.0').catch((e) => (error = e));

assert.match(
error.message,
`Requesting a file by its dataset version is not yet supported. Requested version: 1.0. Please try using the :latest or :draft version instead.`,
);
});

test('should return error when file does not exist', async () => {
let error: ReadError = undefined;

const nonExistentFiledPersistentId = 'nonExistentFiledPersistentId';
await sut.getFile(nonExistentFiledPersistentId, DatasetNotNumberedVersion.LATEST).catch((e) => (error = e));

assert.match(
error.message,
`There was an error when reading the resource. Reason was: [400] Error attempting get the requested data file.`,
);
});
});
});
});
20 changes: 20 additions & 0 deletions test/testHelpers/files/filesHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,3 +107,23 @@ export const uploadFileViaApi = async (datasetId: number, fileName: string, file
},
});
};

export const registerFileViaApi = async (fileId: number): Promise<AxiosResponse> => {
return await enableFilePIDs().then(() => axios.get(`${TestConstants.TEST_API_URL}/admin/${fileId}/registerDataFile`, {
headers: {
'X-Dataverse-Key': process.env.TEST_API_KEY,
}
}));
}

const enableFilePIDs = async (): Promise<AxiosResponse> => {
return await axios.put(`${TestConstants.TEST_API_URL}/admin/settings/:AllowEnablingFilePIDsPerCollection`, "true", {
headers: {
'X-Dataverse-Key': process.env.TEST_API_KEY,
},
}).then(() => axios.put(`${TestConstants.TEST_API_URL}/dataverses/root/attribute/filePIDsEnabled?value=true`, {}, {
headers: {
'X-Dataverse-Key': process.env.TEST_API_KEY,
},
}));
};
Loading
Loading