From 2788a3d97e9243f8ee2560dc037da136d6561b9b Mon Sep 17 00:00:00 2001 From: MellyGray Date: Wed, 17 Jan 2024 16:47:52 +0100 Subject: [PATCH] feat: add totalFilesCount to GetDatasetFiles use case --- src/files/domain/models/FilesSubset.ts | 6 ++ .../domain/repositories/IFilesRepository.ts | 4 +- src/files/domain/useCases/GetDatasetFiles.ts | 6 +- .../infra/repositories/FilesRepository.ts | 8 +-- .../transformers/fileTransformers.ts | 17 +++-- .../integration/files/FilesRepository.test.ts | 70 ++++++++++--------- test/testHelpers/files/filesHelper.ts | 16 +++++ test/unit/files/FilesRepository.test.ts | 16 +++-- 8 files changed, 92 insertions(+), 51 deletions(-) create mode 100644 src/files/domain/models/FilesSubset.ts diff --git a/src/files/domain/models/FilesSubset.ts b/src/files/domain/models/FilesSubset.ts new file mode 100644 index 00000000..2a7f565e --- /dev/null +++ b/src/files/domain/models/FilesSubset.ts @@ -0,0 +1,6 @@ +import { File } from "./File"; + +export interface FilesSubset { + files: File[]; + totalFilesCount: number; +} \ No newline at end of file diff --git a/src/files/domain/repositories/IFilesRepository.ts b/src/files/domain/repositories/IFilesRepository.ts index 622d780e..c89327f0 100644 --- a/src/files/domain/repositories/IFilesRepository.ts +++ b/src/files/domain/repositories/IFilesRepository.ts @@ -1,4 +1,4 @@ -import { File } from '../models/File'; +import { FilesSubset } from "../models/FilesSubset"; import { FileDataTable } from '../models/FileDataTable'; import { FileUserPermissions } from '../models/FileUserPermissions'; import { FileSearchCriteria, FileOrderCriteria } from '../models/FileCriteria'; @@ -14,7 +14,7 @@ export interface IFilesRepository { limit?: number, offset?: number, fileSearchCriteria?: FileSearchCriteria, - ): Promise; + ): Promise; getDatasetFileCounts( datasetId: number | string, diff --git a/src/files/domain/useCases/GetDatasetFiles.ts b/src/files/domain/useCases/GetDatasetFiles.ts index e6536aee..9099f9a3 100644 --- a/src/files/domain/useCases/GetDatasetFiles.ts +++ b/src/files/domain/useCases/GetDatasetFiles.ts @@ -1,10 +1,10 @@ import { UseCase } from '../../../core/domain/useCases/UseCase'; import { IFilesRepository } from '../repositories/IFilesRepository'; -import { File } from '../models/File'; +import { FilesSubset } from "../models/FilesSubset"; import { FileSearchCriteria, FileOrderCriteria } from '../models/FileCriteria'; import { DatasetNotNumberedVersion } from '../../../datasets'; -export class GetDatasetFiles implements UseCase { +export class GetDatasetFiles implements UseCase { private filesRepository: IFilesRepository; constructor(filesRepository: IFilesRepository) { @@ -19,7 +19,7 @@ export class GetDatasetFiles implements UseCase { offset?: number, fileSearchCriteria?: FileSearchCriteria, fileOrderCriteria: FileOrderCriteria = FileOrderCriteria.NAME_AZ, - ): Promise { + ): Promise { return await this.filesRepository.getDatasetFiles( datasetId, datasetVersionId, diff --git a/src/files/infra/repositories/FilesRepository.ts b/src/files/infra/repositories/FilesRepository.ts index b0862885..1e1b9a1d 100644 --- a/src/files/infra/repositories/FilesRepository.ts +++ b/src/files/infra/repositories/FilesRepository.ts @@ -1,7 +1,7 @@ import { ApiRepository } from '../../../core/infra/repositories/ApiRepository'; import { IFilesRepository } from '../../domain/repositories/IFilesRepository'; -import { File } from '../../domain/models/File'; -import { transformFilesResponseToFiles } from './transformers/fileTransformers'; +import {FilesSubset} from "../../domain/models/FilesSubset"; +import { transformFilesResponseToFilesSubset } from './transformers/fileTransformers'; import { FileDataTable } from '../../domain/models/FileDataTable'; import { transformDataTablesResponseToDataTables } from './transformers/fileDataTableTransformers'; import { FileUserPermissions } from '../../domain/models/FileUserPermissions'; @@ -46,7 +46,7 @@ export class FilesRepository extends ApiRepository implements IFilesRepository { limit?: number, offset?: number, fileSearchCriteria?: FileSearchCriteria, - ): Promise { + ): Promise { const queryParams: GetFilesQueryParams = { includeDeaccessioned: includeDeaccessioned, orderCriteria: fileOrderCriteria.toString(), @@ -65,7 +65,7 @@ export class FilesRepository extends ApiRepository implements IFilesRepository { true, queryParams, ) - .then((response) => transformFilesResponseToFiles(response)) + .then((response) => transformFilesResponseToFilesSubset(response)) .catch((error) => { throw error; }); diff --git a/src/files/infra/repositories/transformers/fileTransformers.ts b/src/files/infra/repositories/transformers/fileTransformers.ts index 7cefd232..9ef85d9f 100644 --- a/src/files/infra/repositories/transformers/fileTransformers.ts +++ b/src/files/infra/repositories/transformers/fileTransformers.ts @@ -1,14 +1,21 @@ import { File, FileEmbargo, FileChecksum } from '../../../domain/models/File'; import { AxiosResponse } from 'axios'; +import { FilesSubset } from "../../../domain/models/FilesSubset"; -export const transformFilesResponseToFiles = (response: AxiosResponse): File[] => { - const files: File[] = []; +export const transformFilesResponseToFilesSubset = ( + response: AxiosResponse, +): FilesSubset => { const filesPayload = response.data.data; + const files: File[] = []; // eslint-disable-next-line @typescript-eslint/no-explicit-any - filesPayload.forEach(function (filePayload: any) { - files.push(transformFilePayloadToFile(filePayload)); + filesPayload.forEach(function (filesPayload: any) { + files.push(transformFilePayloadToFile(filesPayload)); }); - return files; + + return { + files: files, + totalFilesCount: response.data.totalCount, + }; }; // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/test/integration/files/FilesRepository.test.ts b/test/integration/files/FilesRepository.test.ts index 91d1ce23..a7eab387 100644 --- a/test/integration/files/FilesRepository.test.ts +++ b/test/integration/files/FilesRepository.test.ts @@ -72,11 +72,12 @@ describe('FilesRepository', () => { false, FileOrderCriteria.NAME_AZ, ); - assert.match(actual.length, 4); - assert.match(actual[0].name, testTextFile1Name); - assert.match(actual[1].name, testTextFile2Name); - assert.match(actual[2].name, testTextFile3Name); - assert.match(actual[3].name, testTabFile4Name); + assert.match(actual.files.length, 4); + assert.match(actual.files[0].name, testTextFile1Name); + assert.match(actual.files[1].name, testTextFile2Name); + assert.match(actual.files[2].name, testTextFile3Name); + assert.match(actual.files[3].name, testTabFile4Name); + assert.match(actual.totalFilesCount, 4); }); test('should return correct files filtering by dataset id, version id, and paginating', async () => { @@ -89,12 +90,13 @@ describe('FilesRepository', () => { 3, undefined, ); - assert.match(actual.length, 1); - assert.match(actual[0].name, testTabFile4Name); + assert.match(actual.files.length, 1); + assert.match(actual.files[0].name, testTabFile4Name); + assert.match(actual.totalFilesCount, 4); }); test('should return correct files filtering by dataset id, version id, and applying newest file criteria', async () => { - let actual = await sut.getDatasetFiles( + const actual = await sut.getDatasetFiles( TestConstants.TEST_CREATED_DATASET_1_ID, latestDatasetVersionId, false, @@ -103,10 +105,11 @@ describe('FilesRepository', () => { undefined, testFileCriteria, ); - assert.match(actual.length, 3); - assert.match(actual[0].name, testTextFile3Name); - assert.match(actual[1].name, testTextFile2Name); - assert.match(actual[2].name, testTextFile1Name); + assert.match(actual.files.length, 3); + assert.match(actual.files[0].name, testTextFile3Name); + assert.match(actual.files[1].name, testTextFile2Name); + assert.match(actual.files[2].name, testTextFile1Name); + assert.match(actual.totalFilesCount, 4); }); test('should return error when dataset does not exist', async () => { @@ -137,11 +140,12 @@ describe('FilesRepository', () => { false, FileOrderCriteria.NAME_AZ, ); - assert.match(actual.length, 4); - assert.match(actual[0].name, testTextFile1Name); - assert.match(actual[1].name, testTextFile2Name); - assert.match(actual[2].name, testTextFile3Name); - assert.match(actual[3].name, testTabFile4Name); + assert.match(actual.files.length, 4); + assert.match(actual.files[0].name, testTextFile1Name); + assert.match(actual.files[1].name, testTextFile2Name); + assert.match(actual.files[2].name, testTextFile3Name); + assert.match(actual.files[3].name, testTabFile4Name); + assert.match(actual.totalFilesCount, 4); }); test('should return correct files filtering by persistent id, version id, and paginating', async () => { @@ -159,8 +163,9 @@ describe('FilesRepository', () => { 3, undefined, ); - assert.match(actual.length, 1); - assert.match(actual[0].name, testTabFile4Name); + assert.match(actual.files.length, 1); + assert.match(actual.files[0].name, testTabFile4Name); + assert.match(actual.totalFilesCount, 4); }); test('should return correct files filtering by persistent id, version id, and applying newest file criteria', async () => { @@ -169,7 +174,7 @@ describe('FilesRepository', () => { latestDatasetVersionId, false, ); - let actual = await sut.getDatasetFiles( + const actual = await sut.getDatasetFiles( testDataset.persistentId, latestDatasetVersionId, false, @@ -178,10 +183,11 @@ describe('FilesRepository', () => { undefined, testFileCriteria, ); - assert.match(actual.length, 3); - assert.match(actual[0].name, testTextFile3Name); - assert.match(actual[1].name, testTextFile2Name); - assert.match(actual[2].name, testTextFile1Name); + assert.match(actual.files.length, 3); + assert.match(actual.files[0].name, testTextFile3Name); + assert.match(actual.files[1].name, testTextFile2Name); + assert.match(actual.files[2].name, testTextFile1Name); + assert.match(actual.totalFilesCount, 4); }); test('should return error when dataset does not exist', async () => { @@ -332,13 +338,13 @@ describe('FilesRepository', () => { describe('getFileDownloadCount', () => { test('should return count filtering by file id and version id', async () => { - const currentTestFiles = await sut.getDatasetFiles( + const currentTestFilesSubset = await sut.getDatasetFiles( TestConstants.TEST_CREATED_DATASET_1_ID, latestDatasetVersionId, false, FileOrderCriteria.NAME_AZ, ); - const testFile = currentTestFiles[0]; + const testFile = currentTestFilesSubset.files[0]; const actual = await sut.getFileDownloadCount(testFile.id); assert.match(actual, 0); }); @@ -357,13 +363,13 @@ describe('FilesRepository', () => { describe('getFileUserPermissions', () => { test('should return user permissions filtering by file id and version id', async () => { - const currentTestFiles = await sut.getDatasetFiles( + const currentTestFilesSubset = await sut.getDatasetFiles( TestConstants.TEST_CREATED_DATASET_1_ID, latestDatasetVersionId, false, FileOrderCriteria.NAME_AZ, ); - const testFile = currentTestFiles[0]; + const testFile = currentTestFilesSubset.files[0]; const actual = await sut.getFileUserPermissions(testFile.id); assert.match(actual.canDownloadFile, true); assert.match(actual.canManageFilePermissions, true); @@ -384,25 +390,25 @@ describe('FilesRepository', () => { describe('getFileDataTables', () => { test('should return data tables filtering by tabular file id and version id', async () => { - const currentTestFiles = await sut.getDatasetFiles( + const currentTestFilesSubset = await sut.getDatasetFiles( TestConstants.TEST_CREATED_DATASET_1_ID, latestDatasetVersionId, false, FileOrderCriteria.NAME_AZ, ); - const testFile = currentTestFiles[3]; + const testFile = currentTestFilesSubset.files[3]; const actual = await sut.getFileDataTables(testFile.id); assert.match(actual[0].varQuantity, 3); }); test('should return error when file is not tabular and version id', async () => { - const currentTestFiles = await sut.getDatasetFiles( + const currentTestFilesSubset = await sut.getDatasetFiles( TestConstants.TEST_CREATED_DATASET_1_ID, latestDatasetVersionId, false, FileOrderCriteria.NAME_AZ, ); - const testFile = currentTestFiles[0]; + const testFile = currentTestFilesSubset.files[0]; let error: ReadError = undefined; diff --git a/test/testHelpers/files/filesHelper.ts b/test/testHelpers/files/filesHelper.ts index 893f06ab..15f6d7e0 100644 --- a/test/testHelpers/files/filesHelper.ts +++ b/test/testHelpers/files/filesHelper.ts @@ -2,6 +2,7 @@ import { File } from '../../../src/files/domain/models/File'; import axios, { AxiosResponse } from 'axios'; import { TestConstants } from '../TestConstants'; import { readFile } from 'fs/promises'; +import {FilesSubset} from "../../../src/files/domain/models/FilesSubset"; interface FileMetadata { categories?: string[] @@ -38,6 +39,17 @@ export const createFileModel = (): File => { }; }; +export const createManyFilesModel = (amount: number): File[] => { + return Array.from({ length: amount }, () => createFileModel()); +} + +export const createFilesSubsetModel = (amount: number): FilesSubset => { + return { + files: createManyFilesModel(amount), + totalFilesCount: amount, + }; +} + export const createFilePayload = (): any => { return { label: 'test', @@ -74,6 +86,10 @@ export const createFilePayload = (): any => { }; }; +export const createManyFilesPayload = (amount: number): any[] => { + return Array.from({ length: amount }, () => createFilePayload()); +} + export const uploadFileViaApi = async (datasetId: number, fileName: string, fileMetadata?: FileMetadata): Promise => { const formData = new FormData(); const file = await readFile(`${__dirname}/${fileName}`); diff --git a/test/unit/files/FilesRepository.test.ts b/test/unit/files/FilesRepository.test.ts index b885a3fe..9cc80632 100644 --- a/test/unit/files/FilesRepository.test.ts +++ b/test/unit/files/FilesRepository.test.ts @@ -5,7 +5,11 @@ import { expect } from 'chai'; import { ReadError } from '../../../src/core/domain/repositories/ReadError'; import { ApiConfig, DataverseApiAuthMechanism } from '../../../src/core/infra/repositories/ApiConfig'; import { TestConstants } from '../../testHelpers/TestConstants'; -import { createFilePayload, createFileModel } from '../../testHelpers/files/filesHelper'; +import { + createFileModel, + createManyFilesPayload, + createFilesSubsetModel +} from '../../testHelpers/files/filesHelper'; import { createFileDataTablePayload, createFileDataTableModel } from '../../testHelpers/files/fileDataTablesHelper'; import { createFileUserPermissionsModel } from '../../testHelpers/files/fileUserPermissionsHelper'; import { FileSearchCriteria, FileAccessStatus, FileOrderCriteria } from '../../../src/files/domain/models/FileCriteria'; @@ -40,10 +44,12 @@ describe('FilesRepository', () => { }); describe('getDatasetFiles', () => { + const testTotalCount = 4; const testFilesSuccessfulResponse = { data: { status: 'OK', - data: [createFilePayload()], + data: createManyFilesPayload(testTotalCount), + totalCount: testTotalCount, }, }; @@ -77,7 +83,7 @@ describe('FilesRepository', () => { headers: TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_API_KEY.headers, }; - const expectedFiles = [testFile]; + const expectedFiles = createFilesSubsetModel(testTotalCount); describe('by numeric id and version id', () => { const expectedApiEndpoint = `${TestConstants.TEST_API_URL}/datasets/${testDatasetId}/versions/${testDatasetVersionId}/files`; @@ -124,7 +130,7 @@ describe('FilesRepository', () => { ); assert.calledWithExactly(axiosGetStub, expectedApiEndpoint, expectedRequestConfigApiKeyWithOptional); - assert.match(actual, [testFile]); + assert.match(actual, expectedFiles); }); test('should return error result on error response', async () => { @@ -185,7 +191,7 @@ describe('FilesRepository', () => { ); assert.calledWithExactly(axiosGetStub, expectedApiEndpoint, expectedRequestConfigApiKeyWithOptional); - assert.match(actual, [testFile]); + assert.match(actual, expectedFiles); }); test('should return error result on error response', async () => {