diff --git a/jest.config.js b/jest.config.js index 476ad23f..9823ab2f 100644 --- a/jest.config.js +++ b/jest.config.js @@ -7,4 +7,5 @@ module.exports = { moduleFileExtensions: ['ts', 'js', 'json', 'node'], coveragePathIgnorePatterns: ['node_modules', 'testHelpers'], globalSetup: '/test/integration/environment/setup.js', + testTimeout: 15000, }; diff --git a/jest.config.unit.js b/jest.config.unit.js index 7144b835..845173e7 100644 --- a/jest.config.unit.js +++ b/jest.config.unit.js @@ -1,5 +1,6 @@ var config = require('./jest.config'); config.modulePathIgnorePatterns = ['/test/integration']; delete config.globalSetup; +delete config.testTimeout; console.log('RUNNING UNIT TESTS'); module.exports = config; diff --git a/src/core/infra/repositories/ApiRepository.ts b/src/core/infra/repositories/ApiRepository.ts index 56d1d1bf..6730e712 100644 --- a/src/core/infra/repositories/ApiRepository.ts +++ b/src/core/infra/repositories/ApiRepository.ts @@ -4,9 +4,11 @@ import { ReadError } from '../../domain/repositories/ReadError'; import { WriteError } from '../../domain/repositories/WriteError'; export abstract class ApiRepository { - public async doGet(apiEndpoint: string, authRequired = false): Promise { + DATASET_VERSION_LATEST = ':latest'; + + public async doGet(apiEndpoint: string, authRequired = false, queryParams: object = {}): Promise { return await axios - .get(this.buildRequestUrl(apiEndpoint), this.buildRequestConfig(authRequired)) + .get(this.buildRequestUrl(apiEndpoint), this.buildRequestConfig(authRequired, queryParams)) .then((response) => response) .catch((error) => { throw new ReadError( @@ -15,9 +17,9 @@ export abstract class ApiRepository { }); } - public async doPost(apiEndpoint: string, data: string | object): Promise { + public async doPost(apiEndpoint: string, data: string | object, queryParams: object = {}): Promise { return await axios - .post(this.buildRequestUrl(apiEndpoint), JSON.stringify(data), this.buildRequestConfig(true)) + .post(this.buildRequestUrl(apiEndpoint), JSON.stringify(data), this.buildRequestConfig(true, queryParams)) .then((response) => response) .catch((error) => { throw new WriteError( @@ -26,8 +28,9 @@ export abstract class ApiRepository { }); } - private buildRequestConfig(authRequired: boolean): AxiosRequestConfig { + private buildRequestConfig(authRequired: boolean, queryParams: object): AxiosRequestConfig { const requestConfig: AxiosRequestConfig = { + params: queryParams, headers: { 'Content-Type': 'application/json' }, }; if (!authRequired) { diff --git a/src/datasets/domain/repositories/IDatasetsRepository.ts b/src/datasets/domain/repositories/IDatasetsRepository.ts index 59aae717..dae4c042 100644 --- a/src/datasets/domain/repositories/IDatasetsRepository.ts +++ b/src/datasets/domain/repositories/IDatasetsRepository.ts @@ -2,8 +2,7 @@ import { Dataset } from '../models/Dataset'; export interface IDatasetsRepository { getDatasetSummaryFieldNames(): Promise; - getDatasetById(datasetId: number, datasetVersionId?: string): Promise; - getDatasetByPersistentId(datasetPersistentId: string, datasetVersionId?: string): Promise; + getDataset(datasetId: number | string, datasetVersionId?: string): Promise; getPrivateUrlDataset(token: string): Promise; getDatasetCitation(datasetId: number, datasetVersionId?: string): Promise; getPrivateUrlDatasetCitation(token: string): Promise; diff --git a/src/datasets/domain/useCases/GetDatasetById.ts b/src/datasets/domain/useCases/GetDataset.ts similarity index 61% rename from src/datasets/domain/useCases/GetDatasetById.ts rename to src/datasets/domain/useCases/GetDataset.ts index 15e14748..959c0929 100644 --- a/src/datasets/domain/useCases/GetDatasetById.ts +++ b/src/datasets/domain/useCases/GetDataset.ts @@ -2,14 +2,14 @@ import { UseCase } from '../../../core/domain/useCases/UseCase'; import { IDatasetsRepository } from '../repositories/IDatasetsRepository'; import { Dataset } from '../models/Dataset'; -export class GetDatasetById implements UseCase { +export class GetDataset implements UseCase { private datasetsRepository: IDatasetsRepository; constructor(datasetsRepository: IDatasetsRepository) { this.datasetsRepository = datasetsRepository; } - async execute(datasetId: number, datasetVersionId?: string): Promise { - return await this.datasetsRepository.getDatasetById(datasetId, datasetVersionId); + async execute(datasetId: number | string, datasetVersionId?: string): Promise { + return await this.datasetsRepository.getDataset(datasetId, datasetVersionId); } } diff --git a/src/datasets/domain/useCases/GetDatasetByPersistentId.ts b/src/datasets/domain/useCases/GetDatasetByPersistentId.ts deleted file mode 100644 index 66a39a3b..00000000 --- a/src/datasets/domain/useCases/GetDatasetByPersistentId.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { UseCase } from '../../../core/domain/useCases/UseCase'; -import { IDatasetsRepository } from '../repositories/IDatasetsRepository'; -import { Dataset } from '../models/Dataset'; - -export class GetDatasetByPersistentId implements UseCase { - private datasetsRepository: IDatasetsRepository; - - constructor(datasetsRepository: IDatasetsRepository) { - this.datasetsRepository = datasetsRepository; - } - - async execute(datasetPersistentId: string, datasetVersionId?: string): Promise { - return await this.datasetsRepository.getDatasetByPersistentId(datasetPersistentId, datasetVersionId); - } -} diff --git a/src/datasets/index.ts b/src/datasets/index.ts index 90394f78..dfd59277 100644 --- a/src/datasets/index.ts +++ b/src/datasets/index.ts @@ -1,7 +1,6 @@ import { DatasetsRepository } from './infra/repositories/DatasetsRepository'; import { GetDatasetSummaryFieldNames } from './domain/useCases/GetDatasetSummaryFieldNames'; -import { GetDatasetById } from './domain/useCases/GetDatasetById'; -import { GetDatasetByPersistentId } from './domain/useCases/GetDatasetByPersistentId'; +import { GetDataset } from './domain/useCases/GetDataset'; import { GetPrivateUrlDataset } from './domain/useCases/GetPrivateUrlDataset'; import { GetDatasetCitation } from './domain/useCases/GetDatasetCitation'; import { GetPrivateUrlDatasetCitation } from './domain/useCases/GetPrivateUrlDatasetCitation'; @@ -9,16 +8,14 @@ import { GetPrivateUrlDatasetCitation } from './domain/useCases/GetPrivateUrlDat const datasetsRepository = new DatasetsRepository(); const getDatasetSummaryFieldNames = new GetDatasetSummaryFieldNames(datasetsRepository); -const getDatasetById = new GetDatasetById(datasetsRepository); -const getDatasetByPersistentId = new GetDatasetByPersistentId(datasetsRepository); +const getDataset = new GetDataset(datasetsRepository); const getPrivateUrlDataset = new GetPrivateUrlDataset(datasetsRepository); const getDatasetCitation = new GetDatasetCitation(datasetsRepository); const getPrivateUrlDatasetCitation = new GetPrivateUrlDatasetCitation(datasetsRepository); export { getDatasetSummaryFieldNames, - getDatasetById, - getDatasetByPersistentId, + getDataset, getPrivateUrlDataset, getDatasetCitation, getPrivateUrlDatasetCitation, diff --git a/src/datasets/infra/repositories/DatasetsRepository.ts b/src/datasets/infra/repositories/DatasetsRepository.ts index 56328fcb..f1d1dc52 100644 --- a/src/datasets/infra/repositories/DatasetsRepository.ts +++ b/src/datasets/infra/repositories/DatasetsRepository.ts @@ -4,8 +4,6 @@ import { Dataset } from '../../domain/models/Dataset'; import { transformVersionResponseToDataset } from './transformers/datasetTransformers'; export class DatasetsRepository extends ApiRepository implements IDatasetsRepository { - DATASET_VERSION_LATEST = ':latest'; - public async getDatasetSummaryFieldNames(): Promise { return this.doGet('/datasets/summaryFieldNames') .then((response) => response.data.data) @@ -22,20 +20,21 @@ export class DatasetsRepository extends ApiRepository implements IDatasetsReposi }); } - public async getDatasetById(datasetId: number, datasetVersionId?: string): Promise { + public async getDataset(datasetId: number | string, datasetVersionId?: string): Promise { if (datasetVersionId === undefined) { datasetVersionId = this.DATASET_VERSION_LATEST; } - return this.getDatasetVersion(`/datasets/${datasetId}/versions/${datasetVersionId}`); - } - - public async getDatasetByPersistentId(datasetPersistentId: string, datasetVersionId?: string): Promise { - if (datasetVersionId === undefined) { - datasetVersionId = this.DATASET_VERSION_LATEST; + let endpoint; + if (typeof datasetId === 'number') { + endpoint = `/datasets/${datasetId}/versions/${datasetVersionId}`; + } else { + endpoint = `/datasets/:persistentId/versions/${datasetVersionId}?persistentId=${datasetId}`; } - return this.getDatasetVersion( - `/datasets/:persistentId/versions/${datasetVersionId}?persistentId=${datasetPersistentId}`, - ); + return this.doGet(endpoint, true) + .then((response) => transformVersionResponseToDataset(response)) + .catch((error) => { + throw error; + }); } public async getDatasetCitation(datasetId: number, datasetVersionId?: string): Promise { @@ -49,14 +48,6 @@ export class DatasetsRepository extends ApiRepository implements IDatasetsReposi }); } - private async getDatasetVersion(endpoint: string): Promise { - return this.doGet(endpoint, true) - .then((response) => transformVersionResponseToDataset(response)) - .catch((error) => { - throw error; - }); - } - public async getPrivateUrlDatasetCitation(token: string): Promise { return this.doGet(`/datasets/privateUrlDatasetVersion/${token}/citation`) .then((response) => response.data.data.message) diff --git a/src/files/domain/models/File.ts b/src/files/domain/models/File.ts new file mode 100644 index 00000000..496515fa --- /dev/null +++ b/src/files/domain/models/File.ts @@ -0,0 +1,39 @@ +export interface File { + id: number; + persistentId: string; + name: string; + pidURL?: string; + sizeBytes: number; + version: number; + description?: string; + restricted: boolean; + directoryLabel?: string; + datasetVersionId?: number; + categories?: string[]; + contentType: string; + embargo?: FileEmbargo; + storageIdentifier?: string; + originalFormat?: string; + originalFormatLabel?: string; + originalSize?: number; + originalName?: string; + UNF?: string; + rootDataFileId?: number; + previousDataFileId?: number; + md5?: string; + checksum?: FileChecksum; + metadataId?: number; + tabularTags?: string[]; + creationDate?: Date; + publicationDate?: Date; +} + +export interface FileEmbargo { + dateAvailable: Date; + reason?: string; +} + +export interface FileChecksum { + type: string; + value: string; +} diff --git a/src/files/domain/models/FileDataTable.ts b/src/files/domain/models/FileDataTable.ts new file mode 100644 index 00000000..2c110370 --- /dev/null +++ b/src/files/domain/models/FileDataTable.ts @@ -0,0 +1,82 @@ +export interface FileDataTable { + varQuantity?: number; + caseQuantity?: number; + recordsPerCase?: number; + UNF: string; + dataVariables: FileDataVariable[]; +} + +export interface FileDataVariable { + id: number; + name: string; + label?: string; + weighted: boolean; + variableIntervalType?: FileDataVariableIntervalType; + variableFormatType?: FileDataVariableFormatType; + formatCategory?: string; + format?: string; + isOrderedCategorical: boolean; + fileOrder: number; + UNF: string; + fileStartPosition?: number; + fileEndPosition?: number; + recordSegmentNumber?: number; + numberOfDecimalPoints?: number; + variableMetadata?: FileDataVariableMetadata[]; + invalidRanges?: FileDataVariableInvalidRanges[]; + summaryStatistics?: object; + variableCategories?: FileDataVariableCategory[]; +} + +export interface FileDataVariableMetadata { + id: number; + metadataId: number; + label?: string; + isWeightVar: boolean; + isWeighted: boolean; + weightVariableId?: number; + literalQuestion?: string; + interviewInstruction?: string; + postQuestion?: string; + universe?: string; + notes?: string; + categoryMetadatas: FileDataVariableCategoryMetadata[]; +} + +export interface FileDataVariableCategoryMetadata { + wFreq?: number; + categoryValue?: string; +} + +export interface FileDataVariableInvalidRanges { + beginValue?: string; + hasBeginValueType: boolean; + isBeginValueTypePoint: boolean; + isBeginValueTypeMin: boolean; + isBeginValueTypeMinExcl: boolean; + isBeginValueTypeMax: boolean; + isBeginValueTypeMaxExcl: boolean; + endValue?: string; + hasEndValueType: boolean; + endValueTypeMax: boolean; + endValueTypeMaxExcl: boolean; +} + +export interface FileDataVariableCategory { + label?: string; + value?: string; + isMissing: boolean; + frequency?: number; +} + +export enum FileDataVariableIntervalType { + DISCRETE = 'discrete', + CONTIN = 'contin', + NOMINAL = 'nominal', + DICHOTOMOUS = 'dichotomous', +} + +export enum FileDataVariableFormatType { + NUMERIC = 'NUMERIC', + CHARACTER = 'CHARACTER', +} diff --git a/src/files/domain/models/FileOrderCriteria.ts b/src/files/domain/models/FileOrderCriteria.ts new file mode 100644 index 00000000..49dc2b73 --- /dev/null +++ b/src/files/domain/models/FileOrderCriteria.ts @@ -0,0 +1,8 @@ +export enum FileOrderCriteria { + NAME_AZ = 'NameAZ', + NAME_ZA = 'NameZA', + NEWEST = 'Newest', + OLDEST = 'Oldest', + SIZE = 'Size', + TYPE = 'Type', +} diff --git a/src/files/domain/models/FileUserPermissions.ts b/src/files/domain/models/FileUserPermissions.ts new file mode 100644 index 00000000..25fc6716 --- /dev/null +++ b/src/files/domain/models/FileUserPermissions.ts @@ -0,0 +1,4 @@ +export interface FileUserPermissions { + canDownloadFile: boolean; + canEditOwnerDataset: boolean; +} diff --git a/src/files/domain/repositories/IFilesRepository.ts b/src/files/domain/repositories/IFilesRepository.ts new file mode 100644 index 00000000..9cc94591 --- /dev/null +++ b/src/files/domain/repositories/IFilesRepository.ts @@ -0,0 +1,20 @@ +import { FileOrderCriteria } from '../models/FileOrderCriteria'; +import { File } from '../models/File'; +import { FileDataTable } from '../models/FileDataTable'; +import { FileUserPermissions } from '../models/FileUserPermissions'; + +export interface IFilesRepository { + getDatasetFiles( + datasetId: number | string, + datasetVersionId?: string, + limit?: number, + offset?: number, + orderCriteria?: FileOrderCriteria, + ): Promise; + + getFileDownloadCount(fileId: number | string): Promise; + + getFileUserPermissions(fileId: number | string): Promise; + + getFileDataTables(fileId: number | string): Promise; +} diff --git a/src/files/domain/useCases/GetDatasetFiles.ts b/src/files/domain/useCases/GetDatasetFiles.ts new file mode 100644 index 00000000..e8e62bf7 --- /dev/null +++ b/src/files/domain/useCases/GetDatasetFiles.ts @@ -0,0 +1,22 @@ +import { UseCase } from '../../../core/domain/useCases/UseCase'; +import { FileOrderCriteria } from '../models/FileOrderCriteria'; +import { IFilesRepository } from '../repositories/IFilesRepository'; +import { File } from '../models/File'; + +export class GetDatasetFiles implements UseCase { + private filesRepository: IFilesRepository; + + constructor(filesRepository: IFilesRepository) { + this.filesRepository = filesRepository; + } + + async execute( + datasetId: number | string, + datasetVersionId?: string, + limit?: number, + offset?: number, + orderCriteria?: FileOrderCriteria, + ): Promise { + return await this.filesRepository.getDatasetFiles(datasetId, datasetVersionId, limit, offset, orderCriteria); + } +} diff --git a/src/files/domain/useCases/GetFileDataTables.ts b/src/files/domain/useCases/GetFileDataTables.ts new file mode 100644 index 00000000..d8a65891 --- /dev/null +++ b/src/files/domain/useCases/GetFileDataTables.ts @@ -0,0 +1,15 @@ +import { UseCase } from '../../../core/domain/useCases/UseCase'; +import { IFilesRepository } from '../repositories/IFilesRepository'; +import { FileDataTable } from '../models/FileDataTable'; + +export class GetFileDataTables implements UseCase { + private filesRepository: IFilesRepository; + + constructor(filesRepository: IFilesRepository) { + this.filesRepository = filesRepository; + } + + async execute(fileId: number | string): Promise { + return await this.filesRepository.getFileDataTables(fileId); + } +} diff --git a/src/files/domain/useCases/GetFileDownloadCount.ts b/src/files/domain/useCases/GetFileDownloadCount.ts new file mode 100644 index 00000000..4d2b41eb --- /dev/null +++ b/src/files/domain/useCases/GetFileDownloadCount.ts @@ -0,0 +1,14 @@ +import { UseCase } from '../../../core/domain/useCases/UseCase'; +import { IFilesRepository } from '../repositories/IFilesRepository'; + +export class GetFileDownloadCount implements UseCase { + private filesRepository: IFilesRepository; + + constructor(filesRepository: IFilesRepository) { + this.filesRepository = filesRepository; + } + + async execute(fileId: number | string): Promise { + return await this.filesRepository.getFileDownloadCount(fileId); + } +} diff --git a/src/files/domain/useCases/GetFileUserPermissions.ts b/src/files/domain/useCases/GetFileUserPermissions.ts new file mode 100644 index 00000000..454984ef --- /dev/null +++ b/src/files/domain/useCases/GetFileUserPermissions.ts @@ -0,0 +1,15 @@ +import { UseCase } from '../../../core/domain/useCases/UseCase'; +import { IFilesRepository } from '../repositories/IFilesRepository'; +import { FileUserPermissions } from '../models/FileUserPermissions'; + +export class GetFileUserPermissions implements UseCase { + private filesRepository: IFilesRepository; + + constructor(filesRepository: IFilesRepository) { + this.filesRepository = filesRepository; + } + + async execute(fileId: number | string): Promise { + return await this.filesRepository.getFileUserPermissions(fileId); + } +} diff --git a/src/files/index.ts b/src/files/index.ts new file mode 100644 index 00000000..8dad404b --- /dev/null +++ b/src/files/index.ts @@ -0,0 +1,27 @@ +import { FilesRepository } from './infra/repositories/FilesRepository'; +import { GetDatasetFiles } from './domain/useCases/GetDatasetFiles'; +import { GetFileDownloadCount } from './domain/useCases/GetFileDownloadCount'; +import { GetFileUserPermissions } from './domain/useCases/GetFileUserPermissions'; +import { GetFileDataTables } from './domain/useCases/GetFileDataTables'; + +const filesRepository = new FilesRepository(); + +const getDatasetFiles = new GetDatasetFiles(filesRepository); +const getFileDownloadCount = new GetFileDownloadCount(filesRepository); +const getFileUserPermissions = new GetFileUserPermissions(filesRepository); +const getFileDataTables = new GetFileDataTables(filesRepository); + +export { getDatasetFiles, getFileDownloadCount, getFileUserPermissions, getFileDataTables }; + +export { File, FileEmbargo, FileChecksum } from './domain/models/File'; +export { FileOrderCriteria } from './domain/models/FileOrderCriteria'; +export { + FileDataTable, + FileDataVariable, + FileDataVariableMetadata, + FileDataVariableInvalidRanges, + FileDataVariableCategoryMetadata, + FileDataVariableCategory, + FileDataVariableIntervalType, + FileDataVariableFormatType, +} from './domain/models/FileDataTable'; diff --git a/src/files/infra/repositories/FilesRepository.ts b/src/files/infra/repositories/FilesRepository.ts new file mode 100644 index 00000000..ec1231bd --- /dev/null +++ b/src/files/infra/repositories/FilesRepository.ts @@ -0,0 +1,92 @@ +import { ApiRepository } from '../../../core/infra/repositories/ApiRepository'; +import { IFilesRepository } from '../../domain/repositories/IFilesRepository'; +import { FileOrderCriteria } from '../../domain/models/FileOrderCriteria'; +import { File } from '../../domain/models/File'; +import { transformFilesResponseToFiles } from './transformers/fileTransformers'; +import { FileDataTable } from '../../domain/models/FileDataTable'; +import { transformDataTablesResponseToDataTables } from './transformers/fileDataTableTransformers'; +import { FileUserPermissions } from '../../domain/models/FileUserPermissions'; +import { transformFileUserPermissionsResponseToFileUserPermissions } from './transformers/fileUserPermissionsTransformers'; + +export interface GetFilesQueryParams { + limit?: number; + offset?: number; + orderCriteria?: string; +} + +export class FilesRepository extends ApiRepository implements IFilesRepository { + public async getDatasetFiles( + datasetId: number | string, + datasetVersionId?: string, + limit?: number, + offset?: number, + orderCriteria?: FileOrderCriteria, + ): Promise { + if (datasetVersionId === undefined) { + datasetVersionId = this.DATASET_VERSION_LATEST; + } + let endpoint; + if (typeof datasetId === 'number') { + endpoint = `/datasets/${datasetId}/versions/${datasetVersionId}/files`; + } else { + endpoint = `/datasets/:persistentId/versions/${datasetVersionId}/files?persistentId=${datasetId}`; + } + const queryParams: GetFilesQueryParams = {}; + if (limit !== undefined) { + queryParams.limit = limit; + } + if (offset !== undefined) { + queryParams.offset = offset; + } + if (orderCriteria !== undefined) { + queryParams.orderCriteria = orderCriteria.toString(); + } + return this.doGet(endpoint, true, queryParams) + .then((response) => transformFilesResponseToFiles(response)) + .catch((error) => { + throw error; + }); + } + + public async getFileDownloadCount(fileId: number | string): Promise { + let endpoint; + if (typeof fileId === 'number') { + endpoint = `/files/${fileId}/downloadCount`; + } else { + endpoint = `/files/:persistentId/downloadCount?persistentId=${fileId}`; + } + return this.doGet(endpoint, true) + .then((response) => response.data.data.message as number) + .catch((error) => { + throw error; + }); + } + + public async getFileUserPermissions(fileId: number | string): Promise { + let endpoint; + if (typeof fileId === 'number') { + endpoint = `/access/datafile/${fileId}/userPermissions`; + } else { + endpoint = `/access/datafile/:persistentId/userPermissions?persistentId=${fileId}`; + } + return this.doGet(endpoint, true) + .then((response) => transformFileUserPermissionsResponseToFileUserPermissions(response)) + .catch((error) => { + throw error; + }); + } + + public async getFileDataTables(fileId: string | number): Promise { + let endpoint; + if (typeof fileId === 'number') { + endpoint = `/files/${fileId}/dataTables`; + } else { + endpoint = `/files/:persistentId/dataTables?persistentId=${fileId}`; + } + return this.doGet(endpoint, true) + .then((response) => transformDataTablesResponseToDataTables(response)) + .catch((error) => { + throw error; + }); + } +} diff --git a/src/files/infra/repositories/transformers/fileDataTableTransformers.ts b/src/files/infra/repositories/transformers/fileDataTableTransformers.ts new file mode 100644 index 00000000..55ed3019 --- /dev/null +++ b/src/files/infra/repositories/transformers/fileDataTableTransformers.ts @@ -0,0 +1,164 @@ +import { AxiosResponse } from 'axios'; +import { + FileDataTable, + FileDataVariable, + FileDataVariableIntervalType, + FileDataVariableFormatType, + FileDataVariableMetadata, + FileDataVariableInvalidRanges, + FileDataVariableCategory, + FileDataVariableCategoryMetadata, +} from '../../../domain/models/FileDataTable'; + +export const transformDataTablesResponseToDataTables = (response: AxiosResponse): FileDataTable[] => { + const files: FileDataTable[] = []; + const fileDataTablesPayload = response.data.data; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + fileDataTablesPayload.forEach(function (fileDataTablePayload: any) { + files.push(transformPayloadToFileDataTable(fileDataTablePayload)); + }); + return files; +}; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const transformPayloadToFileDataTable = (fileDataTablePayload: any): FileDataTable => { + return { + ...(fileDataTablePayload.varQuantity && { varQuantity: fileDataTablePayload.varQuantity }), + ...(fileDataTablePayload.caseQuantity && { caseQuantity: fileDataTablePayload.caseQuantity }), + ...(fileDataTablePayload.recordsPerCase && { recordsPerCase: fileDataTablePayload.recordsPerCase }), + UNF: fileDataTablePayload.UNF, + dataVariables: transformPayloadToDataVariables(fileDataTablePayload.dataVariables), + }; +}; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const transformPayloadToDataVariables = (fileDataVariablesPayload: any): FileDataVariable[] => { + const fileDataVariables: FileDataVariable[] = []; + fileDataVariablesPayload.forEach(function (fileDataVariablePayload: any) { + fileDataVariables.push({ + id: fileDataVariablePayload.id, + name: fileDataVariablePayload.name, + ...(fileDataVariablePayload.label && { label: fileDataVariablePayload.label }), + weighted: fileDataVariablePayload.weighted, + ...(fileDataVariablePayload.variableIntervalType && { + variableIntervalType: fileDataVariablePayload.variableIntervalType as FileDataVariableIntervalType, + }), + ...(fileDataVariablePayload.variableFormatType && { + variableFormatType: fileDataVariablePayload.variableFormatType as FileDataVariableFormatType, + }), + ...(fileDataVariablePayload.formatCategory && { formatCategory: fileDataVariablePayload.formatCategory }), + ...(fileDataVariablePayload.format && { format: fileDataVariablePayload.format }), + isOrderedCategorical: fileDataVariablePayload.isOrderedCategorical, + fileOrder: fileDataVariablePayload.fileOrder, + UNF: fileDataVariablePayload.UNF, + ...(fileDataVariablePayload.fileStartPosition && { + fileStartPosition: fileDataVariablePayload.fileStartPosition, + }), + ...(fileDataVariablePayload.fileEndPosition && { fileEndPosition: fileDataVariablePayload.fileEndPosition }), + ...(fileDataVariablePayload.recordSegmentNumber && { + recordSegmentNumber: fileDataVariablePayload.recordSegmentNumber, + }), + ...(fileDataVariablePayload.numberOfDecimalPoints && { + numberOfDecimalPoints: fileDataVariablePayload.numberOfDecimalPoints, + }), + ...(fileDataVariablePayload.variableMetadata && { + variableMetadata: transformPayloadToFileVariableMetadata(fileDataVariablePayload.variableMetadata), + }), + ...(fileDataVariablePayload.invalidRanges && { + invalidRanges: transformPayloadToDataVariableInvalidRanges(fileDataVariablePayload.invalidRanges), + }), + ...(fileDataVariablePayload.summaryStatistics && { + summaryStatistics: fileDataVariablePayload.summaryStatistics, + }), + ...(fileDataVariablePayload.variableCategories && { + variableCategories: transformPayloadToFileDataVariableCategories(fileDataVariablePayload.variableCategories), + }), + }); + }); + return fileDataVariables; +}; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const transformPayloadToFileVariableMetadata = (fileVariableMetadataPayload: any): FileDataVariableMetadata[] => { + const fileDataVariableMetadata: FileDataVariableMetadata[] = []; + fileVariableMetadataPayload.forEach(function (fileVariableMetadataElementPayload: any) { + fileDataVariableMetadata.push({ + id: fileVariableMetadataElementPayload.id, + metadataId: fileVariableMetadataElementPayload.metadataId, + ...(fileVariableMetadataElementPayload.label && { label: fileVariableMetadataElementPayload.label }), + isWeightVar: fileVariableMetadataElementPayload.isWeightVar, + isWeighted: fileVariableMetadataElementPayload.isWeighted, + ...(fileVariableMetadataElementPayload.weightVariableId && { + weightVariableId: fileVariableMetadataElementPayload.weightVariableId, + }), + ...(fileVariableMetadataElementPayload.literalQuestion && { + literalQuestion: fileVariableMetadataElementPayload.literalQuestion, + }), + ...(fileVariableMetadataElementPayload.interviewInstruction && { + interviewInstruction: fileVariableMetadataElementPayload.interviewInstruction, + }), + ...(fileVariableMetadataElementPayload.postQuestion && { + postQuestion: fileVariableMetadataElementPayload.postQuestion, + }), + ...(fileVariableMetadataElementPayload.universe && { universe: fileVariableMetadataElementPayload.universe }), + ...(fileVariableMetadataElementPayload.notes && { notes: fileVariableMetadataElementPayload.notes }), + categoryMetadatas: transformPayloadToFileDataVariableCategoryMetadatas( + fileVariableMetadataElementPayload.categoryMetadatas, + ), + }); + }); + return fileDataVariableMetadata; +}; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const transformPayloadToFileDataVariableCategoryMetadatas = ( + categoryMetadatasPayload: any, +): FileDataVariableCategoryMetadata[] => { + const fileDataVariableCategoryMetadatas: FileDataVariableCategoryMetadata[] = []; + categoryMetadatasPayload.forEach(function (categoryMetadataPayload: any) { + fileDataVariableCategoryMetadatas.push({ + ...(categoryMetadataPayload.wFreq && { wFreq: categoryMetadataPayload.wFreq }), + ...(categoryMetadataPayload.categoryValue && { categoryValue: categoryMetadataPayload.categoryValue }), + }); + }); + return fileDataVariableCategoryMetadatas; +}; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const transformPayloadToDataVariableInvalidRanges = ( + dataVariableInvalidRangesPayload: any, +): FileDataVariableInvalidRanges[] => { + const dataVariableInvalidRanges: FileDataVariableInvalidRanges[] = []; + dataVariableInvalidRangesPayload.forEach(function (dataVariableInvalidRangePayload: any) { + dataVariableInvalidRanges.push({ + ...(dataVariableInvalidRangePayload.beginValue && { beginValue: dataVariableInvalidRangePayload.beginValue }), + hasBeginValueType: dataVariableInvalidRangePayload.hasBeginValueType, + isBeginValueTypePoint: dataVariableInvalidRangePayload.isBeginValueTypePoint, + isBeginValueTypeMin: dataVariableInvalidRangePayload.isBeginValueTypeMin, + isBeginValueTypeMinExcl: dataVariableInvalidRangePayload.isBeginValueTypeMinExcl, + isBeginValueTypeMax: dataVariableInvalidRangePayload.isBeginValueTypeMax, + isBeginValueTypeMaxExcl: dataVariableInvalidRangePayload.isBeginValueTypeMaxExcl, + ...(dataVariableInvalidRangePayload.endValue && { endValue: dataVariableInvalidRangePayload.endValue }), + hasEndValueType: dataVariableInvalidRangePayload.hasEndValueType, + endValueTypeMax: dataVariableInvalidRangePayload.endValueTypeMax, + endValueTypeMaxExcl: dataVariableInvalidRangePayload.endValueTypeMaxExcl, + }); + }); + return dataVariableInvalidRanges; +}; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const transformPayloadToFileDataVariableCategories = ( + fileDataVariableCategoriesPayload: any, +): FileDataVariableCategory[] => { + const fileDataVariableCategories: FileDataVariableCategory[] = []; + fileDataVariableCategoriesPayload.forEach(function (fileDataVariableCategoryPayload: any) { + fileDataVariableCategories.push({ + ...(fileDataVariableCategoryPayload.label && { label: fileDataVariableCategoryPayload.label }), + ...(fileDataVariableCategoryPayload.value && { value: fileDataVariableCategoryPayload.value }), + isMissing: fileDataVariableCategoryPayload.isMissing, + ...(fileDataVariableCategoryPayload.frequency && { frequency: fileDataVariableCategoryPayload.frequency }), + }); + }); + return fileDataVariableCategories; +}; diff --git a/src/files/infra/repositories/transformers/fileTransformers.ts b/src/files/infra/repositories/transformers/fileTransformers.ts new file mode 100644 index 00000000..28319592 --- /dev/null +++ b/src/files/infra/repositories/transformers/fileTransformers.ts @@ -0,0 +1,63 @@ +import { File, FileEmbargo, FileChecksum } from '../../../domain/models/File'; +import { AxiosResponse } from 'axios'; + +export const transformFilesResponseToFiles = (response: AxiosResponse): File[] => { + const files: File[] = []; + const filesPayload = response.data.data; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + filesPayload.forEach(function (filePayload: any) { + files.push(transformFilePayloadToFile(filePayload)); + }); + return files; +}; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const transformFilePayloadToFile = (filePayload: any): File => { + return { + id: filePayload.dataFile.id, + persistentId: filePayload.dataFile.persistentId, + name: filePayload.dataFile.filename, + ...(filePayload.dataFile.pidURL && { pidURL: filePayload.dataFile.pidURL }), + sizeBytes: filePayload.dataFile.filesize, + version: filePayload.version, + ...(filePayload.dataFile.description && { description: filePayload.dataFile.description }), + restricted: filePayload.restricted, + ...(filePayload.dataFile.directoryLabel && { directoryLabel: filePayload.dataFile.directoryLabel }), + ...(filePayload.dataFile.datasetVersionId && { datasetVersionId: filePayload.dataFile.datasetVersionId }), + ...(filePayload.dataFile.categories && { categories: filePayload.dataFile.categories }), + contentType: filePayload.dataFile.contentType, + ...(filePayload.dataFile.embargo && { embargo: transformEmbargoPayloadToEmbargo(filePayload.dataFile.embargo) }), + ...(filePayload.dataFile.storageIdentifier && { storageIdentifier: filePayload.dataFile.storageIdentifier }), + ...(filePayload.dataFile.originalFormat && { originalFormat: filePayload.dataFile.originalFormat }), + ...(filePayload.dataFile.originalFormatLabel && { originalFormatLabel: filePayload.dataFile.originalFormatLabel }), + ...(filePayload.dataFile.originalSize && { originalSize: filePayload.dataFile.originalSize }), + ...(filePayload.dataFile.originalName && { originalName: filePayload.dataFile.originalName }), + ...(filePayload.dataFile.UNF && { UNF: filePayload.dataFile.UNF }), + ...(filePayload.dataFile.rootDataFileId && { rootDataFileId: filePayload.dataFile.rootDataFileId }), + ...(filePayload.dataFile.previousDataFileId && { previousDataFileId: filePayload.dataFile.previousDataFileId }), + ...(filePayload.dataFile.md5 && { md5: filePayload.dataFile.md5 }), + ...(filePayload.dataFile.checksum && { + checksum: transformChecksumPayloadToChecksum(filePayload.dataFile.checksum), + }), + ...(filePayload.dataFile.fileMetadataId && { metadataId: filePayload.dataFile.fileMetadataId }), + ...(filePayload.dataFile.tabularTags && { tabularTags: filePayload.dataFile.tabularTags }), + ...(filePayload.dataFile.creationDate && { creationDate: new Date(filePayload.dataFile.creationDate) }), + ...(filePayload.dataFile.publicationDate && { publicationDate: new Date(filePayload.dataFile.publicationDate) }), + }; +}; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const transformEmbargoPayloadToEmbargo = (embargoPayload: any): FileEmbargo => { + return { + dateAvailable: new Date(embargoPayload.dateAvailable), + ...(embargoPayload.reason && { reason: embargoPayload.reason }), + }; +}; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const transformChecksumPayloadToChecksum = (checksumPayload: any): FileChecksum => { + return { + type: checksumPayload.type, + value: checksumPayload.value, + }; +}; diff --git a/src/files/infra/repositories/transformers/fileUserPermissionsTransformers.ts b/src/files/infra/repositories/transformers/fileUserPermissionsTransformers.ts new file mode 100644 index 00000000..4954c44c --- /dev/null +++ b/src/files/infra/repositories/transformers/fileUserPermissionsTransformers.ts @@ -0,0 +1,12 @@ +import { AxiosResponse } from 'axios'; +import { FileUserPermissions } from '../../../domain/models/FileUserPermissions'; + +export const transformFileUserPermissionsResponseToFileUserPermissions = ( + response: AxiosResponse, +): FileUserPermissions => { + const fileUserPermissionsPayload = response.data.data; + return { + canDownloadFile: fileUserPermissionsPayload.canDownloadFile, + canEditOwnerDataset: fileUserPermissionsPayload.canEditOwnerDataset, + }; +}; diff --git a/src/index.ts b/src/index.ts index 549bb7d2..d5479039 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,3 +4,4 @@ export * from './users'; export * from './auth'; export * from './datasets'; export * from './metadataBlocks'; +export * from './files'; diff --git a/test/integration/datasets/DatasetsRepository.test.ts b/test/integration/datasets/DatasetsRepository.test.ts index 37d6a1fb..9d81bab6 100644 --- a/test/integration/datasets/DatasetsRepository.test.ts +++ b/test/integration/datasets/DatasetsRepository.test.ts @@ -7,14 +7,10 @@ import { ReadError } from '../../../src/core/domain/repositories/ReadError'; describe('DatasetsRepository', () => { const sut: DatasetsRepository = new DatasetsRepository(); - const createdTestDatasetId = 2; const nonExistentTestDatasetId = 100; - ApiConfig.init(TestConstants.TEST_API_URL, DataverseApiAuthMechanism.API_KEY, process.env.TEST_API_KEY); - beforeAll(async () => { - // We update timeout due to experienced timeout errors - jest.setTimeout(10000); + ApiConfig.init(TestConstants.TEST_API_URL, DataverseApiAuthMechanism.API_KEY, process.env.TEST_API_KEY); await createDatasetViaApi() .then() .catch(() => { @@ -22,11 +18,6 @@ describe('DatasetsRepository', () => { }); }); - afterAll(async () => { - // We update timeout back to original default value - jest.setTimeout(5000); - }); - describe('getDatasetSummaryFieldNames', () => { test('should return not empty field list on successful response', async () => { const actual = await sut.getDatasetSummaryFieldNames(); @@ -35,57 +26,58 @@ describe('DatasetsRepository', () => { }); }); - describe('getDatasetById', () => { - test('should return dataset when it exists filtering by id', async () => { - const actual = await sut.getDatasetById(createdTestDatasetId); - expect(actual.id).toBe(createdTestDatasetId); - }); - - test('should return dataset when it exists filtering by id and version id', async () => { - const actual = await sut.getDatasetById(createdTestDatasetId, ':draft'); - expect(actual.id).toBe(createdTestDatasetId); - }); + describe('getDataset', () => { + describe('by numeric id', () => { + test('should return dataset when it exists filtering by id', async () => { + const actual = await sut.getDataset(TestConstants.TEST_CREATED_DATASET_ID); + expect(actual.id).toBe(TestConstants.TEST_CREATED_DATASET_ID); + }); - test('should return error when dataset does not exist', async () => { - let error: ReadError = undefined; - await sut.getDatasetById(nonExistentTestDatasetId).catch((e) => (error = e)); + test('should return dataset when it exists filtering by id and version id', async () => { + const actual = await sut.getDataset(TestConstants.TEST_CREATED_DATASET_ID, ':draft'); + expect(actual.id).toBe(TestConstants.TEST_CREATED_DATASET_ID); + }); - assert.match( - error.message, - `There was an error when reading the resource. Reason was: [404] Dataset with ID ${nonExistentTestDatasetId} not found.`, - ); - }); - }); + test('should return error when dataset does not exist', async () => { + let error: ReadError = undefined; + await sut.getDataset(nonExistentTestDatasetId).catch((e) => (error = e)); - describe('getDatasetByPersistentId', () => { - test('should return dataset when it exists filtering by persistent id', async () => { - const createdDataset = await sut.getDatasetById(createdTestDatasetId); - const actual = await sut.getDatasetByPersistentId(createdDataset.persistentId); - expect(actual.id).toBe(createdTestDatasetId); + assert.match( + error.message, + `There was an error when reading the resource. Reason was: [404] Dataset with ID ${nonExistentTestDatasetId} not found.`, + ); + }); }); + describe('by persistent id', () => { + test('should return dataset when it exists filtering by persistent id', async () => { + const createdDataset = await sut.getDataset(TestConstants.TEST_CREATED_DATASET_ID); + const actual = await sut.getDataset(createdDataset.persistentId); + expect(actual.id).toBe(TestConstants.TEST_CREATED_DATASET_ID); + }); - test('should return dataset when it exists filtering by persistent id and version id', async () => { - const createdDataset = await sut.getDatasetById(createdTestDatasetId); - const actual = await sut.getDatasetByPersistentId(createdDataset.persistentId, ':draft'); - expect(actual.id).toBe(createdTestDatasetId); - }); + test('should return dataset when it exists filtering by persistent id and version id', async () => { + const createdDataset = await sut.getDataset(TestConstants.TEST_CREATED_DATASET_ID); + const actual = await sut.getDataset(createdDataset.persistentId, ':draft'); + expect(actual.id).toBe(TestConstants.TEST_CREATED_DATASET_ID); + }); - test('should return error when dataset does not exist', async () => { - let error: ReadError = undefined; + test('should return error when dataset does not exist', async () => { + let error: ReadError = undefined; - const testWrongPersistentId = 'wrongPersistentId'; - await sut.getDatasetByPersistentId(testWrongPersistentId).catch((e) => (error = e)); + const testWrongPersistentId = 'wrongPersistentId'; + await sut.getDataset(testWrongPersistentId).catch((e) => (error = e)); - assert.match( - error.message, - `There was an error when reading the resource. Reason was: [404] Dataset with Persistent ID ${testWrongPersistentId} not found.`, - ); + assert.match( + error.message, + `There was an error when reading the resource. Reason was: [404] Dataset with Persistent ID ${testWrongPersistentId} not found.`, + ); + }); }); }); describe('getDatasetCitation', () => { test('should return citation when dataset exists', async () => { - const actualDatasetCitation = await sut.getDatasetCitation(createdTestDatasetId); + const actualDatasetCitation = await sut.getDatasetCitation(TestConstants.TEST_CREATED_DATASET_ID); expect(typeof actualDatasetCitation).toBe('string'); }); @@ -107,7 +99,7 @@ describe('DatasetsRepository', () => { let privateUrlToken: string = undefined; beforeAll(async () => { - await createPrivateUrlViaApi(createdTestDatasetId) + await createPrivateUrlViaApi(TestConstants.TEST_CREATED_DATASET_ID) .then((response) => { privateUrlToken = response.data.data.token; }) @@ -120,7 +112,7 @@ describe('DatasetsRepository', () => { test('should return dataset when token is valid', async () => { const actual = await sut.getPrivateUrlDataset(privateUrlToken); - expect(actual.id).toBe(createdTestDatasetId); + expect(actual.id).toBe(TestConstants.TEST_CREATED_DATASET_ID); }); test('should return error when token is not valid', async () => { diff --git a/test/integration/environment/.env b/test/integration/environment/.env index c9e070b5..cb181a92 100644 --- a/test/integration/environment/.env +++ b/test/integration/environment/.env @@ -2,5 +2,5 @@ POSTGRES_VERSION=13 DATAVERSE_DB_USER=dataverse SOLR_VERSION=8.11.1 DATAVERSE_IMAGE_REGISTRY=ghcr.io -DATAVERSE_IMAGE_TAG=9667-metadatablocks-api-extension +DATAVERSE_IMAGE_TAG=9692-files-api-extension-display-data DATAVERSE_BOOTSTRAP_TIMEOUT=5m diff --git a/test/integration/files/FilesRepository.test.ts b/test/integration/files/FilesRepository.test.ts new file mode 100644 index 00000000..263f5aa4 --- /dev/null +++ b/test/integration/files/FilesRepository.test.ts @@ -0,0 +1,227 @@ +import { FilesRepository } from '../../../src/files/infra/repositories/FilesRepository'; +import { ApiConfig, DataverseApiAuthMechanism } from '../../../src/core/infra/repositories/ApiConfig'; +import { assert } from 'sinon'; +import { TestConstants } from '../../testHelpers/TestConstants'; +import { createDatasetViaApi } from '../../testHelpers/datasets/datasetHelper'; +import { uploadFileViaApi } from '../../testHelpers/files/filesHelper'; +import { FileOrderCriteria } from '../../../src/files/domain/models/FileOrderCriteria'; +import { DatasetsRepository } from '../../../src/datasets/infra/repositories/DatasetsRepository'; +import { ReadError } from '../../../src/core/domain/repositories/ReadError'; + +describe('FilesRepository', () => { + const sut: FilesRepository = new FilesRepository(); + + const testTextFile1Name = 'test-file-1.txt'; + const testTextFile2Name = 'test-file-2.txt'; + const testTextFile3Name = 'test-file-3.txt'; + const testTabFile4Name = 'test-file-4.tab'; + + const nonExistentFiledId = 200; + + beforeAll(async () => { + ApiConfig.init(TestConstants.TEST_API_URL, DataverseApiAuthMechanism.API_KEY, process.env.TEST_API_KEY); + await createDatasetViaApi() + .then() + .catch(() => { + fail('Test beforeAll(): Error while creating test Dataset'); + }); + // Uploading test file 1 + await uploadFileViaApi(TestConstants.TEST_CREATED_DATASET_ID, testTextFile1Name) + .then() + .catch((e) => { + console.log(e); + fail(`Tests beforeAll(): Error while uploading file ${testTextFile1Name}`); + }); + // Uploading test file 2 + await uploadFileViaApi(TestConstants.TEST_CREATED_DATASET_ID, testTextFile2Name) + .then() + .catch((e) => { + console.log(e); + fail(`Tests beforeAll(): Error while uploading file ${testTextFile2Name}`); + }); + // Uploading test file 3 + await uploadFileViaApi(TestConstants.TEST_CREATED_DATASET_ID, testTextFile3Name) + .then() + .catch((e) => { + console.log(e); + fail(`Tests beforeAll(): Error while uploading file ${testTextFile3Name}`); + }); + // Uploading test file 4 + await uploadFileViaApi(TestConstants.TEST_CREATED_DATASET_ID, testTabFile4Name) + .then() + .catch((e) => { + console.log(e); + fail(`Tests beforeAll(): Error while uploading file ${testTabFile4Name}`); + }); + }); + + describe('getDatasetFiles', () => { + describe('by numeric id', () => { + test('should return all files filtering by dataset id', async () => { + const actual = await sut.getDatasetFiles(TestConstants.TEST_CREATED_DATASET_ID); + 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); + }); + + test('should return correct files filtering by dataset id and paginating', async () => { + const actual = await sut.getDatasetFiles(TestConstants.TEST_CREATED_DATASET_ID, undefined, 3, 3, undefined); + assert.match(actual.length, 1); + assert.match(actual[0].name, testTabFile4Name); + }); + + test('should return correct files filtering by dataset id and applying order criteria', async () => { + let actual = await sut.getDatasetFiles( + TestConstants.TEST_CREATED_DATASET_ID, + undefined, + undefined, + undefined, + FileOrderCriteria.NEWEST, + ); + assert.match(actual.length, 4); + assert.match(actual[0].name, testTabFile4Name); + assert.match(actual[1].name, testTextFile3Name); + assert.match(actual[2].name, testTextFile2Name); + assert.match(actual[3].name, testTextFile1Name); + }); + + test('should return error when dataset does not exist', async () => { + let error: ReadError = undefined; + + const nonExistentTestDatasetId = 100; + await sut.getDatasetFiles(nonExistentTestDatasetId).catch((e) => (error = e)); + + assert.match( + error.message, + `There was an error when reading the resource. Reason was: [404] Dataset with ID ${nonExistentTestDatasetId} not found.`, + ); + }); + }); + + describe('by persistent id', () => { + const datasetRepository = new DatasetsRepository(); + + test('should return all files filtering by persistent id', async () => { + const testDataset = await datasetRepository.getDataset(TestConstants.TEST_CREATED_DATASET_ID); + const actual = await sut.getDatasetFiles(testDataset.persistentId); + 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); + }); + + test('should return correct files filtering by persistent id and paginating', async () => { + const testDataset = await datasetRepository.getDataset(TestConstants.TEST_CREATED_DATASET_ID); + const actual = await sut.getDatasetFiles(testDataset.persistentId, undefined, 3, 3, undefined); + assert.match(actual.length, 1); + assert.match(actual[0].name, testTabFile4Name); + }); + + test('should return correct files filtering by persistent id and applying order criteria', async () => { + const testDataset = await datasetRepository.getDataset(TestConstants.TEST_CREATED_DATASET_ID); + let actual = await sut.getDatasetFiles( + testDataset.persistentId, + undefined, + undefined, + undefined, + FileOrderCriteria.NEWEST, + ); + assert.match(actual.length, 4); + assert.match(actual[0].name, testTabFile4Name); + assert.match(actual[1].name, testTextFile3Name); + assert.match(actual[2].name, testTextFile2Name); + assert.match(actual[3].name, testTextFile1Name); + }); + + test('should return error when dataset does not exist', async () => { + let error: ReadError = undefined; + + const testWrongPersistentId = 'wrongPersistentId'; + await sut.getDatasetFiles(testWrongPersistentId).catch((e) => (error = e)); + + assert.match( + error.message, + `There was an error when reading the resource. Reason was: [404] Dataset with Persistent ID ${testWrongPersistentId} not found.`, + ); + }); + }); + }); + + describe('getFileDownloadCount', () => { + test('should return count filtering by file id', async () => { + const currentTestFiles = await sut.getDatasetFiles(TestConstants.TEST_CREATED_DATASET_ID); + const testFile = currentTestFiles[0]; + const actual = await sut.getFileDownloadCount(testFile.id); + assert.match(actual, 0); + }); + + test('should return error when file does not exist', async () => { + let error: ReadError = undefined; + + await sut.getFileDownloadCount(nonExistentFiledId).catch((e) => (error = e)); + + assert.match( + error.message, + `There was an error when reading the resource. Reason was: [404] File with ID ${nonExistentFiledId} not found.`, + ); + }); + }); + + describe('getFileUserPermissions', () => { + test('should return user permissions filtering by file id', async () => { + const currentTestFiles = await sut.getDatasetFiles(TestConstants.TEST_CREATED_DATASET_ID); + const testFile = currentTestFiles[0]; + const actual = await sut.getFileUserPermissions(testFile.id); + assert.match(actual.canDownloadFile, true); + assert.match(actual.canEditOwnerDataset, true); + }); + + test('should return error when file does not exist', async () => { + let error: ReadError = undefined; + + await sut.getFileUserPermissions(nonExistentFiledId).catch((e) => (error = e)); + + assert.match( + error.message, + `There was an error when reading the resource. Reason was: [404] File with ID ${nonExistentFiledId} not found.`, + ); + }); + }); + + describe('getFileDataTables', () => { + test('should return data tables filtering by tabular file id', async () => { + const currentTestFiles = await sut.getDatasetFiles(TestConstants.TEST_CREATED_DATASET_ID); + const testFile = currentTestFiles[3]; + const actual = await sut.getFileDataTables(testFile.id); + assert.match(actual[0].varQuantity, 3); + }); + + test('should return error when file is not tabular', async () => { + const currentTestFiles = await sut.getDatasetFiles(TestConstants.TEST_CREATED_DATASET_ID); + const testFile = currentTestFiles[0]; + + let error: ReadError = undefined; + + await sut.getFileDataTables(testFile.id).catch((e) => (error = e)); + + assert.match( + error.message, + 'There was an error when reading the resource. Reason was: [400] This operation is only available for tabular files.', + ); + }); + + test('should return error when file does not exist', async () => { + let error: ReadError = undefined; + + await sut.getFileDataTables(nonExistentFiledId).catch((e) => (error = e)); + + assert.match( + error.message, + `There was an error when reading the resource. Reason was: [404] File with ID ${nonExistentFiledId} not found.`, + ); + }); + }); +}); diff --git a/test/testHelpers/TestConstants.ts b/test/testHelpers/TestConstants.ts index 82c792c9..37dca855 100644 --- a/test/testHelpers/TestConstants.ts +++ b/test/testHelpers/TestConstants.ts @@ -1,6 +1,7 @@ export class TestConstants { static readonly TEST_API_URL = 'http://localhost:8080/api/v1'; static readonly TEST_DUMMY_API_KEY = 'dummyApiKey'; + static readonly TEST_DUMMY_PERSISTENT_ID = 'doi:11.1111/AA1/AA1AAA'; static readonly TEST_ERROR_RESPONSE = { response: { status: 'ERROR', @@ -8,6 +9,7 @@ export class TestConstants { }, }; static readonly TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_API_KEY = { + params: {}, headers: { 'Content-Type': 'application/json', 'X-Dataverse-Key': TestConstants.TEST_DUMMY_API_KEY, @@ -15,13 +17,16 @@ export class TestConstants { }; static readonly TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_SESSION_COOKIE = { withCredentials: true, + params: {}, headers: { 'Content-Type': 'application/json', }, }; static readonly TEST_EXPECTED_UNAUTHENTICATED_REQUEST_CONFIG = { + params: {}, headers: { 'Content-Type': 'application/json', }, }; + static readonly TEST_CREATED_DATASET_ID = 2; } diff --git a/test/testHelpers/files/fileDataTablesHelper.ts b/test/testHelpers/files/fileDataTablesHelper.ts new file mode 100644 index 00000000..94c490bb --- /dev/null +++ b/test/testHelpers/files/fileDataTablesHelper.ts @@ -0,0 +1,83 @@ +import { + FileDataTable, + FileDataVariableIntervalType, + FileDataVariableFormatType, +} from '../../../src/files/domain/models/FileDataTable'; + +export const createFileDataTableModel = (): FileDataTable => { + return { + varQuantity: 21, + caseQuantity: 122, + UNF: 'UNF:6:awd/lz8bNdoXn8f1sjAAAQ==', + dataVariables: [ + { + id: 3, + name: 'test1', + label: 'test1', + weighted: false, + variableIntervalType: FileDataVariableIntervalType.DISCRETE, + variableFormatType: FileDataVariableFormatType.CHARACTER, + isOrderedCategorical: false, + fileOrder: 0, + UNF: 'UNF:6:j194WNS7SAe+mFrz/3oCwQ==', + variableMetadata: [], + summaryStatistics: { + medn: '28.27591', + mode: '.', + min: '27.6741', + }, + }, + { + id: 15, + name: 'test2', + label: 'test2', + weighted: false, + variableIntervalType: FileDataVariableIntervalType.DISCRETE, + variableFormatType: FileDataVariableFormatType.CHARACTER, + isOrderedCategorical: false, + fileOrder: 1, + UNF: 'UNF:6:KPoFCWSEsLpy11Lh11CXWQ==', + variableMetadata: [], + }, + ], + }; +}; + +export const createFileDataTablePayload = (): any => { + return { + varQuantity: 21, + caseQuantity: 122, + UNF: 'UNF:6:awd/lz8bNdoXn8f1sjAAAQ==', + dataVariables: [ + { + id: 3, + name: 'test1', + label: 'test1', + weighted: false, + variableIntervalType: 'discrete', + variableFormatType: 'CHARACTER', + isOrderedCategorical: false, + fileOrder: 0, + UNF: 'UNF:6:j194WNS7SAe+mFrz/3oCwQ==', + variableMetadata: [], + summaryStatistics: { + medn: '28.27591', + mode: '.', + min: '27.6741', + }, + }, + { + id: 15, + name: 'test2', + label: 'test2', + weighted: false, + variableIntervalType: 'discrete', + variableFormatType: 'CHARACTER', + isOrderedCategorical: false, + fileOrder: 1, + UNF: 'UNF:6:KPoFCWSEsLpy11Lh11CXWQ==', + variableMetadata: [], + }, + ], + }; +}; diff --git a/test/testHelpers/files/fileUserPermissionsHelper.ts b/test/testHelpers/files/fileUserPermissionsHelper.ts new file mode 100644 index 00000000..f2af5cc2 --- /dev/null +++ b/test/testHelpers/files/fileUserPermissionsHelper.ts @@ -0,0 +1,8 @@ +import { FileUserPermissions } from '../../../src/files/domain/models/FileUserPermissions'; + +export const createFileUserPermissionsModel = (): FileUserPermissions => { + return { + canDownloadFile: true, + canEditOwnerDataset: true, + }; +}; diff --git a/test/testHelpers/files/filesHelper.ts b/test/testHelpers/files/filesHelper.ts new file mode 100644 index 00000000..97dab816 --- /dev/null +++ b/test/testHelpers/files/filesHelper.ts @@ -0,0 +1,73 @@ +import { File } from '../../../src/files/domain/models/File'; +import axios, { AxiosResponse } from 'axios'; +import { TestConstants } from '../TestConstants'; +import { readFile } from 'fs/promises'; + +export const createFileModel = (): File => { + return { + id: 1, + persistentId: '', + name: 'test', + sizeBytes: 127426, + version: 1, + restricted: false, + contentType: 'image/png', + storageIdentifier: 'local://18945a85439-9fa52783e5cb', + rootDataFileId: 4, + previousDataFileId: 4, + md5: '29e413e0c881e17314ce8116fed4d1a7', + metadataId: 4, + creationDate: new Date('2023-07-11'), + embargo: { + dateAvailable: new Date('2023-07-11'), + reason: 'test', + }, + checksum: { + type: 'MD5', + value: '29e413e0c881e17314ce8116fed4d1a7', + }, + }; +}; + +export const createFilePayload = (): any => { + return { + label: 'test', + restricted: false, + version: 1, + datasetVersionId: 2, + dataFile: { + id: 1, + persistentId: '', + filename: 'test', + contentType: 'image/png', + filesize: 127426, + storageIdentifier: 'local://18945a85439-9fa52783e5cb', + rootDataFileId: 4, + previousDataFileId: 4, + md5: '29e413e0c881e17314ce8116fed4d1a7', + fileMetadataId: 4, + creationDate: '2023-07-11', + varGroups: [], + embargo: { + dateAvailable: '2023-07-11', + reason: 'test', + }, + checksum: { + type: 'MD5', + value: '29e413e0c881e17314ce8116fed4d1a7', + }, + }, + }; +}; + +export const uploadFileViaApi = async (datasetId: number, fileName: string): Promise => { + const formData = new FormData(); + const file = await readFile(`${__dirname}/${fileName}`); + formData.append('file', new Blob([file]), fileName); + return await axios.post(`${TestConstants.TEST_API_URL}/datasets/${datasetId}/add`, formData, { + headers: { + 'Content-Type': 'multipart/form-data', + 'X-Dataverse-Key': process.env.TEST_API_KEY, + }, + }); +}; diff --git a/test/testHelpers/files/test-file-1.txt b/test/testHelpers/files/test-file-1.txt new file mode 100644 index 00000000..75342f57 --- /dev/null +++ b/test/testHelpers/files/test-file-1.txt @@ -0,0 +1 @@ +test file 1 diff --git a/test/testHelpers/files/test-file-2.txt b/test/testHelpers/files/test-file-2.txt new file mode 100644 index 00000000..8c95218f --- /dev/null +++ b/test/testHelpers/files/test-file-2.txt @@ -0,0 +1 @@ +test file 2 diff --git a/test/testHelpers/files/test-file-3.txt b/test/testHelpers/files/test-file-3.txt new file mode 100644 index 00000000..23ea6e58 --- /dev/null +++ b/test/testHelpers/files/test-file-3.txt @@ -0,0 +1 @@ +test file 3 diff --git a/test/testHelpers/files/test-file-4.tab b/test/testHelpers/files/test-file-4.tab new file mode 100644 index 00000000..d750d42d --- /dev/null +++ b/test/testHelpers/files/test-file-4.tab @@ -0,0 +1,11 @@ +position name age +1 "Belle" 36 +2 "Lola" 37 +3 "Jayden" 45 +4 "Margaret" 37 +5 "Russell" 40 +6 "Bertie" 60 +7 "Maud" 34 +8 "Mabel" 31 +9 "Trevor" 51 +10 "Duane" 26 diff --git a/test/unit/datasets/DatasetsRepository.test.ts b/test/unit/datasets/DatasetsRepository.test.ts index 9a1f194b..ca7004cf 100644 --- a/test/unit/datasets/DatasetsRepository.test.ts +++ b/test/unit/datasets/DatasetsRepository.test.ts @@ -76,156 +76,154 @@ describe('DatasetsRepository', () => { }); }); - describe('getDatasetById', () => { - test('should return Dataset when providing id, no version, and response is successful', async () => { - const axiosGetStub = sandbox.stub(axios, 'get').resolves(testDatasetVersionSuccessfulResponse); - const expectedApiEndpoint = `${TestConstants.TEST_API_URL}/datasets/${testDatasetModel.id}/versions/:latest`; - - // API Key auth - let actual = await sut.getDatasetById(testDatasetModel.id); - - assert.calledWithExactly( - axiosGetStub, - expectedApiEndpoint, - TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_API_KEY, - ); - assert.match(actual, testDatasetModel); - - // Session cookie auth - ApiConfig.init(TestConstants.TEST_API_URL, DataverseApiAuthMechanism.SESSION_COOKIE); - actual = await sut.getDatasetById(testDatasetModel.id); - assert.calledWithExactly( - axiosGetStub, - expectedApiEndpoint, - TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_SESSION_COOKIE, - ); - assert.match(actual, testDatasetModel); - }); - - test('should return Dataset when providing id, version, and response is successful', async () => { - const axiosGetStub = sandbox.stub(axios, 'get').resolves(testDatasetVersionSuccessfulResponse); - - const actual = await sut.getDatasetById(testDatasetModel.id, String(testDatasetModel.versionId)); - - assert.calledWithExactly( - axiosGetStub, - `${TestConstants.TEST_API_URL}/datasets/${testDatasetModel.id}/versions/${testDatasetModel.versionId}`, - TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_API_KEY, - ); - assert.match(actual, testDatasetModel); - }); - - test('should return Dataset when providing id, version, and response with license is successful', async () => { - const testDatasetLicense = createDatasetLicenseModel(); - const testDatasetVersionWithLicenseSuccessfulResponse = { - data: { - status: 'OK', - data: createDatasetVersionPayload(testDatasetLicense), - }, - }; - const axiosGetStub = sandbox.stub(axios, 'get').resolves(testDatasetVersionWithLicenseSuccessfulResponse); - - const actual = await sut.getDatasetById(testDatasetModel.id, String(testDatasetModel.versionId)); - - assert.calledWithExactly( - axiosGetStub, - `${TestConstants.TEST_API_URL}/datasets/${testDatasetModel.id}/versions/${testDatasetModel.versionId}`, - TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_API_KEY, - ); - assert.match(actual, createDatasetModel(testDatasetLicense)); - }); - - test('should return Dataset when providing id, version, and response with license without icon URI is successful', async () => { - const testDatasetLicenseWithoutIconUri = createDatasetLicenseModel(false); - const testDatasetVersionWithLicenseSuccessfulResponse = { - data: { - status: 'OK', - data: createDatasetVersionPayload(testDatasetLicenseWithoutIconUri), - }, - }; - const axiosGetStub = sandbox.stub(axios, 'get').resolves(testDatasetVersionWithLicenseSuccessfulResponse); - - const actual = await sut.getDatasetById(testDatasetModel.id, String(testDatasetModel.versionId)); - - assert.calledWithExactly( - axiosGetStub, - `${TestConstants.TEST_API_URL}/datasets/${testDatasetModel.id}/versions/${testDatasetModel.versionId}`, - TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_API_KEY, - ); - assert.match(actual, createDatasetModel(testDatasetLicenseWithoutIconUri)); + describe('getDataset', () => { + describe('by numeric id', () => { + test('should return Dataset when providing id, no version, and response is successful', async () => { + const axiosGetStub = sandbox.stub(axios, 'get').resolves(testDatasetVersionSuccessfulResponse); + const expectedApiEndpoint = `${TestConstants.TEST_API_URL}/datasets/${testDatasetModel.id}/versions/:latest`; + + // API Key auth + let actual = await sut.getDataset(testDatasetModel.id); + + assert.calledWithExactly( + axiosGetStub, + expectedApiEndpoint, + TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_API_KEY, + ); + assert.match(actual, testDatasetModel); + + // Session cookie auth + ApiConfig.init(TestConstants.TEST_API_URL, DataverseApiAuthMechanism.SESSION_COOKIE); + actual = await sut.getDataset(testDatasetModel.id); + assert.calledWithExactly( + axiosGetStub, + expectedApiEndpoint, + TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_SESSION_COOKIE, + ); + assert.match(actual, testDatasetModel); + }); + + test('should return Dataset when providing id, version, and response is successful', async () => { + const axiosGetStub = sandbox.stub(axios, 'get').resolves(testDatasetVersionSuccessfulResponse); + + const actual = await sut.getDataset(testDatasetModel.id, String(testDatasetModel.versionId)); + + assert.calledWithExactly( + axiosGetStub, + `${TestConstants.TEST_API_URL}/datasets/${testDatasetModel.id}/versions/${testDatasetModel.versionId}`, + TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_API_KEY, + ); + assert.match(actual, testDatasetModel); + }); + + test('should return Dataset when providing id, version, and response with license is successful', async () => { + const testDatasetLicense = createDatasetLicenseModel(); + const testDatasetVersionWithLicenseSuccessfulResponse = { + data: { + status: 'OK', + data: createDatasetVersionPayload(testDatasetLicense), + }, + }; + const axiosGetStub = sandbox.stub(axios, 'get').resolves(testDatasetVersionWithLicenseSuccessfulResponse); + + const actual = await sut.getDataset(testDatasetModel.id, String(testDatasetModel.versionId)); + + assert.calledWithExactly( + axiosGetStub, + `${TestConstants.TEST_API_URL}/datasets/${testDatasetModel.id}/versions/${testDatasetModel.versionId}`, + TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_API_KEY, + ); + assert.match(actual, createDatasetModel(testDatasetLicense)); + }); + + test('should return Dataset when providing id, version, and response with license without icon URI is successful', async () => { + const testDatasetLicenseWithoutIconUri = createDatasetLicenseModel(false); + const testDatasetVersionWithLicenseSuccessfulResponse = { + data: { + status: 'OK', + data: createDatasetVersionPayload(testDatasetLicenseWithoutIconUri), + }, + }; + const axiosGetStub = sandbox.stub(axios, 'get').resolves(testDatasetVersionWithLicenseSuccessfulResponse); + + const actual = await sut.getDataset(testDatasetModel.id, String(testDatasetModel.versionId)); + + assert.calledWithExactly( + axiosGetStub, + `${TestConstants.TEST_API_URL}/datasets/${testDatasetModel.id}/versions/${testDatasetModel.versionId}`, + TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_API_KEY, + ); + assert.match(actual, createDatasetModel(testDatasetLicenseWithoutIconUri)); + }); + + test('should return error on repository read error', async () => { + const axiosGetStub = sandbox.stub(axios, 'get').rejects(TestConstants.TEST_ERROR_RESPONSE); + + let error: ReadError = undefined; + await sut.getDataset(testDatasetModel.id).catch((e) => (error = e)); + + assert.calledWithExactly( + axiosGetStub, + `${TestConstants.TEST_API_URL}/datasets/${testDatasetModel.id}/versions/:latest`, + TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_API_KEY, + ); + expect(error).to.be.instanceOf(Error); + }); }); - - test('should return error on repository read error', async () => { - const axiosGetStub = sandbox.stub(axios, 'get').rejects(TestConstants.TEST_ERROR_RESPONSE); - - let error: ReadError = undefined; - await sut.getDatasetById(testDatasetModel.id).catch((e) => (error = e)); - - assert.calledWithExactly( - axiosGetStub, - `${TestConstants.TEST_API_URL}/datasets/${testDatasetModel.id}/versions/:latest`, - TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_API_KEY, - ); - expect(error).to.be.instanceOf(Error); - }); - }); - - describe('getDatasetByPersistentId', () => { - test('should return Dataset when providing persistent id, no version, and response is successful', async () => { - const axiosGetStub = sandbox.stub(axios, 'get').resolves(testDatasetVersionSuccessfulResponse); - const expectedApiEndpoint = `${TestConstants.TEST_API_URL}/datasets/:persistentId/versions/:latest?persistentId=${testDatasetModel.persistentId}`; - - // API Key auth - let actual = await sut.getDatasetByPersistentId(testDatasetModel.persistentId); - - assert.calledWithExactly( - axiosGetStub, - expectedApiEndpoint, - TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_API_KEY, - ); - assert.match(actual, testDatasetModel); - - // Session cookie auth - ApiConfig.init(TestConstants.TEST_API_URL, DataverseApiAuthMechanism.SESSION_COOKIE); - - actual = await sut.getDatasetByPersistentId(testDatasetModel.persistentId); - - assert.calledWithExactly( - axiosGetStub, - expectedApiEndpoint, - TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_SESSION_COOKIE, - ); - assert.match(actual, testDatasetModel); - }); - - test('should return Dataset when providing persistent id, version, and response is successful', async () => { - const axiosGetStub = sandbox.stub(axios, 'get').resolves(testDatasetVersionSuccessfulResponse); - - const actual = await sut.getDatasetByPersistentId( - testDatasetModel.persistentId, - String(testDatasetModel.versionId), - ); - - assert.calledWithExactly( - axiosGetStub, - `${TestConstants.TEST_API_URL}/datasets/:persistentId/versions/${testDatasetModel.versionId}?persistentId=${testDatasetModel.persistentId}`, - TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_API_KEY, - ); - assert.match(actual, testDatasetModel); - }); - - test('should return error on repository read error', async () => { - const axiosGetStub = sandbox.stub(axios, 'get').rejects(TestConstants.TEST_ERROR_RESPONSE); - - let error: ReadError = undefined; - await sut.getDatasetByPersistentId(testDatasetModel.persistentId).catch((e) => (error = e)); - - assert.calledWithExactly( - axiosGetStub, - `${TestConstants.TEST_API_URL}/datasets/:persistentId/versions/:latest?persistentId=${testDatasetModel.persistentId}`, - TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_API_KEY, - ); - expect(error).to.be.instanceOf(Error); + describe('by persistent id', () => { + test('should return Dataset when providing persistent id, no version, and response is successful', async () => { + const axiosGetStub = sandbox.stub(axios, 'get').resolves(testDatasetVersionSuccessfulResponse); + const expectedApiEndpoint = `${TestConstants.TEST_API_URL}/datasets/:persistentId/versions/:latest?persistentId=${testDatasetModel.persistentId}`; + + // API Key auth + let actual = await sut.getDataset(testDatasetModel.persistentId); + + assert.calledWithExactly( + axiosGetStub, + expectedApiEndpoint, + TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_API_KEY, + ); + assert.match(actual, testDatasetModel); + + // Session cookie auth + ApiConfig.init(TestConstants.TEST_API_URL, DataverseApiAuthMechanism.SESSION_COOKIE); + + actual = await sut.getDataset(testDatasetModel.persistentId); + + assert.calledWithExactly( + axiosGetStub, + expectedApiEndpoint, + TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_SESSION_COOKIE, + ); + assert.match(actual, testDatasetModel); + }); + + test('should return Dataset when providing persistent id, version, and response is successful', async () => { + const axiosGetStub = sandbox.stub(axios, 'get').resolves(testDatasetVersionSuccessfulResponse); + + const actual = await sut.getDataset(testDatasetModel.persistentId, String(testDatasetModel.versionId)); + + assert.calledWithExactly( + axiosGetStub, + `${TestConstants.TEST_API_URL}/datasets/:persistentId/versions/${testDatasetModel.versionId}?persistentId=${testDatasetModel.persistentId}`, + TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_API_KEY, + ); + assert.match(actual, testDatasetModel); + }); + + test('should return error on repository read error', async () => { + const axiosGetStub = sandbox.stub(axios, 'get').rejects(TestConstants.TEST_ERROR_RESPONSE); + + let error: ReadError = undefined; + await sut.getDataset(testDatasetModel.persistentId).catch((e) => (error = e)); + + assert.calledWithExactly( + axiosGetStub, + `${TestConstants.TEST_API_URL}/datasets/:persistentId/versions/:latest?persistentId=${testDatasetModel.persistentId}`, + TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_API_KEY, + ); + expect(error).to.be.instanceOf(Error); + }); }); }); diff --git a/test/unit/datasets/GetDatasetById.test.ts b/test/unit/datasets/GetDataset.test.ts similarity index 76% rename from test/unit/datasets/GetDatasetById.test.ts rename to test/unit/datasets/GetDataset.test.ts index 07199b37..9e11aa45 100644 --- a/test/unit/datasets/GetDatasetById.test.ts +++ b/test/unit/datasets/GetDataset.test.ts @@ -1,4 +1,4 @@ -import { GetDatasetById } from '../../../src/datasets/domain/useCases/GetDatasetById'; +import { GetDataset } from '../../../src/datasets/domain/useCases/GetDataset'; import { IDatasetsRepository } from '../../../src/datasets/domain/repositories/IDatasetsRepository'; import { assert, createSandbox, SinonSandbox } from 'sinon'; import { createDatasetModel } from '../../testHelpers/datasets/datasetHelper'; @@ -15,8 +15,8 @@ describe('execute', () => { const testDataset = createDatasetModel(); const datasetsRepositoryStub = {}; const getDatasetStub = sandbox.stub().returns(testDataset); - datasetsRepositoryStub.getDatasetById = getDatasetStub; - const sut = new GetDatasetById(datasetsRepositoryStub); + datasetsRepositoryStub.getDataset = getDatasetStub; + const sut = new GetDataset(datasetsRepositoryStub); const actual = await sut.execute(1); @@ -27,8 +27,8 @@ describe('execute', () => { test('should return error result on repository error', async () => { const datasetsRepositoryStub = {}; const testReadError = new ReadError(); - datasetsRepositoryStub.getDatasetById = sandbox.stub().throwsException(testReadError); - const sut = new GetDatasetById(datasetsRepositoryStub); + datasetsRepositoryStub.getDataset = sandbox.stub().throwsException(testReadError); + const sut = new GetDataset(datasetsRepositoryStub); let actualError: ReadError = undefined; await sut.execute(1).catch((e) => (actualError = e)); diff --git a/test/unit/datasets/GetDatasetByPersistentId.test.ts b/test/unit/datasets/GetDatasetByPersistentId.test.ts deleted file mode 100644 index b3ba5060..00000000 --- a/test/unit/datasets/GetDatasetByPersistentId.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { GetDatasetByPersistentId } from '../../../src/datasets/domain/useCases/GetDatasetByPersistentId'; -import { IDatasetsRepository } from '../../../src/datasets/domain/repositories/IDatasetsRepository'; -import { assert, createSandbox, SinonSandbox } from 'sinon'; -import { createDatasetModel } from '../../testHelpers/datasets/datasetHelper'; -import { ReadError } from '../../../src/core/domain/repositories/ReadError'; - -describe('execute', () => { - const sandbox: SinonSandbox = createSandbox(); - - afterEach(() => { - sandbox.restore(); - }); - - test('should return dataset on repository success', async () => { - const testDataset = createDatasetModel(); - const datasetsRepositoryStub = {}; - const getDatasetStub = sandbox.stub().returns(testDataset); - datasetsRepositoryStub.getDatasetByPersistentId = getDatasetStub; - const sut = new GetDatasetByPersistentId(datasetsRepositoryStub); - - const actual = await sut.execute('1'); - - assert.match(actual, testDataset); - assert.calledWithExactly(getDatasetStub, '1', undefined); - }); - - test('should return error result on repository error', async () => { - const datasetsRepositoryStub = {}; - const testReadError = new ReadError(); - datasetsRepositoryStub.getDatasetByPersistentId = sandbox.stub().throwsException(testReadError); - const sut = new GetDatasetByPersistentId(datasetsRepositoryStub); - - let actualError: ReadError = undefined; - await sut.execute('1').catch((e) => (actualError = e)); - - assert.match(actualError, testReadError); - }); -}); diff --git a/test/unit/files/FilesRepository.test.ts b/test/unit/files/FilesRepository.test.ts new file mode 100644 index 00000000..d6cecfa8 --- /dev/null +++ b/test/unit/files/FilesRepository.test.ts @@ -0,0 +1,479 @@ +import { FilesRepository } from '../../../src/files/infra/repositories/FilesRepository'; +import { assert, createSandbox, SinonSandbox } from 'sinon'; +import axios from 'axios'; +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 { FileOrderCriteria } from '../../../src/files/domain/models/FileOrderCriteria'; +import { createFilePayload, createFileModel } from '../../testHelpers/files/filesHelper'; +import { createFileDataTablePayload, createFileDataTableModel } from '../../testHelpers/files/fileDataTablesHelper'; +import { createFileUserPermissionsModel } from '../../testHelpers/files/fileUserPermissionsHelper'; + +describe('FilesRepository', () => { + const sandbox: SinonSandbox = createSandbox(); + const sut: FilesRepository = new FilesRepository(); + const testFilesSuccessfulResponse = { + data: { + status: 'OK', + data: [createFilePayload()], + }, + }; + const testFile = createFileModel(); + + beforeEach(() => { + ApiConfig.init(TestConstants.TEST_API_URL, DataverseApiAuthMechanism.API_KEY, TestConstants.TEST_DUMMY_API_KEY); + }); + + afterEach(() => { + sandbox.restore(); + }); + + describe('getDatasetFiles', () => { + describe('by numeric id', () => { + const testDatasetId = 1; + + test('should return files on successful response', async () => { + const axiosGetStub = sandbox.stub(axios, 'get').resolves(testFilesSuccessfulResponse); + + const expectedApiEndpoint = `${TestConstants.TEST_API_URL}/datasets/${testDatasetId}/versions/:latest/files`; + const expectedFiles = [testFile]; + + // API Key auth + let actual = await sut.getDatasetFiles(testDatasetId); + + assert.calledWithExactly( + axiosGetStub, + expectedApiEndpoint, + TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_API_KEY, + ); + assert.match(actual, expectedFiles); + + // Session cookie auth + ApiConfig.init(TestConstants.TEST_API_URL, DataverseApiAuthMechanism.SESSION_COOKIE); + + actual = await sut.getDatasetFiles(testDatasetId); + + assert.calledWithExactly( + axiosGetStub, + expectedApiEndpoint, + TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_SESSION_COOKIE, + ); + assert.match(actual, expectedFiles); + }); + + test('should return files when providing id, optional params, and response is successful', async () => { + const axiosGetStub = sandbox.stub(axios, 'get').resolves(testFilesSuccessfulResponse); + + const testVersionId = ':draft'; + const testLimit = 10; + const testOffset = 20; + const testFileOrderCriteria = FileOrderCriteria.NEWEST; + + const actual = await sut.getDatasetFiles( + testDatasetId, + testVersionId, + testLimit, + testOffset, + testFileOrderCriteria, + ); + + const expectedRequestConfig = { + params: { + limit: testLimit, + offset: testOffset, + orderCriteria: testFileOrderCriteria.toString(), + }, + headers: TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_API_KEY.headers, + }; + + assert.calledWithExactly( + axiosGetStub, + `${TestConstants.TEST_API_URL}/datasets/${testDatasetId}/versions/${testVersionId}/files`, + expectedRequestConfig, + ); + assert.match(actual, [testFile]); + }); + + test('should return error result on error response', async () => { + const axiosGetStub = sandbox.stub(axios, 'get').rejects(TestConstants.TEST_ERROR_RESPONSE); + + let error: ReadError = undefined; + await sut.getDatasetFiles(testDatasetId).catch((e) => (error = e)); + + assert.calledWithExactly( + axiosGetStub, + `${TestConstants.TEST_API_URL}/datasets/${testDatasetId}/versions/:latest/files`, + TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_API_KEY, + ); + expect(error).to.be.instanceOf(Error); + }); + }); + + describe('by persistent id', () => { + test('should return files on successful response', async () => { + const axiosGetStub = sandbox.stub(axios, 'get').resolves(testFilesSuccessfulResponse); + const expectedApiEndpoint = `${TestConstants.TEST_API_URL}/datasets/:persistentId/versions/:latest/files?persistentId=${TestConstants.TEST_DUMMY_PERSISTENT_ID}`; + const expectedFiles = [testFile]; + + // API Key auth + let actual = await sut.getDatasetFiles(TestConstants.TEST_DUMMY_PERSISTENT_ID); + + assert.calledWithExactly( + axiosGetStub, + expectedApiEndpoint, + TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_API_KEY, + ); + assert.match(actual, expectedFiles); + + // Session cookie auth + ApiConfig.init(TestConstants.TEST_API_URL, DataverseApiAuthMechanism.SESSION_COOKIE); + + actual = await sut.getDatasetFiles(TestConstants.TEST_DUMMY_PERSISTENT_ID); + + assert.calledWithExactly( + axiosGetStub, + expectedApiEndpoint, + TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_SESSION_COOKIE, + ); + assert.match(actual, expectedFiles); + }); + + test('should return files when providing persistent id, optional params, and response is successful', async () => { + const axiosGetStub = sandbox.stub(axios, 'get').resolves(testFilesSuccessfulResponse); + + const testVersionId = ':draft'; + const testLimit = 10; + const testOffset = 20; + const testFileOrderCriteria = FileOrderCriteria.NEWEST; + + const actual = await sut.getDatasetFiles( + TestConstants.TEST_DUMMY_PERSISTENT_ID, + testVersionId, + testLimit, + testOffset, + testFileOrderCriteria, + ); + + const expectedRequestConfig = { + params: { + limit: testLimit, + offset: testOffset, + orderCriteria: testFileOrderCriteria.toString(), + }, + headers: TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_API_KEY.headers, + }; + + assert.calledWithExactly( + axiosGetStub, + `${TestConstants.TEST_API_URL}/datasets/:persistentId/versions/${testVersionId}/files?persistentId=${TestConstants.TEST_DUMMY_PERSISTENT_ID}`, + expectedRequestConfig, + ); + assert.match(actual, [testFile]); + }); + + test('should return error result on error response', async () => { + const axiosGetStub = sandbox.stub(axios, 'get').rejects(TestConstants.TEST_ERROR_RESPONSE); + + let error: ReadError = undefined; + await sut.getDatasetFiles(TestConstants.TEST_DUMMY_PERSISTENT_ID).catch((e) => (error = e)); + + assert.calledWithExactly( + axiosGetStub, + `${TestConstants.TEST_API_URL}/datasets/:persistentId/versions/:latest/files?persistentId=${TestConstants.TEST_DUMMY_PERSISTENT_ID}`, + TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_API_KEY, + ); + expect(error).to.be.instanceOf(Error); + }); + }); + }); + + describe('getFileDownloadCount', () => { + const testCount = 1; + const testFileDownloadCountResponse = { + data: { + status: 'OK', + data: { + message: `${testCount}`, + }, + }, + }; + + describe('by numeric id', () => { + test('should return count when providing id and response is successful', async () => { + const axiosGetStub = sandbox.stub(axios, 'get').resolves(testFileDownloadCountResponse); + const expectedApiEndpoint = `${TestConstants.TEST_API_URL}/files/${testFile.id}/downloadCount`; + + // API Key auth + let actual = await sut.getFileDownloadCount(testFile.id); + + assert.calledWithExactly( + axiosGetStub, + expectedApiEndpoint, + TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_API_KEY, + ); + assert.match(actual, testCount); + + // Session cookie auth + ApiConfig.init(TestConstants.TEST_API_URL, DataverseApiAuthMechanism.SESSION_COOKIE); + + actual = await sut.getFileDownloadCount(testFile.id); + + assert.calledWithExactly( + axiosGetStub, + expectedApiEndpoint, + TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_SESSION_COOKIE, + ); + assert.match(actual, testCount); + }); + + test('should return error result on error response', async () => { + const axiosGetStub = sandbox.stub(axios, 'get').rejects(TestConstants.TEST_ERROR_RESPONSE); + + let error: ReadError = undefined; + await sut.getFileDownloadCount(testFile.id).catch((e) => (error = e)); + + assert.calledWithExactly( + axiosGetStub, + `${TestConstants.TEST_API_URL}/files/${testFile.id}/downloadCount`, + TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_API_KEY, + ); + expect(error).to.be.instanceOf(Error); + }); + }); + + describe('by persistent id', () => { + test('should return count when providing persistent id and response is successful', async () => { + const axiosGetStub = sandbox.stub(axios, 'get').resolves(testFileDownloadCountResponse); + const expectedApiEndpoint = `${TestConstants.TEST_API_URL}/files/:persistentId/downloadCount?persistentId=${TestConstants.TEST_DUMMY_PERSISTENT_ID}`; + + // API Key auth + let actual = await sut.getFileDownloadCount(TestConstants.TEST_DUMMY_PERSISTENT_ID); + + assert.calledWithExactly( + axiosGetStub, + expectedApiEndpoint, + TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_API_KEY, + ); + assert.match(actual, testCount); + + // Session cookie auth + ApiConfig.init(TestConstants.TEST_API_URL, DataverseApiAuthMechanism.SESSION_COOKIE); + + actual = await sut.getFileDownloadCount(TestConstants.TEST_DUMMY_PERSISTENT_ID); + + assert.calledWithExactly( + axiosGetStub, + expectedApiEndpoint, + TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_SESSION_COOKIE, + ); + assert.match(actual, testCount); + }); + + test('should return error result on error response', async () => { + const axiosGetStub = sandbox.stub(axios, 'get').rejects(TestConstants.TEST_ERROR_RESPONSE); + + let error: ReadError = undefined; + await sut.getFileDownloadCount(TestConstants.TEST_DUMMY_PERSISTENT_ID).catch((e) => (error = e)); + + assert.calledWithExactly( + axiosGetStub, + `${TestConstants.TEST_API_URL}/files/:persistentId/downloadCount?persistentId=${TestConstants.TEST_DUMMY_PERSISTENT_ID}`, + TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_API_KEY, + ); + expect(error).to.be.instanceOf(Error); + }); + }); + }); + + describe('getFileUserPermissions', () => { + const testFileUserPermissions = createFileUserPermissionsModel(); + const testFileUserPermissionsResponse = { + data: { + status: 'OK', + data: testFileUserPermissions, + }, + }; + + describe('by numeric id', () => { + test('should return file user permissions when providing id and response is successful', async () => { + const axiosGetStub = sandbox.stub(axios, 'get').resolves(testFileUserPermissionsResponse); + const expectedApiEndpoint = `${TestConstants.TEST_API_URL}/access/datafile/${testFile.id}/userPermissions`; + + // API Key auth + let actual = await sut.getFileUserPermissions(testFile.id); + + assert.calledWithExactly( + axiosGetStub, + expectedApiEndpoint, + TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_API_KEY, + ); + assert.match(actual, testFileUserPermissions); + + // Session cookie auth + ApiConfig.init(TestConstants.TEST_API_URL, DataverseApiAuthMechanism.SESSION_COOKIE); + + actual = await sut.getFileUserPermissions(testFile.id); + + assert.calledWithExactly( + axiosGetStub, + expectedApiEndpoint, + TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_SESSION_COOKIE, + ); + assert.match(actual, testFileUserPermissions); + }); + + test('should return error result on error response', async () => { + const axiosGetStub = sandbox.stub(axios, 'get').rejects(TestConstants.TEST_ERROR_RESPONSE); + + let error: ReadError = undefined; + await sut.getFileUserPermissions(testFile.id).catch((e) => (error = e)); + + assert.calledWithExactly( + axiosGetStub, + `${TestConstants.TEST_API_URL}/access/datafile/${testFile.id}/userPermissions`, + TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_API_KEY, + ); + expect(error).to.be.instanceOf(Error); + }); + }); + + describe('by persistent id', () => { + test('should return file user permissions when providing persistent id and response is successful', async () => { + const axiosGetStub = sandbox.stub(axios, 'get').resolves(testFileUserPermissionsResponse); + const expectedApiEndpoint = `${TestConstants.TEST_API_URL}/access/datafile/:persistentId/userPermissions?persistentId=${TestConstants.TEST_DUMMY_PERSISTENT_ID}`; + // API Key auth + let actual = await sut.getFileUserPermissions(TestConstants.TEST_DUMMY_PERSISTENT_ID); + + assert.calledWithExactly( + axiosGetStub, + expectedApiEndpoint, + TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_API_KEY, + ); + assert.match(actual, testFileUserPermissions); + + // Session cookie auth + ApiConfig.init(TestConstants.TEST_API_URL, DataverseApiAuthMechanism.SESSION_COOKIE); + + actual = await sut.getFileUserPermissions(TestConstants.TEST_DUMMY_PERSISTENT_ID); + + assert.calledWithExactly( + axiosGetStub, + expectedApiEndpoint, + TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_SESSION_COOKIE, + ); + assert.match(actual, testFileUserPermissions); + }); + + test('should return error result on error response', async () => { + const axiosGetStub = sandbox.stub(axios, 'get').rejects(TestConstants.TEST_ERROR_RESPONSE); + + let error: ReadError = undefined; + await sut.getFileUserPermissions(TestConstants.TEST_DUMMY_PERSISTENT_ID).catch((e) => (error = e)); + + assert.calledWithExactly( + axiosGetStub, + `${TestConstants.TEST_API_URL}/access/datafile/:persistentId/userPermissions?persistentId=${TestConstants.TEST_DUMMY_PERSISTENT_ID}`, + TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_API_KEY, + ); + expect(error).to.be.instanceOf(Error); + }); + }); + }); + + describe('getFileDataTables', () => { + const expectedDataTables = [createFileDataTableModel()]; + const testGetFileDataTablesResponse = { + data: { + status: 'OK', + data: [createFileDataTablePayload()], + }, + }; + + describe('by numeric id', () => { + test('should return data tables when providing id and response is successful', async () => { + const axiosGetStub = sandbox.stub(axios, 'get').resolves(testGetFileDataTablesResponse); + const expectedApiEndpoint = `${TestConstants.TEST_API_URL}/files/${testFile.id}/dataTables`; + + // API Key auth + let actual = await sut.getFileDataTables(testFile.id); + + assert.calledWithExactly( + axiosGetStub, + expectedApiEndpoint, + TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_API_KEY, + ); + assert.match(actual, expectedDataTables); + + // Session cookie auth + ApiConfig.init(TestConstants.TEST_API_URL, DataverseApiAuthMechanism.SESSION_COOKIE); + + actual = await sut.getFileDataTables(testFile.id); + + assert.calledWithExactly( + axiosGetStub, + expectedApiEndpoint, + TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_SESSION_COOKIE, + ); + assert.match(actual, expectedDataTables); + }); + + test('should return error result on error response', async () => { + const axiosGetStub = sandbox.stub(axios, 'get').rejects(TestConstants.TEST_ERROR_RESPONSE); + + let error: ReadError = undefined; + await sut.getFileDataTables(testFile.id).catch((e) => (error = e)); + + assert.calledWithExactly( + axiosGetStub, + `${TestConstants.TEST_API_URL}/files/${testFile.id}/dataTables`, + TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_API_KEY, + ); + expect(error).to.be.instanceOf(Error); + }); + }); + + describe('by persistent id', () => { + test('should return data tables when providing persistent id and response is successful', async () => { + const axiosGetStub = sandbox.stub(axios, 'get').resolves(testGetFileDataTablesResponse); + const expectedApiEndpoint = `${TestConstants.TEST_API_URL}/files/:persistentId/dataTables?persistentId=${TestConstants.TEST_DUMMY_PERSISTENT_ID}`; + + // API Key auth + let actual = await sut.getFileDataTables(TestConstants.TEST_DUMMY_PERSISTENT_ID); + + assert.calledWithExactly( + axiosGetStub, + expectedApiEndpoint, + TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_API_KEY, + ); + assert.match(actual, expectedDataTables); + + // Session cookie auth + ApiConfig.init(TestConstants.TEST_API_URL, DataverseApiAuthMechanism.SESSION_COOKIE); + + actual = await sut.getFileDataTables(TestConstants.TEST_DUMMY_PERSISTENT_ID); + + assert.calledWithExactly( + axiosGetStub, + expectedApiEndpoint, + TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_SESSION_COOKIE, + ); + assert.match(actual, expectedDataTables); + }); + + test('should return error result on error response', async () => { + const axiosGetStub = sandbox.stub(axios, 'get').rejects(TestConstants.TEST_ERROR_RESPONSE); + + let error: ReadError = undefined; + await sut.getFileDataTables(TestConstants.TEST_DUMMY_PERSISTENT_ID).catch((e) => (error = e)); + + assert.calledWithExactly( + axiosGetStub, + `${TestConstants.TEST_API_URL}/files/:persistentId/dataTables?persistentId=${TestConstants.TEST_DUMMY_PERSISTENT_ID}`, + TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_API_KEY, + ); + expect(error).to.be.instanceOf(Error); + }); + }); + }); +}); diff --git a/test/unit/files/GetDatasetFiles.test.ts b/test/unit/files/GetDatasetFiles.test.ts new file mode 100644 index 00000000..94b449f1 --- /dev/null +++ b/test/unit/files/GetDatasetFiles.test.ts @@ -0,0 +1,39 @@ +import { GetDatasetFiles } from '../../../src/files/domain/useCases/GetDatasetFiles'; +import { IFilesRepository } from '../../../src/files/domain/repositories/IFilesRepository'; +import { assert, createSandbox, SinonSandbox } from 'sinon'; +import { ReadError } from '../../../src/core/domain/repositories/ReadError'; +import { File } from '../../../src/files/domain/models/File'; +import { createFileModel } from '../../testHelpers/files/filesHelper'; + +describe('execute', () => { + const sandbox: SinonSandbox = createSandbox(); + + afterEach(() => { + sandbox.restore(); + }); + + test('should return files on repository success', async () => { + const testFiles: File[] = [createFileModel()]; + const filesRepositoryStub = {}; + const getDatasetFilesStub = sandbox.stub().returns(testFiles); + filesRepositoryStub.getDatasetFiles = getDatasetFilesStub; + const sut = new GetDatasetFiles(filesRepositoryStub); + + const actual = await sut.execute(1); + + assert.match(actual, testFiles); + assert.calledWithExactly(getDatasetFilesStub, 1, undefined, undefined, undefined, undefined); + }); + + test('should return error result on repository error', async () => { + const filesRepositoryStub = {}; + const testReadError = new ReadError(); + filesRepositoryStub.getDatasetFiles = sandbox.stub().throwsException(testReadError); + const sut = new GetDatasetFiles(filesRepositoryStub); + + let actualError: ReadError = undefined; + await sut.execute(1).catch((e: ReadError) => (actualError = e)); + + assert.match(actualError, testReadError); + }); +}); diff --git a/test/unit/files/GetFileDataTables.test.ts b/test/unit/files/GetFileDataTables.test.ts new file mode 100644 index 00000000..34104e2a --- /dev/null +++ b/test/unit/files/GetFileDataTables.test.ts @@ -0,0 +1,40 @@ +import { GetFileDataTables } from '../../../src/files/domain/useCases/GetFileDataTables'; +import { IFilesRepository } from '../../../src/files/domain/repositories/IFilesRepository'; +import { assert, createSandbox, SinonSandbox } from 'sinon'; +import { ReadError } from '../../../src/core/domain/repositories/ReadError'; +import { FileDataTable } from '../../../src/files/domain/models/FileDataTable'; +import { createFileDataTableModel } from '../../testHelpers/files/fileDataTablesHelper'; + +describe('execute', () => { + const sandbox: SinonSandbox = createSandbox(); + const testFileId = 1; + + afterEach(() => { + sandbox.restore(); + }); + + test('should return file data tables on repository success', async () => { + const testDataTables: FileDataTable[] = [createFileDataTableModel()]; + const filesRepositoryStub = {}; + const getFileDataTablesStub = sandbox.stub().returns(testDataTables); + filesRepositoryStub.getFileDataTables = getFileDataTablesStub; + const sut = new GetFileDataTables(filesRepositoryStub); + + const actual = await sut.execute(testFileId); + + assert.match(actual, testDataTables); + assert.calledWithExactly(getFileDataTablesStub, testFileId); + }); + + test('should return error result on repository error', async () => { + const filesRepositoryStub = {}; + const testReadError = new ReadError(); + filesRepositoryStub.getFileDataTables = sandbox.stub().throwsException(testReadError); + const sut = new GetFileDataTables(filesRepositoryStub); + + let actualError: ReadError = undefined; + await sut.execute(testFileId).catch((e: ReadError) => (actualError = e)); + + assert.match(actualError, testReadError); + }); +}); diff --git a/test/unit/files/GetFileDownloadCount.test.ts b/test/unit/files/GetFileDownloadCount.test.ts new file mode 100644 index 00000000..694959b3 --- /dev/null +++ b/test/unit/files/GetFileDownloadCount.test.ts @@ -0,0 +1,51 @@ +import { GetFileDownloadCount } from '../../../src/files/domain/useCases/GetFileDownloadCount'; +import { IFilesRepository } from '../../../src/files/domain/repositories/IFilesRepository'; +import { assert, createSandbox, SinonSandbox } from 'sinon'; +import { ReadError } from '../../../src/core/domain/repositories/ReadError'; +import { TestConstants } from '../../testHelpers/TestConstants'; + +describe('execute', () => { + const sandbox: SinonSandbox = createSandbox(); + const testFileId = 1; + const testCount = 10; + + afterEach(() => { + sandbox.restore(); + }); + + test('should return count on repository success filtering by id', async () => { + const filesRepositoryStub = {}; + const getFileGuestbookResponsesCountStub = sandbox.stub().returns(testCount); + filesRepositoryStub.getFileDownloadCount = getFileGuestbookResponsesCountStub; + const sut = new GetFileDownloadCount(filesRepositoryStub); + + const actual = await sut.execute(testFileId); + + assert.match(actual, testCount); + assert.calledWithExactly(getFileGuestbookResponsesCountStub, testFileId); + }); + + test('should return count on repository success filtering by persistent id', async () => { + const filesRepositoryStub = {}; + const getFileGuestbookResponsesCountStub = sandbox.stub().returns(testCount); + filesRepositoryStub.getFileDownloadCount = getFileGuestbookResponsesCountStub; + const sut = new GetFileDownloadCount(filesRepositoryStub); + + const actual = await sut.execute(TestConstants.TEST_DUMMY_PERSISTENT_ID); + + assert.match(actual, testCount); + assert.calledWithExactly(getFileGuestbookResponsesCountStub, TestConstants.TEST_DUMMY_PERSISTENT_ID); + }); + + test('should return error result on repository error', async () => { + const filesRepositoryStub = {}; + const testReadError = new ReadError(); + filesRepositoryStub.getFileDownloadCount = sandbox.stub().throwsException(testReadError); + const sut = new GetFileDownloadCount(filesRepositoryStub); + + let actualError: ReadError = undefined; + await sut.execute(testFileId).catch((e) => (actualError = e)); + + assert.match(actualError, testReadError); + }); +}); diff --git a/test/unit/files/GetFileUserPermissions.test.ts b/test/unit/files/GetFileUserPermissions.test.ts new file mode 100644 index 00000000..d113e730 --- /dev/null +++ b/test/unit/files/GetFileUserPermissions.test.ts @@ -0,0 +1,39 @@ +import { GetFileUserPermissions } from '../../../src/files/domain/useCases/GetFileUserPermissions'; +import { IFilesRepository } from '../../../src/files/domain/repositories/IFilesRepository'; +import { assert, createSandbox, SinonSandbox } from 'sinon'; +import { ReadError } from '../../../src/core/domain/repositories/ReadError'; +import { createFileUserPermissionsModel } from '../../testHelpers/files/fileUserPermissionsHelper'; + +describe('execute', () => { + const sandbox: SinonSandbox = createSandbox(); + const testFileId = 1; + + afterEach(() => { + sandbox.restore(); + }); + + test('should return file user permissions on repository success', async () => { + const testFileUserPermissions = createFileUserPermissionsModel(); + const filesRepositoryStub = {}; + const getFileUserPermissionsStub = sandbox.stub().returns(testFileUserPermissions); + filesRepositoryStub.getFileUserPermissions = getFileUserPermissionsStub; + const sut = new GetFileUserPermissions(filesRepositoryStub); + + const actual = await sut.execute(testFileId); + + assert.match(actual, testFileUserPermissions); + assert.calledWithExactly(getFileUserPermissionsStub, testFileId); + }); + + test('should return error result on repository error', async () => { + const filesRepositoryStub = {}; + const testReadError = new ReadError(); + filesRepositoryStub.getFileUserPermissions = sandbox.stub().throwsException(testReadError); + const sut = new GetFileUserPermissions(filesRepositoryStub); + + let actualError: ReadError = undefined; + await sut.execute(testFileId).catch((e: ReadError) => (actualError = e)); + + assert.match(actualError, testReadError); + }); +});